Netty中遇到的問題:ctx.writeAndFlush造成非連續的請求


目的

為了保證客戶端的請求是順序發送到服務端的。

實現方法(初版)

ctx.writeAndFlush被包裝在用synchronized修飾的send方法中,客戶端統一調用sendRequest方法。
在這里插入圖片描述

有多個線程使用sendRequest方法

1.eventLoop處理channelRead的結果,並調用sendRequest發送到服務端。
2.業務線程(如心跳線程,http請求對應的線程)去調用ctx.writeAndFlush
在這里插入圖片描述

造成的后果

從客戶端看日志是順序的,但是服務端總是收到非連續的請求。

代碼

服務端
檢測客戶端的請求cmdCnt,如果小於當前服務端的值,那么就認為是過期的命令
在這里插入圖片描述

客戶端
同步的sendRequest方法,保證不同線程都是順序執行,cmdCnt順序增長。
除了EventLoop線程,業務線程也會調用到sendRequest
在這里插入圖片描述
EventLoop線程執行sendRequest
在這里插入圖片描述

EventLoop處理任務流程

在這里插入圖片描述

原因

這是EventLoop的一個特性:【非EventLoop線程A】調用ctx.writeAndFlush時,不會直接執行,而是放入taskQueue隊列中(等待之后eventLoop線程去執行)。
當【非EventLoop線程A】執行同步的sendRequest並調用ctx.writeAndFlush的同時。 EventLoop線程此時如果正“忙”,也調用到sendRequest,但被synchronized阻塞了。 一旦 A線程將本次發送的內容打包為taskB到taskQueue(EventLoop->SingleThreadEventExecutor里面的taskQueue)。那EventLoop會在A線程offerTask完成,並釋放synchronized對應的對象鎖后。先執行本次sendRequest從taskQueue取出

解決方法

全部改為非業務線程去執行同步的sendRequest方法,去執行ctx.writeAndFlush,最終都提交給隊列。那么順序提交到隊列,就順序取出發送到服務端。避免了EventLoop線程直接執行writeAndFlush。

解決問題的代碼
注意,內部的sendRequest才是同步方法。
在這里插入圖片描述

代碼請求流程

由於EventLoop即處理了channelRead(讀) 以及后面的sendRequest(業務操作),
又處理writeAndFlush(寫)。
在這里插入圖片描述
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM