0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

xxl-job通信设计流程

jf_ro2CN3Fa 来源:c1n.cn 2024-01-30 09:34 次阅读

通信底层介绍

xxl-job 使用 netty http 的方式进行通信,虽然也支持 Mina,jetty,netty tcp 等方式,但是代码里面固定写死的是 netty http。

通信整体流程

我以调度器通知执行器执行任务为例,绘制的活动图:

2417a940-b8c3-11ee-8b88-92fbcf53809c.png

活动图

惊艳的设计

看完了整个处理流程代码,设计上可以说独具匠心,将 netty,多线程的知识运用得行云流水。

我现在就将这些设计上出彩的点总结如下:

使用动态代理模式,隐藏通信细节

xxl-job 定义了两个接口 ExecutorBiz,AdminBiz,ExecutorBiz 接口中封装了向心跳,暂停,触发执行等操作,AdminBiz 封装了回调,注册,取消注册操作,接口的实现类中,并没有通信相关的处理。

XxlRpcReferenceBean 类的 getObject() 方法会生成一个代理类,这个代理类会进行远程通信。

全异步处理

执行器收到消息进行反序列化,并没有同步执行任务代码,而是将任务信息存储在 LinkedBlockingQueue 中,异步线程从这个队列中获取任务信息,然后执行。

而任务的处理结果,也不是说处理完之后,同步返回的,也是放到回调线程的阻塞队列中,异步的将处理结果返回回去。

这样处理的好处就是减少了 netty 工作线程的处理时间,提升了吞吐量。

对异步处理的包装

对异步处理进行了包装,代码看起来是同步调用的。

我们看下调度器,XxlJobTrigger 类触发任务执行的代码:

