響應式編程實現異步 RPC,提升 Xxl-Job 調度吞吐量
在xxl-job中,RPC即用于調度中心請求執行器執行job、kill job,也用于執行器請求調度中心主動注冊、執行結果上報。
xxl-job實現的RPC類似Feign框架,是基于http這種七層協議實現的,而http協議是無狀態的,因此一個連接不能同時被用于多個線程發送請求,只能等待一個請求響應后再放入連接池被其它線程使用。
對于執行器而言,由于只與調度中心交互,請求量也少,因此這種RPC實現不會對執行器性能有什么影響。
調度中心則不同,它需要同時與多個執行器交互,如果同一時刻需要下發幾百個執行job的請求給執行器,使用這種阻塞的RPC,意味著需要開啟幾百個線程,使用幾百個連接發送請求,而這幾百個線程都需要阻塞等待響應,Job越多,需要的線程數就會越多,對調動中心的性能影響就越大。
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核心數個工作線程處理向執行器發送RPC請求,reactor-netty-http在一個線程上完成請求發送后,就會繼續處理其它請求發送,當輪詢到某些連接收到客戶端響應事件后,再處理這些響應,釋放連接回連接池,調回doNext。
最終從效果上看,基于reactor-netty-http實現的RPC,類似于dubbo使用長連接實現的異步RPC。
圖片
reactor-netty-http可能會創建大量連接,但不會創建大量線程,可用使用netstat觀察連接數的增長,使用jstack工具觀察reactor-netty-http創建的線程數。
要解決調度的性能問題,除了異步RPC是不夠的,異步RPC只能幫我們解決下發請求的阻塞問題。而且響應式編程要求整個鏈路上必須無阻塞。那么異步回調的事件消費也必須是異步的。
同時,我們將執行器節點信息、Job數據也完全存儲在內存中,讓觸發->job查詢->執行器查詢->執行器節點查詢->日記打印->調度下發整條鏈路都完全無阻塞。而數據的一致性,則通過分布式一致性算法保證,為了穩定以及開發簡單,我們基于zookeeper實現。




































