XXL-JOB(V2.0.2)源碼解讀:一篇帶你理解執(zhí)行器源碼

接著上篇文章,上篇文章詳細(xì)分析了XXL-JOB調(diào)度中心的源碼,這篇文章我們一起看一看執(zhí)行器端是如何工作的
實(shí)際在執(zhí)行器應(yīng)用中,內(nèi)嵌了一個(gè)jetty服務(wù)器, 服務(wù)在xxlJobExecutor 初始化的時(shí)候啟動(dòng)。當(dāng)執(zhí)行器端啟動(dòng)時(shí)會(huì)定時(shí)向注冊中心進(jìn)行自動(dòng)注冊,并且當(dāng)調(diào)度中心有任務(wù)觸發(fā)的時(shí)候也會(huì)發(fā)起RPC請求,請求執(zhí)行器執(zhí)行具體的任務(wù)
首先我們看一下執(zhí)行器端集成XXL-JOB所做過的配置信息XxlJobConfig,同樣是一個(gè)基于JavaConfig的配置信息

/**
 * xxl-job config
 *
 * @author xuxueli 2017-04-28
 */
@Configuration
public class XxlJobConfig {
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;

    @Value("${xxl.job.executor.appname}")
    private String appName;

    @Value("${xxl.job.executor.ip}")
    private String ip;

    @Value("${xxl.job.executor.port}")
    private int port;

    @Value("${xxl.job.accessToken}")
    private String accessToken;

    @Value("${xxl.job.executor.logpath}")
    private String logPath;

    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;


    @Bean(initMethod = "start", destroyMethod = "destroy")
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        //創(chuàng)建執(zhí)行器對象
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        //設(shè)置一些屬性,屬性值是從配置文件中得到的
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppName(appName);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
        return xxlJobSpringExecutor;
  }

    /**
     * 針對多網(wǎng)卡、容器內(nèi)部署等情況,可借助 "spring-cloud-commons" 提供的 "InetUtils" 組件靈活定制注冊IP;
     *
     *      1、引入依賴:
     *          <dependency>
     *             <groupId>org.springframework.cloud</groupId>
     *             <artifactId>spring-cloud-commons</artifactId>
     *             <version>${version}</version>
     *         </dependency>
     *
     *      2、配置文件,或者容器啟動(dòng)變量
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
     *
     *      3、獲取IP
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
     */
}

在容器初始化好xxlJobExecutor后會(huì)執(zhí)行其initMethod,也就是start方法,我們進(jìn)去看看這個(gè)方法做了些什么

public void start() throws Exception {
    this.initJobHandlerRepository(applicationContext);
    GlueFactory.refreshInstance(1);
    super.start();
}

這里首先會(huì)調(diào)用initJobHandlerRepository(applicationContext)方法,進(jìn)去這個(gè)方法:

private void initJobHandlerRepository(ApplicationContext applicationContext) {
    if (applicationContext != null) {
        //從上下文中獲取到所有的帶有@JobHandler注解的Bean
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);
        if (serviceBeanMap != null && serviceBeanMap.size() > 0) {
            Iterator i$ = serviceBeanMap.values().iterator();

            while(i$.hasNext()) {
                Object serviceBean = i$.next();
                if (serviceBean instanceof IJobHandler) { //如果實(shí)現(xiàn)了IJobHandler接口
                    //取出@JobHandler的value值
                    String name = ((JobHandler)serviceBean.getClass().getAnnotation(JobHandler.class)).value();
                    IJobHandler handler = (IJobHandler)serviceBean;
                    if (loadJobHandler(name) != null) {
                        throw new RuntimeException("xxl-job jobhandler naming conflicts.");
                    }
                    //注冊JobHandler
                    registJobHandler(name, handler);
                }
            }
        }
    }
}

跟進(jìn)registJobHandler(name, handler):

public static IJobHandler registJobHandler(String name, IJobHandler jobHandler) {
    logger.info(">>>>>>>>>>> xxl-job register jobhandler success, name:{}, jobHandler:{}", name, jobHandler);
    //將jobHandler放入jobHandlerRepository(本質(zhì)是一個(gè)ConcurrentHashMap)
    return (IJobHandler)jobHandlerRepository.put(name, jobHandler);
}

接著我們再看下super.start()方法做了什么:

public void start() throws Exception {
    //設(shè)置日志目錄
    XxlJobFileAppender.initLogPath(this.logPath);
    //??????這里很重要,這里initAdminBizList方法會(huì)初始化AdminBizList,并且會(huì)創(chuàng)建AdminBiz的動(dòng)態(tài)代理XxlRpcReferenceBean,而最后會(huì)用到這個(gè)類進(jìn)行自動(dòng)注冊(標(biāo)記Tag,下文會(huì)用到這里)
    this.initAdminBizList(this.adminAddresses, this.accessToken);
    //開啟日志清理線程
    JobLogFileCleanThread.getInstance().start((long)this.logRetentionDays);
    //初始化觸發(fā)器回調(diào)線程(用RPC回調(diào)調(diào)度中心接口)
    TriggerCallbackThread.getInstance().start();
    this.port = this.port > 0 ? this.port : NetUtil.findAvailablePort(9999);
    this.ip = this.ip != null && this.ip.trim().length() > 0 ? this.ip : IpUtil.getIp();
    //初始化Rpc服務(wù)
    this.initRpcProvider(this.ip, this.port, this.appName, this.accessToken);
}

