├── .gitattributes ├── README.md ├── docs ├── .nojekyll ├── 2020秋招-小米笔试题-小米之家购物.md ├── README.md └── index.html ├── tf_consumer ├── pom.xml ├── src │ └── main │ │ ├── resources │ │ └── kafka.properties │ │ └── scala │ │ ├── consumer │ │ └── SparkConsumer.scala │ │ └── utils │ │ ├── PropertyUtil.scala │ │ └── RedisUtil.scala └── tf_consumer.iml ├── tf_modeling ├── pom.xml ├── src │ └── main │ │ └── scala │ │ ├── modeling │ │ └── Train.scala │ │ └── utils │ │ └── RedisUtil.scala └── tf_modeling.iml ├── tf_prediction ├── pom.xml ├── src │ └── main │ │ └── scala │ │ ├── predict │ │ └── Prediction.scala │ │ └── utils │ │ └── RedisUtil.scala └── tf_prediction.iml └── tf_producer ├── pom.xml └── src └── main ├── resources └── kafka.properties └── scala ├── producer └── Producer.scala └── utils └── PropertyUtil.scala /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 交通拥堵预测 2 | 3 | **思路**:考虑到没有相应的数据来源,这里是数据使用kafa, 进行自主模拟生产,然后消费预处理后存储到非关系型数据库中,再从redis中读取数据进行数据建模,将建立的模型存储到hdfs上,等进行数据预测时读取hdfs上的预测模型,进行预测。 4 | 5 | **项目环境** 6 | 7 | | 操作系统 | 版本号 | 8 | | -------- | -------------- | 9 | | window | window10家庭版 | 10 | | Linux | Ubuntu18.04 | 11 | 12 | **大数据框架环境** 13 | 14 | | 框架 | 版本号 | 15 | | --------- | ------------------ | 16 | | hadoop | Apache 2.7.2 | 17 | | zookeeper | Apache 3.4.5 | 18 | | kafka | kafka_2.10_0.8.2.1 | 19 | | scala | Apache 2.1.1 | 20 | | spark | 3.0.5 | 21 | | redis | 2.11.8 | 22 | | jdk | 1.8 | 23 | 24 | | 编程工具 | 版本号 | 25 | | ------------- | -------- | 26 | | Intellij IDEA | 2019.1.3 | 27 | 28 | **数据结构** 29 | 30 | | 字段名 | 字段含义 | example | 31 | | ---------- | ---------------------------------------- | ----------- | 32 | | monitor_id | 监测点id,在同一个业务系统中是唯一的 | 0001、002等 | 33 | | speed | 当前某一辆车经过监测点的时速,单位:km/h | 060、010等 | 34 | 35 | **启动大数据集群环境** 36 | 37 | 注意:启动大数据集群环境,这里因为资源有限,我以单节点作为测试,此次启动命令都是绝对启动命令,都是绝对路径,请根据自己实际情况进行启动。 38 | 39 | ``` 40 | 1、启动 hadoop 41 | 42 | $ /usr/local/hadoop/sbin/start-all.sh 43 | 44 | 45 | 启动 kafka http://dblab.xmu.edu.cn/blog/1096-2/#more-1096 46 | 47 | $ /usr/local/kafka/bin/zookeeper-server-start.sh /usr/local/kafka/config/zookeeper.properties 48 | 49 | $ /usr/local/kafka/bin/kafka-server-start.sh /usr/local/kafka/config/server.properties 50 | 51 | 2、创建主题 52 | 53 | $ /usr/local/kafka/bin/kafka-topics.sh --zookeeper localhost:2181 --topic traffic --create --replication-factor 1 --partitions 1 54 | 55 | 创建的主题是否存在。 56 | 57 | $ /usr/local/kafka/bin/kafka-topics.sh --list --zookeeper localhost:2181 58 | 59 | 测试启动成功 60 | 61 | 手动生产消息 62 | $ /usr/local/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic traffic 63 | 64 | 接收 65 | $ /usr/local/kafka/bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic traffic --from-beginning 66 | 67 | 68 | 启动redis 69 | 70 | $ /usr/local/redis/src/redis-server /usr/local/redis/redis.conf 71 | 72 | # 测试启动 73 | $ /usr/local/redis/src/redis-cli -h 192.168.126.132 74 | 75 | ``` 76 | 77 | 关闭集群 78 | 79 | ``` 80 | 关闭zookeeper 81 | $ /usr/local/kafka/bin/zookeeper-server-stop.sh 82 | 83 | 关闭kafka 84 | $ /usr/local/kafka/bin/kafka-server-stop.sh 85 | 86 | 关闭hadoop 87 | $ /usr/local/hadoop/sbin/stop-all.sh 88 | 89 | 关闭redis 90 | $ /usr/local/redis/src/redis-cli -h 192.168.126.132 -p 6379 shutdown 91 | ``` 92 | 93 | 94 | 95 | 96 | 97 | ### 编写代码--创建总工程--traffic_prediction 98 | 99 | - 创建普通Java工程:traffic_prediction 100 | - 删除 src 目录(工程traffic_prediction 作为总工程目录,不编写代码) 101 | 102 | ### 编写生产者 103 | 104 | **思路:** 105 | 106 | **a**)新建子模块工程:tf_produccer 107 | 108 | - New ——> Module ——>选择maven 工程——> 创建完毕——>右键Add Framework Support 添加scala支持 109 | - 创建scala目录并设置为Source Root 110 | 111 | **b)** 配置maven依赖 112 | 113 | **c)** 因为要把数据发送给kafka,所以配置kafka属性,保存于某个配置文件中 114 | 115 | **d)** 编写kafka加载属性的工具类 116 | 117 | **e)** 每隔5分钟,切换一次模拟状态,例如第一个五分钟,车速都在30KM/H以上,下一个五分钟,车速都在10KM/H一下,往复模拟公路一会堵车,一会不堵车的情况。 118 | 119 | **f)** 启动zookeeper,kafka,并创建kafka主题,检查主题存在性 120 | 121 | **g)** 将数据发送至kafka并使用kafka console-consumer进行检测 122 | 123 | 124 | 125 | ### **编写消费者** 126 | 127 | **思路:** 128 | 129 | **a**)新建子工程:tf_consumer 130 | 131 | **b)** 配置maven依赖 132 | 133 | **c**) 配置redis并测试 134 | 135 | **d**) 将刚才kafka.properties以及PropertyUtil拷贝过来 136 | 137 | **e**) 编写redis操作工具类:RedisUtil 138 | 139 | **f**) 读取kafka中的数据,实时保存到redis中,并且按照分钟和监测点聚合车速和车辆个数 140 | 141 | 142 | 143 | ### 编写数据建模代码 144 | 145 | **思路:** 146 | 147 | **a)** 确定要对哪个监测点进行建模,我们称之为目标监测点 148 | 149 | **b**) 找到目标监测点的其他相关监测点(比如相关监测点与目标监测点属于一条公路的) 150 | 151 | **c**) 从redis中访问得到以上所有监测点若干小时内的历史数据信息(一部分作为训练数据,一部分作为测试数据) 152 | 153 | **d**) 提取组装特征向量与目标向量,训练参数集,训练模型 154 | 155 | **e**) 测试模型吻合度,将符合吻合度的模型保存到HDFS中,同时将模型的保存路径放置于redis中 156 | 157 | ### 编写预测模块 158 | 159 | **思路:** 160 | 161 | **a)** 用户传入想要进行预测的时间节点,读取该时间节点之前3分钟,2分钟和1分钟的数据 162 | 163 | **b**)此时应该已经得到了历史数据集,通过该历史数据集预测传入时间点的车流状态 164 | 165 | **提示**:为了方便观察测试,建议传一个历史时间点,这样可以很直观的看到预测结果是否符合期望值。 166 | 167 | 168 | 169 | **警告:代码中涉及到的IP,请自觉更换为你所使用的的集群的IP,我的单机的是192.168.126.132** 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiongmefeishi/traffic_prediction/7388053734a04f08082b9d877aa26aee49c9d22e/docs/.nojekyll -------------------------------------------------------------------------------- /docs/2020秋招-小米笔试题-小米之家购物.md: -------------------------------------------------------------------------------- 1 | **年轻即出发**... 2 | 3 | **简书**:https://www.jianshu.com/u/7110a2ba6f9e 4 | 5 | **知乎**:https://www.zhihu.com/people/zqtao23/posts 6 | 7 | **GitHub源码**:https://github.com/zqtao2332 8 | 9 | **个人网站**:http://www.zqtaotao.cn/ (停止维护更新内容) 10 | 11 | **QQ交流群**:606939954 12 | 13 | ​ 咆哮怪兽一枚...嗷嗷嗷...趁你现在还有时间,尽你自己最大的努力。努力做成你最想做的那件事,成为你最想成为的那种人,过着你最想过的那种生活。也许我们始终都只是一个小人物,但这并不妨碍我们选择用什么样的方式活下去,这个世界永远比你想的要更精彩。 14 | 15 | 16 | 17 | 最后:喜欢编程,对生活充满激情 18 | 19 | ------ 20 | 21 | ------ 22 | 23 | **本节内容预告** 24 | 25 | 2020秋招-小米笔试题-小米之家购物 26 | 27 | ------ 28 | 29 | ------ 30 | 31 | **2020秋招-小米笔试题-小米之家购物** 32 | 33 | 小米之家购物 ---> N 元钱最少能买几件,求最少not最多 34 | 35 | ``` 36 | 时间限制:C/C++语言 1000MS;其他语言 3000MS 37 | 内存限制:C/C++语言 65536KB;其他语言 589824KB 38 | ``` 39 | 40 | 题目描述: 41 | 小米之家有很多米粉喜欢的产品,产品种类很多,价格也不同。比如某签字笔1元,某充电宝79元,某电池1元,某电视1999元等 42 | 43 | 假设库存不限,小明去小米之家买东西,要用光N元预算的钱,请问他最少能买几件产品? 44 | 45 | 输入 46 | 47 | ``` 48 | 第1行为产品种类数 49 | 50 | 接下来的每行为每种产品的价格 51 | 52 | 最后一行为预算金额 53 | ``` 54 | 55 | 输出 56 | 57 | ``` 58 | 能买到的最少的产品的件数,无法没有匹配的返回-1 59 | ``` 60 | 61 | 样例输入 62 | 63 | ``` 64 | 2 65 | 500 66 | 1 67 | 1000 68 | ``` 69 | 70 | 样例输出 71 | 72 | ``` 73 | 2 74 | ``` 75 | 76 | 动态规划 77 | 78 | ``` 79 | 动态规划 80 | dp[i][j] 表示在可以使用 arr[0..i] 任意货币的情况下,组成 j 需要的最小张数 81 | @param arr 货币价值 82 | @param aim 目标值 83 | @return 组成 aim 最少需要货币张数 84 | * 85 | 完全不使用当前货币arr[i]情况下的最少张数,即 dpl[i-1][j]的值 86 | 只使用1张当前货币arr[i]情况下的最少张数,即 dp[i-1][j-arr[i]] + 1 87 | 只使用2张当前货币arr[i]情况下的最少张数,即 dp[i-1][j-2*arr[i]] + 2 88 | 只使用3张当前货币arr[i]情况下的最少张数,即 dp[i-1][j-3*arr[i]] + 3 89 | * 90 | 总结所有情况下,最终取最小张数 91 | dp[i][j]=min{dp[i-1][j-K*arr[i]] + K} (K >= 0) 92 | * 93 | 简化依赖 94 | dp[i][j]=min{dp[i-1][j], dp[i][j-arr[i]] + 1} 95 | 其中 j-arr[i] <0 时越界,说明arr[i] 值太大,用一张货币都会超过钱数 j 96 | * 97 | dp[i][j] 依赖左边一个值和上边一个值 98 | ``` 99 | 100 | 101 | 102 | ``` 103 | package cn.zqtao.examintion; 104 | 105 | 106 | /** 107 | * 小米之家购物 ---> N 元钱最少能买几件,求最少not最多 108 | * 时间限制:C/C++语言 1000MS;其他语言 3000MS 109 | * 内存限制:C/C++语言 65536KB;其他语言 589824KB 110 | * 题目描述: 111 | * 小米之家有很多米粉喜欢的产品,产品种类很多,价格也不同。比如某签字笔1元,某充电宝79元,某电池1元,某电视1999元等 112 | * 113 | * 假设库存不限,小明去小米之家买东西,要用光N元预算的钱,请问他最少能买几件产品? 114 | * 115 | * 输入 116 | * 第1行为产品种类数 117 | * 118 | * 接下来的每行为每种产品的价格 119 | * 120 | * 最后一行为预算金额 121 | * 122 | * 123 | * 输出 124 | * 能买到的最少的产品的件数,无法没有匹配的返回-1 125 | * 126 | * 127 | * 样例输入 128 | * 2 129 | * 500 130 | * 1 131 | * 1000 132 | * 样例输出 133 | * 2 134 | */ 135 | public class Code_04_MinItemByAllMoney { 136 | public static int solution(int[] prices, int budget) { 137 | if (prices == null || prices.length == 0 || budget < 0) return -1; 138 | 139 | int n = prices.length; 140 | int max = Integer.MAX_VALUE; 141 | int[][] dp = new int[n][budget+1]; 142 | for (int j = 1; j <= budget; j++) { 143 | dp[0][j] = max; 144 | if (j - prices[0] >= 0 && dp[0][j - prices[0]] != max){ 145 | dp[0][j] = dp[0][j - prices[0]] + 1; 146 | } 147 | } 148 | int left = 0; 149 | for (int i = 1; i < n; i++) { 150 | for (int j = 1; j <= budget; j++) { 151 | left = max; 152 | if (j - prices[i] >= 0 && dp[i][j - prices[i]] != max){ 153 | left = dp[i][j - prices[i]] + 1; 154 | } 155 | dp[i][j] = Math.min(left, dp[i - 1][j]); 156 | } 157 | } 158 | return dp[n - 1][budget] != max ? dp[n - 1][budget] : -1; 159 | } 160 | 161 | public static void main(String[] args) { 162 | int[] arr = {500, 1}; 163 | System.out.println(solution(arr, 2001)); 164 | } 165 | } 166 | ``` 167 | 168 | **压缩动态规划表空间** 169 | 170 | ``` 171 | public static int minCoin2(int[] prices, int budget) { 172 | if (prices == null || prices.length == 0 || budget < 0) return -1; 173 | 174 | int[] dp = new int[budget+1]; 175 | int max = Integer.MAX_VALUE; 176 | 177 | for (int j = 1; j <= budget; j++) { // 初始化第一行,只是用prices[0] 的情况下 178 | dp[j] = max; 179 | if (j - prices[0] >= 0 && dp[j - prices[0]] != max) { 180 | dp[j] = dp[j - prices[0]] + 1; 181 | } 182 | } 183 | 184 | int left = 0; 185 | for (int i = 1; i < prices.length; i++) { 186 | for (int j = 1; j <= budget; j++) { 187 | left = max; 188 | if (j - prices[i] >= 0 && dp[j - prices[i]] != max){ 189 | left = dp[j - prices[i]] + 1; 190 | } 191 | dp[j] = Math.min(dp[j], left); 192 | } 193 | } 194 | return dp[budget] == max ? -1 : dp[budget]; 195 | } 196 | ``` 197 | 198 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | - [点击订阅面试进阶指南](https://xiaozhuanlan.com/CyC2018) 2 | 3 | ## ✏️ 算法 4 | 5 | - [剑指 Offer 题解](2020秋招-小米笔试题-小米之家购物.md)
6 | - [剑指 Offer 题解](2020秋招-小米笔试题-小米之家购物.md)
7 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tf_consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.zqtao 8 | tf_consumer 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | 15 | org.apache.kafka 16 | kafka-clients 17 | 0.11.0.2 18 | 19 | 20 | 21 | com.alibaba 22 | fastjson 23 | 1.2.41 24 | 25 | 26 | 27 | org.apache.spark 28 | spark-core_2.11 29 | 2.1.1 30 | 31 | 32 | 33 | org.apache.spark 34 | spark-streaming_2.11 35 | 2.1.1 36 | 37 | 38 | 39 | org.apache.spark 40 | spark-streaming-kafka_2.11 41 | 1.6.3 42 | 43 | 44 | 45 | redis.clients 46 | jedis 47 | 2.9.0 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /tf_consumer/src/main/resources/kafka.properties: -------------------------------------------------------------------------------- 1 | #设置kafka的brokerlist 2 | bootstrap.servers=192.168.126.132:9092 3 | key.deserializer=org.apache.kafka.common.serialization.StringDeserializer 4 | value.deserializer=org.apache.kafka.common.serialization.StringDeserializer 5 | 6 | # 元数据broker列表 7 | metadata.broker.list=192.168.126.132:9092 8 | 9 | acks=all 10 | retries=0 11 | 12 | #设置消费者组 13 | group.id=g_traffic1 14 | 15 | # 设置是否自动确认offset 16 | enable.auto.commit=true 17 | 18 | # 自动确认offset的间隔时间 19 | auto.commit.interval.ms=30000 20 | 21 | # 设置主题 22 | kafka.topics=traffic 23 | 24 | # 设置zk中follower和leader之间的关于kafka的信息同步时间间隔 25 | zookeeper.sync.time.ms=250 26 | num.io.threads=12 27 | batch.size=65536 28 | buffer.memory=524288 29 | # kafka中消息保存的时间 30 | log.retention.hours=2 31 | -------------------------------------------------------------------------------- /tf_consumer/src/main/scala/consumer/SparkConsumer.scala: -------------------------------------------------------------------------------- 1 | package consumer 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.Calendar 5 | 6 | import com.alibaba.fastjson.{JSON, TypeReference} 7 | import kafka.serializer.StringDecoder 8 | import org.apache.spark.streaming.kafka.KafkaUtils 9 | import org.apache.spark.streaming.{Seconds, StreamingContext} 10 | import org.apache.spark.{SparkConf, SparkContext} 11 | import utils.{PropertyUtil, RedisUtil} 12 | 13 | object SparkConsumer { 14 | def main(args: Array[String]): Unit = { 15 | 16 | println("初始化spark") 17 | //初始化Spark 18 | val sparkConf = new SparkConf().setMaster("local[2]").setAppName("TrafficStreaming") 19 | val sc = new SparkContext(sparkConf) 20 | val ssc = new StreamingContext(sc, Seconds(5)) 21 | ssc.checkpoint("./ssc/checkpoint") 22 | 23 | //配置kafka参数 24 | val kafkaParams = Map("metadata.broker.list" -> PropertyUtil.getProperty("metadata.broker.list")) 25 | 26 | //配置kafka主题 27 | val topics = Set(PropertyUtil.getProperty("kafka.topics")) 28 | 29 | //读取kafka主题中的,每一个事件 30 | val kafkaLineDStream = KafkaUtils.createDirectStream[ 31 | String, 32 | String, 33 | StringDecoder, 34 | StringDecoder](ssc, kafkaParams, topics) 35 | .map(_._2) 36 | 37 | //解析json字符串,最终得到的event的形式是:{"0001" -> "57"} 38 | val event = kafkaLineDStream.map(line => { 39 | //使用fastjson解析当前事件中封装的数据信息 40 | val lineJavaMap = JSON.parseObject(line, new TypeReference[java.util.Map[String, String]](){}) 41 | //将这个JavaMap转化成Scala Map 42 | import scala.collection.JavaConverters._ 43 | val lineScalaMap: collection.mutable.Map[String, String] = mapAsScalaMapConverter(lineJavaMap).asScala 44 | println(lineScalaMap) 45 | lineScalaMap 46 | }) 47 | 48 | //将每一条数据按照monitor_id聚合,聚合时每一条数据中的“车辆速度”叠加 49 | //例如,聚合好的数据形式:(monitor_id, (speed, 1)), (0001, (57, 1)) 50 | //最终结果举例:(0001, (1365, 30)) 51 | val sumOfSpeedAndCount = event 52 | .map(e => (e.get("monitor_id").get, e.get("speed").get))//("0001", "57") 53 | .mapValues(s => (s.toInt, 1))//("0001", (57, 1)) (0001, (58, 1)) 54 | .reduceByKeyAndWindow( 55 | (t1: (Int, Int), t2: (Int, Int)) => (t1._1 + t2._1, t1._2 + t2._2), 56 | Seconds(60), 57 | Seconds(60)) 58 | 59 | //定义redis中的数据库索引 60 | val dbIndex = 1 61 | //将采集到的数据,按照每分钟放置于redis中,将用于后边的数据建模 62 | sumOfSpeedAndCount.foreachRDD(rdd => { 63 | rdd.foreachPartition(partitionRecords => { 64 | partitionRecords 65 | .filter((tuple: (String, (Int, Int))) => tuple._2._2 > 0)//过滤车辆个数小于1的情况 66 | .foreach(pair => { 67 | //开始取出这60秒的window中所有的聚合数据 68 | //例如:1356_30 69 | val jedis = RedisUtil.pool.getResource 70 | val monitorId = pair._1 71 | val sumOfSpeed = pair._2._1 72 | val sumOfCarCount = pair._2._2 73 | 74 | //两种情况: 75 | //1、数据生产时,会产生时间戳字段,流入到kafka的事件中 76 | //2、实时数据,数据消费的时间,就是数据时间 77 | val currentTime = Calendar.getInstance().getTime 78 | val dateSDF = new SimpleDateFormat("yyyyMMdd")//用于redis中的key 79 | val hourMinuteSDF = new SimpleDateFormat("HHmm")//用于redis中的fields 80 | 81 | val hourMinuteTime = hourMinuteSDF.format(currentTime)//1634 82 | val date = dateSDF.format(currentTime)//20180203 83 | 84 | jedis.select(dbIndex) 85 | jedis.hset(date + "_" + monitorId, hourMinuteTime, sumOfSpeed + "_" + sumOfCarCount) 86 | println(date + "_" + monitorId) 87 | RedisUtil.pool.returnResource(jedis) 88 | }) 89 | }) 90 | }) 91 | 92 | //spark开始工作 93 | ssc.start() 94 | ssc.awaitTermination() 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tf_consumer/src/main/scala/utils/PropertyUtil.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import java.util.Properties 4 | 5 | object PropertyUtil { 6 | val properties = new Properties() 7 | 8 | try{ 9 | //加载配置属性 10 | val inputStream = ClassLoader.getSystemResourceAsStream("kafka.properties") 11 | properties.load(inputStream) 12 | }catch { 13 | case ex:Exception => println(ex) 14 | }finally {} 15 | 16 | // 通过key得到kafka的属性值 17 | def getProperty(key: String): String = properties.getProperty(key) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tf_consumer/src/main/scala/utils/RedisUtil.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import redis.clients.jedis._ 4 | 5 | object RedisUtil { 6 | //配置redis基本连接参数 7 | val host = "192.168.126.132" 8 | val port = 6379 9 | val timeout = 30000 10 | 11 | val config = new JedisPoolConfig 12 | 13 | //设置允许最大的连接个数 14 | config.setMaxTotal(100) 15 | //最大空闲连接数 16 | config.setMaxIdle(30) 17 | //最小空闲连接数 18 | config.setMinIdle(8) 19 | 20 | //设置连接时的最大等待毫秒数 10 秒 21 | config.setMaxWaitMillis(10000) 22 | //设置在获取连接时,是否检查连接的有效性 23 | config.setTestOnBorrow(true) 24 | //设置释放连接到池中时是否检查有效性 25 | config.setTestOnReturn(true) 26 | 27 | //在连接空闲时,是否检查连接有效性 28 | config.setTestWhileIdle(true) 29 | 30 | //两次扫描之间的时间间隔毫秒数 31 | config.setTimeBetweenEvictionRunsMillis(30000) 32 | //每次扫描的最多的对象数 33 | config.setNumTestsPerEvictionRun(10) 34 | //逐出连接的最小空闲时间,默认是180000(30分钟) 35 | config.setMinEvictableIdleTimeMillis(60000) 36 | 37 | //连接池 38 | lazy val pool = new JedisPool(config, host, port, timeout) 39 | 40 | //释放资源 41 | lazy val hook = new Thread{ 42 | override def run() = { 43 | pool.destroy() 44 | } 45 | } 46 | sys.addShutdownHook(hook) 47 | } 48 | 49 | -------------------------------------------------------------------------------- /tf_consumer/tf_consumer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /tf_modeling/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.zqtao 8 | tf_modeling 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.spark 15 | spark-core_2.11 16 | 2.1.1 17 | 18 | 19 | 20 | redis.clients 21 | jedis 22 | 2.9.0 23 | 24 | 25 | 26 | org.apache.spark 27 | spark-mllib_2.11 28 | 2.1.1 29 | 30 | 31 | -------------------------------------------------------------------------------- /tf_modeling/src/main/scala/modeling/Train.scala: -------------------------------------------------------------------------------- 1 | package modeling 2 | 3 | import java.io.{File, PrintWriter} 4 | import java.text.SimpleDateFormat 5 | import java.util.{Calendar, Date} 6 | 7 | import org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS 8 | import org.apache.spark.mllib.evaluation.MulticlassMetrics 9 | import org.apache.spark.mllib.linalg.Vectors 10 | import org.apache.spark.mllib.regression.LabeledPoint 11 | import org.apache.spark.{SparkConf, SparkContext} 12 | import utils.RedisUtil 13 | 14 | import scala.collection.mutable.ArrayBuffer 15 | 16 | /** 17 | * 数据建模 18 | */ 19 | object Train { 20 | def main(args: Array[String]): Unit = { 21 | //写入文件的输出流 22 | val writer = new PrintWriter(new File("model_train.txt")) 23 | //初始化spark 24 | val sparkConf = new SparkConf() 25 | .setMaster("local[2]") 26 | .setAppName("TrafficModel") 27 | val sc = new SparkContext(sparkConf) 28 | 29 | //定义redis的数据库相关 30 | val dbIndex = 1 31 | //获取redis连接 32 | val jedis = RedisUtil.pool.getResource 33 | jedis.select(dbIndex) 34 | 35 | //设立目标监测点:你要对哪几个监测点进行建模 36 | val monitorIDs = List("0005", "0015") 37 | //取出相关监测点 38 | val monitorRelations = Map[String, Array[String]]( 39 | "0005" -> Array("0003", "0004", "0005", "0006", "0007"), 40 | "0015" -> Array("0013", "0014", "0015", "0016", "0017")) 41 | 42 | //遍历上边所有的监测点,读取数据 43 | monitorIDs.map(monitorID => { 44 | //得到当前“目标监测点”的相关监测点 45 | val monitorRelationArray = monitorRelations(monitorID)//得到的是Array(相关监测点) 46 | 47 | //初始化时间 48 | val currentDate = Calendar.getInstance().getTime 49 | //当前小时分钟数 50 | val hourMinuteSDF = new SimpleDateFormat("HHmm") 51 | //当前年月日 52 | val dateSDF = new SimpleDateFormat("yyyyMMdd") 53 | //以当前时间,格式化好年月日的时间 54 | val dateOfString = dateSDF.format(currentDate) 55 | 56 | //根据“相关监测点”,取得当日的所有的监测点的平均车速 57 | //最终结果样式:(0005, {1033=93_2, 1034=1356_30}) 58 | val relationsInfo = monitorRelationArray.map(monitorID => { 59 | (monitorID, jedis.hgetAll(dateOfString + "_" + monitorID)) 60 | }) 61 | 62 | //确定使用多少小时内的数据进行建模 63 | val hours = 1 64 | //创建3个数组,一个数组用于存放特征向量,一数组用于存放Label向量,一个数组用于存放前两者之间的关联 65 | val dataX = ArrayBuffer[Double]() 66 | val dataY = ArrayBuffer[Double]() 67 | 68 | //用于存放特征向量和特征结果的映射关系 69 | val dataTrain = ArrayBuffer[LabeledPoint]() 70 | 71 | //将时间拉回到1个小时之前,倒序,拉回单位:分钟 72 | 73 | for(i <- Range(60 * hours, 2, -1)){ 74 | dataX.clear() 75 | dataY.clear() 76 | //以下内容包含:线性滤波 77 | for(index <- 0 to 2){ 78 | //当前毫秒数 - 1个小时之前的毫秒数 + 1个小时之前的后0分钟,1分钟,2分钟的毫秒数(第3分钟作为Label向量) 79 | val oneMoment = currentDate.getTime - 60 * i * 1000 + 60 * index * 1000 80 | //拼装出当前(当前for循环这一次的时间)的小时分钟数 81 | val oneHM = hourMinuteSDF.format(new Date(oneMoment))//1441 field 82 | //取得该时刻下里面数据 83 | //取出的数据形式距离:(0005, {1033=93_2, 1034=1356_30}) 84 | for((k, v) <- relationsInfo){ 85 | //如果index==2,意味着前3分钟的数据已经组装到了dataX中,那么下一时刻的数据,如果是目标卡口,则需要存放于dataY中 86 | if(k == monitorID && index == 2){ 87 | val nextMoment = oneMoment + 60 * 1000 88 | val nextHM = hourMinuteSDF.format(new Date(nextMoment))//1027 89 | 90 | //判断是否有数据 91 | if(v.containsKey(nextHM)){ 92 | val speedAndCarCount = v.get(nextHM).split("_") 93 | val valueY = speedAndCarCount(0).toFloat / speedAndCarCount(1).toFloat//得到第4分钟的平均车速 94 | dataY += valueY 95 | } 96 | } 97 | 98 | //组装前3分钟的dataX 99 | if(v.containsKey(oneHM)){ 100 | val speedAndCarCount = v.get(oneHM).split("_") 101 | val valueX = speedAndCarCount(0).toFloat / speedAndCarCount(1).toFloat//得到当前这一分钟的特征值 102 | dataX += valueX 103 | }else{ 104 | dataX += 60.0F // 车速 105 | } 106 | } 107 | } 108 | //准备训练模型 109 | //先将dataX和dataY映射于一个LabeledPoint对象中 110 | if(dataY.toArray.length == 1){ 111 | val label = dataY.toArray.head//答案的平均车速 112 | //label的取值范围是:0~15, 30~60 -----> 0, 1, 2, 3, 4, 5, 6 113 | //真实情况:0~120KM/H车速,划分7个级别,公式就如下: 114 | val record = LabeledPoint(if (label / 10 < 6) (label / 10).toInt else 6, Vectors.dense(dataX.toArray)) 115 | dataTrain += record 116 | } 117 | } 118 | 119 | //将数据集写入到文件中方便查看 120 | dataTrain.foreach(record => { 121 | println(record) 122 | writer.write(record.toString() + "\n") 123 | }) 124 | 125 | //开始组装训练集和测试集 126 | val rddData = sc.parallelize(dataTrain) 127 | val randomSplits = rddData.randomSplit(Array(0.6, 0.4), 11L) 128 | //训练集 129 | val trainData = randomSplits(0) 130 | //测试集 131 | val testData = randomSplits(1) 132 | 133 | //使用训练集进行建模 134 | val model = new LogisticRegressionWithLBFGS().setNumClasses(7).run(trainData) 135 | //完成建模之后,使用测试集,评估模型精确度 136 | val predictionAndLabels = testData.map{ 137 | case LabeledPoint(label, features) => 138 | val prediction = model.predict(features) 139 | (prediction, label) 140 | } 141 | 142 | //得到当前评估值 143 | val metrics = new MulticlassMetrics(predictionAndLabels) 144 | val accuracy = metrics.accuracy//取值范围:0.0~1.0 145 | 146 | println("评估值:" + accuracy) 147 | writer.write(accuracy + "\n") 148 | 149 | //设置评估阈值:超过多少精确度,则保存模型 150 | if(accuracy > 0.0){ 151 | //将模型保存到HDFS 152 | // val hdfsPath = "hdfs://192.168.126.132:8020/traffic/model/" + 153 | val hdfsPath = "hdfs://192.168.126.132:9000/traffic/model/" + 154 | monitorID + 155 | "_" + 156 | new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(System.currentTimeMillis())) 157 | model.save(sc, hdfsPath) 158 | jedis.hset("model", monitorID, hdfsPath) 159 | } 160 | }) 161 | 162 | RedisUtil.pool.returnResource(jedis) 163 | writer.flush 164 | writer.close 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /tf_modeling/src/main/scala/utils/RedisUtil.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import redis.clients.jedis._ 4 | 5 | object RedisUtil { 6 | //配置redis基本连接参数 7 | val host = "192.168.126.132" 8 | val port = 6379 9 | val timeout = 30000 10 | 11 | val config = new JedisPoolConfig 12 | 13 | //设置允许最大的连接个数 14 | config.setMaxTotal(200) 15 | //最大空闲连接数 16 | config.setMaxIdle(50) 17 | //最小空闲连接数 18 | config.setMinIdle(8) 19 | 20 | //设置连接时的最大等待毫秒数 21 | config.setMaxWaitMillis(10000) 22 | //设置在获取连接时,是否检查连接的有效性 23 | config.setTestOnBorrow(true) 24 | //设置释放连接到池中时是否检查有效性 25 | config.setTestOnReturn(true) 26 | 27 | //在连接空闲时,是否检查连接有效性 28 | config.setTestWhileIdle(true) 29 | 30 | //两次扫描之间的时间间隔毫秒数 31 | config.setTimeBetweenEvictionRunsMillis(30000) 32 | //每次扫描的最多的对象数 33 | config.setNumTestsPerEvictionRun(10) 34 | //逐出连接的最小空闲时间,默认是180000(30分钟) 35 | config.setMinEvictableIdleTimeMillis(60000) 36 | 37 | //连接池 38 | lazy val pool = new JedisPool(config, host, port, timeout) 39 | 40 | //释放资源 41 | lazy val hook = new Thread{ 42 | override def run() = { 43 | pool.destroy() 44 | } 45 | } 46 | sys.addShutdownHook(hook) 47 | } 48 | 49 | -------------------------------------------------------------------------------- /tf_modeling/tf_modeling.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /tf_prediction/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.zqtao 8 | tf_prediction 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.spark 15 | spark-core_2.11 16 | 2.1.1 17 | 18 | 19 | 20 | redis.clients 21 | jedis 22 | 2.9.0 23 | 24 | 25 | 26 | org.apache.spark 27 | spark-mllib_2.11 28 | 2.1.1 29 | 30 | 31 | -------------------------------------------------------------------------------- /tf_prediction/src/main/scala/predict/Prediction.scala: -------------------------------------------------------------------------------- 1 | package predict 2 | 3 | import java.text.SimpleDateFormat 4 | 5 | import org.apache.spark.mllib.classification.LogisticRegressionModel 6 | import org.apache.spark.mllib.linalg.Vectors 7 | import org.apache.spark.{SparkConf, SparkContext} 8 | import utils.RedisUtil 9 | 10 | import scala.collection.mutable.ArrayBuffer 11 | 12 | /** 13 | * 堵车预测 14 | */ 15 | object Prediction { 16 | def main(args: Array[String]): Unit = { 17 | //配置spark 18 | val sparkConf = new SparkConf() 19 | .setMaster("local[2]") 20 | .setAppName("TrafficPredict") 21 | 22 | val sc = new SparkContext(sparkConf) 23 | 24 | //时间设置,目的是:为了拼凑出redis中的key和field的字符串 25 | val dateSDF = new SimpleDateFormat("yyyyMMdd") 26 | val hourMinuteSDF = new SimpleDateFormat("HHmm") 27 | //2019-02-05 17:00 28 | val userSDF = new SimpleDateFormat("yyyy-MM-dd HH:mm") 29 | 30 | //定义用于传入的,想要预测是否堵车的日期 31 | // val inputDateString = "2019-06-04 15:41" 32 | // 这里为了测试准确性,使用历史数据进行预测,即由模型推原历史数据 33 | val inputDateString = "2019-06-04 13:56" 34 | val inputDate = userSDF.parse(inputDateString) 35 | 36 | //得到redis中的key,例如:20190205 37 | val dayOfInputDate = dateSDF.format(inputDate) 38 | //1700 39 | val hourMinuteOfInputDate = hourMinuteSDF.format(inputDate) 40 | 41 | val dbIndex = 1 42 | val jedis = RedisUtil.pool.getResource 43 | jedis.select(dbIndex) 44 | 45 | 46 | //想要预测的监测点 47 | val monitorIDs = List("0005", "0015") 48 | val monitorRelations = Map[String, Array[String]]( 49 | "0005" -> Array("0003", "0004", "0005", "0006", "0007"), 50 | "0015" -> Array("0013", "0014", "0015", "0016", "0017")) 51 | 52 | monitorIDs.map(monitorID => { 53 | val monitorRealtionArray = monitorRelations(monitorID) 54 | val relationInfo = monitorRealtionArray.map(monitorID => { 55 | (monitorID, jedis.hgetAll(dayOfInputDate + "_" + monitorID)) 56 | }) 57 | 58 | //装载目标时间点之前的3分钟的历史数据 59 | val dataX = ArrayBuffer[Double]() 60 | 61 | //组装数据的过程 62 | for (index <- Range(3, 0, -1)) { 63 | val oneMoment = inputDate.getTime - 60 * index * 1000 64 | val oneHM = hourMinuteSDF.format(oneMoment) //1657 65 | 66 | for ((k, v) <- relationInfo) { 67 | if (v.containsKey(oneHM)) { 68 | val speedAndCarCount = v.get(oneHM).split("_") 69 | val valueX = speedAndCarCount(0).toFloat / speedAndCarCount(1).toFloat 70 | dataX += valueX 71 | } else { 72 | dataX += 60.0F 73 | } 74 | } 75 | } 76 | 77 | // 打印三分钟之前的数据 78 | println(dataX) 79 | //加载模型 80 | val modelPath = jedis.hget("model", monitorID) 81 | val model = LogisticRegressionModel.load(sc, modelPath) 82 | 83 | //预测 84 | val prediction = model.predict(Vectors.dense(dataX.toArray)) 85 | println(monitorID + ",堵车评估值:" + prediction + ",是否通畅:" + (if (prediction >= 3) "通畅" else "拥堵")) 86 | }) 87 | RedisUtil.pool.returnResource(jedis) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tf_prediction/src/main/scala/utils/RedisUtil.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import redis.clients.jedis._ 4 | 5 | object RedisUtil { 6 | //配置redis基本连接参数 7 | val host = "192.168.126.132" 8 | val port = 6379 9 | val timeout = 30000 10 | 11 | val config = new JedisPoolConfig 12 | 13 | //设置允许最大的连接个数 14 | config.setMaxTotal(200) 15 | //最大空闲连接数 16 | config.setMaxIdle(50) 17 | //最小空闲连接数 18 | config.setMinIdle(8) 19 | 20 | //设置连接时的最大等待毫秒数 21 | config.setMaxWaitMillis(10000) 22 | //设置在获取连接时,是否检查连接的有效性 23 | config.setTestOnBorrow(true) 24 | //设置释放连接到池中时是否检查有效性 25 | config.setTestOnReturn(true) 26 | 27 | //在连接空闲时,是否检查连接有效性 28 | config.setTestWhileIdle(true) 29 | 30 | //两次扫描之间的时间间隔毫秒数 31 | config.setTimeBetweenEvictionRunsMillis(30000) 32 | //每次扫描的最多的对象数 33 | config.setNumTestsPerEvictionRun(10) 34 | //逐出连接的最小空闲时间,默认是180000(30分钟) 35 | config.setMinEvictableIdleTimeMillis(60000) 36 | 37 | //连接池 38 | lazy val pool = new JedisPool(config, host, port, timeout) 39 | 40 | //释放资源 41 | lazy val hook = new Thread{ 42 | override def run() = { 43 | pool.destroy() 44 | } 45 | } 46 | sys.addShutdownHook(hook) 47 | } 48 | 49 | -------------------------------------------------------------------------------- /tf_prediction/tf_prediction.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /tf_producer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.zqtao 8 | tf_producer 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.kafka 14 | kafka-clients 15 | 0.11.0.2 16 | 17 | 18 | 19 | com.alibaba 20 | fastjson 21 | 1.2.41 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tf_producer/src/main/resources/kafka.properties: -------------------------------------------------------------------------------- 1 | #设置kafka的brokerlist 2 | bootstrap.servers=192.168.126.132:9092 3 | key.serializer=org.apache.kafka.common.serialization.StringSerializer 4 | value.serializer=org.apache.kafka.common.serialization.StringSerializer 5 | 6 | acks=all 7 | retries=0 8 | 9 | #设置消费者组 10 | group.id=g_traffic1 11 | 12 | # 设置是否自动确认offset 13 | enable.auto.commit=true 14 | 15 | # 自动确认offset的间隔时间 16 | auto.commit.interval.ms=30000 17 | 18 | # 设置主题 19 | kafka.topics=traffic 20 | 21 | # 设置zk中follower和leader之间的关于kafka的信息同步时间间隔 22 | zookeeper.sync.time.ms=250 23 | num.io.threads=12 24 | batch.size=65536 25 | buffer.memory=524288 26 | # kafka中消息保存的时间 27 | log.retention.hours=2 28 | -------------------------------------------------------------------------------- /tf_producer/src/main/scala/producer/Producer.scala: -------------------------------------------------------------------------------- 1 | package producer 2 | 3 | import java.text.DecimalFormat 4 | import java.util.Calendar 5 | 6 | import org.apache.kafka.clients.producer.{KafkaProducer, ProducerRecord} 7 | import utils.PropertyUtil 8 | 9 | import scala.util.Random 10 | import java._ 11 | 12 | import com.alibaba.fastjson.JSON 13 | /** 14 | * 模拟数据生产 15 | * 随机产生:监测点id,车速(按照5分钟的频率变换堵车状态) 16 | * 序列化为Json 17 | * 发送给kafka 18 | */ 19 | object Producer { 20 | def main(args: Array[String]): Unit = { 21 | //读取kafka配置信息 22 | val props = PropertyUtil.properties 23 | //创建Kafka生产者对象 24 | val producer = new KafkaProducer[String, String](props) 25 | 26 | //模拟产生实时数据,单位:秒 27 | var startTime = Calendar.getInstance().getTimeInMillis() / 1000 28 | //数据模拟,堵车状态切换的周期单位为:秒 29 | val trafficCycle = 300 30 | //开始不停的产生实时数据 31 | val df = new DecimalFormat("0000") 32 | while(true){ 33 | //模拟产生监测点id:0001~0020 34 | val randomMonitorId = df.format(Random.nextInt(20) + 1) 35 | //模拟车速 36 | var randomSpeed = "000" 37 | //得到本条数据产生时的当前时间,单位:秒 38 | val currentTime = Calendar.getInstance().getTimeInMillis() / 1000 39 | //每5分钟切换一次公路状态 40 | if(currentTime - startTime > trafficCycle){ 41 | randomSpeed = new DecimalFormat("000").format(Random.nextInt(15)) 42 | if(currentTime - startTime > trafficCycle * 2){ 43 | startTime = currentTime 44 | } 45 | }else{ 46 | randomSpeed = new DecimalFormat("000").format(Random.nextInt(30) + 30) 47 | } 48 | 49 | //该Map集合用于存放产生出来的数据 50 | val jsonMap = new util.HashMap[String, String]() 51 | jsonMap.put("monitor_id", randomMonitorId) 52 | jsonMap.put("speed", randomSpeed) 53 | 54 | //序列化 55 | val event = JSON.toJSON(jsonMap) 56 | println(event) 57 | 58 | //发送事件到kafka集群中 59 | producer.send(new ProducerRecord[String, String](PropertyUtil.getProperty("kafka.topics"), event.toString)) 60 | Thread.sleep(100) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tf_producer/src/main/scala/utils/PropertyUtil.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import java.util.Properties 4 | 5 | object PropertyUtil { 6 | val properties = new Properties() 7 | 8 | try{ 9 | //加载 kafka 配置属性 10 | val inputStream = ClassLoader.getSystemResourceAsStream("kafka.properties") 11 | properties.load(inputStream) 12 | }catch { 13 | case ex:Exception => println(ex) 14 | }finally {} 15 | 16 | // 通过key得到kafka的属性值 17 | def getProperty(key: String): String = properties.getProperty(key) 18 | 19 | } --------------------------------------------------------------------------------