XXL-JOB解读

源码地址:https://github.com/xuxueli/xxl-job

XXL-JOB解读_第1张图片

数据中心:

主要表:任务信息表、任务执行流水表、执行器信息表、执行器注册表

调度器:

总体流程

启动机器注册监控线程:每30秒清理一次注册表中的无效机器(admin/executor);

启动失败重试监控线程:没10秒检测失败任务,报警和重试;

initRpcProvider:初始化xxlRpcProviderFactory注册服务adminBiz用于响应执行器的注册请求,并将其作为ServletServerHandler的成员变量。当调用JobApiController的api方法是会调用XxlJobScheduler.invokeAdminService(request, response),进一步调用ServletServerHandler的handle方法,handle方法判断如果是服务匹配的请求直接将本服务器信息返回,如果是调用方的服务请求则解析请求参数调用xxlRpcProviderFactory.invokeService,xxlRpcProviderFactory.invokeService通过服务key找到相应的服务类实际就是adminBiz,调用请求中要求的方法来获取返回数据;

开启调度:JobScheduleHelper.getInstance().start(),下面是调度相关的介绍。

调度线程定时5秒执行一次

1. 超时未调度(超过调度时间5秒)的任务不再执行,修改下次执行时间。

2. 超过调度时间但未超时(超过5秒之内)的任务,立即放入执行线程池,再修改执行时间,接着判断下次执行时间若在5秒之内,加入timewheel的map后再次修改下次执行时间。

3. 调度时间在未来5秒之内的(预读5s),基于timewheel时间轮(map<秒数,list<任务实体>>),根据5秒内即将执行的任务的执行时间的秒数,将其放到timeheel对应秒数的list中,修改下次执行时间。

时间轮执行线程定时每1秒执行一次

1. 删除并取出当前秒数的list和前一秒的list立即放入执行线程池。(往前取1秒防止前1秒的任务未执行,比如当前秒数是59,如果57的任务执行时间大于1秒,可能58的任务就没有被执行过,所以59秒的时候取58和59的任务防止这种情况)。

任务执行的快慢线程池(调度器)

将1分钟内任务执行超时(超过500ms视为超时)数量记录在Map,1分钟过期。

快线程池:1分钟内未超时,或超时次数少于10的任务

慢线程池:1分钟为超时次数大于等于10的任务

执行器的路由策略(调度器)

1. 第一个:addressList.get(0)

2. 最后一个:addressList.get(addressList.size()-1)

3. 轮询:ConcurrentMap缓存所有任务当前执行器,缓存24小时更新一次

4. 随机:addressList.get(localRandom.nextInt(addressList.size()))

5. 一致性HASH:将执行器地址映射到0-2^31的数,组成一个map<执行器hash,执行器地址>;再将jobid映射到0-2^31的数,取map中顺时针与jobid的hash最接近的执行器hash,去除执行器地址。为防止执行器节点数太少导致数据倾斜的问题(如图中node4没有的话,数据会倾斜到node3),将每个执行器变成5个虚拟节点(比如node1变成node1-0,node1-1,node1-2……)

XXL-JOB解读_第2张图片

6. 最不经常使用:ConcurrentMap>缓存执行频率,放入新地址,清理不再使用的执行器,并取当前jobid执行频率最低的执行器,每24小时更新缓存

7. 最近最久未使用:ConcurrentMap>,放入新地址,清理不再使用的执行器。LinkedHashMap按照访问顺序排序,每次取第一个即最久未访问的一个。

8. 故障转移:按照顺序依次发送beat信息,检测到故障跳过,检测到正常的返回

9. 忙碌转移:按照顺序依次发送idlebeat进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度

10. 分片广播:广播触发对应集群中所有执行器执行一次任务,并将分片参数(当前分片序号,总结点数)传给执行器,执行器根据分片参数进行分片业务处理。

任务执行过程(调度器)

新代理的生成过程

XXL-JOB解读_第3张图片

通过XxlRpcReferenceBean.getObject()生成代理对象(保存在ConcurrentMap<地址, ExecutorBiz代理对象>),并重写invoke方法。

invoke方法:1. 当执行器路由策略中addressList为空的时候,初始化XxlRpcReferenceBean的时候并无法获取到服务地址,所以invoke中判断地址为空时,通过servicekey去注册中心discover具体的服务地址,根据XxlRpcReferenceBean设置的负载均衡策略(随机、轮询、LRU、LFU、一致性hash)找到服务address。2. 发送xxlRpcRequest。3. 根据XxlRpcReferenceBean设置的响应类型(同步、异步future、回调、单向)返回结果。

响应处理方式

同步方式:设置一个XxlRpcFutureResponse,将XxlRpcInvokerFactory作为其成员变量,在XxlRpcInvokerFactory对象中使用ConcurrentMap<请求id, XxlRpcFutureResponse> 将请求id与future对应,阻塞当前线程,直到future获取到返回结果或超时。

异步方式:同样设置一个XxlRpcFutureResponse,将XxlRpcInvokerFactory作为其成员变量,在XxlRpcInvokerFactory对象中使用ConcurrentMap<请求id, XxlRpcFutureResponse> 将请求id与future对应,将XxlRpcFutureResponse作为成员变量放入一个XxlRpcInvokeFuture对象,再将XxlRpcInvokeFuture作为线程独有变量,在invoke方法中直接返回null。(之后调用方可以去处理其他业务,最后调用方再从线程独有变量中,取出XxlRpcInvokeFuture,调用XxlRpcInvokeFuture的get方法取出XxlRpcFutureResponse成员变量中的真正返回值。)

回调方式:在构建代理的时候设置一个XxlRpcInvokeCallback对象,或是构建代理之后通过XxlRpcInvokeCallback的线程独有变量将回调处理方式——一个XxlRpcInvokeCallback对象封装在里面。同样设置一个XxlRpcFutureResponse,将XxlRpcInvokerFactory作为其成员变量,并将XxlRpcInvokeCallback对象作为成员变量保存在XxlRpcFutureResponse中,在XxlRpcInvokerFactory对象中使用ConcurrentMap<请求id, XxlRpcFutureResponse> 将请求id与future对应,然后在invoke方法中直接返回null。

单向方式:只发出,不做其他处理。

jetty或netty或nettyhttp或其他通讯协议的客户端处理器监听到有数据返回的时候,调用XxlRpcInvokerFactory的方法从map中用requestid取出XxlRpcFutureResponse对象,如果XxlRpcFutureResponse对象有回调处理则处理回调,否则将返回数据set到XxlRpcFutureResponse对象。

执行器

总体流程

在执行器服务启动的时候,将IJobHandler实现类的对象注册到XxlJobExecutor,再调用XxlJobExecutor的start方法启动,启动过程包含初始化日志路径、创建代理对象(保存List中)、初始化日志清理线程、初始化triggerCallbackThread回调线程用于处理其成员变量callBackQueue中的内容、初始化xxlRpcProviderFactory

初始化xxlRpcProviderFactory

启动注册线程,注册当前服务器

启动jetty或netty或nettyhttp或其他通讯协议的服务端,定义一个线程池作为NettyHttpServerHandler的成员变量,将xxlRpcProviderFactory作为NettyHttpServerHandler的成员变量

当jetty或netty或nettyhttp或其他通讯协议的服务端处理器监听到请求时,判断如果是服务匹配的请求直接将本服务器信息返回,如果是调用方的服务请求则解析请求参数调用xxlRpcProviderFactory.invokeService获取放回数据。

xxlRpcProviderFactory.invokeService

xxl-job中invoke会调用ExecutorBizImpl.run来处理请求。ExecutorBizImpl.run处理过程如下:

找到对应的IJobHandler实例,通过jobid找到对应的jobThread,如果没有找到,则创建新的jobThread保存

ConcurrentMap中,并start。根据请求的执行阻塞策略(包含串行,抛弃后面的请求,覆盖前面的请求这三种),确认是否需要终止当前请求,或终止当前线程,或再将本次请求的参数放入jobThread的阻塞队列triggerQueue中,将logid存入triggerLogIdSet中防止重复请求。jobThread的执行就会一直从triggerQueue中获取请求执行IJobHandler的execute,如果设置超时,则使用future获取执行结果,将返回值放入triggerCallbackThread的callBackQueue

 

你可能感兴趣的:(Java)