跟進(jìn) this.initRpcProvider(this.ip, this.port, this.appName, this.accessToken),代碼片段如下:

private void initRpcProvider(String ip, int port, String appName, String accessToken) throws Exception {
    this.xxlRpcInvokerFactory = new XxlRpcInvokerFactory();
    String address = IpUtil.getIpPort(ip, port);
    Map<String, String> serviceRegistryParam = new HashMap();
    serviceRegistryParam.put("appName", appName);
    serviceRegistryParam.put("address", address);
    this.xxlRpcProviderFactory = new XxlRpcProviderFactory();
    this.xxlRpcProviderFactory.initConfig(NetEnum.JETTY, SerializeEnum.HESSIAN.getSerializer(), ip, port, accessToken, XxlJobExecutor.ExecutorServiceRegistry.class, serviceRegistryParam);
    this.xxlRpcProviderFactory.addService(ExecutorBiz.class.getName(), (String)null, new ExecutorBizImpl());
    this.xxlRpcProviderFactory.start();
}

初始化了一個(gè)XxlRpcInvokerFactory和XxlRpcProviderFactory,上面代碼中最重要的是

this.xxlRpcProviderFactory.initConfig(NetEnum.JETTY, SerializeEnum.HESSIAN.getSerializer(), ip, port, accessToken, XxlJobExecutor.ExecutorServiceRegistry.class, serviceRegistryParam);

this.xxlRpcProviderFactory.addService(ExecutorBiz.class.getName(), (String)null, new ExecutorBizImpl());

以上兩步操作:
1.指定了使用jetty服務(wù),序列化工具,IP,端口等信息,并且指定serviceRegistryClass為XxlJobExecutor.ExecutorServiceRegistry.class,執(zhí)行器的自動(dòng)注冊是在這實(shí)現(xiàn)的,稍后會(huì)分析
2.設(shè)置使用ExecutorBizImpl作為服務(wù)的處理類,供給調(diào)用中心調(diào)用
接下來我們看一下初始化完畢后,調(diào)用this.xxlRpcProviderFactory.start()方法做了些什么?

public void start() throws Exception {
    //設(shè)置內(nèi)嵌的jetty服務(wù)器
    this.server = (Server)this.netType.serverClass.newInstance();
    //設(shè)置內(nèi)嵌jetty服務(wù)器啟動(dòng)時(shí)的執(zhí)行任務(wù)
    this.server.setStartedCallback(new BaseCallback() {
        public void run() throws Exception {
            //如果serviceRegistryClass不為null,在上面的initConfig中我們已經(jīng)初始化了,所以程序正常可以進(jìn)入
            if (XxlRpcProviderFactory.this.serviceRegistryClass != null) {
                //反射獲取注冊服務(wù)類
                XxlRpcProviderFactory.this.serviceRegistry = (ServiceRegistry)XxlRpcProviderFactory.this.serviceRegistryClass.newInstance();
                //調(diào)用start執(zhí)行自動(dòng)注冊,因?yàn)槲覀冊谏厦娴呐渲弥性O(shè)置了serviceRegistryClass為XxlJobExecutor.ExecutorServiceRegistry.class,所以會(huì)執(zhí)行ExecutorServiceRegistry中的start方法
                XxlRpcProviderFactory.this.serviceRegistry.start(XxlRpcProviderFactory.this.serviceRegistryParam);
                if (XxlRpcProviderFactory.this.serviceData.size() > 0) {
                    String ipPort = IpUtil.getIpPort(XxlRpcProviderFactory.this.ip, XxlRpcProviderFactory.this.port);
                    Iterator i$ = XxlRpcProviderFactory.this.serviceData.keySet().iterator();

                    while(i$.hasNext()) {
                        String serviceKey = (String)i$.next();
                        //因?yàn)槲覀冊谏厦娴呐渲弥性O(shè)置了serviceRegistryClass為XxlJobExecutor.ExecutorServiceRegistry.class,所以執(zhí)行registry函數(shù)內(nèi)部直接返回false,可以點(diǎn)進(jìn)去看看
                        XxlRpcProviderFactory.this.serviceRegistry.registry(serviceKey, ipPort);
                    }
                }
            }

        }
    });
    //設(shè)置內(nèi)嵌jetty服務(wù)器關(guān)閉時(shí)的執(zhí)行任務(wù)
    this.server.setStopedCallback(new BaseCallback() {
        public void run() {
            if (XxlRpcProviderFactory.this.serviceRegistry != null) {
                if (XxlRpcProviderFactory.this.serviceData.size() > 0) {
                    String ipPort = IpUtil.getIpPort(XxlRpcProviderFactory.this.ip, XxlRpcProviderFactory.this.port);
                    Iterator i$ = XxlRpcProviderFactory.this.serviceData.keySet().iterator();

                    while(i$.hasNext()) {
                        String serviceKey = (String)i$.next();
                        XxlRpcProviderFactory.this.serviceRegistry.remove(serviceKey, ipPort);
                    }
                }

                XxlRpcProviderFactory.this.serviceRegistry.stop();
                XxlRpcProviderFactory.this.serviceRegistry = null;
            }

        }
    });
    //啟動(dòng)服務(wù)器(做個(gè)標(biāo)識(shí),下面會(huì)用到,引用標(biāo)識(shí):JETTY)
    this.server.start(this);
}

