Thrift源码系列----2.TTransport层源码分析

前言

        前一章为大家介绍了Thrift类体系,给大家一个整体上的认识,本章开始对每一层的实现细节进行研究,这里我们从与业务分离、处于最底层的TTransport层源码开始。

阻塞与非阻塞TTransport

        看源码前,有必要先向大家说明一下,阻塞、非阻塞服务的服务端与客户端是如何搭配使用TTransport,这里还是只研究我们常用的类,下图是TTransport类间的关系图:


这里写图片描述


  1. 服务端为阻塞式服务时,使用TServerSocket,接收到客户端的请求创建一个TSocket通信,客户端使用TSocket配合即可。
  2. 服务端为非阻塞式服务时,使用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提供了哪些方法以及他们的作用。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spark-Thrift和Spark-SQL是Spark框架中的两个组件,它们有以下区别: 1. Spark-SQL是Spark的一个模块,用于处理结构化数据,支持SQL查询和DataFrame API。它提供了一种高效且易于使用的方法来处理和分析结构化数据。用户可以使用SQL语句或DataFrame API来查询和操作数据。Spark-SQL允许用户直接在Spark应用程序中使用SQL查询,而无需编写复杂的MapReduce代码。 2. Spark-Thrift是Spark的一个独立服务,它提供了一个标准的Thrift接口,用于执行SQL查询。它可以作为一个独立的进程运行,并通过网络接收来自客户端的SQL查询请求,并将查询转发到Spark集群中的Spark-SQL模块进行处理。Spark-Thrift使得可以使用不同的编程语言,如Java、Python、R等,通过Thrift接口与Spark集群交互。 因此,Spark-SQL是Spark框架中用于处理结构化数据的模块,而Spark-Thrift是提供Thrift接口让用户可以使用不同编程语言与Spark-SQL模块交互的独立服务。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [CDH15.0支持spark-sql和spark-thrift-server](https://blog.csdn.net/u012458821/article/details/87635599)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [122.Thrift和spark-sql客户端部署](https://blog.csdn.net/m0_47454596/article/details/126856172)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值