定时器与系统时间(续)

摘要:
其他问题处理:因为步长为1秒+擦除毫秒。解决方案:初始计时器时间设置为XX:擦除毫秒不会影响Calendar=Calendar的正常计时器执行。getInstance();在心跳开始之前,它将延迟大约200毫秒。无明显不良影响。c) 心跳时间点不是XX:心跳时间点仍可能被推到XX:XX:

额外问题处理:

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

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) 定时器顺便解决了服务器系统时间问题

免责声明:文章转载自《定时器与系统时间(续)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇pdflib的使用代码【 Linux 】Systemd 使用说明(1)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android 开机问题知多少

极力推荐文章:欢迎收藏Android 干货分享 阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: 一、 如何抓取开机问题Log 二、开机问题Log 分析流程 三、 kernel Log 搜索关键字fs_mgr 初步分析定位 四、uart lo...

Redis集群实现分布式锁-RedLock

一、redis集群分布式锁 Redis单节点实现分布式锁 ,如果通过sentinel保证高可用,如果master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况: 1. 客户端1在Redis的master节点上拿到了锁。 2. Master宕机了,存储锁的key还没有来得及同步到Slave上。 3. master故障,发生故障转移,slave节点升...

linux ls命令按时间显示文件

  本文介绍下,使用ls命令显示文件,并按时间排序的方法,供大家学习参考。 在linux系统中,使用ls命令按时间排序文件,其实很简单,如下: #ls -tr 即可按时间排序当前目录下的文件。 附,ls命令的参数中文详解: -a 列出目录下的所有文件,包括以 . 开头的隐含文件。 -b 把文件名中不可输出的字符用反斜杠加字符编号(就象在C语言里一样)的形式...

TCP 之 FIN_WAIT_2状态处理流程

概述 在主动关闭方发送了FIN之后,进入FIN_WAIT_1状态,在此状态收到了ACK,则进入FIN_WAIT_2状态,而FIN_WAIT_2后续要做的工作是等待接收对端发过来的FIN包,并且发送ACK,进而进入到TIME_WAIT状态;本文主要关注从FIN_WAIT_1进入FIN_WAIT_2状态,以及在FIN_WAIT_2状态来包或者定时器触发后的处理...

Clickhouse TTL的那些事

TTL简介 ClickHouse原生支持数据生命周期(TTL)管理的功能 TTL即Time To Live,表示数据的存活时间。在MergeTree中,可以为某个列字段或者整张表设置TTL。当时间达到时,若列字段级别的TTL则会删除这一列的数据;若表级别的TTL则会删除整张表的数据;若同时设置了列级别的和表级别的TTL则以先到期的为准。 无论列级别还是表级...

[转]centos6 与 7 其中的一些区别

# vi /etc/ssh/sshd_config    #将MaxAuthTries注释去掉    MaxAuthTries 5(登录次数) UseDNS no   默认是yes 的,把这个改为no,可以大大减少认证的时间 centos7 yum装的服务器,或者自带的,启动命令有区别 现在是   systemctl   sshd.serviec star...