Thrift学习&实践

557 阅读5分钟

Thrift介绍(维基百科)

Thrift是一种接口描述语言和二进制通讯协议,[1]它被用来定义和创建跨语言的服务。[2]它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。它通过一个代码生成引擎联合了一个软件栈,来创建不同程度的、无缝的跨平台高效服务,可以使用C#、C++(基于POSIX兼容系统[3])、Cappuccino、[4]Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk。[5]虽然它以前是由Facebook开发的,但它现在是Apache软件基金会的开源项目了。该实现被描述在2007年4月的一篇由Facebook发表的技术论文中,该论文现由Apache掌管

准备工作

  1. 安装:打开terminal,执行brew install thrift(通过brew info thrift查看一下版本,要和项目中pom文件版本一致)

image.png 2. 项目依赖:

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

Thrift使用

  1. 创建一个thrift文件:
namespace java service.demo
service Hello{
    string helloString(1:string para)
}
  1. terminal进入hello.thrift文件目录,执行生成指令:

thrift -r -gen java Hello.thrift

3.执行之后会多出一个gen目录,里面包括生成的Hello.java文件:

image.png

里面包括接口定义Hello.Iface,服务底层通信细节,客户端调用逻辑Hello.Client和服务端处理逻辑Hello.processor(这些都是thrift帮助我们生成好了的)

4.创建Iface借口的实现类,增加实现逻辑

import org.apache.thrift.TException;

public class HelloServiceImpl implements Hello.Iface{

    @Override
    public String helloString(String para) throws TException {
        return "result:" + para;
    }
}

5.创建服务端server,将实现类作为hanlder传递给Thrift服务器(processor)

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;

public class HelloServiceServer {
    /**
     * 启动thrift服务器
     */
    public static void main(String[] args) {
        System.out.println("服务端开启....");
        TProcessor tProcessor = new Hello.Processor<Hello.Iface>(new HelloServiceImpl());
        //简单的单线程服务模型
        try {
            TServerSocket serverTransport = new TServerSocket(9898);
            TServer.Args tArgs = new TServer.Args(serverTransport);
            tArgs.processor(tProcessor);
            tArgs.protocolFactory(new TBinaryProtocol.Factory());
            TServer server = new TSimpleServer(tArgs);
            server.serve();
        } catch (TTransportException e) {
            throw new RuntimeException(e);
        }

    }
}

6.创建客户端client,和server监听同样的端口


import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

public class HelloServiceClient {

    public static void main(String[] args) {
        System.out.println("客户端启动....");
        TTransport transport = null;
        try {
            transport = new TSocket("localhost", 9898, 30000);
            // 协议要和服务端一致
            TProtocol protocol = new TBinaryProtocol(transport);
            Hello.Client client = new Hello.Client(protocol);
            transport.open();
            String result = client.helloString("哈哈");
            System.out.println(result);
            Thread.sleep(300000);
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }
}

7.验证

先启动服务端,在启动客户端,客户端console会打印:result:哈哈

RPC & Thrift工作流程

RPC框架本质就是封装除了code部分外的全部其他step,使得开发变得更容易

image.png

如上图,Thrift分层(C/S架构),其中Tprotocal是用于数据类型解析的,Ttransport是用于以字节流的方式接收和发送消息体。

Thrift数据类型

基础类型

  • bool:布尔值,true 或 false,对应 Java 的 boolean

  • byte:8 位有符号整数,对应 Java 的 byte

  • i16:16 位有符号整数,对应 Java 的 short

  • i32:32 位有符号整数,对应 Java 的 int

  • i64:64 位有符号整数,对应 Java 的 long

  • double:64 位浮点数,对应 Java 的 double

  • string:utf-8编码的字符串,对应 Java 的 String

结构体类型

  • struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean

容器类型

  • list:对应 Java 的 ArrayList
  • set:对应 Java 的 HashSet
  • map:对应 Java 的 HashMap

异常类型

  • exception:对应 Java 的 Exception

服务类型

  • service:对应服务的类

枚举类型

enum Color{

    RED,

    BLUE   }

命名空间

可以理解成java中的packet,用于避免一些代码冲突,每种语言都有属于自己的命名空间的方式,比如java语言,就可以使用java语言的格式

namespace java com.DQicy.project

Thrift数据解析协议(TProtocal)

Thrift数据传输分为二进制传输和文本传输,二进制传输在传输速率和节省带宽上更有优势。

  • TBinaryProtocol:使用二进制编码格式传输,是thrift的默认传输协议
  • TCompactProtocol:使用压缩格式传输
  • TJSONProtocol :使用JSON格式传输
  • TDebugProtocol – 使用易懂可读的文本格式进行传输,以便于debug
  • TSimpleJSONProtocol – 提供JSON只写的协议,适用于通过脚本语言解析

Thrift传输格式(TTransport)

Thrift传输格式分为客户端和服务端。

  • TSocket:阻塞式IO的Transport实现,用在客户端.
  • TServerSocket:非阻塞式Socket,用于服务器端,用于监听TSocket.
  • TNonblockingSocket:非阻塞式IO的实现
  • TMemoryInputTransport: 封装了一个字节数组byte[]来做输入流的封装
  • TFramedTransport: 同样使用非阻塞方式,按块的大小进行传输,输入流封装了   TMemoryInputTransport

Thrift服务类型(TServer)

Thrift服务类型分为阻塞和非阻塞式。

TSimpleServer

   只有一个工作线程,循环监听传入的请求并进行处理,处理完成后才能接收下一个请求,是一种阻塞IO。效率较低。

  TNonblockingServer

  非阻塞IO实现IO多路复用,可以同时监听多个Socket。但业务处理仍然采用单线程,效率不高,多个请求仍然需要排队处理。

  TThreadPoolServer

通过线程池进行处理,主线程只负责accept请求(监听Socket)。有新请求到来时,会拉起一个线程进行处理,效率较高。一旦并发量很大的时候(超过线程池数量),后面来的请求也只能排队等待。

  TThreadedSelectorServer

  内部有一个专门监听Socket的线程 + 多个处理线程 + 负载均衡线程(专门负责决定将Socket进行分配)。
 

参考文章

www.cnblogs.com/fingerboy/p… juejin.cn/post/684490… www.cnblogs.com/fingerboy/p…