zoukankan      html  css  js  c++  java
  • Apache Thrift系列详解(一)- 概述与入门

    前言
    Thrift是一个轻量级、跨语言的远程服务调用框架,最初由Facebook开发,后面进入Apache开源项目。它通过自身的IDL中间语言, 并借助代码生成引擎生成各种主流语言的RPC服务端/客户端模板代码。

    Thrift支持多种不同的编程语言,包括C++、Java、Python、PHP、Ruby等,本系列主要讲述基于Java语言的Thrift的配置方式和具体使用。

    正文
    Thrift的技术栈
    Thrift对软件栈的定义非常的清晰, 使得各个组件能够松散的耦合, 针对不同的应用场景, 选择不同是方式去搭建服务。

    Thrift软件栈分层从下向上分别为:传输层(Transport Layer)、协议层(Protocol Layer)、处理层(Processor Layer)和服务层(Server Layer)。

    传输层(Transport Layer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;比如说TCP/IP传输等。

    协议层(Protocol Layer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说JSON、XML、二进制数据等。

    处理层(Processor Layer):处理层是由具体的IDL(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的Handler进行处理。

    服务层(Server Layer):整合上述组件,提供具体的网络线程/IO服务模型,形成最终的服务。

    Thrift的特性
    (一) 开发速度快
    通过编写RPC接口Thrift IDL文件,利用编译生成器自动生成服务端骨架(Skeletons)和客户端桩(Stubs)。从而省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作。

    服务端:只需要按照服务骨架即接口,编写好具体的业务处理程序(Handler)即实现类即可。
    客户端:只需要拷贝IDL定义好的客户端桩和服务对象,然后就像调用本地对象的方法一样调用远端服务。
    (二) 接口维护简单
    通过维护Thrift格式的IDL(接口描述语言)文件(注意写好注释),即可作为给Client使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且Thrift协议可灵活支持接口的可扩展性。

    (三) 学习成本低
    因为其来自Google Protobuf开发团队,所以其IDL文件风格类似Google Protobuf,且更加易读易懂;特别是RPC服务接口的风格就像写一个面向对象的Class一样简单。

    初学者只需参照:http://thrift.apache.org/,一个多小时就可以理解Thrift IDL文件的语法使用。

    (四) 多语言/跨语言支持
    Thrift支持C++、 Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk等多种语言,即可生成上述语言的服务器端和客户端程序。

    对于我们经常使用的Java、PHP、Python、C++支持良好,虽然对iOS环境的Objective-C(Cocoa)支持稍逊,但也完全满足我们的使用要求。

    (五) 稳定/广泛使用
    Thrift在很多开源项目中已经被验证是稳定和高效的,例如Cassandra、Hadoop、HBase等;国外在Facebook中有广泛使用,国内包括百度、美团小米、和饿了么等公司。

    Thrift的数据类型
    Thrift 脚本可定义的数据类型包括以下几种类型:

    基本类型:
      bool: 布尔值
      byte: 8位有符号整数
      i16: 16位有符号整数
      i32: 32位有符号整数
      i64: 64位有符号整数
      double: 64位浮点数
      string: UTF-8编码的字符串
      binary: 二进制串
    结构体类型:
      struct: 定义的结构体对象
    容器类型:
      list: 有序元素列表
      set: 无序无重复元素集合
      map: 有序的key/value集合
    异常类型:
      exception: 异常类型
    服务类型:
      service: 具体对应服务的类
    Thrift的协议
    Thrift可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本(text)和二进制(binary)传输协议。为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:

    TBinaryProtocol:二进制编码格式进行数据传输
    TCompactProtocol:高效率的、密集的二进制编码格式进行数据传输
    TJSONProtocol: 使用JSON文本的数据编码协议进行数据传输
    TSimpleJSONProtocol:只提供JSON只写的协议,适用于通过脚本语言解析
    Thrift的传输层
    常用的传输层有以下几种:

    TSocket:使用阻塞式I/O进行传输,是最常见的模式
    TNonblockingTransport:使用非阻塞方式,用于构建异步客户端
    TFramedTransport:使用非阻塞方式,按块的大小进行传输,类似于Java中的NIO
    Thrift的服务端类型
    TSimpleServer:单线程服务器端,使用标准的阻塞式I/O
    TThreadPoolServer:多线程服务器端,使用标准的阻塞式I/O
    TNonblockingServer:单线程服务器端,使用非阻塞式I/O
    THsHaServer:半同步半异步服务器端,基于非阻塞式IO读写和多线程工作任务处理
    TThreadedSelectorServer:多线程选择器服务器端,对THsHaServer在异步IO模型上进行增强
    Thrift入门示例
    (一) 编写Thrift IDL文件
    a). 下载0.10.0的Thrift IDL编译器,下载地址:http://thrift.apache.org/docs/install。 通过编译生成器生成.java接口的类文件。

    b). 下载Windows安装环境的.exe文件,将thrift.exe的路径加入环境变量中。在Idea上安装Thrift编辑插件。

    c). 编写hello.thrift的IDL文件:

    service HelloWorldService {
    string say(1: string username)
    }

    d). 使用代码生成工具生成代码,执行以下命令:

    thrift -gen java hello.thrift
    1
    e). 由于未指定代码生成的目标目录,生成的类文件默认存放在gen-java目录下。这里生成一个HelloWorldService.java类文件,文件大小超过数千行,下面截取一部分核心代码。

    public class HelloWorldService {
    public interface Iface {
    public String say(String username) throws org.apache.thrift.TException;
    }

    public interface AsyncIface {
    public void say(String username, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws org.apache.thrift.TException;
    }

    public static class Client extends org.apache.thrift.TServiceClient implements Iface {
    public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {
    public Factory() {
    }

    public Client getClient(org.apache.thrift.protocol.TProtocol prot) {
    return new Client(prot);
    }

    public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
    return new Client(iprot, oprot);
    }
    }

    public Client(org.apache.thrift.protocol.TProtocol prot) {
    super(prot, prot);
    }

    public Client(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
    super(iprot, oprot);
    }

    public String say(String username) throws org.apache.thrift.TException {
    send_say(username);
    return recv_say();
    }
    // 省略.....
    }

    public static class AsyncClient extends org.apache.thrift.async.TAsyncClient implements AsyncIface {
    public static class Factory implements org.apache.thrift.async.TAsyncClientFactory<AsyncClient> {
    private org.apache.thrift.async.TAsyncClientManager clientManager;
    private org.apache.thrift.protocol.TProtocolFactory protocolFactory;

    public Factory(org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.protocol.TProtocolFactory protocolFactory) {
    this.clientManager = clientManager;
    this.protocolFactory = protocolFactory;
    }

    public AsyncClient getAsyncClient(org.apache.thrift.transport.TNonblockingTransport transport) {
    return new AsyncClient(protocolFactory, clientManager, transport);
    }
    }

    public AsyncClient(org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.transport.TNonblockingTransport transport) {
    super(protocolFactory, clientManager, transport);
    }

    public void say(String username, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws org.apache.thrift.TException {
    checkReady();
    say_call method_call = new say_call(username, resultHandler, this, ___protocolFactory, ___transport);
    this.___currentMethod = method_call;
    ___manager.call(method_call);
    }
    // 省略.....
    }
    // 省略.....
    }

    对于开发人员而言,使用原生的Thrift框架,仅需要关注以下四个核心内部接口/类:Iface, AsyncIface, Client和AsyncClient。

    Iface:服务端通过实现HelloWorldService.Iface接口,向客户端的提供具体的同步业务逻辑。
    AsyncIface:服务端通过实现HelloWorldService.Iface接口,向客户端的提供具体的异步业务逻辑。
    Client:客户端通过HelloWorldService.Client的实例对象,以同步的方式访问服务端提供的服务方法。
    AsyncClient:客户端通过HelloWorldService.AsyncClient的实例对象,以异步的方式访问服务端提供的服务方法。
    (二) 新建Maven工程
    a). 新建maven工程,引入thrift的依赖,这里使用的是版本0.10.0。

    <dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.10.0</version>
    </dependency>

    b). 将生成类的HelloWorldService.java源文件拷贝进项目源文件目录中,并实现HelloWorldService.Iface的定义的say()方法。

    HelloWorldServiceImpl.java

    public class HelloWorldServiceImpl implements HelloWorldService.Iface {
    @Override
    public String say(String username) throws TException {
    return "Hello " + username;
    }
    }

    c). 服务器端程序编写:

    SimpleServer.java

    public class SimpleServer {
    public static void main(String[] args) throws Exception {
    ServerSocket serverSocket = new ServerSocket(ServerConfig.SERVER_PORT);
    TServerSocket serverTransport = new TServerSocket(serverSocket);
    HelloWorldService.Processor processor =
    new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

    TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();
    TSimpleServer.Args tArgs = new TSimpleServer.Args(serverTransport);
    tArgs.processor(processor);
    tArgs.protocolFactory(protocolFactory);

    // 简单的单线程服务模型 一般用于测试
    TServer tServer = new TSimpleServer(tArgs);
    System.out.println("Running Simple Server");
    tServer.serve();
    }
    }

    d). 客户端程序编写:

    SimpleClient.java

    public class SimpleClient {
    public static void main(String[] args) {
    TTransport transport = null;
    try {
    transport = new TSocket(ServerConfig.SERVER_IP, ServerConfig.SERVER_PORT, ServerConfig.TIMEOUT);
    TProtocol protocol = new TBinaryProtocol(transport);
    HelloWorldService.Client client = new HelloWorldService.Client(protocol);
    transport.open();

    String result = client.say("Leo");
    System.out.println("Result =: " + result);
    } catch (TException e) {
    e.printStackTrace();
    } finally {
    if (null != transport) {
    transport.close();
    }
    }
    }
    }

    e). 运行服务端程序,服务端在指定端口监听客户端的连接请求,控制台输出启动日志:

    f). 运行客户端程序,客户端通过网络请求HelloWorldService的say()方法的具体实现,控制台输出返回结果:

    这里使用的一个基于单线程同步的简单服务模型,一般仅用于入门学习和测试!

    总结
    本文对Thrift的概念做了相关介绍,体验了一番thrift程序如何编写!
    ————————————————

    原文链接:https://blog.csdn.net/baidu_22254181/article/details/82814489

  • 相关阅读:
    element-ui 和ivew-ui的table导出export纯前端(可用)
    webstrom 2019 注册码(可用 2019年10月14日08:59:18)
    intellji IDEA 2019版激活码(亲测可用 2019年10月14日08:53:54)
    java开发相关工具安装包分享
    js有关字符串拼接问题
    js增删class的方法
    有关定位问题
    关于网页元素居中常见的两种方法
    有关css编写文字动态下划线
    js获取时间及转化
  • 原文地址:https://www.cnblogs.com/sea520/p/12165769.html
Copyright © 2011-2022 走看看