Netty只提供的異步傳輸數據的方式,可是並無實現多路複用的client。
java
一個分佈式的客戶端代碼基本是這個樣子的:git
public Response sent(final Request request) { channel.writeAndFlush(request); return clientChannelInitializer.getResponse(request.getMessageId()); }
首先經過channel發送一個請求到server,而後等待server返回的結果。github
可是Netty是異步的,writeAndFlush這個方法,只是告訴server,我要發數據了,而後就立刻返回了。因此這時直接調用getResponse,會得不到值。由於netty是在回調裏面寫返回值的。數據庫
解決的辦法是,使用BlockingQueue接收返回的數據。一旦BlockingQueue有數據了,就take出來。若是沒數據,就一直等待。服務器
在單線程裏,這個辦法沒問題。這就要確保Netty的client只被一個線程訪問。若是是多線程同時訪問,由於異步的緣由,有可能第二個線程的返回值被第一個線程拿到。舉個例子:多線程
線程A使用client發送請求異步
線程A從BlockingQueue中取數據。這時Queue爲空,線程A等待。分佈式
server接受並開啓一個線程處理請求A優化
線程B使用client發送請求spa
線程B從BlockingQueue中取數據。這時Queue爲空,線程B也等待。
server接受並開啓另外一個線程處理請求B
線程B的請求處理速度較快,先返回
client將返回值B寫入BlockingQueue
BlockQueue有數據了,線程A take到數據,可是是線程B的結果
這就致使request和response不匹配。
要解決這個問題有3個辦法。
使用短鏈接,每次請求new一個client,接收的response後close掉這個client。這個的缺點很明顯,這就至關於http服務器了,不能發揮內網長鏈接的優點。
使用鏈接池,每次從鏈接池裏拿一個client,接收完response後將這個client返還鏈接池。這個就有點像數據庫鏈接池。這個方法沒什麼缺點,可是須要本身實現鏈接池。
使用單一client,可是在request中生成一個惟一的messageId,能夠是nanoTime。而後server處理完後,在response中也返回這個messageId。這樣在client不是維護一個BlockingQueue,而是維護一個ConcurrentHashMap,key是messageId,value是一個空的BlockingQueue。當client發送resquest到server時,在Map裏寫入messageId,並實例化一個BlockingQueue(爲了優化,size能夠是1)。而後等待這個BlockingQueue有值。在接收到response的回調方法裏,根據messageId取出blockingQueue的值,而後刪除掉這個Key。
我本身實現了第三種方式,具體代碼請參見: