spring+ibatis基于方法粒度的轻量级容错重试机制方案设计
背景:
? ? ? 容错重试机制,是系统的一种自我调节,是对系统鲁棒性的一种考量,在很多后台程序中都经常涉及,特别是基于 ? task的系统中,往往这类系统要处理的事情很多,一个task的完成时间比较长,涉及的环境也比较复杂,出现很多临时错误的概率较大,比如IO读取出错,网络临时不可用等等,同时这种系统往往对响应时间要求不是很高,更加看重系统的稳定和鲁棒性;另外对于依赖于第三方的远程调用,或者说其他资源的获取,也常常涉及要考虑容错重试;让程序在不人工干预的情况下,处理更多的场景;
?
适用场景:
? ? 从上面的描述我们可以总结它的适用场景:
? ? 1、系统对响应时间不太关心;
? ? 2、系统对鲁棒性要求较高;
? ? 3、系统涉及远程调用,资源获取;
?
设计目标:
? ? 1、通用性:希望作为一个独立的模块,为需要的程序提高方便的使用;
? ? 2、轻量级:希望是一个轻量级的实现,不对使用的系统有太强的侵入性;
? ? 3、细粒度:重试以方法为单位,而不用重试整个task;
?
概要设计:
? ? ?要保持重试模块的独立性,不侵入到原有的系统中,首先的面临的问题是,需要重试的数据从何而来,我们很容易想到DB,那么整个系统应该分为两大块,一是client,负责将需要重试的数据放入DB,这个也就是各个应用程序要做的事情;第二是server,负责将DB中数据取出,做相应的处理;大家可以看到这是一个典型的生产者-消费者模式;
?
?
?
这里区分应用程序和容错服务器,只是概念上的,因为容错服务器事实上必须依赖于引用程序(需要执行部分引用程序);所以在实际应用中,一般在一台虚拟机上,如果是应用本身在多台服务器上的话,可以通过配置项决定是否启用容错重试功能;
?
详细设计-UML图:
?
?
1、系统的UML类图
?
2、类图说明:
?
? ?1)、从上图可以看出系统主要可以分为三大块的内容,第一块是TaskExecutor 类以上部分,通过spring的TaskExecutor与下面的模块弱依赖,这一块主要负责从数据库去取出需要处理的、并且应该自己(loadbalance)处理的数据;讲数据封装在一个Notify中,交给NotifyServerServices处理;
? ?2)、第二块为TaskExecutor和RetryHandlerStrategy之前的部分;这一部分主要关注容错重试处理后的工作;如:成功则删除DB中的记录,否则,负责判断是否还需要重试,间隔时间等;
? ?3)、第三块为每个系统自己实现的各种RetryHandlerStrategy类;他们负责真正的重试工作;这里所有的类,可以看成是一个sever,对于client端来说,是非常简单的,因为它只需要讲数据插入到数据库,可以通过一个clientService提供一个createNotify方法,供应用调用;
?
详细设计-数据库设计:
?
1、一共需要两张表,task、task_history;两张字段完全一样:
?
字段类型描述可空默认值task_idvarchar2PK、UUIDNOTcreate_timeDATE创建时间NOThandle_timeDATE任务待执行的时间NOTtask_handlervarchar2任务处理器类型NOTload_balance_numnumber负责均衡值NOT0task_parametervarchar2执行任务的参数,json格式YESretry_countnumber已经重试的次数NOT0retry_reasonvarchar2失败的原因YES?
说明:
1)、load_balance_num:当使用集群的时候可以考虑,可以通过上图的LoadBalanceNumStrategy类来控制他的值,比如平均分布,比如按照机器的性能使用权重分布;
2)、task_parameter:这个用来保存重试的参数,可以约定为一种格式,自己方便解析就好了,比如json、xml等;
3)、retry_count:当系统要求我最多重试5次的时候可以使用这个参数;当5次后还是失败,直接移动要历史表中,人工处理;
4)、handle_time:当然系统要求第二次重试的时候时间间隔30分钟的时候使用;当处理失败的时候更新这个时间;
5)、task_handler:任务处理器类型,比如上面类图中的RetryHandler,通过spring,得到RetryHandler的实例来做处理;
?
关键类伪代码:
?
从上面的设计图可以发现主要只有两个类,即是:NotifyScheduleMainExecutor和NotifyServerServiceImpl,其他的都是一些策略类;这里伪代码描述这个两个类的逻辑,策略类可以自己选择不同的实现;
?
1、NotifyScheduleMainExecutor:
?
?
?总结:
现在回去看系统的目标实现情况:
1、通用性:整个模块对应用系统的侵入性是很小了,可以打包为一个二方库,在公司范围的使用;对于应用来说只增加几个配置文件,在需要重试的地方,通过通过接口,完全于模块解耦;
2、轻量级:很明显,模块只是依赖spring,
3、细粒度:在上面的设计中,并没有特别强调细粒度,是因为对于选择多大粒度完全由应用自己决定,应用在自己的重试实现类和方法之间平衡,对模块来讲,没有任何限制;
?
?
1 楼 cn-done 2011-08-21 可否分享源代码? 2 楼 cn-done 2011-08-21 retry_reason varchar2 失败的原因 YES
执行多次,每次的失败原因如何处理?存在一个字段? 3 楼 诸葛不亮 2011-08-22 cn-done 写道retry_reason varchar2 失败的原因 YES
执行多次,每次的失败原因如何处理?存在一个字段?
这里设计的时候只考虑保存最后一次失败的原因,如果要保存全部的失败原因,可以通过追加在后面的方式实现,不过有没有必要,需要考虑一下?至于源代码的话,不是我个人的东西,就不便分享了。 thx!