go实现thrift的网络传输性能及需要注意问题示例解析

 更新时间:2023年09月06日 14:11:24   作者:donnie4w  
这篇文章主要为大家介绍了go实现thrift的网络传输性能及需要注意问题示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

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->

  • writeField1->WriteFieldBegin-> WriteByte-> io
  • -> Write16 -> io
  • ->WriteBinary-> Write32 -> io
  • ->Write -> io
  • writeField2->WriteFieldBegin-> WriteByte -> io
  • ->Write16 -> io
  • ->Write64-> Write -> io
  • 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();
      }

传输方式都是相似的

实现方式各个语言都相似,当然,数据写入顺序肯定是一样的。

以上就是go实现thrift的网络及传输性能需要注意问题示例解析的详细内容,更多关于go thrift的网络传输的资料请关注脚本之家其它相关文章!

相关文章

  • Golang中的深拷贝与浅拷贝使用

    Golang中的深拷贝与浅拷贝使用

    本文主要介绍了Golang中的深拷贝与浅拷贝使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Go语言编程中对文件读写的基本方法整理

    Go语言编程中对文件读写的基本方法整理

    这篇文章主要介绍了Go语言编程中对文件读写的基本方法整理,是Go语言入门学习中的基础知识,需要的朋友可以参考下
    2015-10-10
  • Go环境变量配置,及GOROOT、GOPATH的区别小结

    Go环境变量配置,及GOROOT、GOPATH的区别小结

    本文主要介绍了Go环境变量配置,及GOROOT、GOPATH的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-09-09
  • Go工具链之代码测试神器go test详解

    Go工具链之代码测试神器go test详解

    这篇文章主要给大家介绍Go 工具链go test,go test 是 Go 工具链中的一个命令,用于编译和运行按照要求编写的 Golang 测试代码,并生成测试报告,感兴趣的同学跟着小编一起来看看本文吧
    2023-07-07
  • Golang 错误捕获Panic与Recover的使用

    Golang 错误捕获Panic与Recover的使用

    对于Go语言的错误是通过返回值的方式,本文主要介绍了Golang 错误捕获Panic与Recover的使用,文中根据实例编码详细介绍的十分详尽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Golang关键字defer的用法详解

    Golang关键字defer的用法详解

    defer是Go里面的一个关键字,用在方法或函数前面,作为方法或函数的延迟调用。这篇文章主要为大家介绍了defer的简单使用,需要的可以参考一下
    2023-05-05
  • go select编译期的优化处理逻辑使用场景分析

    go select编译期的优化处理逻辑使用场景分析

    select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。接下来通过本文给大家介绍go select编译期的优化处理逻辑使用场景分析,感兴趣的朋友一起看看吧
    2021-06-06
  • Go pprof内存指标含义备忘录及案例分析

    Go pprof内存指标含义备忘录及案例分析

    这篇文章主要介绍了Go pprof内存指标含义备忘录问题,小编特此把问题及案例分享到脚本之家平台供大家学习,需要的朋友可以参考下
    2020-03-03
  • go defer return panic 执行顺序示例详解

    go defer return panic 执行顺序示例详解

    这篇文章主要介绍了go defer return panic 执行顺序,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-01-01
  • Go语言中切片使用的注意事项小结

    Go语言中切片使用的注意事项小结

    切片是引用类型,相信对大家来说都不陌生,下面这篇文章主要给大家总结介绍了关于Go语言中切片使用的一些注意事项,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2018-01-01

最新评论