xxl-job核心(version: 2.3.1)
核心是执行器,执行器类: XxlJobExecutor,实现类为: XxlJobSpringExecutor 类定义:
public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware , SmartInitializingSingleton , DisposableBean {
@Override
public void afterSingletonsInstantiated ( ) {
. . .
}
. . .
}
ApplicationContextAware 获取运行时上下文 DisposableBean 释放资源 SmartInitializingSingleton(单例bean实例化完成后执行) 主要方法
注入 XxlJobSpringExecutor
项目使用中,需注入 XxlJobSpringExecutor 类 例如: xxl-job 和 springboot集成的依赖 XxlJobConfig 类定义
@Configuration
public class XxlJobConfig {
@Value ( "${xxl.job.admin.addresses}" )
private String adminAddresses;
@Value ( "${xxl.job.accessToken}" )
private String accessToken;
@Value ( "${xxl.job.executor.appname}" )
private String appname;
@Value ( "${xxl.job.executor.address}" )
private String address;
@Value ( "${xxl.job.executor.ip}" )
private String ip;
@Value ( "${xxl.job.executor.port}" )
private int port;
@Value ( "${xxl.job.executor.logpath}" )
private String logPath;
@Value ( "${xxl.job.executor.logretentiondays}" )
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor ( ) {
logger. info ( ">>>>>>>>>>> xxl-job config init." ) ;
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor ( ) ;
xxlJobSpringExecutor. setAdminAddresses ( adminAddresses) ;
xxlJobSpringExecutor. setAppname ( appname) ;
xxlJobSpringExecutor. setAddress ( address) ;
xxlJobSpringExecutor. setIp ( ip) ;
xxlJobSpringExecutor. setPort ( port) ;
xxlJobSpringExecutor. setAccessToken ( accessToken) ;
xxlJobSpringExecutor. setLogPath ( logPath) ;
xxlJobSpringExecutor. setLogRetentionDays ( logRetentionDays) ;
return xxlJobSpringExecutor;
}
}
XxlJobSpringExecutor#afterSingletonsInstantiated 实现
public void afterSingletonsInstantiated ( ) {
initJobHandlerMethodRepository ( applicationContext) ;
GlueFactory . refreshInstance ( 1 ) ;
try {
super . start ( ) ;
} catch ( Exception e) {
throw new RuntimeException ( e) ;
}
}
XxlJobSpringExecutor#initJobHandlerMethodRepository 实现(寻找 @XxlJob 注解) 需注意 @XxlJob 注解只能加在方法上(旧版本注解为 @JobHandler, 直接写在类/接口上)
private void initJobHandlerMethodRepository ( ApplicationContext applicationContext) {
if ( applicationContext == null ) {
return ;
}
String [ ] beanDefinitionNames = applicationContext. getBeanNamesForType ( Object . class , false , true ) ;
for ( String beanDefinitionName : beanDefinitionNames) {
Object bean = applicationContext. getBean ( beanDefinitionName) ;
Map < Method , XxlJob > annotatedMethods = null ;
try {
annotatedMethods = MethodIntrospector . selectMethods ( bean. getClass ( ) ,
new MethodIntrospector. MetadataLookup < XxlJob > ( ) {
@Override
public XxlJob inspect ( Method method) {
return AnnotatedElementUtils . findMergedAnnotation ( method, XxlJob . class ) ;
}
} ) ;
} catch ( Throwable ex) {
logger. error ( "xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "]." , ex) ;
}
if ( annotatedMethods== null || annotatedMethods. isEmpty ( ) ) {
continue ;
}
for ( Map. Entry < Method , XxlJob > methodXxlJobEntry : annotatedMethods. entrySet ( ) ) {
Method executeMethod = methodXxlJobEntry. getKey ( ) ;
XxlJob xxlJob = methodXxlJobEntry. getValue ( ) ;
registJobHandler ( xxlJob, bean, executeMethod) ;
}
}
}
XxlJobExecutor#registJobHandler(注册 JobHandler)
protected void registJobHandler ( XxlJob xxlJob, Object bean, Method executeMethod) {
. . .
registJobHandler ( name, new MethodJobHandler ( bean, executeMethod, initMethod, destroyMethod) ) ;
}
MethodJobHandler 类(将需要执行的任务,封装为 MethodJobHandler 对象)
public class MethodJobHandler extends IJobHandler {
. . .
@Override
public void execute ( ) throws Exception {
Class < ? > [ ] paramTypes = method. getParameterTypes ( ) ;
if ( paramTypes. length > 0 ) {
method. invoke ( target, new Object [ paramTypes. length] ) ;
} else {
method. invoke ( target) ;
}
}
. . .
}
XxlJobExecutor#registJobHandler
private static ConcurrentMap < String , IJobHandler > jobHandlerRepository = new ConcurrentHashMap < String , IJobHandler > ( ) ;
public static IJobHandler registJobHandler ( String name, IJobHandler jobHandler) {
logger. info ( ">>>>>>>>>>> xxl-job register jobhandler success, name:{}, jobHandler:{}" , name, jobHandler) ;
return jobHandlerRepository. put ( name, jobHandler) ;
}
放入 jobHandlerRepository, key为 @XxlJob注解的value属性, value为 通过目标类和需执行的方法等封装的 MethodJobHandler
XxlJobExecutor#start 实现
public void start ( ) throws Exception {
XxlJobFileAppender . initLogPath ( logPath) ;
initAdminBizList ( adminAddresses, accessToken) ;
JobLogFileCleanThread . getInstance ( ) . start ( logRetentionDays) ;
TriggerCallbackThread . getInstance ( ) . start ( ) ;
initEmbedServer ( address, ip, port, appname, accessToken) ;
}
TriggerCallbackThread#start
public void start ( ) {
if ( XxlJobExecutor . getAdminBizList ( ) == null ) {
logger. warn ( ">>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null." ) ;
return ;
}
triggerCallbackThread = new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
while ( ! toStop) {
try {
HandleCallbackParam callback = getInstance ( ) . callBackQueue. take ( ) ;
if ( callback != null ) {
List < HandleCallbackParam > callbackParamList = new ArrayList < HandleCallbackParam > ( ) ;
int drainToNum = getInstance ( ) . callBackQueue. drainTo ( callbackParamList) ;
callbackParamList. add ( callback) ;
if ( callbackParamList!= null && callbackParamList. size ( ) > 0 ) {
doCallback ( callbackParamList) ;
}
}
} catch ( Exception e) {
if ( ! toStop) {
logger. error ( e. getMessage ( ) , e) ;
}
}
}
try {
List < HandleCallbackParam > callbackParamList = new ArrayList < HandleCallbackParam > ( ) ;
int drainToNum = getInstance ( ) . callBackQueue. drainTo ( callbackParamList) ;
if ( callbackParamList!= null && callbackParamList. size ( ) > 0 ) {
doCallback ( callbackParamList) ;
}
} catch ( Exception e) {
if ( ! toStop) {
logger. error ( e. getMessage ( ) , e) ;
}
}
logger. info ( ">>>>>>>>>>> xxl-job, executor callback thread destroy." ) ;
}
} ) ;
triggerCallbackThread. setDaemon ( true ) ;
triggerCallbackThread. setName ( "xxl-job, executor TriggerCallbackThread" ) ;
triggerCallbackThread. start ( ) ;
triggerRetryCallbackThread = new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
while ( ! toStop) {
try {
retryFailCallbackFile ( ) ;
} catch ( Exception e) {
if ( ! toStop) {
logger. error ( e. getMessage ( ) , e) ;
}
}
try {
TimeUnit . SECONDS. sleep ( RegistryConfig . BEAT_TIMEOUT) ;
} catch ( InterruptedException e) {
if ( ! toStop) {
logger. error ( e. getMessage ( ) , e) ;
}
}
}
logger. info ( ">>>>>>>>>>> xxl-job, executor retry callback thread destroy." ) ;
}
} ) ;
triggerRetryCallbackThread. setDaemon ( true ) ;
triggerRetryCallbackThread. start ( ) ;
}
XxlJobExecutor#initEmbedServer(初始化netty,绑定端口,交互调度中心,采用netty进行业务http交互,监控调度中心的调度请求,并分配到特定的执行通道进行执行)
private void initEmbedServer ( String address, String ip, int port, String appname, String accessToken) throws Exception {
. . .
embedServer = new EmbedServer ( ) ;
embedServer. start ( address, port, appname, accessToken) ;
}
EmbedServer#start(底层使用 Netty 通讯)
public void start ( final String address, final int port, final String appname, final String accessToken) {
executorBiz = new ExecutorBizImpl ( ) ;
thread = new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
EventLoopGroup bossGroup = new NioEventLoopGroup ( ) ;
EventLoopGroup workerGroup = new NioEventLoopGroup ( ) ;
ThreadPoolExecutor bizThreadPool = new ThreadPoolExecutor (
0 ,
200 ,
60L ,
TimeUnit . SECONDS,
new LinkedBlockingQueue < Runnable > ( 2000 ) ,
new ThreadFactory ( ) {
@Override
public Thread newThread ( Runnable r) {
return new Thread ( r, "xxl-job, EmbedServer bizThreadPool-" + r. hashCode ( ) ) ;
}
} ,
new RejectedExecutionHandler ( ) {
@Override
public void rejectedExecution ( Runnable r, ThreadPoolExecutor executor) {
throw new RuntimeException ( "xxl-job, EmbedServer bizThreadPool is EXHAUSTED!" ) ;
}
} ) ;
try {
ServerBootstrap bootstrap = new ServerBootstrap ( ) ;
bootstrap. group ( bossGroup, workerGroup)
. channel ( NioServerSocketChannel . class )
. childHandler ( new ChannelInitializer < SocketChannel > ( ) {
@Override
public void initChannel ( SocketChannel channel) throws Exception {
channel. pipeline ( )
. addLast ( new IdleStateHandler ( 0 , 0 , 30 * 3 , TimeUnit . SECONDS) )
. addLast ( new HttpServerCodec ( ) )
. addLast ( new HttpObjectAggregator ( 5 * 1024 * 1024 ) )
. addLast ( new EmbedHttpServerHandler ( executorBiz, accessToken, bizThreadPool) ) ;
}
} )
. childOption ( ChannelOption . SO_KEEPALIVE, true ) ;
ChannelFuture future = bootstrap. bind ( port) . sync ( ) ;
logger. info ( ">>>>>>>>>>> xxl-job remoting server start success, nettype = {}, port = {}" , EmbedServer . class , port) ;
startRegistry ( appname, address) ;
future. channel ( ) . closeFuture ( ) . sync ( ) ;
} catch ( InterruptedException e) {
logger. info ( ">>>>>>>>>>> xxl-job remoting server stop." ) ;
} catch ( Exception e) {
logger. error ( ">>>>>>>>>>> xxl-job remoting server error." , e) ;
} finally {
try {
workerGroup. shutdownGracefully ( ) ;
bossGroup. shutdownGracefully ( ) ;
} catch ( Exception e) {
logger. error ( e. getMessage ( ) , e) ;
}
}
}
} ) ;
thread. setDaemon ( true ) ;
thread. start ( ) ;
}
EmbedServer#startRegistry ExecutorRegistryThread#start
public void start ( final String appname, final String address) {
if ( appname== null || appname. trim ( ) . length ( ) == 0 ) {
logger. warn ( ">>>>>>>>>>> xxl-job, executor registry config fail, appname is null." ) ;
return ;
}
if ( XxlJobExecutor . getAdminBizList ( ) == null ) {
logger. warn ( ">>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null." ) ;
return ;
}
registryThread = new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
while ( ! toStop) {
try {
RegistryParam registryParam = new RegistryParam ( RegistryConfig. RegistType . EXECUTOR. name ( ) , appname, address) ;
for ( AdminBiz adminBiz: XxlJobExecutor . getAdminBizList ( ) ) {
try {
ReturnT < String > registryResult = adminBiz. registry ( registryParam) ;
if ( registryResult!= null && ReturnT . SUCCESS_CODE == registryResult. getCode ( ) ) {
registryResult = ReturnT . SUCCESS;
logger. debug ( ">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}" , new Object [ ] { registryParam, registryResult} ) ;
break ;
} else {
logger. info ( ">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}" , new Object [ ] { registryParam, registryResult} ) ;
}
} catch ( Exception e) {
logger. info ( ">>>>>>>>>>> xxl-job registry error, registryParam:{}" , registryParam, e) ;
}
}
} catch ( Exception e) {
if ( ! toStop) {
logger. error ( e. getMessage ( ) , e) ;
}
}
try {
if ( ! toStop) {
TimeUnit . SECONDS. sleep ( RegistryConfig . BEAT_TIMEOUT) ;
}
} catch ( InterruptedException e) {
if ( ! toStop) {
logger. warn ( ">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}" , e. getMessage ( ) ) ;
}
}
}
try {
RegistryParam registryParam = new RegistryParam ( RegistryConfig. RegistType . EXECUTOR. name ( ) , appname, address) ;
for ( AdminBiz adminBiz: XxlJobExecutor . getAdminBizList ( ) ) {
try {
ReturnT < String > registryResult = adminBiz. registryRemove ( registryParam) ;
if ( registryResult!= null && ReturnT . SUCCESS_CODE == registryResult. getCode ( ) ) {
registryResult = ReturnT . SUCCESS;
logger. info ( ">>>>>>>>>>> xxl-job registry-remove success, registryParam:{}, registryResult:{}" , new Object [ ] { registryParam, registryResult} ) ;
break ;
} else {
logger. info ( ">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}" , new Object [ ] { registryParam, registryResult} ) ;
}
} catch ( Exception e) {
if ( ! toStop) {
logger. info ( ">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}" , registryParam, e) ;
}
}
}
} catch ( Exception e) {
if ( ! toStop) {
logger. error ( e. getMessage ( ) , e) ;
}
}
logger. info ( ">>>>>>>>>>> xxl-job, executor registry thread destroy." ) ;
}
} ) ;
registryThread. setDaemon ( true ) ;
registryThread. setName ( "xxl-job, executor ExecutorRegistryThread" ) ;
registryThread. start ( ) ;
}
参考