0.整体思路
Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。Spark使用Scala语言进行实现,它是一种面向对象、函数式编程语言,能够像操作本地集合对象一样轻松地操作分布式数据集,具有运行速度快、易用性好、通用性强以及随处运行等特点,适合大多数批处理工作,并已成为大数据时代企业大数据处理优选技术。
在物联网时代,越来越多的设备将通过互联网连接到公有云或私有云,实时上报设备的状态和业务数据,产生海量规模的原始数据。对大数据的实时处理,Spark是这一场景下的行家里手。但针对于海量的设备连接及与设备的通信并非Spark的强项。因此我们需要寻找其他中间件产品解决这些问题。由于设备采用的通信协议种类繁多(MQTT、CoAP 、HTTP、XMPP、SoAP),因此我们目前聚焦于应用最为广泛的MQTT协议的方案设计。
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布,目前最新版本为v3.1.1。MQTT最大的优点在于可以以极少的代码和有限的带宽,为远程设备提供实时可靠的消息服务。做为一种低开销、低带宽占用的即时通讯协议,MQTT在物联网、小型设备、移动应用等方面有广泛的应用。
MQTT的消息代理(MQTT Broker)很多,经过对开源产品的调研,Mosquitto从成熟度和性能上都十分出色,因此我们选择Mosquitto作为本方案的MQTT代理。
现在既然选定了通信协议,那就要面对Spark如何订阅并处理MQTT消息的问题。很遗憾,原生的Spark并不支持将MQTT作为数据的数据输入流。但是,Apache上有一个专门针对Spark等大数据中间件研发扩展/插件的项目Apache Bahir提供了对于MQTT的支撑。至此我们便形成了Spark+Bahir+Mosquitto的整体设计思路。
1.安装Spark
在Apache Spark官网下载Spark(如果你不想单独安装Hadoop,则请下载已嵌入Hadoop组件的版本):
http://spark.apache.org/downloads.html
设置环境变量
HADOOP_HOME=d:\spark-2.1.1-bin-hadoop2.7
PATH=%PATH%;%HADOOP_HOME%\bin
以Administrator用户启动cmd命令窗口,执行命令:
spark-shell
即可启动spark交互窗口
查看web管理界面
2.安装Mosquitto
在以下网址下载mosquitto,并根据引导安装
https://mosquitto.org/download/
3.安装MQTT客户端工具-mqttfx
如果初次接触MQTT,并希望能够通过一个专业的MQTT客户端发送消息给Mosquitto,mqttfx是你最好的选择。关于mqttfx的使用,请查阅网上相关文档,在此不再赘述。
下载地址:http://www.jensd.de/apps/mqttfx/
4.测试mosquitto可用性
a)启动mqttfx
b)在mqttfx中,创建配置文件,截图如下:
c)基于刚创建的配置文件连接Mosquitto
d)监听名称为“news”(这个可随意)的Topics
e)发送消息
f)查看接收结果
如果走到这一步,恭喜你的Mosquitto工作正常,可以继续深入探索Spark的部分了。
5.开发演示程序
一提到Spark,用过的人都知道总要拿单词统计这一经典案例说事儿,这里也要用这个例子做做文章。以下示例在Spark代码库中的代码JavaNetworkWordCount.java的基础上做了修改,使其能够支持接收并处理MQTT消息。
pom.xml依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.suntrap</groupId>
<artifactId>mqtt_test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mqtt_test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.12</artifactId>
<version>2.4.3</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.bahir/spark-streaming-mqtt -->
<dependency>
<groupId>org.apache.bahir</groupId>
<artifactId>spark-streaming-mqtt_2.11</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package io.suntrap.spark.mqtt.test;
import java.util.Arrays;
import java.util.regex.Pattern;
import scala.Tuple2;
import org.apache.spark.SparkConf;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.mqtt.*;
public class JavaNetworkWordCount {
private static final Pattern SPACE = Pattern.compile(" ");
public static void main(String[] args) throws Exception {
String brokerUrl="tcp://127.0.0.1:1883";
String topic="news";
SparkConf sparkConf = new SparkConf().setAppName("JavaNetworkWordCount").setMaster("local[2]");
JavaStreamingContext ssc = new JavaStreamingContext(sparkConf, Durations.seconds(1));
JavaReceiverInputDStream<String> lines = MQTTUtils.createStream(ssc, brokerUrl, topic);
JavaDStream<String> words = lines.flatMap(x -> Arrays.asList(SPACE.split(x)).iterator());
JavaPairDStream<String, Integer> wordCounts = words.mapToPair(s -> new Tuple2<>(s, 1))
.reduceByKey((i1, i2) -> i1 + i2);
wordCounts.print();
ssc.start();
ssc.awaitTermination();
}
}
6.运行并测试
在mqttfx上再次发送前面报文,演示程序侧将显示报文中各单词的统计结果: