本节以图文并茂的方式尽量让朋友们认清tbs中每一个配置的具体作用,一遍敲黑板,一遍重复:重点!重点!重点!



1、应用配置
<bean id="scheduleManagerFactory" class="com.taobao.pamirs.schedule.strategy.TBScheduleManagerFactory" init-method="init">
    <property name="zkConfig">
        <map>
            <!--注册中心地址,集群,单机均可,推荐集群-->
            <entry key="zkConnectString" value="172.16.60.12:2181,172.16.60.16:2182,172.16.60.33:2183"/>
            <!--调度器注册地址-->
            <entry key="rootPath" value="/tbschedule/wed/tasks"/>
            <!--zookeeper链接超时时间-->
            <entry key="zkSessionTimeout" value="60000"/>
            <!--zookeeper节点auth用户名-->
            <entry key="userName" value=""/>
            <!--zookeeper节点auth密码-->
            <entry key="password" value=""/>
            <!--是否检查调度器注册地址第一级目录是否存在-->
            <entry key="isCheckParentPath" value="true"/>
        </map>
    </property>
</bean>



2、控制台zookeeper数据配置
  • zookeeper地址:必须同应用中保持一致
  • zookeeper超时:应用中为应用连接zk超时时间,控制台为控制台连接zk超时时间。
  • zookeeper根目录:代表每一个调度器在zookeeper中的唯一路径,所有的调度器Server信息,执行器job信息均存于该目录。同时也可以切换该目录地址以切换不同的调度器。(例如:用户信息离线计算分布在一个三机集群,根目录地址为/tbschedule/userinfo/tasks;用户账户信息离线计算分布在另一个三级集群,根目录地址为/tbschedule/useraccounts/accountinfo/tasks。)

        要点1:由于zookeeper新版本权限控制的问题,每次新增节点必须先在控制台配置并保存,然后再启动应用。否则会出现控制台一直连接不上的问题。

  • zookeeper用户:如果需要对节点加密,可以填写此项
  • zookeeper密码:如果需要对节点加密,可以填写此项

        要点2:对Zookeeper根目录配置用户名密码不能保证节点的绝对安全,且不说再进入控制台的时候会保留上一次配置的信息。另利用zookeeper客户端也是可以对节点进行权限消除,删除等操作的。所以作者认为用户名密码不如不配,还省去管理的麻烦。

tbs_sdk setJavaScriptEnabled无效 tbs配置_json



3、控制台调度策略配置

tbs_sdk setJavaScriptEnabled无效 tbs配置_线程组_02

  • 策略名称:调度器唯一标识名,同一个zookeeper根目录下不可重复。
  • 任务类型:分三类,Schedule; Java; Bean,其中Schedule为默认也是重点,下面依次讲解。
  • Schedule:调度器默认实现任务类型,采用内部实现的调度器管理(TBScheduleManagerFactory)、任务执行管理(TBScheduleManagerStatic)、调度策略(ScheduleStrategy),支持隔离域。
  • Java:Java Class实现的自定义调度策略,需继承TBScheduleManager抽象类,自己实现调度策略的所有操作。
  • Bean:Java Bean实现的自定义调度策略,与Java相比只是获取实例的方式不同。配置Java时tbs用的是反射,配置Bean时使用ApplicationContext.getBean()。

    要点1:

  • 隔离域概念:虽说现在大部分企业的个人开发机、开发环境、测试环境网络都是隔离的,但也有不少混用。此时为了不影响其它环境的正常使用就需要用到隔离域。官方没有过多的提及隔离域的用法是因为所谓的隔离域无非就是一个传入selectTasks()的参数而已,与代码耦合,使用不当很可能影响到正常业务。这里只讲解其用法但并不推荐大家使用。
  • 隔离域作用:仅用于隔离IP地址,以避免在IP地址配置为全扫描时造成的环境混乱。
  • 隔离域的配置:在调度策略--任务名称一栏于任务名称之后加$符号后跟自定义域名(不配置默认为BASE),例如:FirstScheduleDemo$TEST,此时隔离域为TEST
  • 隔离域配置图例:

        

tbs_sdk setJavaScriptEnabled无效 tbs配置_java_03

        上图:同一任务,多个隔离域

        

tbs_sdk setJavaScriptEnabled无效 tbs配置_大数据_04

        上图:启动第一个默认策略,运行名为:FirstScheduleDemo -- BASE任务配置

                  启动第二个TEST策略,运行名为:FirstScheduleDemo -- TEST任务配置

        但他们实际都是运行的任务FirstScheduleDemo相同的配置,只是传入selectTasks()方法的第二个参数ownSign值为BASE或者TEST。这个神秘的参数懂了没?没什么卵用对不对?  ╮(╯▽╰)╭

  • 任务名称:
  • 任务类型为Schedule,这里等同于策略名称;
  • 任务类型为Java,这里为Java Class绝对路径;
  • 任务类型为Bean,这里为SpringBean Name;
  • 任务参数:自定义调度策略时需要的动态参数,Schedule任务类型没有实现该方法,因而在Schedule类型任务下无效,无需配置。
  • 单JVM最大线程组数量:
  • 概念:这里的单JVM不太好理解,如果是一路看着作者前几篇教程过来的朋友,把这里理解成单个调度器实例可以启动的最大线程组数量(一个线程组中默认5个子线程)。一个线程组负责执行对应任务中的一个任务项。                                                                                 那么不难理解,如果不限制该值,当所有实例加起来的线程组总数大于任务项时,任务项被均匀分配到线程组中,多出来的线程组作为灾备处于“待命”状态,过多的“待命”必然会有线程资源的浪费。而当其小于任务项时,多余的任务项会被分配到某一线程组下并行,一个线程组处理太多的任务项也会造成该实例占用资源升高,效率降低,极端情况下可能会压死实例。                                                                                                                   如何限制该值,这里官方控制台有一个误导。假设单JVM最大线程组数量为1,最大线程组数量为3,启动一个调度器你会发现该调度器仍会启动3个线程组。此时所谓的“单JVM最大线程组数量”是没有效果的,如果有单线程运行的需求,请配置单JVM最大线程组数量为1,最大线程组数量为1,并将任务配置中的“线程数”设为1。此时该任务处于真正的单线程运行,避免业务处理中的线程安全问题。
  • 作用1:灾备(重要)。当执行任务的调度器为集群时,某一调度器宕机达到60s,tbs会将任务转移到另一个调度器中的线程组运行。
  • 作用2:多重并发执行。知道了线程组的概念,那么我们可以利用任务项同时激活多个线程组,再加上任务执行本身也可以配置线程数。所以此时我们可以做到单个调度器上:(线程组数*子线程数)个线程同时处理任务,作者将此场景称之为多重并发执行,其执行效率相当恐怖,但需要自己处理好任务处理幂等性的问题。
  • 调度器、线程组、任务项的分配原理如下:
1、线程组在调度器中的分配
假设:
2个调度器实例:A B
5个线程组:0,1,2,3,4

其分配机制如下:
A  B
0  1
2  3
4

2、任务项在线程组中的分配
假设:
3个线程组:A B C
10个任务项:0,1,2,3,4,5,6,7,8,9

其分配机制如下:
A   B   C
0   1   2
3   4   5
6   7   8
9
  • 最大线程组数量:所有调度器总计可以运行的线程组数量
  • 注意:结合上述分配图看,理论上,也是大多数情况下,tbs都会启动最大线程组数量的线程组以保证可容灾性。但在实际应用中偶尔也会发生线程组启动不全的情况,这种情况仅限于某任务所配置的任务项刚好整倍数于线程组数的时候。
  • ip地址:调度器IP地址列表


4、控制台任务管理配置(首要按重要性排序,次要按常用度排序)

配置项

说明

要点

任务名称

这个没什么好说的,与策略中不加隔离域的策略名称一致

String.subString(0,indexOf('$'))


任务处理SpringBean

别填错了哈


线程数

tbs处理任务的excute()方法是多线程并且是线程安全的。这里指定线程数量,根据机器配置情况自行调整值大小。


每次获取数据量

影响selectTasks()方法最末一个传入参数eachFetchDataNum,可用作分页。

官方原版只能执行分页大小,没有方法获得当前页码。这无法满足一些数据没有处理标志位字段的场景,而如果自己保存页码的话线程同步和数据一致性为题比较难保证。可以使用作者基于3.2.18-SNAPSHOT版本的优化版,接口增加了页码,多线程组,多线程下保证页码的正确性。下载链接:tbschedule-wed

