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取出

代碼請求流程

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

解決方法

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章