爱奇艺分类点击实时统计

项目源码:

SparkStreaming部分:https://gitee.com/jenrey/project_two

SpringBoot部分:https://gitee.com/jenrey/project_two_two

1.项目需求


2.项目过程


3.数据格式

ver=1&en=e_pv&pl=website&sdk=js&b_rst=1920*1080&u_ud=12GH4079-223E-4A57-AC60-C1A04D8F7A2F&l=zh-CN&u_sd=8E9559B3-DA35-44E1-AC98-85EB37D1F263&c_time=1449137597979&p_url=http://list.iqiyi.com/www/4/---.html

4.项目开发

4.1 配置maven

   
   
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0 </modelVersion>
  6. <groupId>com.aura.spark </groupId>
  7. <artifactId>1711categorycount </artifactId>
  8. <version>1.0-SNAPSHOT </version>
  9. <dependencies>
  10. <dependency>
  11. <groupId>org.apache.hadoop </groupId>
  12. <artifactId>hadoop-client </artifactId>
  13. <version>2.6.5 </version>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.apache.spark </groupId>
  17. <artifactId>spark-streaming_2.11 </artifactId>
  18. <version>2.3.0 </version>
  19. </dependency>
  20. <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming-kafka-0-8_2.11 -->
  21. <dependency>
  22. <groupId>org.apache.spark </groupId>
  23. <artifactId>spark-streaming-kafka-0-8_2.11 </artifactId>
  24. <version>2.3.0 </version>
  25. </dependency>
  26. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-client -->
  27. <dependency>
  28. <groupId>org.apache.hbase </groupId>
  29. <artifactId>hbase-client </artifactId>
  30. <version>0.98.6-hadoop2 </version>
  31. </dependency>
  32. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-server -->
  33. <dependency>
  34. <groupId>org.apache.hbase </groupId>
  35. <artifactId>hbase-server </artifactId>
  36. <version>0.98.6-hadoop2 </version>
  37. </dependency>
  38. </dependencies>
  39. </project>