每次执行数量

仅在使用批量处理接口(IScheduleTaskDealMulti<T>)时生效,tbs会将selectTasks()返回的List均分到每个线程。


执行开始时间

遵循Cron表达式标准:

秒 分 时 日 月 周 年

其中“月”和“周”支持英文缩写

注意:官方版本到3.3.3.2版本对任务执行间隔期的处理仍有线程问题。极易发生在selectTasks()返回size>0到<=0这一瞬间,导致任务由run-->无数据判断本轮调度执行完毕pause-->等待下一次cron开始时间的到来的正常状态机。错误得变更为run-->sleep-->resume的死循环,此时任务不受控,控制台无法终止任务。这个由两个方面带来1、使用Timer.schedule()方法控制任务执行;2、zookeeper数据同本地线程不一致,很好复现。在任意任务selectTasks()方法返回前断点,等待超过zookeeper的timeout(这里指的是zookeeper server配置的timeout而不是tbs的timeout)后放开断点。此时发现zk链接断开,客户端尝试重连。但是客户端重连后基于灾备的考虑没有清理zk数据,此时Timer失效,任务转为“不停尝试获取第一轮数据,但是又获取不到”的状态。如图所示:

tbs_sdk setJavaScriptEnabled无效 tbs配置_线程组_05

如果“没有数据时休眠时长”项用的默认0.5秒并且线程很多的话。发生这一情况将导致CPU时间片频繁切换导致CPU占用飙升。同时大量的日志输出也会导致硬盘IO飙升,在一些小硬盘的虚拟机甚至有可能打满硬盘导致服务器宕机。

一个比较好的Cron表达式生成工具,虽然方便,但强烈建议自己学会Cron表达式,毕竟比正则表达式简单太多了:Cron在线生成工具

常用Cron表达式中各符号的作用:

“*”:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件。

“?”:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。 

“-”:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次。

“/”:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次。

“,”:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。 

执行结束时间

用于确定何时强行终止任务的执行。格式同“执行开始时间”

注意:除非你相当了解你的任务,否则慎用此项!官方版本到3.3.3.2版本对任务定时结束的处理仍然有问题,尤其是在间隔短、连续执行的job上配置结束时间可以说没有任何用处,甚至带来混乱。你会经常看下如下报错:

tbs_sdk setJavaScriptEnabled无效 tbs配置_zookeeper_06

看起来像是任务已终止。但实际上你会发现任务仍然持续执行。

单线程组最大任务项

用于限制单线程组能够处理的最大任务项数量,请结合本文上述“单JVM最大线程组数量”配置项看。

注意:配置此项虽然可以避免某一线程组“过劳死”,但是配置之前请务必确保你的调度器集群中,实例数*单实例线程组数必须大于任务项总数。否非一单发生宕机,而又限制了此项,那么将会有部分任务项无法被执行到。

自定义参数

无特定要求格式,建议分隔符或JSON序列化。大小不得超过4K


任务项

分布式计算时用,这里只是开发人员自己定义的一套数据切分策略的切分键。

注意:tbs只保证任务项的均匀分配与灾备转移,框架本身并不关心开发者采用什么样的分片策略。每个任务项,也可称作分片标记以“,”号分割即可,其如何表示没有特别的格式要求。例如一个三机分布式计算,按task唯一id分片,可传入“0,1,2,3,4”。那么接口中得到的taskItemNum值即为5,selectTasks()取数时,用task唯一id对5取模。得到0,3则在机器A上被取出,得到1,4则在机器B上被取出,得到2则在机器C上被取出执行。

心跳频率

调度器向zookeeper注册中心报告:“我还活着”的时间间隔,保持5s就好。zkServer链接数有限,该值过小会对zkServer造成不必要的压力。该值过大又不能及时发现宕机尝试重连。


假定服务死亡时间

结合心跳频率,倍数于心跳频率时间,并且强校验至少5倍于心跳频率。

注意:调度管理器在某一个调度器死亡超过该项时间,则自动尝试将job转移到其余节点继续执行。注意是继续执行而不是重启,敲黑板,划重点!!

处理模式

SLEEP 和 NOTSLEEP模式,NOTSLEEP模式下,下方两项配置无效。

 

没有数据时休眠时长

字面意思


每次处理完数据后休眠时间

字面意思