spark-streaming集成Kafka处理实时数据
在这篇文章里,我们模拟了一个场景,实时分析订单数据,统计实时收益。
场景模拟
我试图覆盖工程上最为常用的一个场景:
1)首先,向Kafka里实时的写入订单数据,JSON格式,包含订单ID-订单类型-订单收益
2)然后,spark-streaming每十秒实时去消费kafka中的订单数据,并以订单类型分组统计收益
3)最后,spark-streaming统计结果实时的存入本地MySQL。
前提条件
安装
1)spark:我使用的yarn-client模式下的spark,环境中集群客户端已经搞定
2)zookeeper:我使用的是这个集群:10.93.21.21:2181,10.93.18.34:2181,10.93.18.35:2181
3)kafka:我使用的是standalone模式:10.93.21.21:9093
4)mysql:10.93.84.53:3306
语言
python:pykafka,pip install pykafka
java:spark,spark-streaming
下面开始
1、数据写入kafka
- kafka写入
我们使用pykafka模拟数据实时写入,代码如下:
kafka_producer.py
# -* coding:utf8 *-
import time
import json
import uuid
import random
import threading
from pykafka import KafkaClient # 创建kafka实例
hosts = '10.93.21.21:9093'
client = KafkaClient(hosts=hosts) # 打印一下有哪些topic
print client.topics # 创建kafka producer句柄
topic = client.topics['kafka_spark']
producer = topic.get_producer() # work
def work():
while 1:
msg = json.dumps({
"id": str(uuid.uuid4()).replace('-', ''),
"type": random.randint(1, 5),
"profit": random.randint(13, 100)})
producer.produce(msg) # 多线程执行
thread_list = [threading.Thread(target=work) for i in range(10)]
for thread in thread_list:
thread.setDaemon(True)
thread.start() time.sleep(60) # 关闭句柄, 退出
producer.stop()
可以看到,我们写入的形式是一个json,订单id是一个uuid,订单类型type从1-5随机,订单收益profit从13-100随机,形如
{"id": ${uid}, "type": 1, "profit": 30}
注意:1)python对kafka的读写不需要借助zookeeper,2)使用多线程的形式写入,让数据量具有一定的规模。
执行producer,会持续写入数据1分钟。
python kafka_producer.py
- 验证一下
kafka_consumer.py
# -* coding:utf8 *-
from pykafka import KafkaClient hosts = '10.93.21.21:9093'
client = KafkaClient(hosts=hosts)
# 消费者
topic = client.topics['kafka_spark']
consumer = topic.get_simple_consumer(consumer_group='test', auto_commit_enable=True, auto_commit_interval_ms=1,
consumer_id='test')
for message in consumer:
if message is not None:
print message.offset, message.value
执行,可以消费kafka刚才写入的数据
python kafka_consumer.py
2、spark-streaming
1)先解决依赖
其中比较核心的是spark-streaming和kafka集成包spark-streaming-kafka_2.10,还有spark引擎spark-core_2.10
json和mysql看大家爱好。
pom.xml
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka_2.10</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.10</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.10</artifactId>
<version>1.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.19</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
2)MySQL准备
- 建表
我们的结果去向是MySQL,先建立一个结果表。
id:主键,自增id
type:订单类型
profit:每个spark batch聚合出的订单收益结果
time:时间戳
CREATE TABLE `order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` int(11) DEFAULT NULL,
`profit` int(11) DEFAULT NULL,
`time` mediumtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=56 DEFAULT CHARSET=utf8
- Java客户端
采用了单例线程池的模式简单写了一下。
ConnectionPool.java
package com.xiaoju.dqa.realtime_streaming; import java.sql.Connection;
import java.sql.DriverManager;
import java.util.LinkedList; public class ConnectionPool {
private static LinkedList<Connection> connectionQueue; static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} public synchronized static Connection getConnection() {
try {
if (connectionQueue == null) {
connectionQueue = new LinkedList<Connection>();
for (int i = 0; i < 5; i++) {
Connection conn = DriverManager.getConnection(
"jdbc:mysql://10.93.84.53:3306/big_data",
"root",
"1234");
connectionQueue.push(conn);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return connectionQueue.poll(); }
public static void returnConnection(Connection conn){connectionQueue.push(conn);}
}
3)代码实现
我用java写的,不会用scala很尴尬。
即时用java整个的处理过程依然比较简单。跟常见的wordcount也没有多大的差别。
SparkStreaming特点
spark的特点就是RDD,通过对RDD的操作,来屏蔽分布式运算的复杂度。
而spark-streaming的操作对象是RDD的时间序列DStream,这个序列的生成是跟batch的选取有关。例如我这里Batch是10s一个,那么每隔10s会产出一个RDD,对RDD的切割和序列的生成,spark-streaming对我们透明了。唯一暴露给我们的DStream和原生RDD的使用方式基本一致。
这里需要讲解一下MySQL写入注意的事项。
MySQL写入
在处理mysql写入时使用了foreachPartition方法,即,在foreachPartition中使用borrow mysql句柄。
这样做的原因是:
1)你无法再Driver端创建mysql句柄,并通过序列化的形式发送到worker端
2)如果你在处理rdd中创建mysql句柄,很容易对每一条数据创建一个句柄,在处理过程中很快内存就会溢出。
OrderProfitAgg.java
package com.xiaoju.dqa.realtime_streaming; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaPairReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka.KafkaUtils;
import scala.Tuple2; import java.sql.Connection;
import java.sql.Statement;
import java.util.*; /*
* 生产者可以选用kafka自带的producer脚本
* bin/kafka-console-producer.sh --broker-list localhost:9093 --topic test
* */
public class OrderProfitAgg { public static void main(String[] args) throws InterruptedException {
/*
* kafka所注册的zk集群
* */
String zkQuorum = "10.93.21.21:2181,10.93.18.34:2181,10.93.18.35:2181"; /*
* spark-streaming消费kafka的topic名称, 多个以逗号分隔
* */
String topics = "kafka_spark,kafka_spark2"; /*
* 消费组 group
* */
String group = "bigdata_qa"; /*
* 消费进程数
* */
int numThreads = 2; /*
* 选用yarn队列模式, spark-streaming程序的app名称是"order profit"
* */
SparkConf sparkConf = new SparkConf().setMaster("yarn-client").setAppName("order profit"); /*
* 创建sc, 全局唯一, 设置logLevel可以打印一些东西到控制台
* */
JavaSparkContext sc = new JavaSparkContext(sparkConf);
sc.setLogLevel("WARN"); /*
* 创建jssc, spark-streaming的batch是每10s划分一个
* */
JavaStreamingContext jssc = new JavaStreamingContext(sc, Durations.seconds(10)); /*
* 准备topicMap
* */
Map<String ,Integer> topicMap = new HashMap<String, Integer>();
for (String topic : topics.split(",")) {
topicMap.put(topic, numThreads);
} /*
* kafka数据流
* */
List<JavaPairReceiverInputDStream<String, String>> streams = new ArrayList<JavaPairReceiverInputDStream<String, String>>();
for (int i = 0; i < numThreads; i++) {
streams.add(KafkaUtils.createStream(jssc, zkQuorum, group, topicMap));
}
/*
* 从kafka消费数据的RDD
* */
JavaPairDStream<String, String> streamsRDD = streams.get(0);
for (int i = 1; i < streams.size(); i++) {
streamsRDD = streamsRDD.union(streams.get(i));
} /*
* kafka消息形如: {"id": ${uuid}, "type": 1, "profit": 35}
* 统计结果, 以type分组的总收益
* mapToPair, 将kafka消费的数据, 转化为type-profit key-value对
* reduceByKey, 以type分组, 聚合profit
* */
JavaPairDStream<Integer, Integer> profits = streamsRDD.mapToPair(new PairFunction<Tuple2<String, String>, Integer, Integer>() {
@Override
public Tuple2<Integer, Integer> call(Tuple2<String, String> s_tuple2) throws Exception {
JSONObject jsonObject = JSON.parseObject(s_tuple2._2);
return new Tuple2<Integer, Integer>(jsonObject.getInteger("type"), jsonObject.getInteger("profit"));
}
}).reduceByKey(new Function2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer i1, Integer i2) throws Exception {
return i1 + i2;
}
}); /*
* 输出结果到MySQL
* 需要为每一个partition创建一个MySQL句柄, 使用foreachPartition
* */
profits.foreachRDD(new Function<JavaPairRDD<Integer, Integer>, Void>() {
@Override
public Void call(JavaPairRDD<Integer, Integer> integerIntegerJavaPairRDD) throws Exception { integerIntegerJavaPairRDD.foreachPartition(new VoidFunction<Iterator<Tuple2<Integer, Integer>>>() {
@Override
public void call(Iterator<Tuple2<Integer, Integer>> tuple2Iterator) throws Exception {
Connection connection = ConnectionPool.getConnection();
Statement stmt = connection.createStatement();
long timestamp = System.currentTimeMillis();
while(tuple2Iterator.hasNext()) {
Tuple2<Integer, Integer> tuple = tuple2Iterator.next();
Integer type = tuple._1;
Integer profit = tuple._2;
String sql = String.format("insert into `order` (`type`, `profit`, `time`) values (%s, %s, %s)", type, profit, timestamp);
stmt.executeUpdate(sql);
}
ConnectionPool.returnConnection(connection);
}
});
return null;
}
}); /*
* 开始计算, 等待计算结束
* */
jssc.start();
try {
jssc.awaitTermination();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
jssc.close();
}
}
}
4)打包方法
编写pom.xml build tag。
mvn clean package打包即可。
pom.xml
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!--这里要替换成jar包main方法所在类 -->
<!--<mainClass>com.bigdata.qa.hotdog.driver.WordCount</mainClass>-->
<mainClass>com.xiaoju.dqa.realtime_streaming.OrderProfitAgg</mainClass> </manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- 指定在打包节点执行jar包合并操作 -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
3、执行与结果
1)执行kafka_producer.py
python kafka_producer.py
2) 执行spark-streaming
这里使用的是默认参数提交yarn队列。
spark-submit --queue=root.XXXX realtime-streaming-1.0-SNAPSHOT-jar-with-dependencies.jar
3)查看结果
到MySQL中查看结果,每隔10秒会聚合出type=1-5的5条数据。
例如第一条数据,就是type=4这种类型的业务,在10s内收益是555473元。业务量惊人啊。哈哈。
完结。
spark-streaming集成Kafka处理实时数据的更多相关文章
- spark streaming集成kafka
Kakfa起初是由LinkedIn公司开发的一个分布式的消息系统,后成为Apache的一部分,它使用Scala编写,以可水平扩展和高吞吐率而被广泛使用.目前越来越多的开源分布式处理系统如Clouder ...
- Spark Streaming和Kafka整合保证数据零丢失
当我们正确地部署好Spark Streaming,我们就可以使用Spark Streaming提供的零数据丢失机制.为了体验这个关键的特性,你需要满足以下几个先决条件: 1.输入的数据来自可靠的数据源 ...
- spark streaming集成kafka接收数据的方式
spark streaming是以batch的方式来消费,strom是准实时一条一条的消费.当然也可以使用trident和tick的方式来实现batch消费(官方叫做mini batch).效率嘛,有 ...
- 解决spark streaming集成kafka时只能读topic的其中一个分区数据的问题
1. 问题描述 我创建了一个名称为myTest的topic,该topic有三个分区,在我的应用中spark streaming以direct方式连接kakfa,但是发现只能消费一个分区的数据,多次更换 ...
- Spark Streaming消费Kafka Direct方式数据零丢失实现
使用场景 Spark Streaming实时消费kafka数据的时候,程序停止或者Kafka节点挂掉会导致数据丢失,Spark Streaming也没有设置CheckPoint(据说比较鸡肋,虽然可以 ...
- spark streaming 整合 kafka(一)
转载:https://www.iteblog.com/archives/1322.html Apache Kafka是一个分布式的消息发布-订阅系统.可以说,任何实时大数据处理工具缺少与Kafka整合 ...
- Spark Streaming和Kafka整合开发指南(一)
Apache Kafka是一个分布式的消息发布-订阅系统.可以说,任何实时大数据处理工具缺少与Kafka整合都是不完整的.本文将介绍如何使用Spark Streaming从Kafka中接收数据,这里将 ...
- Spark Streaming和Kafka整合开发指南(二)
在本博客的<Spark Streaming和Kafka整合开发指南(一)>文章中介绍了如何使用基于Receiver的方法使用Spark Streaming从Kafka中接收数据.本文将介绍 ...
- Spark Streaming和Kafka集成深入浅出
写在前面 本文主要介绍Spark Streaming基本概念.kafka集成.Offset管理 本文主要介绍Spark Streaming基本概念.kafka集成.Offset管理 一.概述 Spar ...
随机推荐
- Linux 有用工具
``` 小问题,在此记录一下,有时在shell下执行命令重定向到文件时提示无权限-bash: temp_20181015.log: Permission denied,而且加sudo执行依提示无权限, ...
- 019、使用公共Registry (2019-01-10 周四)
参考https://www.cnblogs.com/CloudMan6/p/6896488.html 在DockerHub上注册一个账号,这样就可以将自己构建的镜像上传到DockerHub上供别人 ...
- Java面试题系列(五)
序言 基本数据类型数组和引用数据类型数组的区别 基本数据类型数组: 其中存储的元素为基本类型数据. 引用类型数组: 元素是对象,其中存储的是对象的地址值. 引用数据类型的数组使用规定长度的方式进行初始 ...
- SQL Server进阶(五)子查询
概述 子查询的概念: 当一个查询是另一个查询的条件时,称之为子查询.子查询可以嵌套在主查询中所有位置,包括SELECT.FROM.WHERE.GROUP BY.HAVING.ORDER BY. 外面的 ...
- 通过WifI开发调试Android设备
前言:使用的windows系统,为了可以通过wifi可以直接连接android设备调试,尝试使用以下方法一.使用ADB USB to WIFI 一直不成功. 二.使用命令行方式 1.进入android ...
- 今天终于想明白为什么java包要倒着写
比如 com.baidu.video,因为java内部实际上是以文件夹形式存在的,是按com,baidu,video依次生成文件夹的具体功能的是子文件夹,所以要倒着写.
- weui hd bd ft
weui样式看到hd ,bd, ft hd 是header的缩写 bd 是body的缩写 ft 是footer的缩写
- Linux 下磁盘挂载
Linux 磁盘挂载 新硬盘挂载: fdisk /dev/sdb p # 打印分区 d # 删除分区 n # 创建分区,(一块硬盘最多4个主分区,扩展占一个主分区位置.p主分区 e扩展) w # 保存 ...
- hystrix学习
概述: 字面意思是豪猪.作用是保护你的应用.Netflix会有服务实时调用,hystrix提供服务降级. 目标是将依赖独立化,防止拖垮整个服务.(属于降级服务.) 作用: 第三方接口超时或失败时, ...
- python(四)类变量和实例变量
转载自[1] 实际这是个实例变量是否指向类变量的问题. python的类变量和实例变量,顾名思义,类变量是指跟类的变量,而实例变量,指跟类的具体实例相关联的变量,具体体现为self.x 等.实际要注意 ...