最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端需要知道的Thrift。(含各种对比和使用方法)

    正文概述 掘金(kikooo)   2021-01-10   1462

    先祭出官网。

    昨天有个之前的同事来找我。说是新入职了某大厂,使用的是thrift请求,由于公司内部进行了thrift封装,导致他看不清整个流程,希望我能给他讲讲thrift请求应该是什么样的。。 突然我就很好奇,是什么使美团、头条等等大厂现在都加入了thrift的阵营呢?

    特点

    首先说一下thrift的特点。这个很多介绍thrift的文章里面也都会提到以下几点。我就说一下我个人的理解。

    • 基于二进制的高性能的编解码框架

    二进制:二进制协议比文本形式的协议,发送的数据量肯定是更小,传输效率更高的。

    编解码框架:作为rpc框架,一般都是封装了方法名、方法参数这些方法调用的方式。客户端发送消息。服务端接收到客户端的消息之后,解码消息,然后通过方法调用模型来完成实际服务器端业务方法的调用。

    其实这句话里的关键点应该就是高性能。网上也有很多性能测试能证明thrift的性能远远高于http。比如这篇文章。而这里面的原因,一部分是因为非阻塞io的机制,另一部分的原因也是因为http无连接无状态,而thrift是客户端提前建立连接,在这种测试中有先天优势。

    • 基于NIO的底层通信

    NIO,全称是NoneBlocking IO,非阻塞IO,和BIO(Blocking IO,阻塞IO)相对应。具体的解释和实现机制,不是这篇文章的重点,感兴趣的可以直接看这篇文章了解一下。

    非阻塞IO,区别与BIO,BIO的全称是Blocking IO,阻塞IO。那这个阻塞是什么意思呢?Accept是阻塞的,只有新连接来了,Accept才会返回,主线程才能继Read是阻塞的,只有请求消息来了,Read才能返回,子线程才能继续处理Write是阻塞的,只有客户端把消息收了,Write才能返回,子线程才能继续读取下一个请求所以传统的多线程服务器是BlockingIO模式的,从头到尾所有的线程都是阻塞的。这些线程就干等在哪里,占用了操作系统的调度资源,什么事也不干,是浪费。那么NIO是怎么做到非阻塞的呢。它用的是事件机制。它可以用一个线程把Accept,读写操作,请求处理的逻辑全干了。如果什么事都没得做,它也不会死循环,它会将线程休眠起来,直到下一个事件来了再继续干活,这样的一个线程称之为NIO线程。

    • 相对简单的服务调用模型

    既然是相对简单那就是看跟谁比。和http比的话thrift是需要设计协议的,但是相对于xxx来说,。

    • 使用IDL支持跨平台调用

    到不同平台生成相应的代码。。目前支持了这些语言的使用吧。。

    但是像java这种注解的使用方式的话。。生成idl可能(?)反而是另一个工作量。。(反正我真的见过不只一个写完代码反过来手拼idl的java工程狮。)

    其他rpc框架

    grpc

    中文版文档。

    没有协议层、服务端的什么TBinaryProtocal、TNonBlockingServer这些概念要了解。传输层使用http2协议。

    整体使用起来和thrift非常像。同样有idl,protobuf的文件。同样需要启动客户端服务器。gprc生成的代码会简单易读很多,但是这个好像并没有什么用。。

    也是支持多语言的,但是相对来讲没有thrift支持的那么全面。主要应用是对客户端的支持比较好。 目前提供C系列、Java和Go还有node语言版本。

    优点最主要的是支持流式处理。支持OAuth鉴权。protobuf的高效序列化和反序列化。再就是支持 HTTP 2.0 标准化的协议。哦对还有文档相对健全。。

    相比之下缺点主要集中在支持语言不太多,性能没有thrift好。这个性能测试我感觉比较全面中肯。

    dubbo

    中文官网。

    捐给Apache之前是阿里爸爸的,意味着直接拥有最大语言社区,自带服务注册和服务治理。当时很多人选择dubbo估计也是看在这一点上吧。

    非要和thrift比的话。只能说二者侧重点不同吧。thrift致力于多语言支持,dubbo专注java十几年。因此没有IDL这一机制,直接通过java服务对象接口进行交互。

    和http对比

    都是基于tcp协议实现的。 主要的区别是http是无连接、无状态的协议,而thrift在每次连接、关闭会比较耗性能。而且客户端主动关闭的Socket端会进入TIME_WAIT状态,并且TIME_WAIT状态一般维持在1-4分钟。

    都说thrift省流量和性能好,但是我觉得不一定能体现出来。。 作为一个前端,调用thrift接口,虽然浏览器也可直接发thrift请求,但是出于建立连接性能方面考虑,大部分情况是通过node转发的,感觉比http要麻烦。 而且thrift省流量和性能好这一点其实体现不太出来。。因为浏览器发给node的始终还是http。

    thrift还有一个缺点是完全静态化,所有用到的数据结构都必须事先定义,中途不能更改。若数据结构发生变化,必须重新定义,生成一个新的idl,重新编译。

    详细的可以看这篇文章。

    未来thrift的发展?

    这个问题我也很好奇。thrift肯定不是最新的技术,会昙花一现还是会永久流传这个我真的很好奇。。

    但是任何新技术都是需要时间落地的。目前来看,thrift的各项支持都很好,很多开源项目的周边支持都是thrift的,hbase提供thrift服务,hive,spark sql,cassandra等一系列对外的标准服务接口都是thrift的,以支持多语言。

    thrift有他的优点但是也有他的局限,比如完全静态化就是他的优点也是局限。还有就是thrift不支持大量数据的流式传输。


    thrift的使用流程【node角度】

    首先说一下thrift的核心组成。。这个任何一个教程里都会提到,就简单列一下。

    • TProtocol 协议和编解码组件
    • TTransport 传输组件
    • TProcessor 服务调用组件
    • TServer,Client服务器和客户端组件
    • IDL 接口定义

    顺便说一下。如果想更详细的了解thrift的使用,或者不是前端开发同学,关注点不同的话,我会比较安利这篇文章。至于类型定义什么的也可以去官网查看。

    安装

    brew install thrift
    

    网上有很多更具体的教程,可以参考。

    idl文件

    namespace java hello.test
    
    enum THelloType {
      HI = 1,
      HELLO = 2,
      WELCOME = 3,
    }
    
    struct Error {
      1:  i64 code,
      2:  string message,
    }
    
    struct THelloMessage {
      1:  string msg,
      2:  THelloType helloMsg,
    }
    
    struct TSayHelloResponse {
      1:  bool success,
      2:  Error error,
      3:  THelloMessage data
    }
    
    struct TSayHelloRequest {
      1:  string name,
      2:  list<string> friends,
    }
    
    service TSayHelloService {
      TSayHelloResponse sayHello(1: TSayHelloRequest arg0),
      map<string, set<i32>> searchWhateverByDate(1:string startDate, 2:string endDate, 3:i32 offset, 4:i32 limit)
    }
    
    

    生成js文件

    thrift --gen js:node thrift_file
    

    在koa项目中使用

    首先安装依赖。

    yarn add thrift 
    

    然后启动一个Client服务器。我们以上面那个idl举例。 一般这一步的话就是项目启动的时候执行一次就可以。TProtocol和TTransport选择自己要用的。

    import thrift from 'thrift';
    
    import HelloService from './gen-nodejs/TSayHelloService.js';
     
    const connection = thrift.createConnection('127.0.0.1', 9527, {
            transport : thrift.TBufferedTransport,
            protocol : thrift.TBinaryProtocol
        });
    const client = thrift.createClient(HelloService, connection);
     
    connection.on('error', function(e) {
        console.log(e);
    });
    

    如果一个项目中需要连接多个service的话呢。就搞个连接池。。

    const configs = [{
        serviceName: 'hello',
        service: require('./gen-nodejs/TSayHelloService.js'),
        types: require('./gen-nodejs/hello_types'),
        port: 9527,
    }];
    const pools = {};
    
    configs.forEach(item => {
        let connection = thrift.createConnection('127.0.0.1', item.port, {
            transport : thrift.TBufferedTransport,
            protocol : thrift.TBinaryProtocol
        });
        pools[item.serviceName] = thrift.createClient(item.service, connection);
    });
    

    发送请求。

    const HELLOTYPES = require('./gen-nodejs/hello_types')
    
    pools[hello].sayHello(new HELLOTYPES.TSayHelloRequest({
        name: 'kikooo',
        friends: ['xiaoa', 'xiaob'],
    }), function(err, res) {
        console.log('say hello success');
    });
    

    这里说的就只是使用原生的thrift包。当然各个厂子可能都有自己特殊的封装。

    常见封装

    一般情况下,在thrift发送请求的整个流程里,有以下几个点可能会被封装。初次使用的同学可以参照自己厂子的情况对照看一下。

    建立连接阶段: 对createConnection进行封装,不暴露创建过程。例如:

    new ConnectionPool({
        serviceList: config,
        retry: {
          retries: 0, // 不重试
          minTimeout: 3 * 1000,
          maxTimeout: 10 * 1000,
          randomize: false
        }
    });
    

    配置和服务发现阶段: 大部分公司都会自建服务发现,有的公司甚至可能不暴露thrift文件,直接服务发现的时候获取。 配置可能就长这样:

    const services = {
        Hello: {
            filename: 'hello.thrift',
        }
    };
    // 或者这样
    const services = [{
        remoteKey: 'xxxxx',
        service: HelloService,
        serviceName: 'blablabla'
    }];
    // 或者直接没有本地配置,远程配了当前这个服务对应的需要的服务列表啊啥的,从服务发现把所有服务都拉来,调用的时候直接调。
    

    发送请求阶段: 可能会被封装成中间件啊各种各样的形式,直接引包就用,例如:

    app.use(thriftrequest({
        config,
    }));
    
    const res = await ctx.thriftrequest.functionService.function(req);
    
    // 或者这样。。
    const HelloRequest = serviceExecuter({
      remoteKey: 'global.hello',
      service: HelloService,
    });
    
    const res = await HelloRequest(methodName, params);
    

    广告时间

    最后的最后,前些日子 (刚写这文的时候是2020年9月。我拖延症太严重了哭。) 刚写了个thrift接口mock的包,支持多种使用方法,不要脸的放一下链接,egg-thrift-mock,虽然叫这个名字但是不止能在egg里用的。 欢迎大家下载、试用、交流、提需求、提bug~~~

    我知道很多大厂可能都会有自己的thrift-mock服务器。但是emmm。万一没有呢嘿嘿~~

    参考

    • Why Thrift, Why not HTTP RPC(JSON+gzip)
    • Thrift RPC 框架分析
    • Moving From Apache Thrift to gRPC: A Perspective From Alluxio
    • Thrift入门 | RPC基础&&Thrift概念
    • NIO相关基础篇
    • RPC vs Thrift
    • 由浅入深了解Thrift(二)——Thrift工作原理

    起源地 » 前端需要知道的Thrift。(含各种对比和使用方法)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元