1. 程式人生 > >thrift的TTransport連線池注意事項[Java版本說明]

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.

The option <B>must</B> be enabled
     *  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操作,避免被其他通訊使用出現異常。