首页 > 代码库 > 【原】定时器与系统时间(续)

【原】定时器与系统时间(续)

额外问题处理:

--------------------------------------------------------------------------------

13) 定时器之外的一些处理
    a) window环境下,定时器通知执行定时任务的时间点可能误差1毫秒;Linux环境也有类似情况,但是误差频率低很多(只是在指定时间点前后1毫秒误差,心跳步长不差)
        可能的影响:
            以每秒任务为例, 10:00:00.000 TaskA -> 10:00:00.999 TaskB -> 10:00:02.000 TaskC -> 10:00:03.001 TaskD
            会导致第2个任务(TaskB)丢失, 因为步长是1秒钟 + 抹去毫秒计算
        代码解决方案:
            定时器初始时间设置为 XX:XX:XX:500,以两秒的中间段为起点开始心跳,这样误差1毫秒时候,抹去毫秒不影响正常定时器执行

    Calendar cal = Calendar.getInstance();    int millis = cal.get(Calendar.MILLISECOND);    int delay = (1500 - millis) % 1000;        // 下个0.5秒开始运行(定时器偶尔会前后误差1毫秒)                timerService.scheduleAtFixedRate(new Runnable() {..}, delay, TIMER_STEP, TimeUnit.MILLISECONDS);

    b) 线程池启动,大概会延迟200毫秒后才开始心跳

        没有多大不良影响
    c) 心跳时间点未 XX:XX:XX:500, 正常情况下没有问题。
        问题是每小时同步一次服务器系统时间,可能依然导致心跳时间点押到 XX:XX:XX:000 上,从而引起问题a),当然这个概率极低
        可以考虑心跳时长为 100毫秒
    d) 任务执行接口传入当前时间作为参数
        定时器(心跳)线程,定时挑选符合执行条件的任务,扔到任务池去执行。
        因为任务执行时间过长、任务锁、线程池资源不足等条件导致的任务内部log时间不符合要求是任务本身的事情,反正定时器通知你了,不用纠结定时器问题
    e) 任务有唯一id,防止重复添加任务, 一次性任务没有Id
        任务基类对id做了处理,加了统一前缀,方便log跟踪

 

定时任务优化:

--------------------------------------------------------------------------------

14) 现在定时器定时执行任务的功能已经完成:我在指定的时间通知你做指定的任务。不过总有一些奇怪的任务,用这样更简单的语言不方便表达。
    比如,每隔10分钟统计一次在线人数。
    当前接口可以处理,不过感觉非常笨拙:

        regPerHourTask (0, 0, TaskA);        regPerHourTask (10, 0, TaskA);        regPerHourTask (20, 0, TaskA);        regPerHourTask (30, 0, TaskA);        regPerHourTask (40, 0, TaskA);        regPerHourTask (50, 0, TaskA);

    当然也可以尝试

        regPerMinuteTask (0, new Task () {            每10次执行1次,忽略9次        });

    于是抽象出一个固定分钟执行的任务,你告诉我你要几分钟执行一次,然后实现我的执行接口就是了

 

    这样又产生了一个新的问题,比如,每隔5分钟统计一次在线人数。

        2014-12-20 13:58:12,503 INFO 人数为N // 当前log是        2014-12-20 13:03:12,503 INFO 人数为N        2014-12-20 13:08:12,503 INFO 人数为N        2014-12-20 13:13:12,503 INFO 人数为N        2014-12-20 13:18:12,503 INFO 人数为N        2014-12-20 13:22:53,321 INFO 人数为N // 重启服务器后:        2014-12-20 13:27:53,321 INFO 人数为N        2014-12-20 13:32:53,321 INFO 人数为N        2014-12-20 13:37:53,321 INFO 人数为N

    看起来有点小晕

    解决方案:
        固定分钟执行的任务, 初始增加一个 baseTime 参数, 时间计算依赖 baseTime 去选择执行或忽略

        2014-12-20 13:00:00,000 INFO 人数为N  // 当前log是        2014-12-20 13:05:00,000 INFO 人数为N        2014-12-20 13:10:00,000 INFO 人数为N        2014-12-20 13:15:00,000 INFO 人数为N        2014-12-20 13:20:00,000 INFO 人数为N        2014-12-20 13:30:00,000 INFO 人数为N  // 重启服务器后        2014-12-20 13:35:00,000 INFO 人数为N        2014-12-20 13:40:00,000 INFO 人数为N        2014-12-20 13:45:00,000 INFO 人数为N

    这样 grep 出来log对时间就舒服多了

 

定时任务优化(二):

--------------------------------------------------------------------------------

 

15) 先做了到点通知功能,后来额外加了定时通知功能;突然发现,这个只是个正常的任务功能,还有不正常的情况。
    比如,我们需要远程请求加载数据,可能这数据相对重要一些,但是只要一次取对的数据就可以了,问题它可能失败。
    换个描述方式就是,我需要去其它地方拿数据,直到拿到为止。当然现在的定时器也支持这个任务,尽管不友好:
        注册一个每分钟任务,如果发现任务完成了,就移除。
    但是实际问题可能更复杂一些:
        如果任务执行失败,我就5秒后再执行一次;
        如果再失败,我就30秒后执行一次;
        如果再失败,就3分钟后执行;
        如果再失败,就算了,不能无休止执行;也可能过了一定期限后,执行成功也没有意义
    在自己的任务里可以自行处理类似逻辑,不过这也是可以抽象的,于是需要增加一个固执的任务,失败后休息一定时间再执行
    规定的失败次数(可尝试次数)用完后,没有完成也不再执行了
    这里的需要通知任务执行的时候,执行函数是有成功与否的返回值。

 

总结

--------------------------------------------------------------------------------

16) 总结
    a) 定时器通知【可以执行的指定任务】了
    b) 定时器【定时】通知【可以执行的指定任务】了
    c) 定时器【多次】通知【需要执行成功的指定任务】了
    d) 定时器顺便解决了服务器系统时间问题

 

【原】定时器与系统时间(续)