thrift的TTransport連線池注意事項[Java版本說明]
thrift長連線,基於維護TTransport連線池實現。
一般步驟:
1.對要通訊的伺服器ip和port,建立足夠的TTransport例項並維護起來。
2.在通訊的時候,從中選擇空閒的TTransport連線使用。
3.通訊結束後歸還TTransport例項,以供下次使用。
bug描述:
在上面的3步驟中,如果在通訊過程中所發生的超時異常(SocketTimeoutException),僅僅是捕獲和歸還TTransport資源並且供下次服務通訊使用。
就有可能會出現這次所超時的請求的返回結果,會被下個使用該TTransport進行通訊所獲取到。
這個奇怪的現象可以這樣描述,假設A使用了TTransport-1進行了通訊,原本應該返回結果A,但是由於超時,該A歸還了TTransport-1資源,
接著到B需要通訊,恰好拿到了TTransport-1進行通訊,預期結果是B。但這時候TTransport-1上次超時的A請求返回了結果A,這時候B會接受到A結果。
原因是:
TTransport transport = new TFramedTransport(new TSocket(ip, port, timeout));
TTransport是使用TSocket進行通訊的,而TSocket其實是Socket的封裝
socket_ = new Socket();
try {
socket_.setSoLinger(false, 0);
socket_.setTcpNoDelay(true);
socket_.setKeepAlive(true);
socket_.setSoTimeout(socketTimeout_);
} catch (SocketException sx) {
LOGGER.error("Could not configure socket.", sx);
}
而TSocket所傳輸的timeout就是Socket裡面的setSoTimeout,而該方法的java描述為:
* Enable/disable {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}
* with the specified timeout, in milliseconds. With this option set
* to a non-zero timeout, a read() call on the InputStream associated with
* this Socket will block for only this amount of time. If the timeout
* expires, a <B>java.net.SocketTimeoutException</B> is raised, though the
* Socket is still valid.
* prior to entering the blocking operation to have effect. The
* timeout must be {@code > 0}.
* A timeout of zero is interpreted as an infinite timeout.
這裡的意思大致是socket請求超時了,會丟擲SocketTimeoutException,但是原來的Socket仍然有效,也就意味著之前所超時的請求,如果伺服器繼續返回結果,
客戶端沒有關閉的Socket是能夠繼續收到結果資料的。
因此對於超時而又繼續使用的Socket會有可能繼續接受到之前的舊結果。也就會出現上面thrift長連線使用TTranposrt通訊,如果僅僅是對於超時的TTransport歸還資源供下次通訊使用,就有可能會出現上面所描述的bug現象了。
我的做法:
對於出現超時異常,TTransport直接進行close操作,避免被其他通訊使用出現異常。