From 756d0eb3152bb2d78fadd0f6feb32d24103dbfc5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BD=97=E7=A5=A5?= <1366971433@qq.com>
Date: Thu, 18 Apr 2019 16:51:38 +0800
Subject: [PATCH] =?UTF-8?q?strom=E9=9B=86=E6=88=90=E5=85=B6=E4=BB=96?=
=?UTF-8?q?=E6=A1=86=E6=9E=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 4 +
code/Storm/storm-hbase-integration/pom.xml | 88 +++++++++++-----
.../com/heibaiying/WordCountToHBaseApp.java | 20 ++--
.../com/heibaiying/component/CountBolt.java | 47 +++++++++
.../com/heibaiying/component/SplitBolt.java | 1 -
.../src/main/resources/assembly.xml | 25 -----
code/Storm/storm-hdfs-integration/pom.xml | 27 ++++-
...CountToHdfsApp.java => DataToHdfsApp.java} | 19 ++--
code/Storm/storm-kafka-integration/pom.xml | 94 ++++++++++++++++++
.../heibaiying/kafka/read/LogConsoleBolt.java | 40 ++++++++
.../kafka/read/ReadingFromKafkaApp.java | 61 ++++++++++++
.../kafka/write/DataSourceSpout.java | 52 ++++++++++
.../kafka/write/WritingToKafkaApp.java | 67 +++++++++++++
code/Storm/storm-redis-integration/pom.xml | 52 ++++++++--
.../com/heibaiying/WordCountToRedisApp.java | 4 -
.../src/main/resources/assembly.xml | 25 -----
pictures/storm-hbase-result.png | Bin 0 -> 32984 bytes
pictures/storm-hdfs-result.png | Bin 0 -> 38477 bytes
pictures/storm-jar-complie-error.png | Bin 0 -> 48468 bytes
pictures/storm-kafka-producer.png | Bin 0 -> 4896 bytes
pictures/storm-kafka-receiver.png | Bin 0 -> 53305 bytes
pictures/strom-kafka-consumer.png | Bin 0 -> 27873 bytes
22 files changed, 516 insertions(+), 110 deletions(-)
create mode 100644 code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/CountBolt.java
delete mode 100644 code/Storm/storm-hbase-integration/src/main/resources/assembly.xml
rename code/Storm/storm-hdfs-integration/src/main/java/com.heibaiying/{WordCountToHdfsApp.java => DataToHdfsApp.java} (83%)
create mode 100644 code/Storm/storm-kafka-integration/pom.xml
create mode 100644 code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/LogConsoleBolt.java
create mode 100644 code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/ReadingFromKafkaApp.java
create mode 100644 code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/DataSourceSpout.java
create mode 100644 code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/WritingToKafkaApp.java
delete mode 100644 code/Storm/storm-redis-integration/src/main/resources/assembly.xml
create mode 100644 pictures/storm-hbase-result.png
create mode 100644 pictures/storm-hdfs-result.png
create mode 100644 pictures/storm-jar-complie-error.png
create mode 100644 pictures/storm-kafka-producer.png
create mode 100644 pictures/storm-kafka-receiver.png
create mode 100644 pictures/strom-kafka-consumer.png
diff --git a/README.md b/README.md
index a9aef49..fc14aca 100644
--- a/README.md
+++ b/README.md
@@ -77,6 +77,10 @@ TODO
2. [Storm核心概念详解](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Storm核心概念详解.md)
3. [Storm单机版本环境搭建](https://github.com/heibaiying/BigData-Notes/blob/master/notes/installation/Storm%E5%8D%95%E6%9C%BA%E7%89%88%E6%9C%AC%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)
4. [Storm编程模型详解](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Storm编程模型详解.md)
+5. Storm整合Redis
+6. Storm整合HDFS/HBase
+7. Storm整合Kafka
+8. Storm Topology的两种打包方式
## 六、Flume
diff --git a/code/Storm/storm-hbase-integration/pom.xml b/code/Storm/storm-hbase-integration/pom.xml
index c0446a9..8a5c725 100644
--- a/code/Storm/storm-hbase-integration/pom.xml
+++ b/code/Storm/storm-hbase-integration/pom.xml
@@ -9,36 +9,9 @@
1.0
- UTF-8
1.2.2
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 8
- 8
-
-
-
- maven-assembly-plugin
-
-
- src/main/resources/assembly.xml
-
-
-
- com.heibaiying.wordcount.ClusterWordCountApp
-
-
-
-
-
-
-
@@ -54,4 +27,65 @@
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 8
+ 8
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+ true
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.sf
+ META-INF/*.DSA
+ META-INF/*.dsa
+ META-INF/*.RSA
+ META-INF/*.rsa
+ META-INF/*.EC
+ META-INF/*.ec
+ META-INF/MSFTSIG.SF
+ META-INF/MSFTSIG.RSA
+
+
+
+
+
+ org.apache.storm:storm-core
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/WordCountToHBaseApp.java b/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/WordCountToHBaseApp.java
index 3844b63..57c4aa9 100644
--- a/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/WordCountToHBaseApp.java
+++ b/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/WordCountToHBaseApp.java
@@ -1,5 +1,6 @@
package com.heibaiying;
+import com.heibaiying.component.CountBolt;
import com.heibaiying.component.DataSourceSpout;
import com.heibaiying.component.SplitBolt;
import org.apache.storm.Config;
@@ -18,9 +19,6 @@ import java.util.Map;
/**
* 进行词频统计 并将统计结果存储到HBase中
- *
- * 编译打包: mvn clean assembly:assembly -Dmaven.test.skip=true
- * hdfs://hadoop001:8020/hbase
*/
public class WordCountToHBaseApp {
@@ -45,11 +43,13 @@ public class WordCountToHBaseApp {
// 定义流数据与HBase中数据的映射
SimpleHBaseMapper mapper = new SimpleHBaseMapper()
.withRowKeyField("word")
- .withColumnFields(new Fields("word"))
- .withCounterFields(new Fields("count"))
- .withColumnFamily("cf");
+ .withColumnFields(new Fields("word","count"))
+ .withColumnFamily("info");
- // 给HBaseBolt传入表名、数据映射关系、和HBase的配置信息
+ /*
+ * 给HBaseBolt传入表名、数据映射关系、和HBase的配置信息
+ * 表需要预先创建: create 'WordCount','info'
+ */
HBaseBolt hbase = new HBaseBolt("WordCount", mapper)
.withConfigKey("hbase.conf");
@@ -58,12 +58,14 @@ public class WordCountToHBaseApp {
builder.setSpout(DATA_SOURCE_SPOUT, new DataSourceSpout(),1);
// split
builder.setBolt(SPLIT_BOLT, new SplitBolt(), 1).shuffleGrouping(DATA_SOURCE_SPOUT);
+ // count
+ builder.setBolt(COUNT_BOLT, new CountBolt(),1).shuffleGrouping(SPLIT_BOLT);
// save to HBase
- builder.setBolt(HBASE_BOLT, hbase, 1).fieldsGrouping(SPLIT_BOLT, new Fields("word"));
+ builder.setBolt(HBASE_BOLT, hbase, 1).shuffleGrouping(COUNT_BOLT);
// 如果外部传参cluster则代表线上环境启动,否则代表本地启动
- if (args.length > 1 && args[1].equals("cluster")) {
+ if (args.length > 0 && args[0].equals("cluster")) {
try {
StormSubmitter.submitTopology("ClusterWordCountToRedisApp", config, builder.createTopology());
} catch (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) {
diff --git a/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/CountBolt.java b/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/CountBolt.java
new file mode 100644
index 0000000..29974df
--- /dev/null
+++ b/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/CountBolt.java
@@ -0,0 +1,47 @@
+package com.heibaiying.component;
+
+import org.apache.storm.task.OutputCollector;
+import org.apache.storm.task.TopologyContext;
+import org.apache.storm.topology.OutputFieldsDeclarer;
+import org.apache.storm.topology.base.BaseRichBolt;
+import org.apache.storm.tuple.Fields;
+import org.apache.storm.tuple.Tuple;
+import org.apache.storm.tuple.Values;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 进行词频统计
+ */
+public class CountBolt extends BaseRichBolt {
+
+ private Map counts = new HashMap<>();
+
+ private OutputCollector collector;
+
+
+ @Override
+ public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
+ this.collector=collector;
+ }
+
+ @Override
+ public void execute(Tuple input) {
+ String word = input.getStringByField("word");
+ Integer count = counts.get(word);
+ if (count == null) {
+ count = 0;
+ }
+ count++;
+ counts.put(word, count);
+ // 输出
+ collector.emit(new Values(word, String.valueOf(count)));
+
+ }
+
+ @Override
+ public void declareOutputFields(OutputFieldsDeclarer declarer) {
+ declarer.declare(new Fields("word", "count"));
+ }
+}
diff --git a/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/SplitBolt.java b/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/SplitBolt.java
index b315f11..06bc1d8 100644
--- a/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/SplitBolt.java
+++ b/code/Storm/storm-hbase-integration/src/main/java/com/heibaiying/component/SplitBolt.java
@@ -6,7 +6,6 @@ import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
-import org.apache.storm.tuple.Values;
import java.util.Map;
diff --git a/code/Storm/storm-hbase-integration/src/main/resources/assembly.xml b/code/Storm/storm-hbase-integration/src/main/resources/assembly.xml
deleted file mode 100644
index dec0017..0000000
--- a/code/Storm/storm-hbase-integration/src/main/resources/assembly.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- with-dependencies
-
-
-
- jar
-
-
- false
-
-
- /
- true
- true
- runtime
-
-
- org.apache.storm:storm-core
-
-
-
-
\ No newline at end of file
diff --git a/code/Storm/storm-hdfs-integration/pom.xml b/code/Storm/storm-hdfs-integration/pom.xml
index 5d1ad12..f65e03c 100644
--- a/code/Storm/storm-hdfs-integration/pom.xml
+++ b/code/Storm/storm-hdfs-integration/pom.xml
@@ -43,10 +43,24 @@
*:*
- org.apache.storm:storm-core
+ META-INF/*.SF
+ META-INF/*.sf
+ META-INF/*.DSA
+ META-INF/*.dsa
+ META-INF/*.RSA
+ META-INF/*.rsa
+ META-INF/*.EC
+ META-INF/*.ec
+ META-INF/MSFTSIG.SF
+ META-INF/MSFTSIG.RSA
+
+
+ org.apache.storm:storm-core
+
+
@@ -82,6 +96,17 @@
storm-hdfs
${storm.version}
+
+ org.apache.hadoop
+ hadoop-common
+ 2.6.0-cdh5.15.2
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+
org.apache.hadoop
hadoop-client
diff --git a/code/Storm/storm-hdfs-integration/src/main/java/com.heibaiying/WordCountToHdfsApp.java b/code/Storm/storm-hdfs-integration/src/main/java/com.heibaiying/DataToHdfsApp.java
similarity index 83%
rename from code/Storm/storm-hdfs-integration/src/main/java/com.heibaiying/WordCountToHdfsApp.java
rename to code/Storm/storm-hdfs-integration/src/main/java/com.heibaiying/DataToHdfsApp.java
index ff9d139..cb37014 100644
--- a/code/Storm/storm-hdfs-integration/src/main/java/com.heibaiying/WordCountToHdfsApp.java
+++ b/code/Storm/storm-hdfs-integration/src/main/java/com.heibaiying/DataToHdfsApp.java
@@ -20,18 +20,19 @@ import org.apache.storm.hdfs.bolt.sync.SyncPolicy;
import org.apache.storm.topology.TopologyBuilder;
/**
- * 进行词频统计 并将统计结果存储到HDFS中
- *
- * hdfs://hadoopp001:8020 path
+ * 将样本数据存储到HDFS中
*/
-public class WordCountToHdfsApp {
+public class DataToHdfsApp {
private static final String DATA_SOURCE_SPOUT = "dataSourceSpout";
private static final String HDFS_BOLT = "hdfsBolt";
public static void main(String[] args) {
- // 定义存储文本的分隔符
+ // 指定Hadoop的用户名 如果不指定,则在HDFS创建目录时候有可能抛出无权限的异常(RemoteException: Permission denied)
+ System.setProperty("HADOOP_USER_NAME", "root");
+
+ // 定义输出字段(Field)之间的分隔符
RecordFormat format = new DelimitedRecordFormat()
.withFieldDelimiter("|");
@@ -41,7 +42,7 @@ public class WordCountToHdfsApp {
// 文件策略: 每个文件大小上限1M,超过限定时,创建新文件并继续写入
FileRotationPolicy rotationPolicy = new FileSizeRotationPolicy(1.0f, Units.MB);
- // 定义完整路径
+ // 定义存储路径
FileNameFormat fileNameFormat = new DefaultFileNameFormat()
.withPath("/storm-hdfs/");
@@ -57,20 +58,20 @@ public class WordCountToHdfsApp {
// 构建Topology
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout(DATA_SOURCE_SPOUT, new DataSourceSpout());
- // save to HBase
+ // save to HDFS
builder.setBolt(HDFS_BOLT, hdfsBolt, 1).shuffleGrouping(DATA_SOURCE_SPOUT);
// 如果外部传参cluster则代表线上环境启动,否则代表本地启动
if (args.length > 0 && args[0].equals("cluster")) {
try {
- StormSubmitter.submitTopology("ClusterWordCountToHdfsApp", new Config(), builder.createTopology());
+ StormSubmitter.submitTopology("ClusterDataToHdfsApp", new Config(), builder.createTopology());
} catch (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) {
e.printStackTrace();
}
} else {
LocalCluster cluster = new LocalCluster();
- cluster.submitTopology("LocalWordCountToHdfsApp",
+ cluster.submitTopology("LocalDataToHdfsApp",
new Config(), builder.createTopology());
}
}
diff --git a/code/Storm/storm-kafka-integration/pom.xml b/code/Storm/storm-kafka-integration/pom.xml
new file mode 100644
index 0000000..f1ffe16
--- /dev/null
+++ b/code/Storm/storm-kafka-integration/pom.xml
@@ -0,0 +1,94 @@
+
+
+ 4.0.0
+
+ com.heibaiying
+ storm-kafka-integration
+ 1.0
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 8
+ 8
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+ true
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.sf
+ META-INF/*.DSA
+ META-INF/*.dsa
+ META-INF/*.RSA
+ META-INF/*.rsa
+ META-INF/*.EC
+ META-INF/*.ec
+ META-INF/MSFTSIG.SF
+ META-INF/MSFTSIG.RSA
+
+
+
+
+
+ org.apache.storm:storm-core
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.2.2
+ 2.2.0
+
+
+
+
+ org.apache.storm
+ storm-core
+ ${storm.version}
+
+
+ org.apache.storm
+ storm-kafka-client
+ ${storm.version}
+
+
+ org.apache.kafka
+ kafka-clients
+ ${kafka.version}
+
+
+
+
\ No newline at end of file
diff --git a/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/LogConsoleBolt.java b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/LogConsoleBolt.java
new file mode 100644
index 0000000..16b65f2
--- /dev/null
+++ b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/LogConsoleBolt.java
@@ -0,0 +1,40 @@
+package com.heibaiying.kafka.read;
+
+import org.apache.storm.task.OutputCollector;
+import org.apache.storm.task.TopologyContext;
+import org.apache.storm.topology.OutputFieldsDeclarer;
+import org.apache.storm.topology.base.BaseRichBolt;
+import org.apache.storm.tuple.Tuple;
+
+import java.util.Map;
+
+/**
+ * 打印从Kafka中获取的数据
+ */
+public class LogConsoleBolt extends BaseRichBolt {
+
+
+ private OutputCollector collector;
+
+ public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
+ this.collector=collector;
+ }
+
+ public void execute(Tuple input) {
+ try {
+ String value = input.getStringByField("value");
+ System.out.println("received from kafka : "+ value);
+ // 必须ack,否则会重复消费kafka中的消息
+ collector.ack(input);
+ }catch (Exception e){
+ e.printStackTrace();
+ collector.fail(input);
+ }
+
+
+ }
+
+ public void declareOutputFields(OutputFieldsDeclarer declarer) {
+
+ }
+}
diff --git a/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/ReadingFromKafkaApp.java b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/ReadingFromKafkaApp.java
new file mode 100644
index 0000000..fdddd44
--- /dev/null
+++ b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/read/ReadingFromKafkaApp.java
@@ -0,0 +1,61 @@
+package com.heibaiying.kafka.read;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.storm.Config;
+import org.apache.storm.LocalCluster;
+import org.apache.storm.StormSubmitter;
+import org.apache.storm.generated.AlreadyAliveException;
+import org.apache.storm.generated.AuthorizationException;
+import org.apache.storm.generated.InvalidTopologyException;
+import org.apache.storm.kafka.spout.KafkaSpout;
+import org.apache.storm.kafka.spout.KafkaSpoutConfig;
+import org.apache.storm.kafka.spout.KafkaSpoutRetryExponentialBackoff;
+import org.apache.storm.kafka.spout.KafkaSpoutRetryExponentialBackoff.TimeInterval;
+import org.apache.storm.kafka.spout.KafkaSpoutRetryService;
+import org.apache.storm.topology.TopologyBuilder;
+
+/**
+ * 从Kafka中读取数据
+ */
+public class ReadingFromKafkaApp {
+
+ private static final String BOOTSTRAP_SERVERS = "hadoop001:9092";
+ private static final String TOPIC_NAME = "storm-topic";
+
+ public static void main(String[] args) {
+
+ final TopologyBuilder builder = new TopologyBuilder();
+ builder.setSpout("kafka_spout", new KafkaSpout<>(getKafkaSpoutConfig(BOOTSTRAP_SERVERS, TOPIC_NAME)), 1);
+ builder.setBolt("bolt", new LogConsoleBolt()).shuffleGrouping("kafka_spout");
+
+ // 如果外部传参cluster则代表线上环境启动,否则代表本地启动
+ if (args.length > 0 && args[0].equals("cluster")) {
+ try {
+ StormSubmitter.submitTopology("ClusterReadingFromKafkaApp", new Config(), builder.createTopology());
+ } catch (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) {
+ e.printStackTrace();
+ }
+ } else {
+ LocalCluster cluster = new LocalCluster();
+ cluster.submitTopology("LocalReadingFromKafkaApp",
+ new Config(), builder.createTopology());
+ }
+ }
+
+ private static KafkaSpoutConfig getKafkaSpoutConfig(String bootstrapServers, String topic) {
+ return KafkaSpoutConfig.builder(bootstrapServers, topic)
+ // 除了分组ID,以下配置都是可选的。分组ID必须指定,否则会抛出InvalidGroupIdException异常
+ .setProp(ConsumerConfig.GROUP_ID_CONFIG, "kafkaSpoutTestGroup")
+ // 定义重试策略
+ .setRetry(getRetryService())
+ // 定时提交偏移量的时间间隔,默认是15s
+ .setOffsetCommitPeriodMs(10_000)
+ .build();
+ }
+
+ // 定义重试策略
+ private static KafkaSpoutRetryService getRetryService() {
+ return new KafkaSpoutRetryExponentialBackoff(TimeInterval.microSeconds(500),
+ TimeInterval.milliSeconds(2), Integer.MAX_VALUE, TimeInterval.seconds(10));
+ }
+}
diff --git a/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/DataSourceSpout.java b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/DataSourceSpout.java
new file mode 100644
index 0000000..2769046
--- /dev/null
+++ b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/DataSourceSpout.java
@@ -0,0 +1,52 @@
+package com.heibaiying.kafka.write;
+
+import org.apache.storm.shade.org.apache.commons.lang.StringUtils;
+import org.apache.storm.spout.SpoutOutputCollector;
+import org.apache.storm.task.TopologyContext;
+import org.apache.storm.topology.OutputFieldsDeclarer;
+import org.apache.storm.topology.base.BaseRichSpout;
+import org.apache.storm.tuple.Fields;
+import org.apache.storm.tuple.Values;
+import org.apache.storm.utils.Utils;
+
+import java.util.*;
+
+/**
+ * 产生词频样本的数据源
+ */
+public class DataSourceSpout extends BaseRichSpout {
+
+ private List list = Arrays.asList("Spark", "Hadoop", "HBase", "Storm", "Flink", "Hive");
+
+ private SpoutOutputCollector spoutOutputCollector;
+
+ @Override
+ public void open(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector) {
+ this.spoutOutputCollector = spoutOutputCollector;
+ }
+
+ @Override
+ public void nextTuple() {
+ // 模拟产生数据
+ String lineData = productData();
+ spoutOutputCollector.emit(new Values("key",lineData));
+ Utils.sleep(1000);
+ }
+
+ @Override
+ public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {
+ outputFieldsDeclarer.declare( new Fields("key", "message"));
+ }
+
+
+ /**
+ * 模拟数据
+ */
+ private String productData() {
+ Collections.shuffle(list);
+ Random random = new Random();
+ int endIndex = random.nextInt(list.size()) % (list.size()) + 1;
+ return StringUtils.join(list.toArray(), "\t", 0, endIndex);
+ }
+
+}
\ No newline at end of file
diff --git a/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/WritingToKafkaApp.java b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/WritingToKafkaApp.java
new file mode 100644
index 0000000..281824b
--- /dev/null
+++ b/code/Storm/storm-kafka-integration/src/main/java/com/heibaiying/kafka/write/WritingToKafkaApp.java
@@ -0,0 +1,67 @@
+package com.heibaiying.kafka.write;
+
+import org.apache.storm.Config;
+import org.apache.storm.LocalCluster;
+import org.apache.storm.StormSubmitter;
+import org.apache.storm.generated.AlreadyAliveException;
+import org.apache.storm.generated.AuthorizationException;
+import org.apache.storm.generated.InvalidTopologyException;
+import org.apache.storm.kafka.bolt.KafkaBolt;
+import org.apache.storm.kafka.bolt.mapper.FieldNameBasedTupleToKafkaMapper;
+import org.apache.storm.kafka.bolt.selector.DefaultTopicSelector;
+import org.apache.storm.topology.TopologyBuilder;
+
+import java.util.Properties;
+
+/**
+ * 写入数据到Kafka的特定主题中
+ */
+public class WritingToKafkaApp {
+
+ private static final String BOOTSTRAP_SERVERS = "hadoop001:9092";
+ private static final String TOPIC_NAME = "storm-topic";
+
+ public static void main(String[] args) {
+
+
+ TopologyBuilder builder = new TopologyBuilder();
+
+ // 定义Kafka生产者属性
+ Properties props = new Properties();
+ /*
+ * 指定broker的地址清单,清单里不需要包含所有的broker地址,生产者会从给定的broker里查找其他broker的信息。
+ * 不过建议至少要提供两个broker的信息作为容错。
+ */
+ props.put("bootstrap.servers", BOOTSTRAP_SERVERS);
+ /*
+ * acks 参数指定了必须要有多少个分区副本收到消息,生产者才会认为消息写入是成功的。
+ * acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。
+ * acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
+ * acks=all : 只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
+ */
+ props.put("acks", "1");
+ props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+ props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
+
+ KafkaBolt bolt = new KafkaBolt()
+ .withProducerProperties(props)
+ .withTopicSelector(new DefaultTopicSelector(TOPIC_NAME))
+ .withTupleToKafkaMapper(new FieldNameBasedTupleToKafkaMapper<>());
+
+ builder.setSpout("sourceSpout", new DataSourceSpout(), 1);
+ builder.setBolt("kafkaBolt", bolt, 1).shuffleGrouping("sourceSpout");
+
+
+ if (args.length > 0 && args[0].equals("cluster")) {
+ try {
+ StormSubmitter.submitTopology("ClusterWritingToKafkaApp", new Config(), builder.createTopology());
+ } catch (AlreadyAliveException | InvalidTopologyException | AuthorizationException e) {
+ e.printStackTrace();
+ }
+ } else {
+ LocalCluster cluster = new LocalCluster();
+ cluster.submitTopology("LocalWritingToKafkaApp",
+ new Config(), builder.createTopology());
+ }
+ }
+}
diff --git a/code/Storm/storm-redis-integration/pom.xml b/code/Storm/storm-redis-integration/pom.xml
index 04c43b4..52ee5b8 100644
--- a/code/Storm/storm-redis-integration/pom.xml
+++ b/code/Storm/storm-redis-integration/pom.xml
@@ -23,18 +23,52 @@
8
+
- maven-assembly-plugin
+ org.apache.maven.plugins
+ maven-shade-plugin
-
- src/main/resources/assembly.xml
-
-
-
- com.heibaiying.wordcount.ClusterWordCountApp
-
-
+ true
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.sf
+ META-INF/*.DSA
+ META-INF/*.dsa
+ META-INF/*.RSA
+ META-INF/*.rsa
+ META-INF/*.EC
+ META-INF/*.ec
+ META-INF/MSFTSIG.SF
+ META-INF/MSFTSIG.RSA
+
+
+
+
+
+ org.apache.storm:storm-core
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+
+
diff --git a/code/Storm/storm-redis-integration/src/main/java/com/heibaiying/WordCountToRedisApp.java b/code/Storm/storm-redis-integration/src/main/java/com/heibaiying/WordCountToRedisApp.java
index c1591d5..f3b7553 100644
--- a/code/Storm/storm-redis-integration/src/main/java/com/heibaiying/WordCountToRedisApp.java
+++ b/code/Storm/storm-redis-integration/src/main/java/com/heibaiying/WordCountToRedisApp.java
@@ -17,10 +17,6 @@ import org.apache.storm.topology.TopologyBuilder;
/**
* 进行词频统计 并将统计结果存储到Redis中
- *
- * 编译打包: mvn clean assembly:assembly -Dmaven.test.skip=true
- * 提交Topology到集群: storm jar /usr/appjar/storm-redis-integration-1.0-with-dependencies.jar com.heibaiying.WordCountToRedisApp cluster
- * 停止Topology: storm kill ClusterWordCountApp -w 3
*/
public class WordCountToRedisApp {
diff --git a/code/Storm/storm-redis-integration/src/main/resources/assembly.xml b/code/Storm/storm-redis-integration/src/main/resources/assembly.xml
deleted file mode 100644
index dec0017..0000000
--- a/code/Storm/storm-redis-integration/src/main/resources/assembly.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- with-dependencies
-
-
-
- jar
-
-
- false
-
-
- /
- true
- true
- runtime
-
-
- org.apache.storm:storm-core
-
-
-
-
\ No newline at end of file
diff --git a/pictures/storm-hbase-result.png b/pictures/storm-hbase-result.png
new file mode 100644
index 0000000000000000000000000000000000000000..78e1d897170681689c6a9304908ef5d94712533b
GIT binary patch
literal 32984
zcmcG$bzGF~zP=5T0umB}G>CLaD;)yT-6;)9NyiKg(x8NNN=la?44opKf#4z?Yubb8x;Q0{oA__c5z?2~~gn0G*oN4}JyuS(xVHR4LFeo~_ruey^
z{v&W`dVAsLG1i))(8JEOKb{8#my#m&xGv|_H0u{N`bU^b-953x&|X=l)^dFoB1{${
zwvrl4nPe2V3hgf2&0U6OF!WU90k>_1#ma;+YbEQ!rS_o+&4W4`E=r<)fAW-SKw
zQTE3>R7uAeq%H+9&!I=hMJ7Zt>e^!xntwkm6umf(Xt9X9G&UmmY3|OJ
zp;Z%}`q!aOZjy4F`iEgj_h>ewh63hn%CKU1Y{RIfvVs(q`O=lz`<#j4d#p{$h6WA9+2`xXq{@w%~
z^;(-2wH1sc0Nwk({*kcXDAfd42f>Rsh8H0r0gBjfX?={HxciZ?q8>{e^Nl)~YeqVP
zfjKF9jDhmb;&xv$Pv90#0+G#g!yoVY@ZM!_TplvUWaQ^CqT`}sBcKH^Y&Y)IJidKj
zA3e^Cxkxfd8U7)U^KrX`kz4q&;}=VD%BQ&TuJ`RX$k*f!?;S;6K4*y3j@>tQb!G{v
zstB?>haImb;$u%Wc~SN#>-LXo!LDAOtG5RHAfVztP8BN8LgAVYpiif@-7m9H7(Pau
z8bj;bfgWE|2&zfvlQ6!sG7iWr)iq_nz;{ik9>_f`zoGno+!BcBKp!ZyxWg(Tz8nbu9+WW2FAiT;now$Wj0-eCgd0whYO3wMHD3XmtGG1&yG#}A>aHs`I_
z=H!?e49V^;&2SQ~1G*qvC#?fYrW~#U!sR%Zo1NW6D(n3p-A{DOhd1xyCX=i42FEAd
zq#tj_xS#GJre=ii?JCR;Am){^Bu4M*vyTq4*Mp(%zfNNmjCzc@bHzO>VY#hqWt5Ob
zQ;M4ho%AwN^}8IW(~nTj>~LR@piEWGot4s^{TrQIQ?7QMn}Atgxl`M!arG
zmz-wPey@Y0im5&lo@j%fZ|8(=U5@Tg;%5pd3OpQ9-OgFQ{vqU@8$~;#0djl8)O>r`
z*Iv!g(G)9J=OjJrEXJY)NSQIeLBdVtS%2T|
zkYu{67E8AF2Y2+92jkEJpQG9WG~m%C$=FsP2_j1N;%SK2>#C@S>X!^4$lGr%hz+CH
z%MU!<6cZ*!``K;}j_G$?2(5+q6|BiMqWwg*s`XKR7N6G!y_`3GuUCZbaeppY`GZ7q
zuD_R{n2Y~p$;uKh(&<&9F>zS1OGN}$k=d14+TIUY4@)}l=+KMG1o!7qqkBA(2}a?w
zTPj5{UD?Y0a&k|UbDoIYFIR_D`q8KA>w55uQX34qRk(e*`h|)?I8Z)X-#}=MQKLfbW%`;D->C-uD&w>LdE<
z6*jq1MmV!2&wX*l0JHierWTpV8TY|153jWXlVdRB;tC%n@%#^d(pZ1JffQhiEypKZN2UlaGB(dW0g
zPR^Ax_B!50K9il-+gQpoG1Ww7-Cf(dUwnk_F4g%s=)it~Ne*3JOdoOAWifEL=4u*_
z{G7Y=o$`IeLv^UGyhgIM`rFFnm0oWf^_%O>Zb(!ieN5j*CVSAns#*;&IjVm(#!c2Qq7
zE=k>oq=hAi#S(-cvJ)m;7JrKnZ2GWuNyhIhqT)7%*3cYMeK*YqAO2tO>Fw38+*}uu
zzZwIv?NSI!mYlJA!dI##wzp{C2A`juKHmB1JEE1a9~d(lI}Yk!9NlhI{ECg76`(IP
z@}eK)VpaJ0Z4C!Lj
z3TLH0=zM!bQD?TNwS-f|p+Uro~hDt-?
z;1MH4O>dHN;$tJ++j4e&(EGp+kc>Vs^>`afi1S;`$Q-KE}?87j$(Z>`}h!D8M=
zAqtF?mfq&OPO)HRB^jo0=y#&slzHV?(MCw5S9>GRw(S75#C^3x-BjP2($WCbi-a1iGMUX86
zQp}UDn;~v_eTQPlLi^RTpmCRGK^;^>{_#t?p_q=ekblg|?pC=*QkV;5hDdqAa12Vx0=iVHnN!Sv;8-0F~j!LYj^J;i*>dn
zKKU|pW&9#IykgOmibX(LT;@qKPp5LKNz7LV+IIgZVJyT*UpuI~c-VDPjQFw?5k7lG
z#Znp1_bAWo3mkmEDN>6bkBL|ZS+xoYw9i3*KDF1{?=%8+3|d+`4E|VLMI$uYs93x}
zgnYpO-?(63!3IcXtGMdRnmZxc%!X6D`%jy@=cY+!>!9zI_wE8DgREBw~fO!1iF+frsD;X~)C|iY-mAr=n#s!nd65
z3<)D?NGfIgW`11fMbQiXbj36d#R#3V-iezu?dXt
zVM+M7>0}33iN=bmS3g2neD=+|i2F)H8j?Hj)w>nu$n2@ZflDxfI|6T6sBh{KUrTDLVD|TNVPLrVf9T
z$3$2@%cANtJlMqTh=p#uNH8)tl6{YD5uMURydoTm)w8)XYE=%A`8vw{mf|vI&*7#H
zaPga~htQh^w5x+_f}3NJn?_d%S`(wF)rNK&vq11w|K(t}g37h_JDe|A=b_3EA3kD?
z;1jsegrckMiYi&~3$2kJFqc6*R_eglksBJ$(a(~^^mPtA*Uq?OYrYyP)nnyVq~i8!
z7PJu}_x_OICo3y9gS=;2XMd4lW?b@hNMMNK(Wv6MR^h$p&M`zL9@pPP7EjbZfrxtT
zQ!XUXsi(KDZgX{#{?0d!e|)X&)a;gg9;7`OVyO!{Rw44s%~twHDkDPUXp){OQWjA}4iYuJ4xe
z314GNdCD&IsuzfN=8-V_d)om`0LpF&=g9^lSUO}vUU<#c)7Hh?Ote!
zT3;55#~<8dEH;9_Rr?8BJCV^Z$^w42_zC8^8HqD3&9#SwN*SgwhKoFLVfC2(fFrVi
z^QDb`Tx+fLpkoE>1gtUZd9{u*df7tcX%_ejz{h8g+O4d1@1U^tq3c4ls-g}?>Z;j0
zBM^NVq=ve6t)KZ@1cKy>nyA!u!b4#7qEtkjbq6D=B4yVXmET*jx@+?jv}X{SxNSq)
z@3L74MtNVIF>;~vSVSDX(9e@lbr-frBwrQaN1H8Bx+x*n)2+x)yaAB&aAYnCM|I6v
zanEp2E+Lj6*Noed7gUAgk^wrvx+^88$e8MQ&3pgcjQw&hcH}5AmwhmNPjXHh
z1W~%=c#ZbbEp%tbX)Xri*nSmOSAYF}a`67IkBh440Ff$wnjey5y|zHNt%u@xHS?VV
zhq9`ouTf!l8)^g$sFzJUw+e{QFoI(Vjc4!J4UC`Ny;z{wU&jp->6ucBpKNZ2;tA9c
z*Exs?{QP{kB5@X>?8XzjV3)?ep4V{fC>W}1zt7yz>ylC>@5O5~KP_gWCK1b|(1;bxBNnXd9{@gq*!oJBoLTxN-;p(%%wf%U~ps
z&-_S%J7G^(1obhlJh{rg+GWnbSQo$mSOj4raZ|Gt+;|EJ+Rm39318N#fZ>YE;8`O0
ztI_)doK-k%%o^M>ubr?Ojoj-iAk-ad$qrgB_W^#Cbv9avFAAhlcvJu#;vex(JvPIy
z#Suq;N_-nb1$wO~=PM#pM-%+IQNL2|76sw?)!5>g)%a`oiX03$W_+Y3ad;rO5fr0V
zu5c;Llkr0^)7oeU<{M7xP54yCMtC;R&?SQ=Sz{@daD2KDOutNWICZHNwTn*OO;f-s
zBQ?)fB3gX-q4&*PON`$Lt~fYZ>9}QP1;V_
zy1*}BMx*EB8hk_xz=lj{K3}{2PpTVlHE{OF=1(coJ!C9DMu+v!OEHN7)@^)#2~WSo6N!T;z7z8<*ho)Y)ysvu;QkrSng(c*J}j9qAtk<&h&7`)
z59DUTT^nOSQi1aN7C<(-7kPa0NAFO*Q?7Qe?j-My@fL#RF2aoiCy`z+y81B=g-`w`+l*Ac^vu19Kt@E8
z4jzeu#zJ9QX*MOnW?P36m^;%XJ22iskH#=|*a0m_SRlabgh)iB;7m1|a1!TiMdR$U
z?9KeC{kQq_v{s*ERv4kdej6xya9?QW+qe1VYg7otGlboq&FM#cS9=pqXrzKpv+#q3
z!CeIPLgSr7~8`G-O_l7j-z#xARLcbkJ{K^JztD!p5YJ0@b=rjJGM>eQF@3d*wa!
ztxR9OQCMnVU_~Y{Mvw+mN{PL~A?a<9#BFdFiPY!NvH@&CMY;{7ybUjv5)RD+Hv;;f
zg7`q81YdE^w!0bkHa5cyo!R%H8)T5KQ}7ug#9O2jd_8zmI=3rkkAa)~Y{Zmt)Rih7
zf{Ls2RHBOX;mcP+MQmX3NI>%w)&AxiM-_FL?RrRJEErE#BH&TbzJ)Y&gVcBtB!xu&;N*e8^obNeS%UbActW@38p*Ot4airiw%(>6~BKy2tT)foE8SelK3Q
zYt#wT=y5XQqYoTe(OW#q+?C`Fq_Wmg;0zc)mJ99TJc3qn?{Iv+xawPP&{$Pp1*_3Dc>L95}}bZ
zzNqw7B_Wk?e}VqWPwOnV4`Yty<@rO_&?*Y4W`Ea9@26ICTCnIZSF-y%3;BsNUDU6w
zPn?~T6}V9lH(Ekwn`YWlN>Od6f--bB^p^$cQ@qd9zknu!k#pF~__^-j#w#I`94py`
z`hvmyC=N^4C;+!D7Paxd&!KCnNgF3+|31!}usNZ2gTY`eqLc%2@|Ny-lW!O_)+lm?
zl{nqnf;UV~c{AiDp8j~jxKM!izBC>{977X}Wi-;nahe<)16I)CT_0Kx{s@o{k*_B}
zXze&>t4CxRo3XSx;c{QD6g$DN{Zq4OYncQ!S}KwfCK`)63i!cCDLN
z0Y?-F=hKOtrPLFXb6|TV!=dXWyAZLN5!*xWHN6}4{v&Wqc^C%LZ*(18_{@Y@3*a_N
zI8P>Q2CEl<1y0L4xASXZ@8HV-V&Sy7O*WSU74`$$Rd3coycE~xq7*;V=``0_fgsV@
z)~!Ljr#lSDAod8cXqj8`Chw?Q@39TKb{f>Xhr>Mee5n(Ag&W~E~+Hh
zp91ZB;H$P1F`%jofG3}VXoBPeKMV22dT4{T18~I^Q4_cRY~+;IrXGm*Flr0&8uLjL
zb}x>mpmA^a&rv!!29(jDy%-2d87gKgyw~fX_AMBl+FURXmWV6v8U&;@GCb*iZxSE4
z`m*mp7jJ6!G68%-TO~Py0DYkx4`w0IOJ#f*Ey>e6nKJics@Dv9vjjZZVFy1YLqzb^
zQ7ltVvWqrme^v&BJ(}N#S+hLy4M$?PjhS8_HW@nrT$}qT>+i9cKieH%vbUBm3O1Ba
zL>>JZnz(nZRM6rPE;Cz~t4`Jv4dT%SzLJiVb0x=$LrT;Dw%~)Y@QhP-@5th8n`}8F
zdP8Y!?UG5fr#y}vjx+Kgoc0z(pe9_Tm7{C#R7aGuA%<3wTIF*S{k6^`Sdag1eh$?V
zHeZ)BK(E8$?-rCge;dPYbhpbLeV8(Eg}ETY6to$MjRn`A5`y5Ss~01x
zT)8P+R&f@27A6MTGhJki)$y0sessCdHn-{9`^s%Ene@cqWXZkEe><<07$poqX!5527j1uQ#ZB9
zT~d2=lU{O~g00P7^px*y(cQ=%u*Mj@T6oxpV1~71yYY}i(*xv5IB?;Sj~pTa
zuMr>A?E0~rx)VNj7%%L$k!gsb)O8sA=5Si)@$I{Y_kJSf{vi;TRMnlGi*+V?mehk<
z*vs0W;4~x%_zmjm7S>L&{eIb6*n<-<@2FizH&@itZN9VXaRG0SH?fDM3fSy7-sf~g
zrG5eN@hoY{uGtzM&C`BV=*slxNaNWTDzi6<-Xcu7WoMV_>B6E?r-E@ey5|~2kblAy
zWT7X~yi{$iWY$4~pN9CT+Pa(Syn}&6<7JL!o)F))`Fc1zSC_Jd(AYRjLQ*;T6Ai6L
z)r5GYC5`sKU4W=210YMtK2fUy{hY+XuSE*|q}?0O+XW89RSnAIhHo>7T3-s)OHm;r
za@*2=tEuK-W4D?(09pCfXd{|op$rwIpV-$y1kI6hV
zu{(GnA4`C{2qN*P%EvYr?ynUJNjudGyg;XG~u6;rOiOho56ry7*tBjVa~^LE2N
z{_+v)6@}gma7yLbaL{S^%%&VdUXSQ!>A3ic6{
zeT7~M&SQ7I^P=~;?Ss|e&7=tRqs0FdLkEp!A^>F+IS8J6eT*_}jhhVUvSsao
zAy1NON}|#Fd^S_7OT)q}05YXak`&GBr`RSE{fc0f3vO*wl&bWo0KzQmc09uBys5TV
zWjhOVjW9yx!!v8O=KEM~VTb1mF>MaCTu7cbUe>U
zWx56rpXR*Bfaa?n^zSYo*YQ3g_VMWafahD_jnPv8Rh2-#x?J*x^pDWT2
zD6rT;g9f=zm)2J3*QEn7(2L9IMv&}lhHNAOo%Ze4FKMkxw@2AGKLXXex)S;+h{sQ&
zMMUCIb25^%-eGA=4L|k*Qa{tAnNW7?1);zg&bC%%w^xs%>>drYRGv%jYpGm@%~+na
zsC5M|Ji2JIe}rT78tN72KJ^v-c=xn#@qZM>Fs$FmK`1pwwDx?8Bthrf2H?p<;yJ4ii_26&q=&aNZ1tea$JqDciedP&Dd2
zExeEY_>ke==k0voDNIMJWXwYnX5k;KTk0?(H58br0TV!W(eK>H4O}eVw`~<9rFE}x
z1^M3{8$)Wl_#}DUh$H{+IK{1OU#-x)o$gTiWxli;7NH0aj5aXF=^L)yV?h2nDEa4P
zhGFTI(!rBfIGBViD&5*eG19(czH3TDOU5ZN3b!m{d|Un8-^sI02ihS))n7L{^!{pl
zf#T?$0~LECJPH{%MJy7?ev0znrZKTtbs)m8d~`;7J*OuF_okzs!>`ikcLGy+o4|B>Uj0g7zF#%Vag^{!v$2@6)^glj_T|f7
zs7fvoPR^sFAi#H?(qexwOFW!ey0-R!c3SX_U=s<56zzO9sN&RdQ7)cut|-MNB#Rle)J#|y@i6nq
zZMKc@{#4HTG&@N0OuVS-2Z*x-x3RXYlEf8gh8#?E?97DTvT_n(F}i>oa2!})IU_#T
zpIK-gq?($Q@N^8|^NW0t$or#XQgF)MF;?F<5eG53)yo@%?R~;#i?fbmID^wG@M9{G
zj+Gm7DZMB_c1&&zxpyA<~P
zEq39vO8y7U{DWY$uI1;?qHvw4{70xr{2H~9!;j}5`XM6^l|=GSja1j7#4SAb7$~9CSusk6Rqje0AEh;bG(RhiwC#S%
zOQR8fe=MtibnO;-mcoV-T%(=vhDL;uV^+q(!(1id#$Z`3q4_L%?9Ji#5sKHe#lo-O
z4H{fd`g|AZcEiHLil(|{7wVpw&5DL@V>r8H0_-C2nE0dR-kz>Bn(6U2Ri;i}mLqk-Vyuq@)iMzd&I
z(gPAj37zQx4HejDGiXMTg*j!T%G<_sKLThRtm)`dna&3VnjyQKIQZlEjSx7_&yg>0
zjO>~4xlr{Cdm2e+;L4~{I(4g6FJll_DOidoqqt;sSJwJMZX|K_xqG%9gV1F)4iF@|
zhOC8Q^Hv#V!M0~1M0FV#mrA$iHMJb^VU264+pBZ{wkS9%`ZRmXf4q@UM6E_ogp0^4ft7G&WlFO>>rRp
z_S`xYr6<0hw!UpzMs>fQd?V`fz@xopKl>x!)r4ob(LT8$8e;M;bfGH5e($hud~)x{
zfKTZD{kg`cRAGB+wxY$oB9v7*FKMuIE-zcCmv`5RpK9wSk5KsMtay%=qW4HFrxEJB
zht0y8+D^0hedvG}i$CdW4)>O_@Y@HAL);I3a%p;h(VHr~V*QA5<=3E2giTIKzhBh2
z@s%M62y_IZ`zhWJi^sZN5(eW1EHBFl-W66)MRM_6!k-8lwIgX3XF)
z6SL}@pEE7!LWAy+d)rI(&c^|M(ybsxO}`*&=*pa_!_%NoA4nE9Zi-YSQ1ow@=EZB7
z;1nlbNp!{~#$yPeC@|+n=!LT|ZS%=Xu^UI#0sy=3#<%wg_W|*<<1~0D+JL;IGJ`
zdo-(8#mJ)09)EbM%MZ{j;e)ef!o6-K%0kW}osgPxvWxS}gV52smnh%QO4*{vdu!j5
z^BZ!`vrR1VjWUvGI;Giu$`D}umBB>ROtrxaI;M3wJaVz6hh%fPjBy#cI)%>`+eR1w
z!f;Ks`$cQ>>k5F#W)cGyE)r2`w?#%2502k5-cOIRIPfAy#hxFudpE9dD?rooMC?xA
zt5$oM1vspoSVdH#%F?24VMl%2mGFnEJj!DW{En7tN|AGg%
zT1RVfgaiQHEwok6_B^|lFxMa7O$o>kG{)u{v7PDYxIL-S@aLH
zf)ln(oycDg?!)c8o=>LMEWmC!);NOjEC+il8EVg}Pt2?_^2zX~ub|rh6HprGL6kx1
zQHtGxtE@MaPkpD(tn?75ml4K3#@K8V8J}iPzaZptexl$~|I1nC$_@MC2?6ZF90ri@
z2mTKJ@qolmB+iCaUeC|_fr3WdwqaTP?NlEYq=Hd$3VgN)|4XGrodXSGB>3e2i`MLx1^;?Ys7V5hKvsAlrEa2aeEBGJnfo
zKs%;k8~;^rsraq8G*|9w>0E?0i^Ol(qjc~li_u=|<|M1&`u)UU*nu8R7WEyNP!o`-*3vTE145MW
z=jGZsK#A*dYNg3!h*=UuN<8AXNJr~{xoc_c{*5~4`hBNm4P`I-Y!IQ<=Bed%^RRHv
zZ*D27xdwS2;sV+0IF#MapRZM7?||m`dmcGArJcW$)(!;SP#C#YD{*tM39Grt`NnVA
z=tVQ(a=uP0P4U$R24OloiCuYl_0`2Y4G|_EN+Lg7E5G;5ORR_B>Y?gZDc
zGvx_p$KkUUo@$1<|2T3fKH2ri3cFGjV-7Vn1^^6GPg_Qft6#_KDL0bcOhpTaIpRPP
zwSfuL9f2W|e))r{+~qUw5YP=0tf{yxJG^Xv)n`W}>`KW1geGYt8W_66y?Xf%hlRCn
z&s_EBx~OA+dH2IJPZT*roQGvKRG0Yr_S$&l5zbsQY8zsI4_tPf-h39AUT2rhK(#F~
zo)F0F29E~QGFi!01Wg;xwKuw5GiO#`t7plc-$`D1$)K1yVQ=v`(0h5T+Dt2eXRD2}
za2li74TqA(sg7Wa6bB&>INsH4tc~(>O6j5n4*#fzE->aa((A93qTF!6KH}=QRJqYu
zhyPRs+4Y_l-#=e*JGjszy&uv1W$`PzsJbwU*_b_}{-{SPEwt}1F$=<^YnglFt5`^giIM
zBM8kEli>hsy&myu7yg0w69J$O_ct*^A+GZULY3xvA4Ffk5BDRkIb3<_v#=YQ7G_p`
z(|+Hp_uk$`*{bga+d;7H(sKp-vrorFkVSzn*mt<0kakNP==`dEaoNJ05{##p?*Q~c
z+kb<1a77sGp|}h;Z8%O#dIW*|MvNsx2IR^Dk9xA3!4)lVN*XX#H~_{mu9++cca^q+
z__+sy&Vgorm&la~9A0<_oXHkHEz#Zm;>$+qGcH*ZSM{&omJU9Mw{BDpl!KW2cW=zL
z&!5AK1;P7q-^rsQD*}t*-
zn*9i}IC$fZ=>~9#+1q?RYPkC=NS&^F1JG4Sc5;w9=X~kltRrd!?tzNquZBp{oEv|@
zbCxJoI<=XQd8*6pjR%WjZQfSPKCpCGP(VH?_Lj$O>f2t=YTXScTpbRoab1@FhvE_y
zvrzE}5B{7e5cBnJ)gD14!KS;wOcvI`Am1^)je8`OXb->m&%YrF9_3Re61I;R9IEYS
z>2sn2d979x1O{I-G43G$E0gx=&nLa`lBrOMI=*@gYyD1Xjd0_~R#}Uu2E>t(XeDz-
z+(?Z?lmmMt8kRD5JzQmmFuq&rTrzt)_hL2v9^6XrAR@sGe&1lER|^Hy+wUI{jsEws
ziaGVah*j)#ZW_@3&{`t;dvu3o5+;g|zb3?k--AC<-KaL$vFK~B_x&z}4yV11{(CU_
z6oOIm|4vn8iM}qLRPn*z1Z?@&7=;sdh_39(P3li19*nAol-%YKPXmg2icj^_oS%d(
zq9whRh)@&q))A!2@v_TFBvtA;d|vgDIE)MHKIR=x7P9Btw&0(W3I+J0JL#s6(24mu
z-@9I6lU!?#flz*plF82(jRJzL^AnM3Kweqcu|G_$D?VFf7g<~dELuzZ{lkDT_G4|w
zYhNS7Ajr)#G|4HL3&ot}|Fmv(?!=)3*iWwG1Y!B3i118c!qfDNYJ
zuWY`-gI!^Zx}1ks`kz}_Gj2!*>{P;B*k5AKv>?tKWAQb(q{xb{jT!|
zV>MT{w^M8{H??tGi<`(YwPcK@PYYQ95=@jmuDbKmExB>|n0Wt*s-E9kL?->uVN^-}
zB-&57xr7&}l4td&34?oWvPQ)tF)6Uhqs~MUQ-&&vXEhnLHf^pQ>IShuyT*Uw3fDJg
zl*o^7d-f;jvH^hT{t}O6BES||^@mV$eAwh2+Xd8M=S+71LfU{5DE&>3m5BMi&bBn?
z9XAt{nTu;3h<)`S!|xKv<~8L1pDd;D#t2?XsoIg3CN!%}4lFasaER|b*3q@G8X2{{tEJbQ&D$AE
z0;s6L(zvzdF&{0`t4=(vP-m-`{7h_uSxiw~f_#<`Gr7}aEM-}@H;2eBu5r#zOPj;k
zn!7SiKi}A48`+5k1ShN^$&$v61=C1*`~W17?5h*e-?)PKTerEqF~8pr=TrNsV&QvO?kr3@z&1yP(U
zPt^U{vw_FS{s=Df5<7;k7ZmE2FV)}vPt+BUxX8aLxYwKX@bJ1_Eho`{^6>1C)|%s@
zm}|C{HrtEvwPYDFD@ka{q_;5s6*LnY>yk6U>HcZehIwaeb>xXlyIr?mSm
zAMiGyI0>EqGn2*qdh`B3pRjAreYZm-#^zsxcLkCoSlW@of+}BfX^N>|Hg&gMQ21
zi_hsIy)E@Asy5i<8CMj#+lk*IUbY>q(+|cNS1-SBBbgMTjUCjOICRV7zNTC6Kco><
z(V`bBH~PcREnPF_+9ce7e!I4Hye!PWS%#;V+BT{(5@nu!9LXM}fM2VZhR7|z^mq{$
zJ~(4d*%Kx$5=JS$kG8gMxZ>W@YWAazpD#UTHT<8%mdI`QwmZx`VA-r_9>|nRzoCstL_3Wi$is){R|%rkS-`fI!-ia*La_Hv1cslzZm5_yEm5rc!G9c0npa^@QThfMCsni$|ZdTX~y{`%PUcSeGN8eW`LW#LFS)_w>;ouK*H3`Mta{`*!3}Ic*$Gx$agNW{0D?`AMm;31Fewt
zl7ja@K;(l5y(sSIxS1qhx>r|QEO#qUVhrc=u4BF|Mf!ZGfN)oinU#`FOTcvkDhM#u
z@*c5=_~*%}fa=OQ5Cm5O<3RaqmsEL&%?>8kBQu9`BlOm`2YeIDMuyp<
zU`D@dqae7r6y0*nkK}s!!?Pnv+OeTHANkT_Wxv~*$N!qD7(IhA7J<#xbz5r(&x+(h
z6h~3%9j#m@0)NIS`7z1lK&<7mX!*R&Rgz)bEbw;HCD91^R$t){U;=y~iHOfz0WrEm
zC5*v~Kw%tA3YS=};SvkvZxT!D2EN&m9OBVCvZfFka7&wnjLTU!&(c7rvtf=QN(q
z&m}O|LE7`Oc%!@vJ)j0+)xYnt_HSz-uYOB+_?W$o#>|^hBcT1@G{rKXkT4i{`66gv
zDd?AWTMg2DZ{(kwMTZ1x0Q&XZKvsg0v9C^?-}T9X{F-UoN(ur@&luZ(%}gG?Pla>A
z;37nAhLe3VJ}SpTZ1J+S0CF9Kq`Lb9?ds_c=b~ld3E+2Z#l-0Tucm^?!^kCI=LcR7
z3C5$DplE+Owtf5Ig>ISb3W7s?%UXuu!#?|)3)1o8H3}DwfcnZu2SfYTG`1g_(j7=-
zO`lT7eNql$MvrUy01UTk^@12RMvUTwdt0ze{5nAxV5>+w+Yqqp4f7@xw={-8O7}2y5>Ul^CV+W)Y8p2=B^{v7vF%;@
z_Pfq$7I%xMXS;c)wsJZD*E*;sh|d5%#t+Wj5nj+${0^`0G19VsP6TQ5mNCGXY`}m?yZ64rg#RsQm$bLtVM%
z(k;kGeG*4He{3{}cxq`Ny8#CM&4Z&7B(INYUIn2-P%dZ_K!|jVsGH
zT{gD9Tl8Hp@$xDR8gd5%g7b$S+s67Tr8{{!pZ(~fNb=Z|@BA6Onpv*Rxbu>|Bg>lY
zt7XZVdtJHOxt=`XS-==Y)4vKwkZm>)O|CVwFVZTy%YP$<_|93r4REoJn>jac4F(aV
z?qeTan8~$B1CaeICEw2f15U~Em{mEEgs}rl|Dm+MSxZ{F>3hdo`tu}XI4el(=d02C
z0z*Nx0MZ}PuQ`@0^gMn=2~;E;x0k26dpg3Sl!h)&!T9UOdNaUsz3pgvUKGsrmzvcd
z3-nV3rbhfn)``iDQjaU#IN44Cauh3|1X5oj;kW;5ITL+V_40!qyX&!
zZp2nyoC6AM$yLtPgX2HqkL2o;tGJ^VGs)g+=IRxohZ?L7O60wwMkh}iBga*MOCR8U
zD?sA9=;d-*dn;7{zX*`NbbtG=%jj%s*KPp2av6>;j4Ks1*MVJjawx$#gp)~!14j_F
z2;VFiZ~SAPxQfO^fabmHEXs@9QIzveI42MXah3Ro0YyxboDF&*3IG!f-#BDg(0j@q
zB)65w$#Sm;%2Jf+eP@NbWpFthmen(;a-fyN;WIbzkz@?~`i&z~o=2wX|EKorpEAj3
zbT9}lv*GHwPOEoyHGxYun|f7n%ysLCcwP_Th-U1RKku*E<03_y4x_1?kXx6eY{7=%
zC2!~_Q=zF(Cr-YG)IPe6C{s;qAb1sJK&qWMKDgtdE;8AduXn3t`Cn&H=uP)mt#Dj%6Fa$f$+#IfQwDK~S3J<}M(hrtF|A8xB|
zGz-iiZl1%`uTomwKmsi*P?=Pn6^gwTNutT6OS3FSnO905xFt5$?CJS7s9m0g?6?l{
z=N!)!%Q#Ho`wP_SUZ>^=Wgmy9-Md{lvHs_XkZXPvLmz|T@EtmsUfR;+84O2cK5;ky
ztnB6-T5~D1rhwR%OiTT*XL;;O8yWk44fAMR{AH5omNHbG4m0yNv^*}_&b}N>LqJl;
zj~k@PzfzU5cmNZV$vrbxW%T;NBif3@=FI8~Hoj)|AKjxSH7bo{PWi&U@h8Q@8$~P_45+3RX&j}UREPcR--74DQX~h
zBvG#dY6QCKU(9c(C{8+#;$L*0KD_7Pd!@BVHK^p>Oq+f9Y#~$XMIE+JjsV&f8uI1?
zN$aXSzfjkC5h~w^RKuL(w!tlB0~)uJxzjV0vOSeFiUyCkBObjNOQDE6gxe=82U)M7
zN8rWPGS#$Ah<)bht8B&>L#T-FcLs=Z);G#S43IyRe$x|iNX*@xK3r`@Px3K0=b%N{
zKWB`6cOxl*>K`&X3n^Zte;BhejxntKwh@8y)uZXS)s%o`$bG_8V}
zJgbZYl`C(gu|BYXkx_sDU}0$n{4MbzD-dr;rGGUOk7%AGqLsALg5rC;!-7x}6^wY1
z)MdW|T$JeZ($W9d+j)mI)veneL7FrX0R`y@2vP(EL3$NMic|?zr6~}4PXLi72nbS?
zD!m9OEl7)i6zQQ#4ZTPww1kA*75w(zXYX^)KKI_|K6n1b1A#17)_UhyzcJ=J2UNqY
zJa1DIGq$c(1lU9+k08V}yO16fcdjf3fNefWxNy-izeF9Y1!{<|cGiZ5kKce>YpMUF
z*~QQ$2OWyA(~LDok!;QtjIv@Q%6Da`5!gdEd0*@$g#bf1-8N1(D9r=Wm`PF*%{fF~
zWr1O?lwsl96SIKRUE{$)NO+jLlkztxnmx<#jJu)@rzd32h?cvwsfWRlHPWS;uGnln
z%aOattt16=t!3A1UUwDN1oHHT%skRG+dAMO3DQk%25MphNr|D)!ujnC#pyDT7
zBi1_M-wR=B2H#e)Tq_sOudAx6o+sLQYwKajcDX;6MXPEFCsmNRVL#UtY>PRvzgnv(
zRt(n4}FDN#cy^}Gdcu$`nu?e(UQA*&wKS8moZ9PWIEY_jY+01z%`F78uNdCC5
z3z)^l&lT%dW62L$APF8LpVHUP!D?Mz$vnpP#P6`%{=T^JK}U76(3p#nrqgB4VE>(=n$R5QdN5dQmf86`@Im^5+_>u#we!-)36$
zYSs#|<(CboEoxMMAcH1rm|+&Y!XjS#<~|ioO$|`TX_AGB$fgtO)?V=a@|pRlf##@}
z-}vFZ9+
za+M1WBhiJhpJ3X9A0~Dut|j_g(2_lkG*r{tBBtv##{#+7ZsI|mDazrXSs8-{MWMhHRGzz+3sCj=wUPg
z!-)7DtVhID4rx~6KNkWEm%;O+{Dn;U;a^#-IEBGbRRe8U!Idrv>Om&z#VV`ld8RkL
zd;S!tNxmw|EDzCJ@x0V#q%ZD`hrfafH9_yQkOjY*dvv*ZL`lvldH>ShBdVD;(k=XL
z(O>mpNbITT@!UM|OdBh9r*KY!r>GVh4u`)vJzcnVjP9oLVP@X3fP@CN!+qugqI7mO
zrYz0A`=hQyp$D{?jg5l8#_|^m0$nkA!e%7QK~YMBU0MHA7DAC1$UxxR$mTriin8C`
z(A8<}836fwx*-l9+jN=x@JV38Lc7*kRJs4M^hrz&c+#TQ6BzFFW-MkaTnc5VguCZZ>+86-K7VI1Y3
z0~wnFs9XEX$6)?!P>Yv1p1i=){nB^PI>^+z-P=v7Au8q?i?qPKJq?@FVHN+c6mt4>
zYSt`ZU#wjh+;fzn?y6sun*I6;MeLy8buCr+k(OKxjN+01SJ{3COyt1+{O#_HsNx-u
z)b16!{!yvH`ze~W%oD!-^fqfkJp{INIqFC+cH5br*@wU9O@%nvYw>
zB@YpQH9Xe5=8um(9xHpsXpUoo&)DG-a`E5#k@S&d&pTJOqqaDhWG2oBPXGYu8CBoR
zQNd6i9DH@P)Wbo}x_0K(B`ueF#GBQp&u`Wbv*=}xSI-#8N9Vt`9js#MakUgO`@WxF
znFO+OCOLx8{@Q&91^V3lYmnwaGX%`ztr*~Ci_ezjETj}@*21%Kl
zj>~i$>fl$3Z$Y|^tX9j*X_nmCPw(}qeDrR8r-%I+M(h7eEpF`%NV@iW7!lAOv^SW_
zrc+uo@&$_H;`_w@;vI>52FV@ikRv&0vrvEmJbbIzF5}d0MS-)^tNKxrZ9efFD_8f<`0Ytqdrf!E0x1Iw=4BVdzn>j
z-8oFrVJY(&^3^Rju98hZq}Us>=f2e~k_Pi4#fCh4qe&ijN&=pkaT-G8UP{E8>XpCNF8Yd?+EILLPoem8*Mh@8Y&
z-MfcwIr4WTdsy8w3AR=BRlX!1RL5N267w2tk(Gx3r_{1--R^X^cTCk1kDWC3$inx(
z(_oirUR2p(I0PZvD~XJj(`C1W+u0|PZ%I4e`Yq;K&aLrn@SaoK-|m`DY6l}v^moyd
z_wZ#LnPRuLYz6Oh(CXl4!UXNeB_61*6o5tGyJisi`+Kkt`Tf0Jhe9n0>)IG5Q$=t|
zTmn<=+vARva_7)O`^$5YHJCw?lSI01S1#}H)h?0Igc_sMNi+sz0To2qy~T3%jJyOn
zpK@^v>bdLkkifL57F_q19WO94*b8~a#B}d1z&L41yoZB>)Who0`CZA~4DU~{=w_NbNl8xTH7qEAH9eP|0<(R<@;uY7{
zS+9IEsX5P9=Ue7-((OxKV189M4DEh3>5B{tnI`7P+6ph_Dr>D;!1j|fx68@hG-3Xj
zZT{523tz%@HT3R_k5MraMaRe9F5(@qlwu09i3)K*%JROEnjUG676+8)xQ!cBGLtSF
zTiT8W)wH{J3PoM;y-mrc-sN<$yy}hrZ1~NQ}X|zOsssQM*pJ
zi?`f!Ckr^LNL?*_E!;m`OdoNtYhowO+>W5wsCcN#7?4BxQ&GmJTmP`X+@g8%8IHM~
zV`m-S7z}HpsH<9JeRO{%<~%}TFRiQ`kfM*_p$4m88hcV@`?Xinxw-vL%XSb#z$5aL
ztG#)XI`yP0f216c=av@MhCUpB*BVL*{>t8TRmXZ_ZClnKGGlyhNIM1*(ukNcq!;n{
z_2I7Hp(#u0YjBxmM$^mr8&)6>iG
zHzA_8QbXp^k3u4A8sN)XT<%paNJR;pe-*VEXUa6!2RDO%(r~D;|3Sk!{udf90lRp1
z6y+A72Ddp|L!a5${x}rAlJ4{L#b3HD3(#$0fNm4`quca99!WpmmwXT2iTKSh@UT16
zL@0AznA0XTrVIZ-uNT^SC6MR&q{UR-yQj5j~B|m0Mf?5jB)#w)`
zs!T3g#uo4nyNqa#D%mIZL0`$f3XRec4c3`if~fd~Cigw7ixo{>yVlvo50n2KV%vAo56h1bU<-4ns$gm9;%C=BelMNq?ZxV}PKri6nd@T$eAD
z!azX@4Z%YM)wcSVYI7Uo&-%M+i}??#jdroh1yF6#|9h%U;s3pAGfs*P=fuyU5Avd|
zpmbSK46`|hr$5DY2kSxgpyJ^O&?8MCJ48DBG_ET9xoDpec|5OD>iN*r8~5;aa&2PL
zP)*Q1D4UeZ)k>woS7RYVFGM*8Uj8WW!xabyGEF02EcLT2ZdK)WukW@pWS1&?X^0Ij
zBMMu@HENXO2@B16zN^&8GuU8`Lk-mK&R?(EoZ_*$dlQ1Vyvzy{4OD&-f->T7V9_&3~%rJ(AUiI@`65ZY&
zKd7Ah054dAB(G~U@9j=@d|5LqKgIoaI#xTr($vs}-JGCIjRm*H8jr#+SjvaZt&FWt
zHqt7TS3{e#C+Av5Ri4LQ<-Xmxn6jQ1ICt=M3q9$L*z8&_7i-)%aYt#)pv$A->=RLH
zs~`LXE}&c*kJIl3jy};@1rC|%Q)woT?1S}0F?!sL#KGMe?%SJfd$I*<{K`X7LpS|J
zid%-9L^4HXLCE_&)_ijo_A5yt@%xOnt2U<=W=V$vtiEW6)POJR2L~6IGW)&FYF(sJ
zj{|jikje$)TF9VB#H6i>QdT8kMX6Q3hK=tRTAh%y-G38vPu~&5obrDq=1!!E^GMlb
zOx=hIohT4tf*l0Uf(-LPGMN@BA$bfR{GM
zV3LAo9wd)4gslq;G@pmDmPUxEw^uwYr9b#!{_+vZ`u6qP3Hc*dF(;;T#Q9QfO9A68
zk{^$xOn(h-t5(x5|MrMdiuCyPjIlB!I451XJ6Nd1J}NhK{0RcG4_4u&%kUhl4ccbuf1BWy-K3_T?Y*>UtTqW?H8BpyvYFh;vL=N2kr=vMRSi8ZAGilhBHHE4S?YX_
zpzuy=MQe2q*XPb=;&oEHQk=^D-@cNj@Z+Oey`OrDx!8DgTss$2n6YXN^yC=|=6>zm
zGCbDuuv>Hx{8n8cdq_s5rvQLY{PNV
zxc1xeQib$!@}G;vZN24h@(0!Jy*2CI7kv+hvJ(WK`nwbKJHaNVYf>-XX~J8E9{tiJ
zV?@A(yg@8*$hJb;irqH$8#JKhM{R1+@U?rgIRD`n@M}E+^y!&R->~iO2d9~f3KEYp
zQxi9hU-OS8RxxpB!3;Y!seY?t7f`T0tX}rN(R`)^&3AUgQx-}Wz6%Bwgznz;t&}7%
zsl@uysGkj?yHA7L2$(G^xoJ0AeMCQe7qh^`Zd(uMssa$%Dto{HWA;%Zfa53(s27Z0
zxl+|u8nyDTKKlrQ4p|boT*s2nxQz(rUc8e2;Z5Y^!~Y8?*B~zNuyJ417EqUq)(udO
zyXu$|GIfz8;YZ{2uNJm|m15KaDvo`jPuTEEZdlh-k9g|e1OW*C7Xhg675IN50Jrga
zr-sRBa77(%Z>R8~()U#0AyWVAwOXPtGv+X_(@7l0
z)D&9016*nu5jX|)QWU)cvk0MVEW}G#(Io#4J%Z6aw?=oGr{KT2%(=ZsM!2I@AzY^eHFKKrEWA3w&od764W^Rn`XKhy9
zz^sq%$H$vIANvlrSZn>F`WMc{e~bm^=y|SLy|et?RIL)KiMymHcLojb
zcU_&^m)mWj+)WLaI))3Y4_xP#siT_(UdB;|5zbW(-?W(-_Vtn}uzmnTlf_146bS|F
zUpQVmIjR!(+gL~G!%r3Fj!UavIcXVJq#5zU^ENu?T3*9U=#bF`Vw?Nk8IAJb&p{6S
z9jnb2se?J#i%m<`{wrq2lPp_@B6cl3TpK!GH0&6p?fBE4bJ-`QJt6u5cy2ZJ#`
zxHIem5ozlp&3G3~d0Z+N4Hoe9Ki4m5Z8zgJ;a35ntJ!P=8z)G)NgiKHD5O0H$-^#H
zi5tA{pMR9PGYaDM@JEL=6l3|;*Sn>5zR8JoncYKpXIj^yY1T$S0rWCHuA8$_i&C+)
z7G`-0-@7|5_U?cJ{3rwVM7CK6g&I8AhadrSW-9@fEU@9l8tRRAr`3ZnUfc`w%p={u
zpxaAxNDmq;ou_^Z>&-#|PU*y#mj9T~c&NFD|VUgi@Y!|IB^GbM~4K{;&n)YcvjsvzvIs
zqQCH(;cj^{wEOt;GQf{t90=x~k`N25`b%n=J6d$cOFchBQhWKID;u7wf$wYVvJRdx
zwzP@dKUhshBK14AhSG^xVESP+FC~d_@B|bLK2aslnbfCVx=Oaj>J0ETgXDh5`(#T}
zTvA8|uTnff_(@w+1zwee_@1cm<}ko}3PB-U!pg+@CPTtkz!bN}-8o`pBMUEtfYAdt^Ta;l#jAV+E1ppd1c0Siu_dJQM
zC9eJ&z!h)#>5bpTT%>JRvXA-7j?hSCpc?jdGbs`?41g|0^#yc88NXkkK_Z;rLY{79$<+=CiM8Chz%jD@
zss8te0{l22nJUbk60T2P+WaTrWk|X5R1W0x%`)f
zUvbkWE>ndX+8D+R=N9ctx~llHDJKUkaZkcjlRkzi6W)m0#_UouEI7hCpM@6$f>jbx
zWF?V(IttV67?e^xy!SSQcA~UW{FHH5h~>Ihwvz`OSrA|nL-p;Rs&{FpU6UT
zAmvJ`B>?VG%6fVwx%ZEBDd{c3zJo`w+Whh?8$P$-@T^!WJft
z6uox}87ff6YPb9=kzzPtSu;)|oFOwJ3cv&eOhT!D0H}+64QtocrQQ7pPhJzeK@+Iw
z7R5z+1Oq2aTnMFKz%Rau9|T=;o1jbd_Jz$+Q|~zavQ6Y8B4WbKy&}(S#MPT#X@{W^
z@|R17h=(~MmaoY}Pa^W&8&Al}X68{oLAlEdllBJFEBAq>dNyj1mVH_UlS>Hy4QX@B
z$cUIw;apXNesVFNZYusCVD_0=e9aZ5Bh5jln;3WdmqSJu5ex_C%z#(E83uDP$~cOm
z`1&h{qz@j~^-2Y(?rWLP&~te-H%ARh?*Qtv)+Gfl%LRq%vUG0Z%b
z3y{cYsTxB19%35GiSyI1<&aOBQ3=Zt&YCt>eE!CBS=2C&JVT7YB|eP&Lz;XfNE2yD
zvKTja_R0^bj|hmnt}NU^43Ts+mS$9u6M91SaSfScGfUjOA>w4t{7>3M%j190CcFy1
zXZZxczWU1)?6U++K?ZN~hcjPj&+Io)O(Hn50<4QdTKzsG&Twk!YoMt?Oza0@9^NTygMe1Ol}vJ77~_4
zAot`;%y2VqvFpz+e?-@lSAJESFf4M1#DdZ#%TEe@aTfiVB^{y_zR%{G`)p%mb{R0H
z7~4L*UaZ5YQ?UQme@B*-|Cub6>R0C(wuy3C#TcH?p-m&%Mq2m?^>ec#A15!t1r5i>
zRyFdL3Ib04cGmuG`aMM{CpTEL)e@Oe$HN>yjOXM?73Lh%{60fTeIx2d20a?xQlG{t
z3s+!|>=_i-ES~aeUa7q>ELm=%Y6yHzpJZD(os<-{sWd#%Q@~_oH~^Mj)x5;3*~+Oa
z))2{EE3?}1J6@gvGW5#ZHTXoKm+G)=P702vg4WgsdGC@b$i6u4xi46ToZBwXhU4#&
zLr?br3y`$I*m5p&SJdJza$X-xe*}1cGXasAmW{1;;PMNHiAxJyM2qdVrcF)n>3g=%
zZ3IGwH@V(heR2HsIFZG*q^SLX?cb6jAj^G5iq@wBCxpLing-nlwo>yboi|HVs84o1
zHM^%!y=O<`=56|G>EQAlCBlfSp67}K%HF{G0}qI+AK2p|j((O4lo=_GQxe(?-7SA(
zxhFxuL?GY%yVO)MLUwy^6hVn|GLuVi$os_1F6>W$5BL$k@EWfkK#);z=G;qr`mxGt
zc;?X_*~kNA77Gt^#l%eQF=+xjwGjA9%xc)mf6^*<|5dAGJ*r0$w2H|8kybHfz2E6(
z)|p&>?nmi)J)oVk+u()j?Sor^xPrV5YYJnvc6OuhFYdF)xZU0dnfKEEG~g>RRra2e#~6AXfUtg<2iq$Y@0mPS?SG!js^L;)ExyKqYFfkd
z_959-NyMLf6nq#>wXO*TRN+6E&>Ej?x&4V}ng|5atym85K)u!jruSB_Cf~lPihB31
zXlK!=F#F9jiK2bBqNndTfE~EjzH|uMHuA*Wb^3?5C#rSW7kFK;TZe{lD@jbcvJVO)
zcK~_9$x|@KLeB;=MElP#q5g+dl;@vRR6IV2bCOZ)NriAP;H_0g=JP7Cp4Y9D0#(oc
zY8826?
zl!xCP@;l{Ca14-g6%?^;fPRbR#`d;>bA~(>ww+API@%WzGkiB}`R^cCo12pt;@+ki=6wbe+
zGzayiV5`NaXg+ZiruVmfN)`tsQ;bcShD_rmBP7D;2^&p_Vw;#q>K_x%I9Z1(J($Y3
z)+DtD(_wUT3U;1_H+u>GqV~2Z^x`7mSNjFKNpgZ
z=SnaTpY4@%i@8x-xNC7X=Re3hpIC5|#)L)6MvrZiblk_cte?1LbUGN5y|D+#%3W5PyL*}aH$oWbY(r?`{}tr?NYb{Xg%eiI52
z7FIAk4$x550m5WEfKcJiel}z+3hWwTAl$u8Y2A`)@$|g}jK5KbpkMlyj8|fa=;_mg
z&LGULb*eo>H#t3G{EPAf)qSoVn%k)a$BUuujLQ%_va@0Lb)iahC2z8xrl5S%!Zs^t
znqmlqPRX3nuY-KQaqdhI3G|~%;(kkpxy}5T9{D_Z^~(s>GZiq{B6-*^0?rjZ{iai%
zvY-~Lg{)((-B`-eCiVU(I|_s*Q+KwNFXvbLUX?)kS?(98l{_xn+NT3gaIRhee}2EaTDp5MZ30iRR%7onJK&vO~YquJGe1&gZA(($JvqSWn3H}WFwr|toQNLOw$&xGn~R|U=++z_D`Jk
zU*~V;1s*xdUH*w|#6r#z6;0SwF%__NxL;pWsB`!X_joz;L|tdB-A~B}%+uNnO_vHb
zBr9EaHguUiVdde6EmXZd+fp%*@`I8-um`PdC1>wpjAc5CM_EoP@N#G>>l_+gvV}%S
zphu}54bd{sxN%gYn9tDJTuUew@Wl&JJ*pym#jq&p3Ue86)&aG%sXVD>(Y?Dc)af$p
z3yoMp_?1-RpxAJtIM!5uf5;7<6r>%VNIGQ!x{xg6942lXlIvMcUNADV4nolUHJy?w
z=>F3TUv!lOK}f`W2(wgp@ZN7F>jm!Pit&~<(8+j<70$Av0mOs&&?0pfvcUCE!Qly04lmNKFiD-YA@%RVPstO%dS{*edf=)EX^BJVcW&ta>I-^7o5-
z->S303XnhWnv2LJCeVWPb
zYsSy;BsjOt3V$FDv&&B)h}q`P1#?Bz)$^6V=!X^qa%b|*zj_korS1wEaUjZy0R$h#
zFwnr?ou*FTEudPBu{EC_EC9RXGj_0^X^9jcNIkD>x_)QSe8Jci`qzA=lo-}!>Z6G<
z3e|Ww{R~$S2S2-JXB6~8hXWP$G=@WV2v5q|+T^bw=Nip^s(
z!t!MxK>MtZEc^H|fpI7O#Ro!t
z+kP0}>Gkcw`j2wOC3NJxRF9S#&%S+nA};pP>eH(kWX8+og6Q?J*gx08rXc&AJ>>o1
zfu2?g^Rx(SQT6l720wCY_;Qdy=;@7mDBwEzrn)jL|E4ImouPo$$9!RpRZfkmNEI1B
zE^{)rWXr1_l^@*SRlX<1w&1hq410mAKnW>Ne`Y6z$w1p49-zmssd;-|o+62^0Qo^H
zR_@xppO!&K5pJGZmp^wANSJw7A%k(+B5UcU#!e|bBmA@hh9!&tQ&9SrpAd2egt3Y~
z2^>-2`a_##WdHu{n{}AX(D>;f;i{(_5X*iwXfe5)UQ@Ns{ySQ(vscdUQToQr49jHm
z>2}?W*-@FkWk?QXQRQTN>EzXcMqhD8??3--!N$1Z{ez@eR$k=zDf*uaKnUdm=3aID
zg_N4`Hozlk32CgM`82u16lGJ}#N^DkF7jnZ%aJ2w565u!E^HGLz*GQWSX*zM3YiN;
zwVC8VOL=f(hO-XP0PQ2M(xAGccil$PO|7Y_Yx}n;0aJL;HZFW9(YYHS;Vooa+Pfmt
ze=Z~h{b!^5m8C8jZ>!bs-tMufwOcPKby4oG`XW3#UQ+5|C>#(J)C8nJ$Gd=_IsL44
zqr9M&4;6u6m-^c_h_X7USgK*OCcBy14?ddg(KxALh=Ky8RI;ir)NNi!v9O@BH9%xc
zvFLS||G~v=pj{dd87AyvEg=dW$2wW_bgL%N+Hc2^q0Xx0#-IOBJ72jt3+YDi?c)KW
zV)iYII}OqJ8d*EpB@2Gu*i43*0X}TliBK6B9p)JkB*Ikki$5A#32Wji3Vo-LobU{is`jDUmhuo!{j*PS!Z=@<#meht8`2i@<(hY?@@1>|*C$NT
z@2H+Q70e|eg01r&`G`r5CV!HQe-lkt^2|!wLw8UaHigF=mZ&ib;<21WQMDOJ<@^Li1InU9yC}mzi_f*{ODO%n
z4r1g%XjMaVDYqHGA){DFj;$4n3xW=b4;G9y3^CV~4Xhnm
zc4OQGN9#7N1$44D?_SzzIytN3+%oTl$?n7h;k#@?_)ctaUgWEE$ohXMpg*-^=Ltnp
zslvJ9Gi(u%_74`mYG7$qs|-8KLQ0FHq&u!>%YPH<;4oCT8@K?v>uvBY@Z6`=FO8&3
zQMXK82G%u!70_37yaR5Wok+j=n&LPw@O*ICYuZYZixbGjjI^~!
zQC&H9bR3(W24${Gk3VJ`Sbuo6xuMYftG({0)!I5K+mB6B=2y#}<*lzB&J!gfrVmAX
zZUIUEHHWxdJ{fh}pucJio0A@w=dBMgF^ijDQV7h>W=vZ(r=qyE_TC1N@_&*~Iw<85
z>3j*!YYF+-ka4_>fOSe
zTKkO5Icy(k00B!myn38--W19sII${3wCMa%K1yvFq+=!hU`O}KsdE(ie)r)v>!aKE
z$Zn1*pB2c}AQ7V1D=|N6Jp6-`cs9if`(4*+fMS|lRLDStJt)V6SWfGaxG~LnAh8b&
zo$kudaf7Iq3x95xrSa@;kw4`JG56&kg%n_5XTyVu0NMxx@r0m5&xT#|pWh}S^#k?@
vAd=HmRWbl{E2&WKpP&ERU;5`4;yD83e_7xZ92tQ>M5L*vty-pR_3D2CfJ~Vh
literal 0
HcmV?d00001
diff --git a/pictures/storm-hdfs-result.png b/pictures/storm-hdfs-result.png
new file mode 100644
index 0000000000000000000000000000000000000000..79ad0fffc7b781d30c75141743590e8b492982d2
GIT binary patch
literal 38477
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