当前位置: 首页 > 科技观察

响应式编程实现异步RPC,提高Xxl-Job调度吞吐

时间:2023-03-13 19:14:12 科技观察

在xxl-job中,RPC用于请求executor执行job和killjob,也用于executor请求executor主动注册并执行作业报告结果。xxl-job实现的RPC类似于Feign框架。它是基于http的七层协议实现的,http协议是无状态的。所以一个连接不能用于多个线程同时发送请求,只能等待一个请求响应。然后放入连接池中,供其他线程使用。对于执行器来说,由于只与调度中心交互,请求量较小,所以这种RPC实现不会对执行器的性能产生任何影响。调度中心不同。它需要同时与多个执行器进行交互。如果需要同时向执行者发送数百个作业执行请求,使用这种阻塞式RPC意味着需要开启数百个线程,数百个连接发送请求,而这数百个线程需要阻塞等待一个回应。作业越多,需要的线程越多,对调度中心的性能影响越大。即使xxl-job更新到最新的2.x版本,仍然存在性能问题。无非是使用分布式锁和使用同步阻塞的RPC调用。知道了为什么同步RPC会影响调度中心的性能,那么理解为什么异步RPC可以解决这个问题就容易多了。响应式编程通过事件触发回调解决同步阻塞问题,要求整个链路无阻塞,即无I/O阻塞(数据库操作、网络请求响应等)。我们重构了新版本的调度中心(xxl-job),我们使用reactor-netty-http框架来实现异步RPC,当然我们只需要解决调度中心的性能问题,所以执行器无法更改,与旧版本兼容。reactor-netty-http并不是http协议无状态问题的解决方案。尽管如此,一个连接一次只能用于发送一个请求,并且需要等待响应才能用于发送其他请求。但是reactor-netty-http并没有创建线程阻塞等待,而是使用事件轮询的方式来消费响应,将连接释放回连接池。使用reactor-netty-http后,我们只需要配置CPU核心的几个工作线程来处理向executor发送RPC请求即可。reactor-netty-http在一个线程发送完请求后,会继续处理其他的请求。当轮询一些连接并收到客户端响应事件后,处理这些响应,将连接释放回连接池,并回调doNext。最后,在效果上,基于reactor-netty-http实现的RPC和dubbo使用长连接实现的异步RPC类似。图片reactor-netty-http可能会创建大量连接,但不会创建大量线程。可以使用netstat观察连接数的增长情况,使用jstack工具观察reactor-netty-http创建的线程数。解决调度的性能问题,除了异步RPC还不够,异步RPC只能帮我们解决发送请求的阻塞问题。而响应式编程要求整个链接必须是非阻塞的。那么异步回调的事件消费也必须是异步的。同时,我们还将executor节点信息和job数据完整的存储在内存中,这样trigger->job查询->executor查询->executor节点查询->journal打印->调度整个链路是完全非-阻塞。数据的一致性由分布式共识算法保证。为了稳定和简单的开发,我们基于zookeeper来实现。