延迟任务通知服务的线上问题(二)

2023-01-04  本文已影响0人  天草二十六_简村人

一、说在前面的话

本文的问题,需要理解前文https://www.jianshu.com/p/550462158846,发现的问题是通知延迟,原本应该是13点15分回调给业务方的,结果硬是拖到了14点才执行。
延迟的原因初步发现是,没有及时将冷数据区移入至热数据区。

还有一个地方需要解释的是,为什么在13点和13点30分的时候遗漏掉了,而在14点的时候又变成正确的了。

这也就是程序员经常说的偶现问题!!
其实就上文也提到的问题。https://www.jianshu.com/p/fe39f9f486e3

幸运的是,我们每一步的数据转移, 都有充足的日志来给我们证明。根据业务上的唯一标识,可以清晰看出它的流转全过程。

二、取证过程

1、xxl-job之每日拉取当日任务数据到冷数据缓存中

这个任务会更新bucketSize的值,但是只会更新某个jvm节点。

image.png

2、jvm日志,体现了内存变量bucketSize的值

因为线上是部署了两个节点,一个是正确的,另一个则是脏数据。

container_ip:10.224.170.195
2023-01-02 00:00:00.076 INFO [task-notification,,,] 9 --- [ Thread-1030] c.x.c.n.i.job.ColdCacheRefresherJob : 今日的keySize为2

container_ip:10.224.169.197
2023-01-03 00:00:00.082 INFO [task-notification,,,] 8 --- [ Thread-1049] c.x.c.n.i.job.ColdCacheRefresherJob : 今日的keySize为4

container_ip:10.224.169.197
2023-01-04 00:00:00.083 INFO [task-notification,,,] 8 --- [ Thread-1074] c.x.c.n.i.job.ColdCacheRefresherJob : 今日的keySize为4

container_ip:10.224.170.195
2023-01-05 00:00:00.078 INFO [task-notification,,,] 9 --- [ Thread-1122] c.x.c.n.i.job.ColdCacheRefresherJob : 今日的keySize为2

3、xxl-job之冷数据缓存移至热数据缓存区

image.png

三、源码

外围是一个for循环,遍历当天所有的分片。Constants.BUCKET_SIZE的值如果不对,会导致程序未遍历到,也就不会把冷数据过滤至热数据区。

@XxlJob("hotCacheRefresher")
    public ReturnT<String> execute(String s) {
        for (int i = 0; i < Constants.BUCKET_SIZE; i++) {
            String coldKey = String.format(Constants.TASK_COLD, DateUtil.getDateStrForToday(), i);
            try {
                Set<String> coldDataSet = taskRedisOperator.getValuesForSet(coldKey);
                //筛选出热数据
                Set<String> hotDataSet = this.selectHotData(coldDataSet);

                if (!CollectionUtils.isEmpty(hotDataSet)) {
                    log.info("筛选出的热数据为[coldKey={}\t hotDataSet={}]", coldKey, JsonUtils.toJsonString(hotDataSet));
                    //将筛选出的热数据,存入热数据缓存中
                    taskRedisOperator.addDataToHotCache(hotDataSet);
                    //从冷数据缓存里删除
                    taskRedisOperator.removeDatasFromCache(coldKey, hotDataSet);
                }
            } catch (Exception e) {
                log.error("从冷数据缓存中筛选热数据失败。[key={}]", coldKey, e);
            }
        }
        return ReturnT.SUCCESS;
    }
/**
     * 从冷数据中筛选热数据
     *
     * @param coldDataSet
     */
    private Set<String> selectHotData(Set<String> coldDataSet) {
        Set<String> hotDataSet = Sets.newHashSet();
        long systemTime = System.currentTimeMillis();
        coldDataSet.forEach(data -> {
            TaskDTO taskDTO = JsonUtils.parseObject(data, TaskDTO.class);
            // 判断通知时间是否符合热数据
            if (taskDTO.getNotifyTime() < systemTime + 1800 * 1000) {
                hotDataSet.add(data);
            }
        });
        return hotDataSet;
    }

四、未发版前的解决办法

利用重试的机制,在调度时间前,让xxl-job调度到正确的jvm节点。

提高它的轮询频率,由30分钟调整为10分钟。
cron表达式:0 */30 * * * ? 修改为--》 0 */10 * * * ?
同时确定xxl-job任务的路由策略是轮询。

image.png

五、总结

当bucket_size由多变少,只会到处拉取冷数据缓存的时候,空转而已。

但是,当bucket_size由少变多,当xxl-job执行到的节点,bucket_size还是脏数据的时候,就会出现遗漏。

再一次证明是偶现的问题吧!!

上一篇下一篇

猜你喜欢

热点阅读