分享
  1. 首页
  2. 文章

thrift的网络传输性能和需要注意的问题

donnie · · 1893 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

来源: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方法在,分别对各个参数,

依次调用 writeFieldBegin,writeXXX(具体参数类型) ,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.comhttp://tlnet.top/contact 发信给我,谢谢!


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1893 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