當(dāng)上端代碼執(zhí)行this.server.start(this)時(shí),內(nèi)嵌的jetty服務(wù)器就會(huì)啟動(dòng),點(diǎn)進(jìn)this.server.start(this)可以看到:



服務(wù)器啟動(dòng)時(shí)會(huì)觸發(fā)onStart方法,進(jìn)而會(huì)執(zhí)行上面的starCallback邏輯,開始進(jìn)行自動(dòng)注冊,讓再回顧頭來我們看一下XxlRpcProviderFactory.this.serviceRegistry.start(XxlRpcProviderFactory.this.serviceRegistryParam);內(nèi)部邏輯:

public static class ExecutorServiceRegistry extends ServiceRegistry {
    public ExecutorServiceRegistry() {
    }

    public void start(Map<String, String> param) {
        //執(zhí)行器注冊線程開始自動(dòng)注冊
        ExecutorRegistryThread.getInstance().start((String)param.get("appName"), (String)param.get("address"));
    }

    public void stop() {
        ExecutorRegistryThread.getInstance().toStop();
    }

    public boolean registry(String key, String value) {
        return false;
    }

    public boolean remove(String key, String value) {
        return false;
    }

    public TreeSet<String> discovery(String key) {
        return null;
    }
}

跟進(jìn)上面的ExecutorRegistryThread.getInstance().start((String)param.get("appName"), (String)param.get("address"))內(nèi)部:

public void start(final String appName, final String address) {
    if (appName != null && appName.trim().length() != 0) {
        if (XxlJobExecutor.getAdminBizList() == null) {
            logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null.");
        } else {
            this.registryThread = new Thread(new Runnable() {
                public void run() {
                    RegistryParam registryParam;
                    Iterator i$;
                    AdminBiz adminBiz;
                    ReturnT registryResult;
                    while(!ExecutorRegistryThread.this.toStop) { // private volatile boolean toStop = false;會(huì)一直循環(huán)此段代碼(停頓30s)直到設(shè)置toStop為true
                        try {
                            registryParam = new RegistryParam(RegistType.EXECUTOR.name(), appName, address);
                            //??????還記得上面那個(gè)標(biāo)記Tag嗎,在那里我們初始化了AdminBizList
                            i$ = XxlJobExecutor.getAdminBizList().iterator();

                            while(i$.hasNext()) {
                                adminBiz = (AdminBiz)i$.next();

                                try {
                                    //這里不會(huì)真正執(zhí)行 adminBiz.registry(registryParam),只是調(diào)用一下方法,觸發(fā)動(dòng)態(tài)代理
                                    registryResult = adminBiz.registry(registryParam);
                                    if (registryResult != null && 200 == registryResult.getCode()) {
                                        registryResult = ReturnT.SUCCESS;
                                        ExecutorRegistryThread.logger.info(">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                        break;
                                    }

                                    ExecutorRegistryThread.logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                } catch (Exception var6) {
                                    ExecutorRegistryThread.logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, var6);
                                }
                            }
                        } catch (Exception var7) {
                            ExecutorRegistryThread.logger.error(var7.getMessage(), var7);
                        }

                        try {
                            TimeUnit.SECONDS.sleep(30L);
                        } catch (InterruptedException var5) {
                            ExecutorRegistryThread.logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", var5.getMessage());
                        }
                    }

                    try {
                        registryParam = new RegistryParam(RegistType.EXECUTOR.name(), appName, address);
                        i$ = XxlJobExecutor.getAdminBizList().iterator();

                        while(i$.hasNext()) {
                            adminBiz = (AdminBiz)i$.next();

                            try {
                                registryResult = adminBiz.registryRemove(registryParam);
                                if (registryResult != null && 200 == registryResult.getCode()) {
                                    registryResult = ReturnT.SUCCESS;
                                    ExecutorRegistryThread.logger.info(">>>>>>>>>>> xxl-job registry-remove success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                    break;
                                }

                                ExecutorRegistryThread.logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                            } catch (Exception var8) {
                                ExecutorRegistryThread.logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, var8);
                            }
                        }
                    } catch (Exception var9) {
                        ExecutorRegistryThread.logger.error(var9.getMessage(), var9);
                    }

                    ExecutorRegistryThread.logger.info(">>>>>>>>>>> xxl-job, executor registry thread destory.");
                }
            });
            this.registryThread.setDaemon(true);
            this.registryThread.start();
        }
    } else {
        logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, appName is null.");
    }
}

在上面的代碼中 registryResult = adminBiz.registry(registryParam)這個(gè)其實(shí)不會(huì)真正去執(zhí)行registry方法,只是為了觸發(fā)動(dòng)態(tài)代理,請看官們返回我在上文中標(biāo)記的Tag部分(ctrl+f),我們找到當(dāng)時(shí)標(biāo)記Tag的代碼:this.initAdminBizList(this.adminAddresses, this.accessToken),跟進(jìn)這個(gè)函數(shù):