4.2 模拟生成样本数据

   
   
  1. package com.jenrey.spark.utils;
  2. import java.io.BufferedWriter;
  3. import java.io.FileWriter;
  4. import java.io.IOException;
  5. import java.util.Random;
  6. /**
  7. * 模拟两万条数据并且保存到指定目录
  8. */
  9. public class SimulateData {
  10. public static void main(String[] args) {
  11. BufferedWriter bw = null;
  12. try {
  13. bw = new BufferedWriter( new FileWriter( "G:\\workspace\\categorycount\\src\\main\\java\\com\\jenrey\\spark\\data.txt"));
  14. int i = 0;
  15. while (i < 20000) {
  16. long time = System.currentTimeMillis();
  17. int categoryid = new Random().nextInt( 23);
  18. bw.write( "ver=1&en=e_pv&pl=website&sdk=js&b_rst=1920*1080&u_ud=12GH4079-223E-4A57-AC60-C1A04D8F7A2F&l=zh-CN&u_sd=8E9559B3-DA35-44E1-AC98-85EB37D1F263&c_time=" + time + "&p_url=http://list.iqiyi.com/www/" + categoryid + "/---.html");
  19. bw.newLine(); //换行
  20. i++;
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. } finally {
  25. try {
  26. bw.close();
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. } finally {
  30. }
  31. }
  32. }
  33. }

运行代码即可,会生成模拟数据

4.3 使用SparkStreaming从kafka中读取数据

   
   
  1. package com.jenrey.spark.category;
  2. import kafka.serializer.StringDecoder;
  3. import org.apache.spark.SparkConf;
  4. import org.apache.spark.api.java.JavaSparkContext;
  5. import org.apache.spark.api.java.function.Function;
  6. import org.apache.spark.streaming.Durations;
  7. import org.apache.spark.streaming.api.java.JavaDStream;
  8. import org.apache.spark.streaming.api.java.JavaStreamingContext;
  9. import org.apache.spark.streaming.kafka.KafkaUtils;
  10. import scala.Tuple2;
  11. import java.util.HashMap;
  12. import java.util.HashSet;
  13. /**
  14. * SparkStreaming的数据来源来自于Kafka的topics的aura
  15. */
  16. public class CategoryRealCount {
  17. public static void main(String[] args) {
  18. //初始化程序入口
  19. SparkConf conf = new SparkConf();
  20. conf.setMaster( "local");
  21. conf.setAppName( "CategoryRealCount");
  22. JavaSparkContext sc = new JavaSparkContext(conf);
  23. JavaStreamingContext ssc = new JavaStreamingContext(sc, Durations.seconds( 3));
  24. /*或者使用下面方法就自动创建SparkContext()
  25. JavaStreamingContext ssc = new JavaStreamingContext(conf, Durations.seconds(3));*/
  26. //读取数据
  27. HashMap<String, String> KafkaParams = new HashMap<>();
  28. KafkaParams.put( "metadata.broker.list", "hadoop04:9092");
  29. HashSet<String> topics = new HashSet<>();
  30. topics.add( "aura");
  31. JavaDStream<String> logDStream = KafkaUtils.createDirectStream(
  32. ssc,
  33. String.class,
  34. String.class,
  35. StringDecoder.class,
  36. StringDecoder.class,
  37. KafkaParams,
  38. topics
  39. ).map( new Function<Tuple2<String, String>, String>() {
  40. @Override
  41. public String call(Tuple2<String, String> tuple2) throws Exception {
  42. //kafka读出来数据是kv的形式[String代表k的数据类型(k可就是偏移位置的信息, String代表v的数据类型(kafka内每一条数据), StringDecoder代表的就是解码器, StringDecoder]
  43. //直接返回的是InputDStream[(String,String)]的KV数据类型,因为偏移位置的信息对我们是没有用的所以我们要.map(_._2)
  44. return tuple2._2;
  45. }
  46. });
  47. //代码的逻辑
  48. logDStream.print();
  49. //启动应用程序
  50. ssc.start();
  51. try {
  52. ssc.awaitTermination();
  53. } catch (InterruptedException e) {
  54. e.printStackTrace();
  55. }
  56. ssc.stop();
  57. }
  58. }

5.启动kafka的HA集群

先启动ZK

[hadoop@hadoop03 kafka_2.11-0.8.2.0]$ bin/kafka-server-start.sh config/server.properties

[hadoop@hadoop04 kafka_2.11-0.8.2.0]$ bin/kafka-server-start.sh config/server.properties

[hadoop@hadoop05 kafka_2.11-0.8.2.0]$ bin/kafka-server-start.sh config/server.properties

创建topics

[hadoop@hadoop04 kafka_2.11-0.8.2.0]$ bin/kafka-topics.sh --create --zookeeper hadoop02:2181 --replication-factor 3 --partitions 3 --topic aura


查看topics副本

[hadoop@hadoop03 kafka_2.11-0.8.2.0]$ bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic aura

删除topics(此方式不一定会删除彻底,理论上还要去tmp/kafka-log/下删除数据及分区,如果flume写入topics不成功建议换个topics试试)

[hadoop@hadoop03 kafka_2.11-0.8.2.0]$ bin/kafka-topics.sh --delete --zookeeper localhost:2181 --topic aura

那么此时的删除并不是真正的删除,而是把topic标记为:marked for deletion

进入zk

zkCli.sh

找到topic所在的目录:ls /brokers/topics

到要删除的topic,执行命令:rmr /brokers/topics/【topic name】即可,此时topic被彻底删除。

6.模拟数据实时产生

[hadoop@hadoop04 ~]$ cat data.txt | while read line  
> do
> echo "$line" >> data.log
> sleep 0.5
> done


克隆会话,使用 tail -F data.log 命令查看实时产生的数据


7.编写并运行flume

[hadoop@hadoop04 ~]$ touch file2kafka.properties

[hadoop@hadoop04 ~]$ vim file2kafka.properties


   
   
  1. a1.sources = r1
  2. a1.sinks = k1
  3. a1.channels = c1
  4. #source
  5. a1.sources.r1.type = exec
  6. a1.sources.r1.command = tail -F /home/hadoop/data.log
  7. #channel
  8. a1.channels.c1.type = memory
  9. #sink
  10. a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
  11. a1.sinks.k1.topic = aura
  12. a1.sinks.k1.brokerList = hadoop04:9092
  13. a1.sinks.k1.requiredAcks = 1
  14. a1.sinks.k1.batchSize = 5
  15. #source -> channel -> sink
  16. a1.sources.r1.channels = c1
  17. a1.sinks.k1.channel = c1

启动flume:

[hadoop@hadoop04 ~]$ flume-ng agent --conf conf --conf-file file2kafka.properties --name a1 -Dflume.hadoop.logger=INFO,console 


或者是下图的日志:



查看kafka消费者能否消费的到数据:

[hadoop@hadoop04 kafka_2.11-0.8.2.0]$ bin/kafka-console-consumer.sh --zookeeper hadoop02:2181 --topic aura --from-beginning



运行categorycount.java程序后的效果图:



出现上图即代表:flume采集日志存到kafka中,然后SparkStreaming从kafka消费数据成功

8.开发HBase相关部分

8.0 开发bean 抽象出来存入HBase数据对象



   
   
  1. package com.jenrey.bean;
  2. import java.io.Serializable;
  3. /**
  4. * 使用面向对象的思想抽象出来的存入HBase中数据类型的类
  5. */
  6. public class CategoryClickCount implements Serializable {
  7. //点击的品类
  8. private String name;
  9. //点击的次数
  10. private long value;
  11. public CategoryClickCount(String name, long value) {
  12. this.name = name;
  13. this.value = value;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. public long getValue() {
  22. return value;
  23. }
  24. public void setValue(long value) {
  25. this.value = value;
  26. }
  27. }

8.1 开发HBaseDao的代码

   
   
  1. package com.jenrey.hbase.dao;
  2. import com.jenrey.bean.CategoryClickCount;
  3. import java.util.List;
  4. /**
  5. * 访问HBase数据库的一些方法
  6. */
  7. public interface HBaseDao {
  8. //往hbase里面插入一条数据
  9. public void save(String tableName, String rowkey, String family, String q, long value);
  10. //按照表名和rowkey查询数据
  11. public List<CategoryClickCount> count(String tableName, String rowkey);
  12. }
8.2 HBase的API实现类

HBaseImpl.java的代码如下:


   
   
  1. package com.jenrey.hbase.dao.impl;
  2. import com.jenrey.bean.CategoryClickCount;
  3. import com.jenrey.hbase.dao.HBaseDao;
  4. import org.apache.hadoop.conf.Configuration;
  5. import org.apache.hadoop.hbase.Cell;
  6. import org.apache.hadoop.hbase.CellUtil;
  7. import org.apache.hadoop.hbase.HBaseConfiguration;
  8. import org.apache.hadoop.hbase.client.*;
  9. import org.apache.hadoop.hbase.filter.PrefixFilter;
  10. import org.apache.hadoop.hbase.util.Bytes;
  11. import java.io.IOException;
  12. import java.util.ArrayList;
  13. import java.util.List;
  14. /**
  15. * HBaseAPI的实现类
  16. */
  17. public class HBaseImpl implements HBaseDao {
  18. //获取数据库连接,类似于JDBC里面的连接池的概念
  19. HConnection htablePool = null;
  20. //只要一new就执行下面构造方法里面的代码,即创建并连接HBase成功
  21. public HBaseImpl() {
  22. //创建HBase的配置文件
  23. Configuration conf = HBaseConfiguration.create();
  24. //设置zookeeper
  25. conf.set( "hbase.zookeeper.quorum", "hadoop02:2181");
  26. try {
  27. //创建连接
  28. htablePool = HConnectionManager.createConnection(conf);
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. /**
  34. * 根据表名获取表对象
  35. *
  36. * @param tableName 表名
  37. * @return 表对象
  38. * HTableInterface 得到这个表的名称(HBase自带API)
  39. */
  40. public HTableInterface getTable(String tableName) {
  41. HTableInterface table = null;
  42. try {
  43. //根据表名获取表对象
  44. table = htablePool.getTable(tableName);
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. }
  48. return table;
  49. }
  50. //
  51. /**
  52. * 实现往hbase里面插入一条数据
  53. *
  54. * @param tableName 表名
  55. * @param rowkey rowkey
  56. * @param family 列簇
  57. * @param q 品类
  58. * @param value 出现了多少次
  59. * hbase:只有一种数据类型,字节数组
  60. */
  61. @Override
  62. public void save(String tableName, String rowkey, String family, String q, long value) {
  63. HTableInterface table = getTable(tableName);
  64. try {
  65. //incrementColumnValue会自动帮我们累加value的值。不需要在读取、累加、返回值
  66. table.incrementColumnValue(rowkey.getBytes(), family.getBytes(), q.getBytes(), value);
  67. } catch (IOException e) {
  68. e.printStackTrace();
  69. } finally {
  70. if (table != null) {
  71. try {
  72. table.close();
  73. } catch (IOException e) {
  74. e.printStackTrace();
  75. }
  76. }
  77. }
  78. }
  79. //
  80. /**
  81. * 实现按照表名和rowkey查询数据
  82. *
  83. * @param tableName 表名
  84. * @param rowkey rowkey
  85. * @return
  86. */
  87. @Override
  88. public List<CategoryClickCount> count(String tableName, String rowkey) {
  89. //我们要的是电影和电视剧出现的次数,所以要封装成一个对象存到List集合中
  90. ArrayList<CategoryClickCount> list = new ArrayList<>();
  91. //获取表对象
  92. HTableInterface table = getTable(tableName);
  93. //前缀过滤器
  94. PrefixFilter prefixFilter = new PrefixFilter(rowkey.getBytes());
  95. //创建扫描仪
  96. Scan scan = new Scan();
  97. //给扫描仪安装过滤器
  98. scan.setFilter(prefixFilter);
  99. //拿着扫描仪去获取数据
  100. try {
  101. ResultScanner scanner = table.getScanner(scan);
  102. //遍历集合,一个结果就是一个Result对象
  103. for (Result result : scanner) {
  104. //我们要的是电影和电视剧出现的次数,所以要封装成一个对象存到List集合中,既是CategoryClickCount对象,name value
  105. //rowkey date_name
  106. //遍历里面的每一个东西,rawCells把里面的每一个东西都看成一个单元格
  107. for(Cell cell:result.rawCells()){
  108. byte[] date_name = CellUtil.cloneRow(cell);
  109. String name = new String(date_name).split( "_")[ 1];
  110. byte[] value = CellUtil.cloneValue(cell);
  111. long count = Bytes.toLong(value);
  112. CategoryClickCount categoryClickCount = new CategoryClickCount(name, count);
  113. list.add(categoryClickCount);
  114. }
  115. }
  116. } catch (IOException e) {
  117. e.printStackTrace();
  118. } finally {
  119. if (table != null) {
  120. try {
  121. table.close();
  122. } catch (IOException e) {
  123. e.printStackTrace();
  124. }
  125. }
  126. }
  127. return list;
  128. }
  129. }

启动hbase集群:

(先启动zk,在启动hdfs)

 启动 HBase 集群启动命令(只在hadoop02启动就可以了,在哪个节点启动哪个节点就是active):start-hbase.sh

查看是否启动成功:


通过访问浏览器页面,格式为”主节点:16010”http://hadoop02:16010/


进入hbase的客户端:

hbase shell 

执行下面的命令创建表:

create 'aura','f'


8.3 测试HBaseAPI代码

先创建一个HbaseFactory.java文件,代码如下:


   
   
  1. package com.jenrey.hbase.dao.factory;
  2. import com.jenrey.hbase.dao.HBaseDao;
  3. import com.jenrey.hbase.dao.impl.HBaseImpl;
  4. /**
  5. * 返回一个HBaseDao对象,HBaseDao是一个接口,HBaseImpl是它的实现类
  6. */
  7. public class HBaseFactory {
  8. public static HBaseDao getHBaseDao() {
  9. return new HBaseImpl();
  10. }
  11. }

再创建一个测试类:

Test.java的代码如下:


   
   
  1. package com.jenrey.test;
  2. import com.jenrey.bean.CategoryClickCount;
  3. import com.jenrey.hbase.dao.HBaseDao;
  4. import com.jenrey.hbase.dao.factory.HBaseFactory;
  5. import java.util.List;
  6. /**
  7. * 测试HBaseImpl代码 (其实也就是测试HBase的API)
  8. */
  9. public class Test {
  10. public static void main(String[] args) {
  11. //TODO:向HBase中插入数据
  12. //获取到HBaseImpl对象
  13. HBaseDao hBaseDao = HBaseFactory.getHBaseDao();
  14. //调用HBaseImpl的实现类的save方法往hbase里面插入一条数据的方法
  15. hBaseDao.save( "aura", "2018-5-23_电影", "f", "name", 10L);
  16. hBaseDao.save( "aura", "2018-5-23_电影", "f", "name", 20L);
  17. List<CategoryClickCount> list = hBaseDao.count( "aura", "2018-5-23_电影");
  18. for(CategoryClickCount cc:list){
  19. System.out.println(cc.getName()+ " "+cc.getCount());
  20. }
  21. }
  22. }

运行测试代码,结果如下即为HBaseAPI成功


再次测试一下代码:


   
   
  1. package com.jenrey.test;
  2. import com.jenrey.bean.CategoryClickCount;
  3. import com.jenrey.hbase.dao.HBaseDao;
  4. import com.jenrey.hbase.dao.factory.HBaseFactory;
  5. import java.util.List;
  6. /**
  7. * 测试HBaseImpl代码 (其实也就是测试HBase的API)
  8. */
  9. public class Test {
  10. public static void main(String[] args) {
  11. //TODO:向HBase中插入数据
  12. //获取到HBaseImpl对象
  13. HBaseDao hBaseDao = HBaseFactory.getHBaseDao();
  14. //调用HBaseImpl的实现类的save方法往hbase里面插入一条数据的方法
  15. /*hBaseDao.save("aura","2018-5-23_电影","f","name",10L);
  16. hBaseDao.save("aura","2018-5-23_电影","f","name",20L);*/
  17. /* List<CategoryClickCount> list = hBaseDao.count("aura", "2018-5-23_电影");
  18. for(CategoryClickCount cc:list){
  19. System.out.println(cc.getName()+" "+cc.getCount());
  20. }*/
  21. hBaseDao.save( "aura", "2018-5-21_电视剧", "f", "name", 11L);
  22. hBaseDao.save( "aura", "2018-5-21_电视剧", "f", "name", 24L);
  23. hBaseDao.save( "aura", "2018-5-23_电视剧", "f", "name", 110L);
  24. hBaseDao.save( "aura", "2018-5-23_电视剧", "f", "name", 210L);
  25. List<CategoryClickCount> list = hBaseDao.count( "aura", "2018-5-2");
  26. for (CategoryClickCount cc : list) {
  27. System.out.println(cc.getName() + " " + cc.getCount());
  28. }
  29. }
  30. }

效果如下:



8.4 完善SparkStreaming代码的开发逻辑

先编写工具类:Utils.java 代码如下


   
   
  1. package com.jenrey.utils;
  2. import java.text.SimpleDateFormat;
  3. import java.util.Date;
  4. import java.util.HashMap;
  5. /**
  6. * 工具类:传入一行数据,返回我们要的数据格式:日期_品类 的字符串
  7. */
  8. public class Utils {
  9. //传入一行内容,返回一个Key
  10. public static String getKey(String line) {
  11. HashMap<String, String> map = new HashMap<String, String>();
  12. map.put( "0", "其他");
  13. map.put( "1", "电视剧");
  14. map.put( "2", "电影");
  15. map.put( "3", "综艺");
  16. map.put( "4", "动漫");
  17. map.put( "5", "纪录片");
  18. map.put( "6", "游戏");
  19. map.put( "7", "资讯");
  20. map.put( "8", "娱乐");
  21. map.put( "9", "财经");
  22. map.put( "10", "网络电影");
  23. map.put( "11", "片花");
  24. map.put( "12", "音乐");
  25. map.put( "13", "军事");
  26. map.put( "14", "教育");
  27. map.put( "15", "体育");
  28. map.put( "16", "儿童");
  29. map.put( "17", "旅游");
  30. map.put( "18", "时尚");
  31. map.put( "19", "生活");
  32. map.put( "20", "汽车");
  33. map.put( "21", "搞笑");
  34. map.put( "22", "广告");
  35. map.put( "23", "原创");
  36. //TODO:切分数据,返回品类ID
  37. String categoryid = line.split( "&")[ 9].split( "/")[ 4];
  38. //获取到品类的名称
  39. String name = map.get(categoryid);
  40. //TODO:切分数据,返回日期
  41. String stringTime = line.split( "&")[ 8].split( "=")[ 1];
  42. //获取日期
  43. String date = getDay(Long.valueOf(stringTime));
  44. //返回我们要的日期_品类的对象
  45. return date+ "_"+name;
  46. }
  47. //格式化日期为我们能看懂的格式
  48. public static String getDay(long time) {
  49. SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MMM-dd");
  50. return simpleDateFormat.format( new Date(time));
  51. }
  52. }

完善之前4.3 的代码CategoryRealCount.java 完善代码逻辑(不再是输出到控制台,而是写入HBase中),先不要运行。先运行Test.java查询一下是否有22号这天的数据,结果再下文有介绍


   
   
  1. package com.jenrey.spark.category;
  2. import com.jenrey.hbase.dao.HBaseDao;
  3. import com.jenrey.hbase.dao.factory.HBaseFactory;
  4. import com.jenrey.utils.Utils;
  5. import kafka.serializer.StringDecoder;
  6. import org.apache.spark.SparkConf;
  7. import org.apache.spark.api.java.JavaPairRDD;
  8. import org.apache.spark.api.java.function.Function;
  9. import org.apache.spark.api.java.function.Function2;
  10. import org.apache.spark.api.java.function.PairFunction;
  11. import org.apache.spark.api.java.function.VoidFunction;
  12. import org.apache.spark.streaming.Durations;
  13. import org.apache.spark.streaming.api.java.JavaStreamingContext;
  14. import org.apache.spark.streaming.kafka.KafkaUtils;
  15. import scala.Tuple2;
  16. import java.util.HashMap;
  17. import java.util.HashSet;
  18. import java.util.Iterator;
  19. /**
  20. * SparkStreaming的数据来源来自于Kafka的topics的aura
  21. */
  22. public class CategoryRealCount {
  23. public static void main(String[] args) {
  24. /**
  25. * 初始化程序入口
  26. */
  27. SparkConf conf = new SparkConf();
  28. conf.setMaster( "local");
  29. conf.setAppName( "CategoryRealCount");
  30. /*JavaSparkContext sc = new JavaSparkContext(conf);
  31. JavaStreamingContext ssc = new JavaStreamingContext(sc, Durations.seconds(3));*/
  32. //或者使用下面方法就自动创建SparkContext()
  33. JavaStreamingContext ssc = new JavaStreamingContext(conf, Durations.seconds( 3));
  34. ssc.checkpoint( "/home/hadoop/checkpoint");
  35. /**
  36. * 读取数据
  37. */
  38. HashMap<String, String> KafkaParams = new HashMap<>();
  39. KafkaParams.put( "metadata.broker.list", "hadoop04:9092");
  40. HashSet<String> topics = new HashSet<>();
  41. topics.add( "test");
  42. // createDirectStream使用直连的方式读取kafka中的数据
  43. KafkaUtils.createDirectStream(
  44. ssc,
  45. String.class, //返回的key类型
  46. String.class, //返回的value类型
  47. StringDecoder.class, //解码器
  48. StringDecoder.class, //解码器
  49. KafkaParams,
  50. topics
  51. ).map( new Function<Tuple2<String, String>, String>() {
  52. @Override
  53. public String call(Tuple2<String, String> tuple2) throws Exception {
  54. //kafka读出来数据是kv的形式[String代表k的数据类型(k可就是偏移位置的信息, String代表v的数据类型(kafka内每一条数据), StringDecoder代表的就是解码器, StringDecoder]
  55. //直接返回的是InputDStream[(String,String)]的KV数据类型,因为偏移位置的信息对我们是没有用的所以我们要.map(_._2)
  56. return tuple2._2;
  57. }
  58. })
  59. /**
  60. * 代码的逻辑
  61. */
  62. //logDStream.print();
  63. .mapToPair( new PairFunction<String, String, Long>() {
  64. public Tuple2<String, Long> call(String line) throws Exception {
  65. return new Tuple2<String, Long>(Utils.getKey(line), 1L);
  66. }
  67. }).reduceByKey( new Function2<Long, Long, Long>() {
  68. @Override
  69. public Long call(Long x, Long y) throws Exception {
  70. return x + y;
  71. }
  72. })
  73. //TODO:插入到HBase数据库
  74. .foreachRDD( new VoidFunction<JavaPairRDD<String, Long>>() {
  75. @Override
  76. public void call(JavaPairRDD<String, Long> rdd) throws Exception {
  77. rdd.foreachPartition( new VoidFunction<Iterator<Tuple2<String, Long>>>() {
  78. @Override
  79. public void call(Iterator<Tuple2<String, Long>> partition) throws Exception {
  80. //获取连接HBase的连接对象
  81. HBaseDao hBaseDao = HBaseFactory.getHBaseDao();
  82. while (partition.hasNext()) {
  83. Tuple2<String, Long> tuple = partition.next();
  84. hBaseDao.save( "aura", tuple._1, "f", "name", tuple._2);
  85. System.out.println(tuple._1 + " " + tuple._2);
  86. }
  87. }
  88. });
  89. }
  90. });
  91. /**
  92. * 启动应用程序
  93. */
  94. ssc.start();
  95. try {
  96. ssc.awaitTermination();
  97. } catch (InterruptedException e) {
  98. e.printStackTrace();
  99. }
  100. ssc.stop();
  101. }
  102. }

修改Test.java中的代码如下:(目的在于从HBase中查询上面的代码是否写入到HBase中)

下图是未运行写入HBase数据的代码(CategoryRealCount.java )截图:


运行CategoryRealCount.java 代码;

下图是运行CategoryRealCount.java 后的截图


下图是运行CategoryRealCount.java的结果截图:


需要注意的是图中为中文字,所以使用Test.java查询的时候要注意传入的日期

9 Springboot项目

9.1 创建SpringBoot


如果成功就一直下一步,如果失败请参照下图


进入https://start.spring.io

然后下载压缩包在解压。在通过IDEA导入项目

9.2 开发SpringBoot项目
9.2.1 新建templates 目录

如果我们要写前后台的项目的话,除了导入包还固定要有一个目录 templates 名字不能瞎起,必须叫这个名


9.2.2 配置pom.xml 文件

   
   
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0 </modelVersion>
  5. <groupId>com.jenrey </groupId>
  6. <artifactId>spring_boot </artifactId>
  7. <version>0.0.1-SNAPSHOT </version>
  8. <packaging>jar </packaging>
  9. <name>spring_boot </name>
  10. <description>Demo project for Spring Boot </description>
  11. <parent>
  12. <groupId>org.springframework.boot </groupId>
  13. <artifactId>spring-boot-starter-parent </artifactId>
  14. <version>2.0.2.RELEASE </version>
  15. <relativePath/> <!-- lookup parent from repository -->
  16. </parent>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8 </project.reporting.outputEncoding>
  20. <java.version>1.8 </java.version>
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot </groupId>
  25. <artifactId>spring-boot-starter-web </artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot </groupId>
  29. <artifactId>spring-boot-starter-test </artifactId>
  30. <scope>test </scope>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework.boot </groupId>
  34. <artifactId>spring-boot-starter-thymeleaf </artifactId>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.apache.hadoop </groupId>
  38. <artifactId>hadoop-client </artifactId>
  39. <version>2.6.5 </version>
  40. </dependency>
  41. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-client -->
  42. <dependency>
  43. <groupId>org.apache.hbase </groupId>
  44. <artifactId>hbase-client </artifactId>
  45. <version>0.98.6-hadoop2 </version>
  46. </dependency>
  47. <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-server -->
  48. <dependency>
  49. <groupId>org.apache.hbase </groupId>
  50. <artifactId>hbase-server </artifactId>
  51. <version>0.98.6-hadoop2 </version>
  52. </dependency>
  53. </dependencies>
  54. <build>
  55. <plugins>
  56. <plugin>
  57. <groupId>org.springframework.boot </groupId>
  58. <artifactId>spring-boot-maven-plugin </artifactId>
  59. </plugin>
  60. </plugins>
  61. </build>
  62. </project>

注意我们要有下面这个包


按照上面的配置文件配置即可

9.2.3 SpringBoot快速入门之Hello代码

新建一个Hello.java


代码如下:


   
   
  1. package com.jenrey.spring_boot;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. @Controller
  6. public class Hello {
  7. @RequestMapping(value = "/test1")
  8. @ResponseBody
  9. public String test1(){
  10. return "1711";
  11. }
  12. }

运行代码

点击右上角的运行按钮


出现下图所示即为发布好了。不需要Tomcat之类的。



打开浏览器输出下面的网址:

http://localhost:8080/test1


9.2.4 快速入门之页面跳转

修改Hello.java代码如下


   
   
  1. package com.jenrey.spring_boot;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. @Controller
  6. public class Hello {
  7. @RequestMapping(value = "/test1")
  8. @ResponseBody
  9. public String test1(){
  10. return "1711";
  11. }
  12. /**
  13. * 其实下面名字可以不一样,但是我们一般是一样的
  14. * 注意我们没有写@ResponseBody ,所以我们要在templates目录下新建一个和index1一样名字的HTML文件
  15. * 这样我们输入网址http://localhost:8080/test2
  16. * 就会自动跳转到index1页面(因为名字一样,index1.html)
  17. */
  18. @RequestMapping(value = "/test2")
  19. public String test2(){
  20. return "index1";
  21. }
  22. }

在templates 目录下新建一个HTML的页面


代码如下:


   
   
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>测试 </title>
  6. </head>
  7. <body>
  8. <h1>今天天气不错 </h1>
  9. </body>
  10. </html>

9.2.5 ECharts 使用

下载链接:http://echarts.baidu.com/download.html

引入ECharts及JQ:

注意需要先在resources目录下创建一个新的目录 static 目录

之后可以创建个js文件夹,因为后面可能还有css等


修改index1.html的代码如下


   
   
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>测试 </title>
  6. </head>
  7. <script src="js/echarts.min.js"> </script>
  8. <body>
  9. <div id="main" style="width: 600px;height: 400px;"> </div>
  10. <script type="text/javascript">
  11. // 基于准备好的dom,初始化echarts实例
  12. var myChart = echarts.init( document.getElementById( 'main'));
  13. // 指定图表的配置项和数据
  14. var option = {
  15. title: {
  16. text: 'ECharts 入门示例'
  17. },
  18. tooltip: {},
  19. legend: {
  20. data:[ '销量']
  21. },
  22. xAxis: {
  23. data: [ "衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
  24. },
  25. yAxis: {},
  26. series: [{
  27. name: '销量',
  28. type: 'bar',
  29. data: [ 5, 20, 36, 10, 10, 20]
  30. }]
  31. };
  32. // 【05】使用刚指定的配置项和数据显示图表。
  33. myChart.setOption(option);
  34. </script>
  35. </body>
  36. </html>

访问:http://localhost:8080/test2


到此这个简单案例展示完成,我们在新建一个index2.html网页


在Hello.java中增加下面代码


   
   
  1. @RequestMapping(value = "/test3")
  2. public String test3(){
  3. return "index2";
  4. }

在index2.html中写下面代码:


   
   
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>实时统计品类点击 </title>
  6. </head>
  7. <script src="js/echarts.min.js"> </script>
  8. <body>
  9. <div id="main" style="width: 600px;height: 400px;"> </div>
  10. <script type="text/javascript">
  11. // 基于准备好的dom,初始化echarts实例
  12. var myChart = echarts.init( document.getElementById( 'main'));
  13. // 指定图表的配置项和数据
  14. option = {
  15. title : {
  16. text: '某站点用户访问来源',
  17. subtext: '纯属虚构',
  18. x: 'center'
  19. },
  20. tooltip : {
  21. trigger: 'item',
  22. formatter: "{a} <br/>{b} : {c} ({d}%)"
  23. },
  24. legend: {
  25. orient: 'vertical',
  26. left: 'left',
  27. data: [ '直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']
  28. },
  29. series : [
  30. {
  31. name: '访问来源',
  32. type: 'pie',
  33. radius : '55%',
  34. center: [ '50%', '60%'],
  35. data:[
  36. { value: 335, name: '直接访问'},
  37. { value: 310, name: '邮件营销'},
  38. { value: 234, name: '联盟广告'},
  39. { value: 135, name: '视频广告'},
  40. { value: 1548, name: '搜索引擎'}
  41. ],
  42. itemStyle: {
  43. emphasis: {
  44. shadowBlur: 10,
  45. shadowOffsetX: 0,
  46. shadowColor: 'rgba(0, 0, 0, 0.5)'
  47. }
  48. }
  49. }
  50. ]
  51. };
  52. // 【05】使用刚指定的配置项和数据显示图表。
  53. myChart.setOption(option);
  54. </script>
  55. </body>
  56. </html>

发布

http://localhost:8080/test3


把之前的项目导入到我们的SpringBoot中。复制bean和hbase两个目录下的所有文件就行了。


10. 自动刷新页面实时数据开发展示阶段

编写Hello.java 的代码如下:


   
   
  1. package com.jenrey.spring_boot;
  2. import com.jenrey.bean.CategoryClickCount;
  3. import com.jenrey.hbase.dao.HBaseDao;
  4. import com.jenrey.hbase.dao.factory.HBaseFactory;
  5. import org.springframework.stereotype.Controller;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.ResponseBody;
  8. import java.text.SimpleDateFormat;
  9. import java.util.Date;
  10. import java.util.List;
  11. @Controller
  12. public class Hello {
  13. @RequestMapping(value = "/test1")
  14. @ResponseBody
  15. public String test1(){
  16. return "1711";
  17. }
  18. /**
  19. * 其实下面名字可以不一样,但是我们一般是一样的
  20. * 注意我们没有写@ResponseBody ,所以我们要在templates目录下新建一个和index1一样名字的HTML文件
  21. * 这样我们输入网址http://localhost:8080/test2
  22. * 就会自动跳转到index1页面(因为名字一样,index1.html)
  23. */
  24. @RequestMapping(value = "/test2")
  25. public String test2(){
  26. return "index1";
  27. }
  28. @RequestMapping(value = "/test3")
  29. public String test3(){
  30. return "index2";
  31. }
  32. @RequestMapping(value = "/getList")
  33. @ResponseBody
  34. public List<CategoryClickCount> getList(){
  35. System.out.println( "======过来了么?======");
  36. /*获取当前系统时间,需要注意我们用的是昨天的数据,但是真正开发应该用下面注释的代码进行逻辑
  37. long time = System.currentTimeMillis();
  38. SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
  39. String today = simpleDateFormat.format(new Date(time));*/
  40. HBaseDao hBaseDao = HBaseFactory.getHBaseDao();
  41. //注意实际情况下面的日期不应该写死
  42. List<CategoryClickCount> list = hBaseDao.count( "aura", "2018-五月-22");
  43. System.out.println(list.size());
  44. return list;
  45. }
  46. }

修改index2.html 代码如下:


   
   
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>实时统计品类点击 </title>
  6. </head>
  7. <script src="js/echarts.min.js"> </script>
  8. <script src="js/jquery-3.1.1.min.js"> </script>
  9. <body>
  10. <div id="main" style="width: 600px;height: 400px;"> </div>
  11. <script type="text/javascript">
  12. // 基于准备好的dom,初始化echarts实例
  13. var myChart = echarts.init( document.getElementById( 'main'));
  14. var datas;
  15. //ajax
  16. $.ajax({
  17. //想访问的URL
  18. url: "getList",
  19. //异步还是同步,我们是同步
  20. async: false,
  21. //数据类型(指定返回数据类型自动变成json格式)
  22. dataType: 'json',
  23. //调用回调函数
  24. success: function (data) {
  25. datas=data
  26. }
  27. })
  28. // 指定图表的配置项和数据
  29. option = {
  30. title : {
  31. text: '品类点击实时情况',
  32. subtext: '纯属虚构',
  33. x: 'center'
  34. },
  35. tooltip : {
  36. trigger
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值