起源:thrift的网络传输性能和须要留神的问题
thrift应该是目前反对编程语言品种最多的跨语言 rpc服务框架, http://thrift.apache.org/

thrift实现了残缺的网络服务,所以个别应用thrift时,会应用到thrift的服务框架。当然,也能够用本人曾经实现的网络服务,用io流对接thrift接口的输入输出流实现thrift的接入。

无论是用thrift的网络实现,还是本人实现的网络服务,只有对接thrift,在调用thrift接口实现rpc时,都是走thrift的网络传输方式。

thrift的网络传输实现形式 不适宜也不反对压力较大的网络传输需要。实际上,调用一次thrift接口,并不是只调一次网络io写数据,而是拆分为屡次写数据传送。

调用一个thrift 的接口发送数据时,thrift会将这个操作拆分为几个操作:

调用thrift的办法时:thrift会找到这个办法所在的对象,调用write办法,

在write办法在,别离对各个参数,顺次调用 writeFieldBeginwriteXXX(具体参数类型) ,WriteFieldStop 等函数
每次调用也同时调用网络io写相应数据.

以目前最新的thrift-0.18.1实现为例

比方 go的实现:

func (p *ItnetPonMergeArgs) Write(ctx context.Context, oprot thrift.TProtocol) error {  if err := oprot.WriteStructBegin(ctx, "PonMerge_args"); err != nil {    return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) }  if p != nil {    if err := p.writeField1(ctx, oprot); err != nil { return err }    if err := p.writeField2(ctx, oprot); err != nil { return err }  }  if err := oprot.WriteFieldStop(ctx); err != nil {    return thrift.PrependError("write field stop error: ", err) }  if err := oprot.WriteStructEnd(ctx); err != nil {    return thrift.PrependError("write struct stop error: ", err) }  return nil}//第一个参数func (p *ItnetPonMergeArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {  if err := oprot.WriteFieldBegin(ctx, "pblist", thrift.LIST, 1); err != nil { //WriteFieldBegin    return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:pblist: ", p), err) }  if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Pblist)); err != nil {    return thrift.PrependError("error writing list begin: ", err)  }  for _, v := range p.Pblist {     if err := v.Write(ctx, oprot); err != nil {      return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err)    }  }  if err := oprot.WriteListEnd(ctx); err != nil {    return thrift.PrependError("error writing list end: ", err)  }  if err := oprot.WriteFieldEnd(ctx); err != nil {    return thrift.PrependError(fmt.Sprintf("%T write field end error 1:pblist: ", p), err) }  return err}//第二个参数,操作相似第一个参数func (p *ItnetPonMergeArgs) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {  if err := oprot.WriteFieldBegin(ctx, "id", thrift.I64, 2); err != nil {    return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:id: ", p), err) }  if err := oprot.WriteI64(ctx, int64(p.ID)); err != nil {  return thrift.PrependError(fmt.Sprintf("%T.id (2) field write error: ", p), err) }  if err := oprot.WriteFieldEnd(ctx); err != nil {    return thrift.PrependError(fmt.Sprintf("%T write field end error 2:id: ", p), err) }  return err}

调用Pon(ItnetPonMergeArgs)办法的thrift传输程序是:

Write->

  1. writeField1->WriteFieldBegin-> WriteByte-> io
  2. -> Write16 -> io
  3. ->WriteBinary-> Write32 -> io
  4. ->Write -> io
  5. writeField2->WriteFieldBegin-> WriteByte -> io
  6. ->Write16 -> io
  7. ->Write64-> Write -> io
  8. WriteFieldStop ->io

能够看到,一次简略的办法调用,如果办法中有两个参数, 则至多有8次io流写数据调用。

如果参数多时,或是参数中一个构造体的变量多时,则会有更多的io流写数据调用。

在海量的网络传输中,这样的传输方式,网络io流写数据调用成倍增加,海量网络io数据写入导致性能急剧下降。

thrift设计的传输层提供了zlib协定压缩,在zlib压缩发送的状况下,将数据进行了整体压缩收发,zlib分为2次发送后,接收端再解压;

以go为例子:

能够在 compress/flate 看到zlib的写数据最终io写入调用:

func (d *compressor) syncFlush() error {    if d.err != nil {        return d.err    }    d.sync = true    d.step(d)    if d.err == nil {        d.w.writeStoredHeader(0, false)   //第一次调用        d.w.flush()                       //第二次调用        d.err = d.w.err    }    d.sync = false    return d.err}//两次io数据写入 

所以,在海量调用thrift办法的状况下,zlib模式的性能要远超非zlib的状况。然而zlib压缩会比拟耗费内存,大量应用时可能导致频繁gc,也可能导致性能降落。当然,即使如此,大部分状况下zlib传输仍然比非zlib传输的性能要好许多。

其余语言的实现:比方 java:

public void write(org.apache.thrift.protocol.TProtocol oprot, SelectByIdxLimit_args struct) throws org.apache.thrift.TException {        struct.validate();        oprot.writeStructBegin(STRUCT_DESC);        if (struct.name != null) {          oprot.writeFieldBegin(NAME_FIELD_DESC);  //io调用          oprot.writeString(struct.name);          //io调用          oprot.writeFieldEnd();        }        if (struct.column != null) {          oprot.writeFieldBegin(COLUMN_FIELD_DESC);  //io调用          oprot.writeString(struct.column);           //io调用          oprot.writeFieldEnd();        }        if (struct.value != null) {          oprot.writeFieldBegin(VALUE_FIELD_DESC);   //io调用          {            oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRING, struct.value.size()));            for (java.nio.ByteBuffer _iter49 : struct.value)            {              oprot.writeBinary(_iter49);          //io调用            }            oprot.writeListEnd();          }          oprot.writeFieldEnd();        }        oprot.writeFieldBegin(START_ID_FIELD_DESC); //io调用        oprot.writeI64(struct.startId);             //io调用               oprot.writeFieldEnd();        oprot.writeFieldBegin(LIMIT_FIELD_DESC);    //io调用        oprot.writeI64(struct.limit);                //io调用        oprot.writeFieldEnd();        oprot.writeFieldStop();                    //io调用        oprot.writeStructEnd();      }

传输方式都是类似的
实现形式各个语言都类似,当然,数据写入程序必定是一样的。


有任何问题或倡议请Email:donnie4w@gmail.com或 http://tlnet.top/contact 发信给我,谢谢!