private void initAdminBizList(String adminAddresses, String accessToken) throws Exception {
    if (adminAddresses != null && adminAddresses.trim().length() > 0) {
        String[] arr$ = adminAddresses.trim().split(",");
        int len$ = arr$.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            String address = arr$[i$];
            if (address != null && address.trim().length() > 0) {
                String addressUrl = address.concat("/api");
                //這里的getObject會(huì)創(chuàng)建動(dòng)態(tài)代理
                AdminBiz adminBiz = (AdminBiz)(new XxlRpcReferenceBean(NetEnum.JETTY, SerializeEnum.HESSIAN.getSerializer(), CallType.SYNC, AdminBiz.class, (String)null, 10000L, addressUrl, accessToken, (XxlRpcInvokeCallback)null)).getObject();
                if (adminBizList == null) {
                    adminBizList = new ArrayList();
                }

                adminBizList.add(adminBiz);
            }
        }
    }
}

跟進(jìn)getObject:

public Object getObject() {
    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{this.iface}, new InvocationHandler() {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String className = method.getDeclaringClass().getName();
            if (Object.class.getName().equals(className)) {
                XxlRpcReferenceBean.logger.info(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}.{}]", className, method.getName());
                throw new XxlRpcException("xxl-rpc proxy class-method not support");
            } else {
                String address = XxlRpcReferenceBean.this.routeAddress();
                if (address != null && address.trim().length() != 0) {
                    //構(gòu)建XxlRpcRequest
                    XxlRpcRequest xxlRpcRequest = new XxlRpcRequest();
                    xxlRpcRequest.setRequestId(UUID.randomUUID().toString());
                    xxlRpcRequest.setCreateMillisTime(System.currentTimeMillis());
                    xxlRpcRequest.setAccessToken(XxlRpcReferenceBean.this.accessToken);
                    xxlRpcRequest.setClassName(className);
                    xxlRpcRequest.setMethodName(method.getName());
                    xxlRpcRequest.setParameterTypes(method.getParameterTypes());
                    xxlRpcRequest.setParameters(args);
                    XxlRpcFutureResponse invokeFuture;
                    if (CallType.SYNC != XxlRpcReferenceBean.this.callType) {
                        if (CallType.FUTURE == XxlRpcReferenceBean.this.callType) {
                            invokeFuture = null;

                            try {
                                XxlRpcInvokeFuture invokeFuturex = new XxlRpcInvokeFuture(new XxlRpcFutureResponse(xxlRpcRequest, (XxlRpcInvokeCallback)null));
                                XxlRpcInvokeFuture.setFuture(invokeFuturex);
                                XxlRpcReferenceBean.this.client.asyncSend(address, xxlRpcRequest);
                                return null;
                            } catch (Exception var16) {
                                XxlRpcReferenceBean.logger.info(">>>>>>>>>>> xxl-job, invoke error, address:{}, XxlRpcRequest{}", address, xxlRpcRequest);
                                invokeFuture.stop();
                                throw (Throwable)(var16 instanceof XxlRpcException ? var16 : new XxlRpcException(var16));
                            }
                        } else if (CallType.CALLBACK == XxlRpcReferenceBean.this.callType) {
                            XxlRpcInvokeCallback finalInvokeCallback = XxlRpcReferenceBean.this.invokeCallback;
                            XxlRpcInvokeCallback threadInvokeCallback = XxlRpcInvokeCallback.getCallback();
                            if (threadInvokeCallback != null) {
                                finalInvokeCallback = threadInvokeCallback;
                            }

                            if (finalInvokeCallback == null) {
                                throw new XxlRpcException("xxl-rpc XxlRpcInvokeCallback(CallType=" + CallType.CALLBACK.name() + ") cannot be null.");
                            } else {
                                try {
                                    new XxlRpcFutureResponse(xxlRpcRequest, finalInvokeCallback);
                                    XxlRpcReferenceBean.this.client.asyncSend(address, xxlRpcRequest);
                                    return null;
                                } catch (Exception var15) {
                                    XxlRpcReferenceBean.logger.info(">>>>>>>>>>> xxl-job, invoke error, address:{}, XxlRpcRequest{}", address, xxlRpcRequest);
                                    XxlRpcFutureResponseFactory.removeInvokerFuture(xxlRpcRequest.getRequestId());
                                    throw (Throwable)(var15 instanceof XxlRpcException ? var15 : new XxlRpcException(var15));
                                }
                            }
                        } else if (CallType.ONEWAY == XxlRpcReferenceBean.this.callType) {
                            XxlRpcReferenceBean.this.client.asyncSend(address, xxlRpcRequest);
                            return null;
                        } else {
                            throw new XxlRpcException("xxl-rpc callType[" + XxlRpcReferenceBean.this.callType + "] invalid");
                        }
                    } else {
                        //因?yàn)槲覀冎付薈allType為CallType.SYNC,進(jìn)入這里
                        Object var9;
                        try {
                            invokeFuture = new XxlRpcFutureResponse(xxlRpcRequest, (XxlRpcInvokeCallback)null);
                            //發(fā)送RPC請求,進(jìn)行自動(dòng)注冊
                            XxlRpcReferenceBean.this.client.asyncSend(address, xxlRpcRequest);
                            XxlRpcResponse xxlRpcResponse = invokeFuture.get(XxlRpcReferenceBean.this.timeout, TimeUnit.MILLISECONDS);
                            if (xxlRpcResponse.getErrorMsg() != null) {
                                throw new XxlRpcException(xxlRpcResponse.getErrorMsg());
                            }

                            var9 = xxlRpcResponse.getResult();
                        } catch (Exception var17) {
                            XxlRpcReferenceBean.logger.info(">>>>>>>>>>> xxl-job, invoke error, address:{}, XxlRpcRequest{}", address, xxlRpcRequest);
                            throw (Throwable)(var17 instanceof XxlRpcException ? var17 : new XxlRpcException(var17));
                        } finally {
                            XxlRpcFutureResponseFactory.removeInvokerFuture(xxlRpcRequest.getRequestId());
                        }

                        return var9;
                    }
                } else {
                    throw new XxlRpcException("xxl-rpc reference bean[" + className + "] address empty");
                }
            }
        }
    });
}

以上就是執(zhí)行器端自動(dòng)注冊的流程
那么,執(zhí)行器又是如何接收調(diào)度中心的調(diào)度請求并觸發(fā)jobHandler執(zhí)行的呢?
這里我們需要看一下內(nèi)置jetty啟動(dòng)做了什么,我們找到上文埋下的的引用標(biāo)識(shí)JETTY那部分描述:


啟動(dòng)內(nèi)置jetty

跟進(jìn)這個(gè)start方法:

public void start(final XxlRpcProviderFactory xxlRpcProviderFactory) throws Exception {
    this.thread = new Thread(new Runnable() {
        public void run() {
            JettyServer.this.server = new org.eclipse.jetty.server.Server(new QueuedThreadPool());
            ServerConnector connector = new ServerConnector(JettyServer.this.server);
            connector.setPort(xxlRpcProviderFactory.getPort());
            JettyServer.this.server.setConnectors(new Connector[]{connector});
            HandlerCollection handlerc = new HandlerCollection();
            //設(shè)置一個(gè)處理器JettyServerHandler(重點(diǎn))
            handlerc.setHandlers(new Handler[]{new JettyServerHandler(xxlRpcProviderFactory)});
            JettyServer.this.server.setHandler(handlerc);

            try {
                JettyServer.this.server.start();
                JettyServer.logger.info(">>>>>>>>>>> xxl-rpc remoting server start success, nettype = {}, port = {}", JettyServer.class.getName(), xxlRpcProviderFactory.getPort());
                JettyServer.this.onStarted();
                JettyServer.this.server.join();
            } catch (Exception var12) {
                JettyServer.logger.error(">>>>>>>>>>> xxl-rpc remoting server start error.", var12);
            } finally {
                try {
                    JettyServer.this.stop();
                } catch (Exception var11) {
                    JettyServer.logger.error(var11.getMessage(), var11);
                }

            }

        }
    });
    //設(shè)置為守護(hù)線程
    this.thread.setDaemon(true);
    this.thread.start();
}

通過handlerc.setHandlers(new Handler[]{new JettyServerHandler(xxlRpcProviderFactory)})我們可以直到j(luò)etty服務(wù)器接收到請求后會(huì)使用JettyServerHandler的hadle方法處理:

public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    StringBuffer stringBuffer;
    if (!"/services".equals(target)) {
        stringBuffer = null;

        XxlRpcRequest xxlRpcRequest;
        try {
            // 獲取遠(yuǎn)程請求信息xxlRpcRequest
            xxlRpcRequest = this.parseRequest(request);
        } catch (Exception var8) {
            this.writeResponse(baseRequest, response, ThrowableUtil.toString(var8).getBytes());
            return;
        }
        //處理遠(yuǎn)程請求信息xxlRpcRequest
        XxlRpcResponse xxlRpcResponse = this.xxlRpcProviderFactory.invokeService(xxlRpcRequest);
        byte[] responseBytes = this.xxlRpcProviderFactory.getSerializer().serialize(xxlRpcResponse);
        this.writeResponse(baseRequest, response, responseBytes);
    } else {
        stringBuffer = new StringBuffer("<ui>");
        Iterator i$ = this.xxlRpcProviderFactory.getServiceData().keySet().iterator();

        while(i$.hasNext()) {
            String serviceKey = (String)i$.next();
            stringBuffer.append("<li>").append(serviceKey).append(": ").append(this.xxlRpcProviderFactory.getServiceData().get(serviceKey)).append("</li>");
        }

        stringBuffer.append("</ui>");
        this.writeResponse(baseRequest, response, stringBuffer.toString().getBytes());
    }
}

跟進(jìn) this.xxlRpcProviderFactory.invokeService(xxlRpcRequest):

