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操作,避免被其他通信使用出现异常。