前言
前一章为大家介绍了Thrift类体系,给大家一个整体上的认识,本章开始对每一层的实现细节进行研究,这里我们从与业务分离、处于最底层的TTransport层源码开始。
阻塞与非阻塞TTransport
看源码前,有必要先向大家说明一下,阻塞、非阻塞服务的服务端与客户端是如何搭配使用TTransport,这里还是只研究我们常用的类,下图是TTransport类间的关系图:
- 服务端为阻塞式服务时,使用TServerSocket,接收到客户端的请求创建一个TSocket通信,客户端使用TSocket配合即可。
- 服务端为非阻塞式服务时,使用TNonblockingServerSocket,接收到客户端的请求创建一个TNonblockingSocket通信,在读完客户端的请求数据后,保存为本地一个TTransport对象,然后封装为TFramedTransport对象进行处理(详见AbstractNonblockingServer.Args类源码),客户端使用TSocket,并且使用TFramedTransport封装。
备注:当客户端是异步时的使用情况较复杂,这里暂不讨论。
TTransport类体系
TTransport的源码
作为所有TTransport层的父类,还是先看看它的源码。
public abstract class TTransport implements Closeable {
//当前连接是否已打开
public abstract boolean isOpen();
//是否还有数据需要读,当连接关闭时认为无数据可读
public boolean peek() {
return isOpen();
}
//打开当前连接,可用于IO读写
public abstract void open()
throws TTransportException;
//关闭当前连接
public abstract void close();
//向buf字节数组中写入数据,从off开始,最多读len长度的字节,最后返回实际向buf写入的字节数
public abstract int read(byte[] buf, int off, int len)
throws TTransportException;
//确保向buf中从off开始写入,len长度的字节,这里通过循环调用上面的方法实现最后返回向buf写入的字节数
public int readAll(byte[] buf, int off, int len)
throws TTransportException {
int got = 0;
int ret = 0;
while (got < len) {//没有读完继续下一次读取,直接读到的数据大于等于需要的len长度
ret = read(buf, off+got, len-got);
if (ret <= 0) {
throw new TTransportException(
"Cannot read. Remote side has closed. Tried to read "
+ len
+ " bytes, but only got "
+ got
+ " bytes. (This is often indicative of an internal error on the server side. Please check your server logs.)");
}
got += ret;
}
return got;
}
//将buf中的数据全部发送出去
public void write(byte[] buf) throws TTransportException {
write(buf, 0, buf.length);
}
//将buf的数据,从off开始,发送len长度的数据出去
public abstract void write(byte[] buf, int off, int len)
throws TTransportException;
//清空transport中的数据缓存
public void flush()
throws TTransportException {}
//下面四个方法,与ByteBuffer的原理类似
//获取到本地缓存的数据,没有缓存直接返回空
public byte[] getBuffer() {
return null;
}
//返回本地缓存下一个读取位置,没有缓存返回0即可
public int getBufferPosition() {
return 0;
}
//获取本地缓存中的字节数,没有缓存返回-1
public int getBytesRemainingInBuffer() {
return -1;
}
//从本地缓存中消费n个字节
public void consumeBuffer(int len) {}
}
TTransport主要作用是定义了IO读写操作以及本地缓存的操作,下面来看TIOStreamTransport是如何实现的。
TIOStreamTransport
顾名思义,这是一个面向流的Transport类,下面是源码。
public class TIOStreamTransport extends TTransport {
private static final Logger LOGGER = LoggerFactory.getLogger(TIOStreamTransport.class.getName());
protected InputStream inputStream_ = null;//输入流
protected OutputStream outputStream_ = null;//输出流
protected TIOStreamTransport() {}
public TIOStreamTransport(InputStream is) {
inputStream_ = is;
}
public TIOStreamTransport(OutputStream os) {
outputStream_ = os;
}
public TIOStreamTransport(InputStream is, OutputStream os) {
inputStream_ = is;
outputStream_ = os;
}
//所有的流,必须在构造时就被打开,所以这里一直返回true
public boolean isOpen() {
return true;
}
//因为流必须已经被打开,所以这里什么也不做
public void open() throws TTransportException {}
//同时将输出、输入流关闭
public void close() {
if (inputStream_ != null) {
try {
inputStream_.close();
} catch (IOException iox) {
LOGGER.warn("Error closing input stream.", iox);
}
inputStream_ = null;
}
if (outputStream_ != null) {
try {
outputStream_.close();
} catch (IOException iox) {
LOGGER.warn("Error closing output stream.", iox);
}
outputStream_ = null;
}
}
//使用输入流读取
public int read(byte[] buf, int off, int len) throws TTransportException {
if (inputStream_ == null) {
throw new TTransportException(TTransportException.NOT_OPEN, "Cannot read from null inputStream");
}
int bytesRead;
try {
bytesRead = inputStream_.read(buf, off, len);
} catch (IOException iox) {
throw new TTransportException(TTransportException.UNKNOWN, iox);
}
if (bytesRead < 0) {
throw new TTransportException(TTransportException.END_OF_FILE);
}
return bytesRead;
}
//使用输出流写出
public void write(byte[] buf, int off, int len) throws TTransportException {
if (outputStream_ == null) {
throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to null outputStream");
}
try {
outputStream_.write(buf, off, len);
} catch (IOException iox) {
throw new TTransportException(TTransportException.UNKNOWN, iox);
}
}
//清空输出流
public void flush() throws TTransportException {
if (outputStream_ == null) {
throw new TTransportException(TTransportException.NOT_OPEN, "Cannot flush null outputStream");
}
try {
outputStream_.flush();
} catch (IOException iox) {
throw new TTransportException(TTransportException.UNKNOWN, iox);
}
}
}
该类中的源码很简单,有IO编程经验的同学都可以轻松看懂。
TSocket
TSocket是直接继承TIOStreamTransport,也是我们在阻塞型服务中直接使用到的TTransport类,下面是源码:
//继承自TIOStreamTransport,父类已经将IO流的相关操作封装好
public class TSocket extends TIOStreamTransport {
private static final Logger LOGGER = LoggerFactory.getLogger(TSocket.class.getName());
private Socket socket_;//java中的Socket
private String host_;//服务端host
private int port_;//服务端服务端口
private int socketTimeout_;//Socket读超时时间
private int connectTimeout_;//连接超时时间
public TSocket(Socket socket) throws TTransportException {
socket_ = socket;
try {
socket_.setSoLinger(false, 0);
socket_.setTcpNoDelay(true);
socket_.setKeepAlive(true);
} catch (SocketException sx) {
LOGGER.warn("Could not configure socket.", sx);
}
//当前Socket已打开时,初始化本地输入、输出流变量
if (isOpen()) {
try {
inputStream_ = new BufferedInputStream(socket_.getInputStream(), 1024);
outputStream_ = new BufferedOutputStream(socket_.getOutputStream(), 1024);
} catch (IOException iox) {
close();
throw new TTransportException(TTransportException.NOT_OPEN, iox);
}
}
}
public TSocket(String host, int port) {
this(host, port, 0);
}
public TSocket(String host, int port, int timeout) {
this(host, port, timeout, timeout);
}
public TSocket(String host, int port, int socketTimeout, int connectTimeout) {
host_ = host;
port_ = port;
socketTimeout_ = socketTimeout;
connectTimeout_ = connectTimeout;
initSocket();//根据上面的参数创建Socket
}
//初始化Socket
private void initSocket() {
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);
}
}
public void setTimeout(int timeout) {
this.setConnectTimeout(timeout);
this.setSocketTimeout(timeout);
}
public void setConnectTimeout(int timeout) {
connectTimeout_ = timeout;
}
public void setSocketTimeout(int timeout) {
socketTimeout_ = timeout;
try {
socket_.setSoTimeout(timeout);
} catch (SocketException sx) {
LOGGER.warn("Could not set socket timeout.", sx);
}
}
//Socket为空时初始化
public Socket getSocket() {
if (socket_ == null) {
initSocket();
}
return socket_;
}
//Socket是否已经打开
public boolean isOpen() {
if (socket_ == null) {
return false;
}
return socket_.isConnected();
}
//打开连接,主要作用是初始化Socket、输入输出流
public void open() throws TTransportException {
if (isOpen()) {
throw new TTransportException(TTransportException.ALREADY_OPEN, "Socket already connected.");
}
if (host_ == null || host_.length() == 0) {
throw new TTransportException(TTransportException.NOT_OPEN, "Cannot open null host.");
}
if (port_ <= 0 || port_ > 65535) {
throw new TTransportException(TTransportException.NOT_OPEN, "Invalid port " + port_);
}
if (socket_ == null) {
initSocket();
}
try {
socket_.connect(new InetSocketAddress(host_, port_), connectTimeout_);
inputStream_ = new BufferedInputStream(socket_.getInputStream(), 1024);
outputStream_ = new BufferedOutputStream(socket_.getOutputStream(), 1024);
} catch (IOException iox) {
close();
throw new TTransportException(TTransportException.NOT_OPEN, iox);
}
}
//关闭当前连接
public void close() {
super.close();//先关闭输入输出流
//再关闭Socket
if (socket_ != null) {
try {
socket_.close();
} catch (IOException iox) {
LOGGER.warn("Could not close socket.", iox);
}
socket_ = null;
}
}
}
至此,TSocket的实现介绍完毕,主要的read、write操作都是使用父类的方法,而该类只需要根据Socket初始化父类的输入、输出流变量即可。
TFramedTransport
TFramedTransport该类主要是起到封装和缓冲作用,真正IO读写还是依赖内部的TTransport成员变量,在服务端为非阻塞的情况下使用,这是由于非阻塞的服务端都是使用了Java Nio,而Java Nio是无法知道什么时候读完了一次请求的所有数据,所以使用TFramedTransport来设置前四个字节表示此次数据请求的size,这样,服务端读取数据时设置size+4的ByteBuffer即可,读满ByteBuffer代表此次请求的数据全部读完,由于TFramedTransport中使用到了TMemoryInputTransport,所以先看下TMemoryInputTransport的源码。
public final class TMemoryInputTransport extends TTransport {
private byte[] buf_; //保存字节的数组
private int pos_;//可读的位置
private int endPos_;//最后可读的位置
public TMemoryInputTransport() {
}
public TMemoryInputTransport(byte[] buf) {
reset(buf);
}
public TMemoryInputTransport(byte[] buf, int offset, int length) {
reset(buf, offset, length);
}
//重置数组对象
public void reset(byte[] buf) {
reset(buf, 0, buf.length);
}
public void reset(byte[] buf, int offset, int length) {
buf_ = buf;
pos_ = offset;
endPos_ = offset + length;
}
public void clear() {
buf_ = null;
}
@Override
public void close() {}
@Override
public boolean isOpen() {
return true;
}
@Override
public void open() throws TTransportException {}
//获取到buf_字节数组对象
@Override
public byte[] getBuffer() {
return buf_;
}
//拿到可以开始读的位置
public int getBufferPosition() {
return pos_;
}
//字节数组中还有多少字节可读
public int getBytesRemainingInBuffer() {
return endPos_ - pos_;
}
//pos_向后移len个位置
public void consumeBuffer(int len) {
pos_ += len;
}
//先看数组中还有多少可读字节,取请求读长度和实际可读字节中较小者后,如果该值大于0,则将buf_中从pos_可读位置开始拷贝amtToRead个字节到buf中,同时将可读位置向后调整amtToRead长度,返回读了amtToRead个字节
@Override
public int read(byte[] buf, int off, int len) throws TTransportException {
int bytesRemaining = getBytesRemainingInBuffer();
int amtToRead = (len > bytesRemaining ? bytesRemaining : len);
if (amtToRead > 0) {
System.arraycopy(buf_, pos_, buf, off, amtToRead);
consumeBuffer(amtToRead);
}
return amtToRead;
}
//不支持写
@Override
public void write(byte[] buf, int off, int len) throws TTransportException {
throw new UnsupportedOperationException("No writing allowed!");
}
}
该类的源码还是比较简单的,原理和ByteBuffer类似,不过仅能用作读。需要理解read方法的作用,这里会有一个下标pos_控制可读取数据位置,一旦读完到endPos_,再调read则一直返回0,下面贴TFramedTransport的源码。
public class TFramedTransport extends TTransport {
protected static final int DEFAULT_MAX_LENGTH = 16384000;//默认的本地缓存最大字节数
private int maxLength_;//当前对象的本地缓存最大字节数
private TTransport transport_ = null;//当前对象通过该对象实现数据的读取与写入
private final TByteArrayOutputStream writeBuffer_ =
new TByteArrayOutputStream(1024);//本地缓存,用于输出,继承自ByteArrayOutputStream,知道可以将字节数组输出即可
private TMemoryInputTransport readBuffer_ = new TMemoryInputTransport(new byte[0]);//用于数据读取的本地缓存
//工厂类,将一个TTransport对象封装为一个TFramedTransport对象
public static class Factory extends TTransportFactory {
private int maxLength_;
public Factory() {
maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
}
public Factory(int maxLength) {
maxLength_ = maxLength;
}
@Override
public TTransport getTransport(TTransport base) {
return new TFramedTransport(base, maxLength_);
}
}
public TFramedTransport(TTransport transport, int maxLength) {
transport_ = transport;
maxLength_ = maxLength;
}
public TFramedTransport(TTransport transport) {
transport_ = transport;
maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
}
//下面三个方法都调用transport相应方法实现,作用相同
public void open() throws TTransportException {
transport_.open();
}
public boolean isOpen() {
return transport_.isOpen();
}
public void close() {
transport_.close();
}
//在一次客户端的请求过程或服务端处理请求的过程中,肯定要多次调用该方法读数据,但只有第一次调用时由于readBuffer_字节为0或保存着上次缓存的内容且已经读完,所以从readBuffer_肯定读出的got<=0,从而进入readFrame()方法,将数据从transport_读到本地缓存readBuffer_,后续数据的读取got肯定大于0,直到本次的请求处理完毕
public int read(byte[] buf, int off, int len) throws TTransportException {
//readBuffer_已初始化
if (readBuffer_ != null) {
int got = readBuffer_.read(buf, off, len);//源码在上面,清楚两点即可,readBuffer_已读完或字节数为0时 肯定返回got<0。也就是说,在一次客户端的请求中第一次调用该方法时,肯定返回got<0,就可以进入 readFrame()方法。
if (got > 0) {
return got;
}
}
readFrame();//从transport_读到本地缓存readBuffer_
return readBuffer_.read(buf, off, len);//向buf读入数据
}
private final byte[] i32buf = new byte[4];//保存本次请求数据的长度
//将数据从transport_读到本地缓存readBuffer_中
private void readFrame() throws TTransportException {
transport_.readAll(i32buf, 0, 4);//先读前4个字节,代表数据的长度
int size = decodeFrameSize(i32buf);//由于客户端服务端会对 数据size进行序列化,所以这里需要反序列化
if (size < 0) {
close();
throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!");
}
if (size > maxLength_) {
close();
throw new TTransportException(TTransportException.CORRUPTED_DATA,
"Frame size (" + size + ") larger than max length (" + maxLength_ + ")!");
}
byte[] buff = new byte[size];
transport_.readAll(buff, 0, size);//将完整的数据读到buff
readBuffer_.reset(buff);//重置本地缓存
}
//下面四个方法都是调用readBuffer_实现
@Override
public byte[] getBuffer() {
return readBuffer_.getBuffer();
}
@Override
public int getBufferPosition() {
return readBuffer_.getBufferPosition();
}
@Override
public int getBytesRemainingInBuffer() {
return readBuffer_.getBytesRemainingInBuffer();
}
@Override
public void consumeBuffer(int len) {
readBuffer_.consumeBuffer(len);
}
//下面两个方法需要结合使用,write是向本地缓存写入数据,写完后,所有的调用方都要对输出流调用flush进行清空,所以下面一定会进入到flush方法,再通过transport_将本地缓存的数据写出去
public void write(byte[] buf, int off, int len) throws TTransportException {
writeBuffer_.write(buf, off, len);
}
@Override
public void flush() throws TTransportException {
byte[] buf = writeBuffer_.get();
int len = writeBuffer_.len();
writeBuffer_.reset();//清空,为下次作准备
encodeFrameSize(len, i32buf);//序列化操作,与上面readFrame的反序列化对应
transport_.write(i32buf, 0, 4);//先返回数据大小,与readFrame的先读4字节对应
transport_.write(buf, 0, len);//再返回真实数据
transport_.flush();//清空
}
//序列化反序列化数据大小size,序列化是将每个字节高位都位移到低位组成byte数组,反序列化反之
public static final void encodeFrameSize(final int frameSize, final byte[] buf) {
buf[0] = (byte)(0xff & (frameSize >> 24));
buf[1] = (byte)(0xff & (frameSize >> 16));
buf[2] = (byte)(0xff & (frameSize >> 8));
buf[3] = (byte)(0xff & (frameSize));
}
public static final int decodeFrameSize(final byte[] buf) {
return
((buf[0] & 0xff) << 24) |
((buf[1] & 0xff) << 16) |
((buf[2] & 0xff) << 8) |
((buf[3] & 0xff));
}
}
源码还是较易懂,TFramedTransport通过将前四字节表示数据长度,对本地缓存进行读写,从而对上层提供的读写方法可保证读入、写出的数据的完整性,下面再上一张图,介绍客户端一次请求的完整流程,帮助理解TFramedTransport在非阻塞服务中的功能,可能会牵扯到之前讲过的一些源码,忘记的同学可以回顾下,TProtocol、TProcessor等会简单略过。
TNonblockingSocket
接下来看下该类源码,顾名思义,该类用于非阻塞型Socket通信,在服务端、客户端均能使用(客户端使用时为异步客户端,过于复杂,这里不讨论,同步客户端使用TFrameTransport即可),还是需要看看其父类TNonblockingTransport的源码:
public abstract class TNonblockingTransport extends TTransport {
//@see java.nio.channels.SocketChannel#connect(SocketAddress remote) 这是源码的注释,即可理解为该方法是需要调用SocketChannel.connect
public abstract boolean startConnect() throws IOException;
//完成连接,同样要参考SocketChannel.finishConnect()
public abstract boolean finishConnect() throws IOException;
//很熟悉的方法,对,就是将当前对象注册到selector上
public abstract SelectionKey registerSelector(Selector selector, int interests) throws IOException;
//向buffer中读数据
public abstract int read(ByteBuffer buffer) throws IOException;
//向buffer中写数据
public abstract int write(ByteBuffer buffer) throws IOException;
}
这里不解释太多,使用到Java Nio中的概念,不懂得同学还请自行百度,下面看TNonblockingSocket源码:
public class TNonblockingSocket extends TNonblockingTransport {
private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingSocket.class.getName());
private final SocketAddress socketAddress_;//host port信息,在连接懒加载时使用
private final SocketChannel socketChannel_;//Java Nio 非阻塞读写
//下面两个构造方法都只是初始化成员变量,并不open connecttion
public TNonblockingSocket(String host, int port) throws IOException {
this(host, port, 0);
}
public TNonblockingSocket(String host, int port, int timeout) throws IOException {
this(SocketChannel.open(), timeout, new InetSocketAddress(host, port));
}
//这个构造方法要求socketChannel已经打开connection,非阻塞服务使用该方法创建连接
public TNonblockingSocket(SocketChannel socketChannel) throws IOException {
this(socketChannel, 0, null);
if (!socketChannel.isConnected()) throw new IOException("Socket must already be connected");
}
//socketChannel相关参数设置
private TNonblockingSocket(SocketChannel socketChannel, int timeout, SocketAddress socketAddress)
throws IOException {
socketChannel_ = socketChannel;
socketAddress_ = socketAddress;
socketChannel.configureBlocking(false);
Socket socket = socketChannel.socket();
socket.setSoLinger(false, 0);
socket.setTcpNoDelay(true);
socket.setKeepAlive(true);
setTimeout(timeout);
}
//将变量socketChannel_注册到selector,返回SelectionKey
public SelectionKey registerSelector(Selector selector, int interests) throws IOException {
return socketChannel_.register(selector, interests);
}
//设置超时时间
public void setTimeout(int timeout) {
try {
socketChannel_.socket().setSoTimeout(timeout);
} catch (SocketException sx) {
LOGGER.warn("Could not set socket timeout.", sx);
}
}
public SocketChannel getSocketChannel() {
return socketChannel_;
}
//连接是否已经处于连接状态,isConnected方法在已经调用了close后会返回false,但isOpen方法会返回true
public boolean isOpen() {
return socketChannel_.isOpen() && socketChannel_.isConnected();
}
//不要调用该方法,提供了 懒加载startConnect()方法来打开连接
public void open() throws TTransportException {
throw new RuntimeException("open() is not implemented for TNonblockingSocket");
}
//往buffer读数据,非阻塞服务端调用该方法
public int read(ByteBuffer buffer) throws IOException {
return socketChannel_.read(buffer);
}
//方法作用见父类,这里用socketChannel_实现
public int read(byte[] buf, int off, int len) throws TTransportException {
if ((socketChannel_.validOps() & SelectionKey.OP_READ) != SelectionKey.OP_READ) {
throw new TTransportException(TTransportException.NOT_OPEN,
"Cannot read from write-only socket channel");
}
try {
return socketChannel_.read(ByteBuffer.wrap(buf, off, len));
} catch (IOException iox) {
throw new TTransportException(TTransportException.UNKNOWN, iox);
}
}
//向buffer写数据,非阻塞服务端调用该方法
public int write(ByteBuffer buffer) throws IOException {
return socketChannel_.write(buffer);
}
//方法作用见父类,这里用socketChannel_实现
public void write(byte[] buf, int off, int len) throws TTransportException {
if ((socketChannel_.validOps() & SelectionKey.OP_WRITE) != SelectionKey.OP_WRITE) {
throw new TTransportException(TTransportException.NOT_OPEN,
"Cannot write to write-only socket channel");
}
try {
socketChannel_.write(ByteBuffer.wrap(buf, off, len));
} catch (IOException iox) {
throw new TTransportException(TTransportException.UNKNOWN, iox);
}
}
//SocketChannel不支持,为空即可
public void flush() throws TTransportException {
}
//关闭socketChannel_
public void close() {
try {
socketChannel_.close();
} catch (IOException iox) {
LOGGER.warn("Could not close socket.", iox);
}
}
//开始连接
public boolean startConnect() throws IOException {
return socketChannel_.connect(socketAddress_);
}
//连接是否完成
public boolean finishConnect() throws IOException {
return socketChannel_.finishConnect();
}
}
对TNonblockingSocket,这里我们清楚其底层是依赖SocketChannel实现即可,后并无难点,TTransport体系常用类源码就介绍到此,下一节看看TServerTransport类体系。
TServerTransport
TServerTransport
这一块的类都是用于服务端监听端口,惯例,还是先看看顶层父类的源码:
public abstract class TServerTransport implements Closeable {
//抽象参数类
public static abstract class AbstractServerTransportArgs<T extends AbstractServerTransportArgs<T>> {
int backlog = 0; //接收请求的队列大小
int clientTimeout = 0;//客户端超时时间
InetSocketAddress bindAddr;//监听地址
public T backlog(int backlog) {
this.backlog = backlog;
return (T) this;
}
public T clientTimeout(int clientTimeout) {
this.clientTimeout = clientTimeout;
return (T) this;
}
public T port(int port) {
this.bindAddr = new InetSocketAddress(port);
return (T) this;
}
public T bindAddr(InetSocketAddress bindAddr) {
this.bindAddr = bindAddr;
return (T) this;
}
}
//开始监听客户端请求
public abstract void listen() throws TTransportException;
//监听到请求后,创建一个TTransport对象
public final TTransport accept() throws TTransportException {
TTransport transport = acceptImpl();
if (transport == null) {
throw new TTransportException("accept() may not return NULL");
}
return transport;
}
//创建TTransport对象的真正实现
protected abstract TTransport acceptImpl() throws TTransportException;
//停止监听
public abstract void close();
//可选,作用是将当前对象从accept、listen方法的阻塞状态中打断
public void interrupt() {}
}
TServerSocket
阻塞服务监听客户端请求时使用的TServerSocket相对简单,所以先看其源码:
public class TServerSocket extends TServerTransport {
private static final Logger LOGGER = LoggerFactory.getLogger(TServerSocket.class.getName());
private ServerSocket serverSocket_ = null; //很熟悉吧,真正监听客户端请求的对象
private int clientTimeout_ = 0;//接收到客户端请求后,创建连接时的超时时间
//参数类,多了ServerSocket对象
public static class ServerSocketTransportArgs extends AbstractServerTransportArgs<ServerSocketTransportArgs> {
ServerSocket serverSocket;
public ServerSocketTransportArgs serverSocket(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
return this;
}
}
//下面多个构造方法这里就不详述了
public TServerSocket(ServerSocket serverSocket) throws TTransportException {
this(serverSocket, 0);
}
public TServerSocket(ServerSocket serverSocket, int clientTimeout) throws TTransportException {
this(new ServerSocketTransportArgs().serverSocket(serverSocket).clientTimeout(clientTimeout));
}
public TServerSocket(int port) throws TTransportException {
this(port, 0);
}
public TServerSocket(int port, int clientTimeout) throws TTransportException {
this(new InetSocketAddress(port), clientTimeout);
}
public TServerSocket(InetSocketAddress bindAddr) throws TTransportException {
this(bindAddr, 0);
}
public TServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException {
this(new ServerSocketTransportArgs().bindAddr(bindAddr).clientTimeout(clientTimeout));
}
//上面所有的构造方法都会调到这里,不多讲,ServerSocket相关设置
public TServerSocket(ServerSocketTransportArgs args) throws TTransportException {
clientTimeout_ = args.clientTimeout;
if (args.serverSocket != null) {
this.serverSocket_ = args.serverSocket;
return;
}
try {
serverSocket_ = new ServerSocket();
serverSocket_.setReuseAddress(true);
serverSocket_.bind(args.bindAddr, args.backlog);
} catch (IOException ioe) {
close();
throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".", ioe);
}
}
//确定在accept的时候不要阻塞
public void listen() throws TTransportException {
if (serverSocket_ != null) {
try {
serverSocket_.setSoTimeout(0);
} catch (SocketException sx) {
LOGGER.error("Could not set socket timeout.", sx);
}
}
}
//accept到Socket并封装成TSocket
protected TSocket acceptImpl() throws TTransportException {
if (serverSocket_ == null) {
throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket.");
}
try {
Socket result = serverSocket_.accept();
TSocket result2 = new TSocket(result);
result2.setTimeout(clientTimeout_);
return result2;
} catch (IOException iox) {
throw new TTransportException(iox);
}
}
//关闭serverSocket_
public void close() {
if (serverSocket_ != null) {
try {
serverSocket_.close();
} catch (IOException iox) {
LOGGER.warn("Could not close server socket.", iox);
}
serverSocket_ = null;
}
}
//线程安全的中断方法
public void interrupt() {
close();
}
public ServerSocket getServerSocket() {
return serverSocket_;
}
}
源码很简单,依赖ServerSocket实现,这里不多讲。
TNonblockingServerSocket
该类继承自TNonblockingServerTransport,所以先看看TNonblockingServerTransport:
public abstract class TNonblockingServerTransport extends TServerTransport {
//向selector注册对象
public abstract void registerSelector(Selector selector);
}
非阻塞服务监听客户端请求时使用:
public class TNonblockingServerSocket extends TNonblockingServerTransport {
private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingServerSocket.class.getName());
private ServerSocketChannel serverSocketChannel = null;//Java Nio相关,用监听客户端请求
private ServerSocket serverSocket_ = null;//serverSocketChannel中的对象
private int clientTimeout_ = 0;//创建客户端连接超时时间
//参数类,直接继承
public static class NonblockingAbstractServerSocketArgs extends
AbstractServerTransportArgs<NonblockingAbstractServerSocketArgs> {}
public TNonblockingServerSocket(int port) throws TTransportException {
this(port, 0);
}
public TNonblockingServerSocket(int port, int clientTimeout) throws TTransportException {
this(new NonblockingAbstractServerSocketArgs().port(port).clientTimeout(clientTimeout));
}
public TNonblockingServerSocket(InetSocketAddress bindAddr) throws TTransportException {
this(bindAddr, 0);
}
public TNonblockingServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException {
this(new NonblockingAbstractServerSocketArgs().bindAddr(bindAddr).clientTimeout(clientTimeout));
}
//上面四个构造方法都会调到这里
public TNonblockingServerSocket(NonblockingAbstractServerSocketArgs args) throws TTransportException {
clientTimeout_ = args.clientTimeout;
try {
serverSocketChannel = ServerSocketChannel.open();//创建ServerSocketChannel
serverSocketChannel.configureBlocking(false);//设置非阻塞模式
serverSocket_ = serverSocketChannel.socket();//根据serverSocketChannel创建ServerSocket
serverSocket_.setReuseAddress(true);
serverSocket_.bind(args.bindAddr, args.backlog);//监听端口
} catch (IOException ioe) {
serverSocket_ = null;
throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".");
}
}
//确保接收请求时不要阻塞
public void listen() throws TTransportException {
// Make sure not to block on accept
if (serverSocket_ != null) {
try {
serverSocket_.setSoTimeout(0);
} catch (SocketException sx) {
sx.printStackTrace();
}
}
}
//接收客户端请求,并封装成TNonblockingSocket对象
protected TNonblockingSocket acceptImpl() throws TTransportException {
if (serverSocket_ == null) {
throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket.");
}
try {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel == null) {
return null;
}
TNonblockingSocket tsocket = new TNonblockingSocket(socketChannel);
tsocket.setTimeout(clientTimeout_);
return tsocket;
} catch (IOException iox) {
throw new TTransportException(iox);
}
}
//向selector注册接收请求事件用于接收新请求
public void registerSelector(Selector selector) {
try {
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (ClosedChannelException e) {
}
}
//关闭监听
public void close() {
if (serverSocket_ != null) {
try {
serverSocket_.close();
} catch (IOException iox) {
LOGGER.warn("WARNING: Could not close server socket: " + iox.getMessage());
}
serverSocket_ = null;
}
}
//线程安全的,打断listen、accept可能产生的阻塞
public void interrupt() {
close();
}
//获取监听端口
public int getPort() {
if (serverSocket_ == null)
return -1;
return serverSocket_.getLocalPort();
}
}
TNonblockingServerSocket该类也比较容易理解,依赖Java Nio中的ServerSocketChannel来实现接收客户端请求,并将新连接封装为Thrift中的TNonblockingSocket对象。
总结
这一章给大家介绍了TTransport层的关键类源码,也是在开发中接触最多的类,都较简单,依赖了Java中的Bio、Nio相关类实现,下一章节,我们来看看基于TTransport层的TProtocol提供了哪些方法以及他们的作用。