publicstaticReturnTrunExecutor(TriggerParamtriggerParam,Stringaddress){
ReturnTrunResult=null;
try{
ExecutorBizexecutorBiz=XxlJobScheduler.getExecutorBiz(address);
//这里面做了很多异步处理,最终同步得到处理结果
runResult=executorBiz.run(triggerParam);
}catch(Exceptione){
logger.error(">>>>>>>>>>>xxl-jobtriggererror,pleasecheckiftheexecutor[{}]isrunning.",address,e);
runResult=newReturnT(ReturnT.FAIL_CODE,ThrowableUtil.toString(e));
}

StringBufferrunResultSB=newStringBuffer(I18nUtil.getString("jobconf_trigger_run")+":");
runResultSB.append("
address:").append(address); runResultSB.append("
code:").append(runResult.getCode()); runResultSB.append("
msg:").append(runResult.getMsg()); runResult.setMsg(runResultSB.toString()); returnrunResult; }

ExecutorBiz.run 方法我们说过了,是走的动态代理,和执行器进行通信,执行器执行结果也是异步处理完,才返回的,而这里看到的 run 方法是同步等待处理结果返回。

我们看下xxl-job是如何同步获取处理结果的:调度器向执行器发出消息后,该线程阻塞。等到执行器处理完毕后,将处理结果返回,唤醒被阻塞的线程,调用处拿到返回值。

动态代理代码如下:

//代理类中的触发调用
if(CallType.SYNC==callType){
//future-responseset
XxlRpcFutureResponsefutureResponse=newXxlRpcFutureResponse(invokerFactory,xxlRpcRequest,null);
try{
//doinvoke
client.asyncSend(finalAddress,xxlRpcRequest);

//futureget
XxlRpcResponsexxlRpcResponse=futureResponse.get(timeout,TimeUnit.MILLISECONDS);
if(xxlRpcResponse.getErrorMsg()!=null){
thrownewXxlRpcException(xxlRpcResponse.getErrorMsg());
}
returnxxlRpcResponse.getResult();
}catch(Exceptione){
logger.info(">>>>>>>>>>>xxl-rpc,invokeerror,address:{},XxlRpcRequest{}",finalAddress,xxlRpcRequest);

throw(einstanceofXxlRpcException)?e:newXxlRpcException(e);
}finally{
//future-responseremove
futureResponse.removeInvokerFuture();
}
}

XxlRpcFutureResponse 类中实现了线程的等待,和线程唤醒的处理:

//返回结果,唤醒线程
publicvoidsetResponse(XxlRpcResponseresponse){
this.response=response;
synchronized(lock){
done=true;
lock.notifyAll();
}
}

@Override
publicXxlRpcResponseget(longtimeout,TimeUnitunit)throwsInterruptedException,ExecutionException,TimeoutException{
if(!done){
synchronized(lock){
try{
if(timeout< 0) {
            //线程阻塞
                        lock.wait();
                    } else {
                        long timeoutMillis = (TimeUnit.MILLISECONDS==unit)?timeout:TimeUnit.MILLISECONDS.convert(timeout , unit);
                        lock.wait(timeoutMillis);
                    }
                } catch (InterruptedException e) {
                    throw e;
                }
            }
        }

        if (!done) {
            throw new XxlRpcException("xxl-rpc, request timeout at:"+ System.currentTimeMillis() +", request:" + request.toString());
        }
        return response;
    }

有的同学可能会问了,调度器接收到返回结果,怎么确定唤醒哪个线程呢?

每一次远程调用,都会生成 uuid 的请求 id,这个 id 是在整个调用过程中一直传递的,就像一把钥匙,在你回家的的时候,拿着它就带开门。

这里拿着请求 id 这把钥匙,就能找到对应的 XxlRpcFutureResponse,然后调用 setResponse 方法,设置返回值,唤醒线程。

publicvoidnotifyInvokerFuture(StringrequestId,finalXxlRpcResponsexxlRpcResponse){


//通过requestId找到XxlRpcFutureResponse,
finalXxlRpcFutureResponsefutureResponse=futureResponsePool.get(requestId);
if(futureResponse==null){
return;
}
if(futureResponse.getInvokeCallback()!=null){

//callbacktype
try{
executeResponseCallback(newRunnable(){
@Override
publicvoidrun(){
if(xxlRpcResponse.getErrorMsg()!=null){
futureResponse.getInvokeCallback().onFailure(newXxlRpcException(xxlRpcResponse.getErrorMsg()));
}else{
futureResponse.getInvokeCallback().onSuccess(xxlRpcResponse.getResult());
}
}
});
}catch(Exceptione){
logger.error(e.getMessage(),e);
}
}else{
//里面调用lock的notify方法
futureResponse.setResponse(xxlRpcResponse);
}

//doremove
futureResponsePool.remove(requestId);

}
审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 通信技术
    +关注

    关注

    20

    文章

    1055

    浏览量

    91586
  • 线程
    +关注

    关注

    0

    文章

    489

    浏览量

    19495
  • 调度器
    +关注

    关注

    0

    文章

    95

    浏览量

    5161

原文标题:xxl-job惊艳的设计,怎能叫人不爱

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    《分布式_Job》——定时XXL_JOB_使用总结

    《分布式_Job》——定时XXL_JOB_使用总结
    发表于 07-08 17:56

    Spark job是怎么被调度执行的

    Spark job 的执行流程简介
    发表于 08-22 08:24

    运行调度中心后访问出现500错误怎么解决

    XXL-Job 访问调度中心出现 500 应用程序异常
    发表于 11-08 09:39

    关于XXL-JOB定时调度器的使用总结

    XXL-JOB 定时调度器 使用小结
    发表于 04-23 14:53

    xxl conf admin在linux下面的自启动

    【配置中心】xxl-conf配置3 - xxl-conf-admin在linux下面的自启动
    发表于 06-10 17:30

    适用于RS-232通讯的ADM2xxL系列特性和典型应用实例

    适用于RS-232通讯的ADM2xxL系列特性和典型应用实例:
    发表于 06-17 11:26 22次下载
    适用于RS-232通讯的ADM2<b class='flag-5'>xxL</b>系列特性和典型应用实例

    GSM通信流程

    GSM通信流程包括两方面的内容:呼叫基本流程,信令基本流程。其中,呼叫流程主要包含:移动主叫流程
    发表于 07-29 16:34 42次下载

    GSM通信流程

    GSM通信流程包括两方面的内容:呼叫基本流程,信令基本流程。其中,呼叫流程主要包含:移动主叫流程
    发表于 08-06 10:52 23次下载

    AN-375:用于RS-232通信的ADM2xxL系列

    AN-375:用于RS-232通信的ADM2xxL系列
    发表于 05-07 19:32 8次下载
    AN-375:用于RS-232<b class='flag-5'>通信</b>的ADM2<b class='flag-5'>xxL</b>系列

    BK-JOB运维脚本管理系统

    bk-job.zip
    发表于 04-28 10:31 0次下载
    BK-<b class='flag-5'>JOB</b>运维脚本管理系统

    如何通过Output Job输出原理图变量

    在Output Job中选择[Project Physical Documents],将会显示编译后的原理图图纸,其中包含图纸上不同的变量元素。
    的头像 发表于 09-08 11:20 654次阅读

    什么是定时任务 xxl-job架构设计方案

    同一个执行器集群内AppName(xxl.job.executor.appname)需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。
    发表于 11-14 12:44 896次阅读

    xxl-job惊艳的设计,怎能叫人不爱

    xxl-job 定义了两个接口 ExecutorBiz,AdminBiz,ExecutorBiz 接口中封装了向心跳,暂停,触发执行等操作,AdminBiz 封装了回调,注册,取消注册操作,接口的实现类中,并没有通信相关的处理。
    的头像 发表于 12-22 14:43 487次阅读

    单向ESD保护二极管-PESD9XXL_SER

    单向ESD保护二极管-PESD9XXL_SER
    发表于 02-09 21:31 0次下载
    单向ESD保护二极管-PESD9<b class='flag-5'>XXL</b>_SER

    分布式定时调度:xxl-job最佳实践方法

    定时任务是按照指定时间周期运行任务。使用场景为在某个固定时间点执行,或者周期性的去执行某个任务,比如:每天晚上24点做数据汇总,定时发送短信等。
    的头像 发表于 11-30 11:06 405次阅读
    分布式定时调度:<b class='flag-5'>xxl-job</b>最佳实践方法