1. 程式人生 > >5、thrift原始碼分析之_TBinaryProtocol(二進位制)

5、thrift原始碼分析之_TBinaryProtocol(二進位制)

  在上一篇文章中我們分析了thrift協議的總介面,這個總介面是一個抽象類,它只是聲明瞭讀方法和寫方法,具體按照什麼方式讀和寫,這需要子類去實現它。這次我們就分析一下利用二進位制格式讀和寫所有的資料。

  RPC框架呼叫使用到的協議主要是傳送函式相關的資訊到伺服器和接受伺服器傳回的介面。

  在介紹TBinaryProtocol之前,我們首先看看怎樣通過協議傳送相關函式資訊的,還是以UserService為例

在UserService中

public void send_getUser() throws org.apache.thrift.TException
    {
      //建立一個getUser_agrs物件
      getUser_args args = new getUser_args();
      //呼叫TServiceClient中的sendBase()方法,向服務端傳送資訊
      sendBase("getUser", args);
    }
我們在看一下sendBase()方法的具體實現
       /**
	 * 客戶端通過協議向服務端傳送功能資訊  oproto_ :TProtocol
	 * @param methodName 功能方法名稱,就是thrift檔案中定義的方法名稱
	 * @param args  TBase封裝了讀方法和寫方法
	 * @throws TException
	 */
	protected void sendBase(String methodName, TBase args) throws TException {
		//寫入函式呼叫的訊息
	    oprot_.writeMessageBegin(new TMessage(methodName, TMessageType.CALL, ++seqid_));
	    //把協議傳輸到服務端
	    args.write(oprot_);
	    //結束傳輸層的寫入
	    oprot_.writeMessageEnd();
	    //重新整理傳輸流,讓其馬上執行
	    oprot_.getTransport().flush();
	  }
  從上面的程式碼中可以看出,不同的具體協議,writeMessageBegin()方法處理格式是不同的,往伺服器傳遞時資訊的形式也是不同的。在這段程式碼上中有一個TBase,這個類封裝了讀和寫的操作
/**
   * Reads the TObject from the given input protocol.
   *
   * @param iprot Input protocol
   */
  public void read(TProtocol iprot) throws TException;

  /**
   * Writes the objects out to the protocol
   *
   * @param oprot Output protocol
   */
  public void write(TProtocol oprot) throws TException;
  接下來開始進入今天的主題TBinaryProtocol,看看它是怎樣在writeMessageBegin方法中處理資料格式的
/**
	 * 
	 * @param message  客戶端和服務端傳遞資訊的媒介
	 * <p></p>
	 * @throws TException
	 */
	public void writeMessageBegin(TMessage message) throws TException {
	    if (strictWrite_) {//判斷是否強制寫入版本號,是
	      int version = VERSION_1 | message.type;
	      writeI32(version);//寫入版本號
	      writeString(message.name);//寫入功能方法的名稱
	      writeI32(message.seqid);//寫入客戶端的標識,這個標識是自動增加的
	    } else {//否
	      writeString(message.name);//寫入功能方法的名稱
	      writeByte(message.type);//寫入型別
	      writeI32(message.seqid);//寫入客戶端的標識,這個標識是自動增加的
	    }
	  }  
  寫入版本號的目的就是使客戶端和服務端使用相同的協議傳輸資料,保證資料格式的正確性,接下來看一下具體資料型別的寫法
 public void writeI32(int i32) throws TException {
    i32out[0] = (byte)(0xff & (i32 >> 24));
    i32out[1] = (byte)(0xff & (i32 >> 16));
    i32out[2] = (byte)(0xff & (i32 >> 8));
    i32out[3] = (byte)(0xff & (i32));
    trans_.write(i32out, 0, 4);
  }
從這也可以看出真正進行網路I/O通訊的是通過TTtransport進行的。

讀方法

 public TMessage readMessageBegin() throws TException {
    int size = readI32();
    if (size < 0) {
      int version = size & VERSION_MASK;
      if (version != VERSION_1) {
        throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");
      }
      return new TMessage(readString(), (byte)(size & 0x000000ff), readI32());
    } else {
      if (strictRead_) {
        throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
      }
      return new TMessage(readStringBody(size), readByte(), readI32());
    }
  }

  在上一篇文章中我們已經介紹了,每一個具體的協議中都有一個工廠類,這個工廠類都實現了TProtocolFactory