[APP大数据项目]第六天笔记

spark streaming 实现用户的准实时更新

  1. spark thrift server 替换hiveserver2
  2. HBase创建app_users 表 :
    基本属性字段,firsttime(第一次启动时间), lasttime(最后一次启动时间)
  3. 使用phoenix,Hbase上的SQL支持

实现思路

  1. spark streaming 从kafka接收消息,设置每5秒为一个窗口
  2. 一个窗口期会来很多数据,要做的是:
    1)如果该 [app, 用户, version] 没在表中出现,则插入一天新记录,firsttime 和 lasttime 相同
    2) 如果该 [ app, 用户, version ] 出现过,则更新lasttime即可
  3. 由于我们只需要第一次和最后一次的数据,因此需要对窗口期内的数据,按【app,用户和version】聚合,然后排序, 取出第一次和最后一次做插入更新HBase即可
具体实现

1.创建StreamingContext,配置Kafka Consumer, 使用Kafka消费队列作为spark输入流

import com.alibaba.fastjson.JSONObject;
import com.it18zhang.app.common.AppStartupLog;
import com.it18zhang.app.common.Constants;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.streaming.Seconds;
import org.apache.spark.api.java.function.*;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaInputDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka010.ConsumerStrategies;
import org.apache.spark.streaming.kafka010.KafkaUtils;
import org.apache.spark.streaming.kafka010.LocationStrategies;
import scala.Tuple2;

import java.sql.*;
import java.util.*;

// 创建spark context
SparkConf conf = new SparkConf();
conf.setAppName("App-Logs-Spark-Streaming");
conf.setMaster("local[4]") ;

JavaStreamingContext sc = new JavaStreamingContext(conf, Seconds.apply(5));

// 配置kafka
Map props = new HashMap();
// kafka 集群地址
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, Constants.KAFKA_CONSUMER_SERVERS);
// 序列化方式
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName());
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getCanonicalName());
props.put(ConsumerConfig.GROUP_ID_CONFIG, "1");
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"lastest");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");

// 设置主题
List topics = new ArrayList() ;
topics.add("topic-app-startup");

// 创建kafka流
JavaInputDStream> ds1 = KafkaUtils.createDirectStream(sc,
        LocationStrategies.PreferBrokers(), ConsumerStrategies.Subscribe(topics,props));

2.将输入流的JSON String 通过Map 转为自定义的Log对象

// 通过Map实现流变化
Function, AppStartupLog> mapFunc = new Function, AppStartupLog>() {
    public AppStartupLog call(ConsumerRecord msg) throws Exception {
        String topic = msg.topic();
        String key = (String) msg.key();
        String value = (String) msg.value();
        AppStartupLog log = JSONObject.parseObject(value, AppStartupLog.class);
        return log;
    }
};

JavaDStream ds2 = ds1.map(mapFunc);

3.将log对象 通过pairMap 映射为(key, logAgg)的键值对

 其中logAgg类是为了方便聚合操作的自定义类,在Log类的基础上加入了firstTime,lastTime的属性,还包括log对象的成员
// 通过AppStartupLog 抽取出appid deviceid version 作为key值
// 将AppStartupLog转换为包含firstTime 和lastTime 的聚合类AppStartupLogAgg
// AppStartupLog => (key, AppStartupLogAgg)
//
PairFunction pairFunc = new PairFunction() {
   public Tuple2 call(AppStartupLog appStartupLog) throws Exception {
       String appid = appStartupLog.getAppId();
       String deviceid = appStartupLog.getDeviceId();
       String appversion = appStartupLog.getAppVersion();
       String key = appid + "," + deviceid + ","+appversion;

       AppStartupLogAgg value = new AppStartupLogAgg();
       value.setLog(appStartupLog);
       value.setFirstTime(appStartupLog.getCreatedAtMs());
       value.setLastTime(appStartupLog.getCreatedAtMs());
       return new Tuple2(key, value);
   }
};

JavaPairDStream ds3 = ds2.mapToPair(pairFunc);

4.根据新的键值通过reduceByKey聚合

// 通过reduceByKey聚合
Function2 reduceFunc = new Function2() {
    public AppStartupLogAgg call(AppStartupLogAgg v1, AppStartupLogAgg v2) throws Exception {
        v1.setFirstTime(Math.min(v1.getFirstTime(),v2.getFirstTime()));
        v1.setLastTime(Math.max(v1.getLastTime(), v2.getLastTime()));
        return v1;
    }
};

JavaPairDStream ds4 = ds3.reduceByKey(reduceFunc);

5.原来的想法是对聚合以后的每个partition(相同key)的数据进行一次数据插入,但由于key值为appid+deviceid+version,每个分区内的数据较少, 这样频繁插入的代价(比如开启链接)相对较高。因此考虑按appid再分一次组,将相同appid的数据放在一起后再进行插入。

// 将key值 转变为 appid
PairFunction,String,AppStartupLogAgg> pairFunc2 = new PairFunction, String, AppStartupLogAgg>() {
    public Tuple2 call(Tuple2 tuple) throws Exception {
        String key = tuple._2().getLog().getAppId();
        return new Tuple2(key, tuple._2());
    }
};
JavaPairDStream ds5 = ds4.mapToPair(pairFunc2);

6.循环插入

//循环聚合结果,插入phoenix库
ds6.foreachRDD(new VoidFunction>>() {
    public void call(JavaPairRDD> rdd) throws Exception {
        // 对每个rdd 里对每个 执行插入操作
        rdd.foreach(new VoidFunction>>() {
            public void call(Tuple2> tt) throws Exception {
                String appid = tt._1() ;
                Iterator it = tt._2().iterator();

                Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
                Connection conn = DriverManager.getConnection("jdbc:phoenix:s202:2181");
                conn.setAutoCommit(false);
                //循环所有聚合数据
                while(it.hasNext()){
                    AppStartupLogAgg agg = it.next();
                    upsert2Phoenix(conn, appid, agg);
                }
                conn.commit();
            }
        });
    }
});

7.执行

sc.start();
sc.awaitTermination();

简单扩展:HIVE和HBASE区别

限制

Hive目前不支持更新操作。另外,由于hive在hadoop上运行批量操作,它需要花费很长的时间,通常是几分钟到几个小时才可以获取到查询的结果。Hive必须提供预先定义好的schema将文件和目录映射到列,并且Hive与ACID不兼容。

HBase查询是通过特定的语言来编写的,这种语言需要重新学习。类SQL的功能可以通过Apache Phonenix实现,但这是以必须提供schema为代价的。另外,Hbase也并不是兼容所有的ACID特性,虽然它支持某些特性。最后但不是最重要的–为了运行Hbase,Zookeeper是必须的,zookeeper是一个用来进行分布式协调的服务,这些服务包括配置服务,维护元信息和命名空间服务。

应用场景

Hive适合用来对一段时间内的数据进行分析查询,例如,用来计算趋势或者网站的日志。Hive不应该用来进行实时的查询。因为它需要很长时间才可以返回结果。

Hbase非常适合用来进行大数据的实时查询。Facebook用Hbase进行消息和实时的分析。它也可以用来统计Facebook的连接数。

总结

Hive和Hbase是两种基于Hadoop的不同技术–Hive是一种类SQL的引擎,并且运行MapReduce任务,Hbase是一种在Hadoop之上的NoSQL 的Key/vale数据库。当然,这两种工具是可以同时使用的。就像用Google来搜索,用FaceBook进行社交一样,Hive可以用来进行统计查询,HBase可以用来进行实时查询,数据也可以从Hive写到Hbase,设置再从Hbase写回Hive。

你可能感兴趣的:(大数据)