public XxlRpcResponse invokeService(XxlRpcRequest xxlRpcRequest) {
    XxlRpcResponse xxlRpcResponse = new XxlRpcResponse();
    xxlRpcResponse.setRequestId(xxlRpcRequest.getRequestId());
    String serviceKey = makeServiceKey(xxlRpcRequest.getClassName(), xxlRpcRequest.getVersion());
    //獲取serviceBean
    Object serviceBean = this.serviceData.get(serviceKey);
    if (serviceBean == null) {
        xxlRpcResponse.setErrorMsg("The serviceKey[" + serviceKey + "] not found.");
        return xxlRpcResponse;
    } else if (System.currentTimeMillis() - xxlRpcRequest.getCreateMillisTime() > 180000L) {
        xxlRpcResponse.setErrorMsg("The timestamp difference between admin and executor exceeds the limit.");
        return xxlRpcResponse;
    } else if (this.accessToken != null && this.accessToken.trim().length() > 0 && !this.accessToken.trim().equals(xxlRpcRequest.getAccessToken())) {
        xxlRpcResponse.setErrorMsg("The access token[" + xxlRpcRequest.getAccessToken() + "] is wrong.");
        return xxlRpcResponse;
    } else {
        try {
            Class<?> serviceClass = serviceBean.getClass();
            String methodName = xxlRpcRequest.getMethodName();
            Class<?>[] parameterTypes = xxlRpcRequest.getParameterTypes();
            Object[] parameters = xxlRpcRequest.getParameters();
            Method method = serviceClass.getMethod(methodName, parameterTypes);
            method.setAccessible(true);
            Object result = method.invoke(serviceBean, parameters);
            xxlRpcResponse.setResult(result);
        } catch (Throwable var11) {
            logger.error("xxl-rpc provider invokeService error.", var11);
            xxlRpcResponse.setErrorMsg(ThrowableUtil.toString(var11));
        }

        return xxlRpcResponse;
    }
}

上面代碼中Object serviceBean = this.serviceData.get(serviceKey);實(shí)際獲取的bean就是前文我們看到的initRpcProvider方法中
this.xxlRpcProviderFactory.addService(ExecutorBiz.class.getName(), (String)null, new ExecutorBizImpl());中設(shè)置的值,其實(shí)也就是ExecutorBizImpl這個(gè)類,回顧一下,看下下面的2個(gè)圖片,還有印象嗎


初始化RpcProvider

初始化ServiceData

通過請求參數(shù)中的methodName:run,我們會(huì)通過反射執(zhí)行ExecutorBizImpl中的run方法:

public ReturnT<String> run(TriggerParam triggerParam) {
    JobThread jobThread = XxlJobExecutor.loadJobThread(triggerParam.getJobId());
    IJobHandler jobHandler = jobThread != null ? jobThread.getHandler() : null;
    String removeOldReason = null;
    GlueTypeEnum glueTypeEnum = GlueTypeEnum.match(triggerParam.getGlueType());
    IJobHandler originJobHandler;
    //取出任務(wù)對應(yīng)的本地線程池中的線程信息
    if (GlueTypeEnum.BEAN == glueTypeEnum) { // Bean模式
        //取出要執(zhí)行的JobHandler
        originJobHandler = XxlJobExecutor.loadJobHandler(triggerParam.getExecutorHandler());
        if (jobThread != null && jobHandler != originJobHandler) {
            removeOldReason = "change jobhandler or glue type, and terminate the old job thread.";
            jobThread = null;
            jobHandler = null;
        }

        if (jobHandler == null) {
            jobHandler = originJobHandler;
            if (originJobHandler == null) {
                return new ReturnT(500, "job handler [" + triggerParam.getExecutorHandler() + "] not found.");
            }
        }
    } else if (GlueTypeEnum.GLUE_GROOVY == glueTypeEnum) {
        if (jobThread != null && (!(jobThread.getHandler() instanceof GlueJobHandler) || ((GlueJobHandler)jobThread.getHandler()).getGlueUpdatetime() != triggerParam.getGlueUpdatetime())) {
            removeOldReason = "change job source or glue type, and terminate the old job thread.";
            jobThread = null;
            jobHandler = null;
        }

        if (jobHandler == null) {
            try {
                originJobHandler = GlueFactory.getInstance().loadNewInstance(triggerParam.getGlueSource());
                jobHandler = new GlueJobHandler(originJobHandler, triggerParam.getGlueUpdatetime());
            } catch (Exception var7) {
                logger.error(var7.getMessage(), var7);
                return new ReturnT(500, var7.getMessage());
            }
        }
    } else {
        if (glueTypeEnum == null || !glueTypeEnum.isScript()) {
            return new ReturnT(500, "glueType[" + triggerParam.getGlueType() + "] is not valid.");
        }

        if (jobThread != null && (!(jobThread.getHandler() instanceof ScriptJobHandler) || ((ScriptJobHandler)jobThread.getHandler()).getGlueUpdatetime() != triggerParam.getGlueUpdatetime())) {
            removeOldReason = "change job source or glue type, and terminate the old job thread.";
            jobThread = null;
            jobHandler = null;
        }

        if (jobHandler == null) {
            jobHandler = new ScriptJobHandler(triggerParam.getJobId(), triggerParam.getGlueUpdatetime(), triggerParam.getGlueSource(), GlueTypeEnum.match(triggerParam.getGlueType()));
        }
    }

    if (jobThread != null) {
        //取出任務(wù)配置的阻塞策略
        ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(triggerParam.getExecutorBlockStrategy(), (ExecutorBlockStrategyEnum)null);
        if (ExecutorBlockStrategyEnum.DISCARD_LATER == blockStrategy) { // 丟棄后續(xù)
            if (jobThread.isRunningOrHasQueue()) {
                return new ReturnT(500, "block strategy effect:" + ExecutorBlockStrategyEnum.DISCARD_LATER.getTitle());
            }
        } else if (ExecutorBlockStrategyEnum.COVER_EARLY == blockStrategy && jobThread.isRunningOrHasQueue()) { //覆蓋之前
            removeOldReason = "block strategy effect:" + ExecutorBlockStrategyEnum.COVER_EARLY.getTitle();
            jobThread = null;
        }
    }

    if (jobThread == null) {
        //說明走的是覆蓋之前的阻塞策略
        jobThread = XxlJobExecutor.registJobThread(triggerParam.getJobId(), (IJobHandler)jobHandler, removeOldReason);
    }
    //放進(jìn)觸發(fā)隊(duì)列,觸發(fā)任務(wù)執(zhí)行
    ReturnT<String> pushResult = jobThread.pushTriggerQueue(triggerParam);
    return pushResult;
}

