这个需求是是按照实际工作中的项目改写的一个例子。
业务需求: 1. 实时统计某市银行流水交易总额
2. 实时统计某市银行某地区的流水交易总额
3. 每隔10s统一次每个地区近一年内的交易总额
系统需求: 保证数据不丢失、系统可容错
分析:这是一个实时流处理系统的常见应用,在网页点击分析中也可以看见类似的例子,kafka具有高可扩展性、容错性、高吞吐量、高并发、分布式等特性,常用于实时流中做消息传输,同时也起到了消息缓冲的作用,因此在这个例子中使用kafka对接外部系统做消息的传输。关于流式处理系统现在常用的有sparkStreaming、Storm,Storm是一个真正的流式处理系统,每次处理处理一条消息,SparkStreaming 看做一个伪流式处理系统,每次处理一个很小batch的数据,它基于SparkCore ,因此继承了sparkCore的很多特性,例如:任务执行优化、内存分布式计算、可容错、迭代式计算等,除了可类似于sparkCore RDD的算子外,还提供了状态函数与窗口函数,可对接kafka 、flume、文件、监听端口等,在我们这个例子中使用SparkStreaming 正好能满足我们的要求。Hbase是一个分布式的KV存储系统,能够快速读写数据,在实时流中Hbase用于存储源报文再好不过了。
设计: 使用kafka对接外部系统,kafka客户端消息的传输有两种方式:sync与async,即同步方式与异步方式,同步方式表示没发送一条消息就提交一次,这种做法能够保证数据不丢失,异步方式是批量发送提交,当客户端的消息达到一定大小就将其提交给broker,这种做法性能高,为了保证数据的不丢失,采用同步方式提交,另外要求该topic的所有副本都收到消息才算成功;kafka与SparkStreaming对接提供了两种方式,一种是reciver,一种是direct,这两种方式都可以做到数据容错,但是reciver方式代价更大,需要预写日志、在内存序列化中保证两份(默认),而direct方式直接读取kafka中消息,使用消息offset手动提交,只有在处理成功的情况下才去读取下一条消息,因此在这里使用direct方式获取消息。我们要求系统能够7*24小时不间断的工作,那就是保证系统的容错性,即时系统出现故障,也能够即时恢复到最近的状态,sparkStreaming中提供了checkpoint 机制,可保存系统的元数据信息与数据信息,恢复时直接读取元数据信息与数据信息恢复到最近的状态。统计流水交易总额与某地区的流水交易总额使用updateStateByKey,每隔10s统一次每个地区近一年内的交易总额使用reduceByKeyAndWindow算子操作。对于将源始报文保存到hbase,直接使用hbase api。
实现:
启动服务:hdfs、yarn、hbase、kafka
创建topic : ./kafka-topics.sh --zookeeper hadoop1:2181 --partitions 1 --replication-factor 1 --create --topic topicName(只有一台服务器创建一个分区一个副本)
涉及四个topic 1. 源报文:topic2 2. 交易总额:topic_all
创建hbase表:create 'trade','cf'
3 . 地区交易总额 :topic_regionsum 4. 每隔10s每个地区近一年内的交易总额 topic_regionsumOneYear
创建maven java project项目,使用maven添加依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.10</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.8.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.6</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
</dependency>
</dependencies>
编码:
public class Demo2 {
private static String SPARK_MASTER="yarn"; //使用yarn作为资源框架
private static String SPARK_DEPLOY_MODE="client";
private static String SPARK_APP_NAME="sparkStreaming";
private static long SPARK_BATCH_SIZE_M=10000; //批处理间隔10s
private static long SPARK_WIN_LEN=120000; //窗口大小 2min (方便演示)
private static long SPARK_WIN_SLID=10000; //窗口滑动间隔10s
//checkpoint的路径
private static String SPARK_CHECKPOINT_DIR="hdfs://hadoop1:9000/spark/streaming/checkpoint3";
private static String KAFKA_BOOTSTRAP_SERVERS="hadoop1:9092";
private static String KAFKA_KEY_DESERIALIZER="org.apache.kafka.common.serialization.StringDeserializer";
private static String KAFKA_VALUE_DESERIALIZER="org.apache.kafka.common.serialization.StringDeserializer";
private static String KAFKA_GROUP_ID="g1";
private static String KAFKA_AUTO_OFFSET_RESET="largest";
private static String KAFKA_ENABLE_AUTO_COMMIT="false";
private static String KAFKA_TOPIC="topic2";