|
|
|
@ -22,20 +22,8 @@ import java.util.List;
|
|
|
|
|
@Slf4j
|
|
|
|
|
public class AustinSink implements SinkFunction<AnchorInfo> {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* batch的超时时间和大小
|
|
|
|
|
*/
|
|
|
|
|
private static final Integer BATCH_SIZE = 10;
|
|
|
|
|
private static final Long TIME_OUT = 2000L;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用ThreadLocal来暂存数据做批量处理
|
|
|
|
|
*/
|
|
|
|
|
private static ThreadLocal<List<AnchorInfo>> baseAnchors = ThreadLocal.withInitial(() -> new ArrayList<>(BATCH_SIZE));
|
|
|
|
|
private static ThreadLocal<Long> lastClearTime = ThreadLocal.withInitial(() -> new Long(System.currentTimeMillis()));
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void invoke(AnchorInfo anchorInfo) throws Exception {
|
|
|
|
|
public void invoke(AnchorInfo anchorInfo, Context context) throws Exception {
|
|
|
|
|
realTimeData(anchorInfo);
|
|
|
|
|
offlineDate(anchorInfo);
|
|
|
|
|
}
|
|
|
|
@ -46,43 +34,34 @@ public class AustinSink implements SinkFunction<AnchorInfo> {
|
|
|
|
|
* 1.用户维度(查看用户当天收到消息的链路详情),数量级大,只保留当天
|
|
|
|
|
* 2.消息模板维度(查看消息模板整体下发情况),数量级小,保留30天
|
|
|
|
|
*
|
|
|
|
|
* @param anchorInfo
|
|
|
|
|
* @param info
|
|
|
|
|
*/
|
|
|
|
|
private void realTimeData(AnchorInfo anchorInfo) {
|
|
|
|
|
baseAnchors.get().add(anchorInfo);
|
|
|
|
|
if (baseAnchors.get().size() >= BATCH_SIZE || System.currentTimeMillis() - lastClearTime.get() >= TIME_OUT) {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
LettuceRedisUtils.pipeline(redisAsyncCommands -> {
|
|
|
|
|
List<RedisFuture<?>> redisFutures = new ArrayList<>();
|
|
|
|
|
for (AnchorInfo info : baseAnchors.get()) {
|
|
|
|
|
/**
|
|
|
|
|
* 1.构建userId维度的链路信息 数据结构list:{key,list}
|
|
|
|
|
* key:userId,listValue:[{timestamp,state,businessId},{timestamp,state,businessId}]
|
|
|
|
|
*/
|
|
|
|
|
SimpleAnchorInfo simpleAnchorInfo = SimpleAnchorInfo.builder().businessId(info.getBusinessId()).state(info.getState()).timestamp(info.getTimestamp()).build();
|
|
|
|
|
for (String id : info.getIds()) {
|
|
|
|
|
redisFutures.add(redisAsyncCommands.lpush(id.getBytes(), JSON.toJSONString(simpleAnchorInfo).getBytes()));
|
|
|
|
|
redisFutures.add(redisAsyncCommands.expire(id.getBytes(), (DateUtil.endOfDay(new Date()).getTime() - DateUtil.current()) / 1000));
|
|
|
|
|
}
|
|
|
|
|
private void realTimeData(AnchorInfo info) {
|
|
|
|
|
try {
|
|
|
|
|
LettuceRedisUtils.pipeline(redisAsyncCommands -> {
|
|
|
|
|
List<RedisFuture<?>> redisFutures = new ArrayList<>();
|
|
|
|
|
/**
|
|
|
|
|
* 1.构建userId维度的链路信息 数据结构list:{key,list}
|
|
|
|
|
* key:userId,listValue:[{timestamp,state,businessId},{timestamp,state,businessId}]
|
|
|
|
|
*/
|
|
|
|
|
SimpleAnchorInfo simpleAnchorInfo = SimpleAnchorInfo.builder().businessId(info.getBusinessId()).state(info.getState()).timestamp(info.getTimestamp()).build();
|
|
|
|
|
for (String id : info.getIds()) {
|
|
|
|
|
redisFutures.add(redisAsyncCommands.lpush(id.getBytes(), JSON.toJSONString(simpleAnchorInfo).getBytes()));
|
|
|
|
|
redisFutures.add(redisAsyncCommands.expire(id.getBytes(), (DateUtil.endOfDay(new Date()).getTime() - DateUtil.current()) / 1000));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 2.构建消息模板维度的链路信息 数据结构hash:{key,hash}
|
|
|
|
|
* key:businessId,hashValue:{state,stateCount}
|
|
|
|
|
*/
|
|
|
|
|
redisFutures.add(redisAsyncCommands.hincrby(String.valueOf(info.getBusinessId()).getBytes(),
|
|
|
|
|
String.valueOf(info.getState()).getBytes(), info.getIds().size()));
|
|
|
|
|
redisFutures.add(redisAsyncCommands.expire(String.valueOf(info.getBusinessId()).getBytes(), DateUtil.offsetDay(new Date(), 30).getTime()));
|
|
|
|
|
}
|
|
|
|
|
return redisFutures;
|
|
|
|
|
});
|
|
|
|
|
/**
|
|
|
|
|
* 2.构建消息模板维度的链路信息 数据结构hash:{key,hash}
|
|
|
|
|
* key:businessId,hashValue:{state,stateCount}
|
|
|
|
|
*/
|
|
|
|
|
redisFutures.add(redisAsyncCommands.hincrby(String.valueOf(info.getBusinessId()).getBytes(),
|
|
|
|
|
String.valueOf(info.getState()).getBytes(), info.getIds().size()));
|
|
|
|
|
redisFutures.add(redisAsyncCommands.expire(String.valueOf(info.getBusinessId()).getBytes(), DateUtil.offsetDay(new Date(), 30).getTime()));
|
|
|
|
|
return redisFutures;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("AustinSink#invoke error: {}", Throwables.getStackTraceAsString(e));
|
|
|
|
|
} finally {
|
|
|
|
|
lastClearTime.set(System.currentTimeMillis());
|
|
|
|
|
baseAnchors.get().clear();
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("AustinSink#invoke error: {}", Throwables.getStackTraceAsString(e));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|