通過上面我們可以發(fā)現(xiàn), 執(zhí)行executorBiz的run 方法的時(shí)候, 首先會(huì)通過JOBID,從本地線程庫里面獲取該任務(wù)對應(yīng)的線程,同時(shí),如果任務(wù)的JobHandler有更新的話,那么會(huì)自動(dòng)使用最新的jobHandler , 同時(shí)根據(jù)任務(wù)的阻塞策略。 執(zhí)行不同的操作。 最終,如果是第一次執(zhí)行任務(wù)的時(shí)候,系統(tǒng)會(huì)分配給改任務(wù)一個(gè)線程,同時(shí)啟動(dòng)該線程。
單獨(dú)說一下阻塞策略:覆蓋之前的實(shí)現(xiàn)方式

public static JobThread registJobThread(int jobId, IJobHandler handler, String removeOldReason) {
    //給當(dāng)前任務(wù)創(chuàng)建一個(gè)新的任務(wù)線程
    JobThread newJobThread = new JobThread(jobId, handler);
    //啟動(dòng)新的任務(wù)線程
    newJobThread.start();
    logger.info(">>>>>>>>>>> xxl-job regist JobThread success, jobId:{}, handler:{}", new Object[]{jobId, handler});
    //新任務(wù)覆蓋老任務(wù)
    JobThread oldJobThread = (JobThread)jobThreadRepository.put(jobId, newJobThread);
    if (oldJobThread != null) {
        //中斷老任務(wù)線程
        oldJobThread.toStop(removeOldReason);
        oldJobThread.interrupt();
    }
    return newJobThread;
}

接下來,可以在具體看一下JobThread 的run方法,看下最終的任務(wù)是如何執(zhí)行的

