仓库位置
日志生成:https://github.com/SmallScorpion/gmall-mock.git
日志服务器:https://github.com/SmallScorpion/gmall-spark-ch-es-realtime.git
需求
分析
- 做日活,取启动日志“GMALL_SPARK_CK_ES_START”中的数据(事件日志也可以做,但是会麻烦一点)
- 消费kafka中的数据。
- 利用redis过滤当日已经计入的日活设备
- 把每批次新增的当日日活信息保存到ES中(也可以做一层聚合数据量变小之后保存到redis或者MDB中)
- 从ES中查询出数据,发布成数据接口,通可视化化工程调用。
redis
# bind 128.0.0.1
# protected-mode no
# daemonize yes
启动:/etc/module/redis/src/redis-server /etc/module/redis/redis.conf
查看:ps -ef | grep redis
redisUtil
package com.warehouse.gmall.realtime.util
import redis.clients.jedis.{Jedis, JedisPool, JedisPoolConfig}
/**
* redisUtil
*/
object RedisUtil {
var jedisPool:JedisPool=null
def getJedisClient: Jedis = {
if(jedisPool==null){
// println("开辟一个连接池")
val config = PropertiesUtil.load("config.properties")
val host = config.getProperty("redis.host")
val port = config.getProperty("redis.port")
val jedisPoolConfig = new JedisPoolConfig()
jedisPoolConfig.setMaxTotal(100) //最大连接数
jedisPoolConfig.setMaxIdle(20) //最大空闲
jedisPoolConfig.setMinIdle(20) //最小空闲
jedisPoolConfig.setBlockWhenExhausted(true) //忙碌时是否等待
jedisPoolConfig.setMaxWaitMillis(500)//忙碌时等待时长 毫秒
jedisPoolConfig.setTestOnBorrow(true) //每次获得连接的进行测试
jedisPool=new JedisPool(jedisPoolConfig,host,port.toInt)
}
// println(s"jedisPool.getNumActive = ${jedisPool.getNumActive}")
// println("获得一个连接")
jedisPool.getResource
}
}
业务代码
// TODO redis去重
val jsonObjDstream: DStream[JSONObject] = recordInputStream.map { record =>
// 提取日志
val jsonString: String = record.value()
val jsonObj: JSONObject = JSON.parseObject(jsonString)
// 提取事件戳
val ts: lang.Long = jsonObj.getLong("ts")
val datehourString: String = new SimpleDateFormat("yyyy-MM-dd HH")
.format(new Date(ts))
val dateHour: Array[String] = datehourString.split(" ")
// 提取时间戳
jsonObj.put("dt", dateHour(0))
jsonObj.put("hr", dateHour(1))
jsonObj
}
/*
// 利用redis保存今天访问过系统的用户清单
// 清单在redis中保存
// string(k-v) hash( k-(k,v) ) list set zset(k-v-s(排序评分))
// redis : type->Set key->dau-2020-06-17 value->mid
val filterDstream: DStream[JSONObject] = jsonObjDstream.filter { jsonObj =>
// 获取字段
val dt: String = jsonObj.getString("dt")
val mid: String = jsonObj.getJSONObject("common").getString("mid")
// 获取连接
val jedis: Jedis = RedisUtil.getJedisClient
val dauKey: String = "dau:" + dt
// 如果为存在则保存,返回1,如果已存在则不保存 返回0
val isNew = jedis.sadd(dauKey, mid)
jedis.close()
if (isNew == 1L) {
true
} else {
false
}
}
*/
val filterDstream: DStream[JSONObject] = jsonObjDstream.mapPartitions { jsonObjItr =>
// 获取连接:一个分区只申请一个连接
val jedis: Jedis = RedisUtil.getJedisClient
// 结果集
val filterList = new ListBuffer[JSONObject]
val jsonList: List[JSONObject] = jsonObjItr.toList
println("过滤前" + jsonList.size)
for (jsonObj <- jsonList) {
// 获取字段
val dt: String = jsonObj.getString("dt")
val mid: String = jsonObj.getJSONObject("common").getString("mid")
val dauKey: String = "dau:" + dt
// 如果为存在则保存,返回1,如果已存在则不保存 返回0
val isNew = jedis.sadd(dauKey, mid)
// 过期日期
jedis.expire( dauKey, 3600 * 24 )
if (isNew == 1L) {
filterList += jsonObj
}
}
jedis.close()
println("过滤后" + jsonList.size)
filterList.toIterator
}
filterDstream.print(10000)