public void run() {
    try {
        // 執(zhí)行IJobHandler 中的init方法,以后如果有一些,在執(zhí)行handler之前的初始化的工作,可以覆寫這個(gè)方法
        this.handler.init();
    } catch (Throwable var26) {
        logger.error(var26.getMessage(), var26);
    }

    final TriggerParam triggerParam;
    ReturnT executeResult;
    while(!this.toStop) {
        this.running = false;
        //執(zhí)行次數(shù)
        ++this.idleTimes;
        triggerParam = null;
        executeResult = null;
        boolean var16 = false;

        ReturnT stopResult;
        label302: {
            try {
                var16 = true;
                從linkBlockingQueue中獲取數(shù)據(jù),如果3秒獲取不到,則返回null
                triggerParam = (TriggerParam)this.triggerQueue.poll(3L, TimeUnit.SECONDS);
                if (triggerParam != null) {
                    this.running = true;
                    // 將運(yùn)行次數(shù)清空,保證運(yùn)行90秒空閑之后會(huì)被移除
                    this.idleTimes = 0;
                    // 去掉這條數(shù)據(jù)
                    this.triggerLogIdSet.remove(triggerParam.getLogId());
                    // 記錄日志
                    String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTim()), triggerParam.getLogId());
                    XxlJobFileAppender.contextHolder.set(logFileName);
                    
                // 寫入分片信息, 將當(dāng)前機(jī)器的分片標(biāo)記和分片總數(shù)寫入到ShardingUtil中,到時(shí)候,可以在handler中通過這個(gè)工具類獲取
                    ShardingUtil.setShardingVo(new ShardingVO(triggerParam.getBroadcastIndex(), triggerParam.getBroadcastTotal()));
                    XxlJobLogger.log("<br>----------- xxl-job job execute start -----------<br>----------- Param:" + triggerParam.getExecutorParams(), new Object[0]);
                    // 超時(shí)時(shí)間>0
                    if (triggerParam.getExecutorTimeout() > 0) {
                        Thread futureThread = null;

                        try {
                            //開啟新線程執(zhí)行
                            FutureTask<ReturnT<String>> futureTask = new FutureTask(new Callable<ReturnT<String>>() {
                                public ReturnT<String> call() throws Exception {
                                    return JobThread.this.handler.execute(triggerParam.getExecutorParams());
                                }
                            });
                            futureThread = new Thread(futureTask);
                            futureThread.start();
                            executeResult = (ReturnT)futureTask.get((long)triggerParam.getExecutorTimeout(), TimeUnit.SECONDS);
                        } catch (TimeoutException var24) {
                            XxlJobLogger.log("<br>----------- xxl-job job execute timeout", new Object[0]);
                            XxlJobLogger.log(var24);
                            executeResult = new ReturnT(IJobHandler.FAIL_TIMEOUT.getCode(), "job execute timeout ");
                        } finally {
                            futureThread.interrupt();
                        }
                    } else {
                        // 否則直接執(zhí)行
                        executeResult = this.handler.execute(triggerParam.getExecutorParams());
                    }

                    if (executeResult == null) {
                        executeResult = IJobHandler.FAIL;
                    }

                    XxlJobLogger.log("<br>----------- xxl-job job execute end(finish) -----------<br>----------- ReturnT:" + executeResult, new Object[0]);
                    var16 = false;
                } else if (this.idleTimes > 30) {
                    // 每3秒獲取一次數(shù)據(jù),獲取30次都沒有獲取到數(shù)據(jù)之后,則現(xiàn)場被清除
                    XxlJobExecutor.removeJobThread(this.jobId, "excutor idel times over limit.");
                    var16 = false;
                } else {
                    var16 = false;
                }
                break label302;
            } catch (Throwable var27) {
                if (this.toStop) {
                    XxlJobLogger.log("<br>----------- JobThread toStop, stopReason:" + this.stopReason, new Object[0]);
                }

                StringWriter stringWriter = new StringWriter();
                var27.printStackTrace(new PrintWriter(stringWriter));
                String errorMsg = stringWriter.toString();
                executeResult = new ReturnT(500, errorMsg);
                XxlJobLogger.log("<br>----------- JobThread Exception:" + errorMsg + "<br>----------- xxl-job job execute end(error) -----------", new Object[0]);
                var16 = false;
            } finally {
                if (var16) {
                    if (triggerParam != null) {
                        if (!this.toStop) {
                            TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), executeResult));
                        } else {
                            ReturnT<String> stopResult = new ReturnT(500, this.stopReason + " [job running,killed]");
                            TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), stopResult));
                        }
                    }

                }
            }

            if (triggerParam != null) {
                if (!this.toStop) {
                    TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), executeResult));
                } else {
                    stopResult = new ReturnT(500, this.stopReason + " [job running,killed]");
                    TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), stopResult));
                }
            }
            continue;
        }

        if (triggerParam != null) {
            if (!this.toStop) {
                TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), executeResult));
            } else {
                stopResult = new ReturnT(500, this.stopReason + " [job running,killed]");
                TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), stopResult));
            }
        }
    }
    // 當(dāng)現(xiàn)場被終止之后,隊(duì)列里面剩余的未執(zhí)行的任務(wù),將被終止的這些任務(wù)放入隊(duì)列,供日志監(jiān)控線程來處理,回調(diào)給調(diào)度中心
    while(this.triggerQueue != null && this.triggerQueue.size() > 0) {
        triggerParam = (TriggerParam)this.triggerQueue.poll();
        if (triggerParam != null) {
            executeResult = new ReturnT(500, this.stopReason + " [job not executed, in the job queue, killed.]");
            TriggerCallbackThread.pushCallBack(new HandleCallbackParam(triggerParam.getLogId(), triggerParam.getLogDateTim(), executeResult));
        }
    }

    try {
        this.handler.destroy();
    } catch (Throwable var23) {
        logger.error(var23.getMessage(), var23);
    }

    logger.info(">>>>>>>>>>> xxl-job JobThread stoped, hashCode:{}", Thread.currentThread());
}

至此全部結(jié)束 。。。。。
后續(xù)更新任務(wù)調(diào)度回調(diào)源碼解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,135評(píng)論 1 32
  • v2.0.2 Release Notes 1、底層通訊方案優(yōu)化:升級(jí)較新版本xxl-rpc,由"JETTY"方案調(diào)...
    許雪里閱讀 1,319評(píng)論 3 4
  • ??一個(gè)任務(wù)通常就是一個(gè)程序,每個(gè)運(yùn)行中的程序就是一個(gè)進(jìn)程。當(dāng)一個(gè)程序運(yùn)行時(shí),內(nèi)部可能包含了多個(gè)順序執(zhí)行流,每個(gè)順...
    OmaiMoon閱讀 1,699評(píng)論 0 12
  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說明:當(dāng)在唯一索引所對應(yīng)的列上鍵入重復(fù)值時(shí),會(huì)觸發(fā)此異常。 O...
    我想起個(gè)好名字閱讀 5,407評(píng)論 0 9
  • 一位得了帕金森癥和抑郁癥的老大爺說:“害死我得了”,他的老伴說:“能害死就好了”。
    There一Is一A一Door閱讀 321評(píng)論 0 1