thrift学习

一、RPC 简介

  • RPC(Remote Procedure Call,即:远程过程调用):调用远程计算机上的服务
  • RPC 需要解决如下几个问题:
    • 解决通讯问题:建立服务器与客户端的 TCP 或 HTTP 连接,所有服务器与客户端的数据交互都是在这个连接里进行

      TCP 是传输层协议,HTTP 是应用层协议,而传输层较应用层更加底层,在数据传输方面,越底层越快
      即 TCP 一般比 HTTP 快,比如:早期 Web Service 基于 HTTP 协议的 RPC 具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC

    • 解决服务发现问题:A 服务如何告诉 B 服务它提供了什么样的能力,一般都会有注册中心等做服务发现,目前流行的都是使用 zookeeper 做注册中心进行服务发现
    • 解决传输问题:服务调用需要将底层数据通过 TCP 或 HTTP 进行传输,则如何高效的序列化以及反序列化就是需要解决的问题

      市面上有很多优秀的序列化框架,比如:Protobuf、Kryo、Hessian、Jackson 等,它们可以取代 Java 默认的序列化,从而提供更高效的性能

二、Thrift 架构

在这里插入图片描述
如图:

  • 黄色部分是用户实现的业务逻辑

  • 褐色部分是根据 Thrift 定义的服务接口描述文件生成的客户端和服务器端代码框架

    Processor 类主要是开发 Thrift 服务器程序时使用,该类内部定义了一个map,保存了所有函数名到函数对象的映射,一旦Thrift接到一个函数调用请求,就从该map中根据函数名字找到该函数的函数对象,然后执行

  • 红色部分是根据 Thrift 文件生成代码实现数据的读写操作

  • TProtocol 传输协议:用来描述 what is transmitted

    Thrift 可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本和二进制传输协议

    • 为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议

    例如:实现了TProtocol接口的TBinaryProtocol类,对于readDouble()函数就是按照二进制的方式读取出一个Double类型的数据。同理,写入时也会按照对应的协议进行写入操作


    目前Thrift支持的协议有这些种:

    • TBinaryProtocol:二进制编码格式进行数据传输
    • TCompactProtocol:高效率的、密集的二进制编码格式进行数据传输
    • TJSONProtocol:使用 JSON 的数据编码协议进行数据传输
    • TSimpleJSONProtocol:只提供 JSON 只写的协议,适用于通过脚本语言解析
  • TTransport 传输层:用来描述how to transmitted

    传输层实际上可以理解为对 I/O 层操作的一个封装,更直观的理解为它封装了一个socket,不同的实现类有不同的封装方式,常见的一般为阻塞式,同步非阻塞式和异步非阻塞,目前支持的有如下几种:

    • TFramedTransport:使用非阻塞方式,按块的大小进行传输
    • TSocket:使用阻塞式 IO 进行传输
    • TNonblockingTransport:使用非阻塞方式,用于构建异步客户端

七层网络模型:
在这里插入图片描述在这里插入图片描述
从时序图知:

  • server 端本身有使用线程池,这样是为了能同时提供多个连接对外服务
  • Thrift 的 server 端还提供了异步非阻塞 I/O 的server场景,不需要占用链接,等响应之后,找到之前的链接在反馈给客户端,这样一来能大大的提高并发

三、Thrift 时序图

1、server 端时序图

在这里插入图片描述

2、client 端时序图

在这里插入图片描述

四、Thrift 优势

1、序列化后的数据大小

在这里插入图片描述

2、序列化的耗时及 CPU 占用

在这里插入图片描述

五、Thrift 数据类型

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:未知编码文本或二进制字符串,对应 Java 的 String
  • 结构体类型 struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean
  • 容器类型:
    • list:对应 Java 的 ArrayList
    • set:对应 Java 的 HashSet
    • map:对应 Java 的 HashMap
  • 异常类型 exception:对应 Java 的 Exception
  • 服务类型 service:对应服务的类

六、案例

1、同步案例

Hello.thrift

 namespace java service.demo 
 
 service Hello{ 
  	string helloString(1:string para) 
  	i32 helloInt(1:i32 para) 
  	bool helloBoolean(1:bool para) 
  	void helloVoid() 
  	string helloNull() 
 }

创建 HelloServiceImpl.java 文件并实现 Hello.java 文件中的 Hello.Iface 接口:

 package service.demo; 
 
 import org.apache.thrift.TException; 
 
 public class HelloServiceImpl implements Hello.Iface { 
    @Override 
    public boolean helloBoolean(boolean para) throws TException { 
        return para; 
    } 
    
    @Override 
    public int helloInt(int para) throws TException { 
        try { 
            Thread.sleep(20000); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        return para; 
    } 
    
    @Override 
    public String helloNull() throws TException { 
        return null; 
    } 
    
    @Override 
    public String helloString(String para) throws TException { 
        return para; 
    } 
    
    @Override 
    public void helloVoid() throws TException { 
        System.out.println("Hello World"); 
    } 
}

创建服务器端实现代码,将 HelloServiceImpl 作为具体的处理器传递给 Thrift 服务器:

 package service.server; 
 
 import org.apache.thrift.TProcessor; 
 import org.apache.thrift.protocol.TBinaryProtocol; 
 import org.apache.thrift.protocol.TBinaryProtocol.Factory; 
 import org.apache.thrift.server.TServer; 
 import org.apache.thrift.server.TThreadPoolServer; 
 import org.apache.thrift.transport.TServerSocket; 
 import org.apache.thrift.transport.TTransportException; 
 import service.demo.Hello; 
 import service.demo.HelloServiceImpl; 

 public class HelloServiceServer { 
    //启动 Thrift 服务器
    public static void main(String[] args) { 
        try { 
            // 设置服务端口为 7911 
            TServerSocket serverTransport = new TServerSocket(7911); 
            // 设置协议工厂为 TBinaryProtocol.Factory 
            Factory proFactory = new TBinaryProtocol.Factory(); 
            // 关联处理器与 Hello 服务的实现
            TProcessor processor = new Hello.Processor(new HelloServiceImpl()); 
            TServer server = new TThreadPoolServer(processor, serverTransport, 
                    proFactory); 
            System.out.println("Start server on port 7911..."); 
            server.serve(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } 
    } 
}

创建客户端实现代码,调用 Hello.client 访问服务端的逻辑实现:

 package service.client; 
 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; 
 import service.demo.Hello; 

 public class HelloServiceClient { 
	//调用 Hello 服务 
    public static void main(String[] args) { 
        try { 
            // 设置调用的服务地址为本地,端口为 7911 
            TTransport transport = new TSocket("localhost", 7911); 
            transport.open(); 
            // 设置传输协议为 TBinaryProtocol 
            TProtocol protocol = new TBinaryProtocol(transport); 
            Hello.Client client = new Hello.Client(protocol); 
            // 调用服务的 helloVoid 方法
            client.helloVoid(); 
            transport.close(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } catch (TException e) { 
            e.printStackTrace(); 
        } 
    } 
}

2、异步案例

Thrift 提供非阻塞的调用方式,可构建异步客户端:Thrift 提供了新的类 TAsyncClientManager 用于管理客户端的请求,在一个线程上追踪请求和响应,同时通过接口 AsyncClient 传递标准的参数和 callback 对象,服务调用完成后,callback 提供了处理调用结果和异常的方法


CallBack 的实现:MethodCallback.java

 package service.callback; 
 
 import org.apache.thrift.async.AsyncMethodCallback; 

 public class MethodCallback implements AsyncMethodCallback { 
    Object response = null; 

    public Object getResult() { 
        // 返回结果值
        return this.response; 
    } 

    // 处理服务返回的结果值
    @Override 
    public void onComplete(Object response) { 
        this.response = response; 
    } 
    
    // 处理调用服务过程中出现的异常
    @Override 
    public void onError(Throwable throwable) { 

    } 
}

创建非阻塞服务器端实现代码,将 HelloServiceImpl 作为具体的处理器传递给异步 Thrift 服务器:

 package service.server; 
 
 import org.apache.thrift.server.TNonblockingServer; 
 import org.apache.thrift.server.TServer; 
 import org.apache.thrift.transport.TNonblockingServerSocket; 
 import org.apache.thrift.transport.TNonblockingServerTransport; 
 import org.apache.thrift.transport.TTransportException; 
 import service.demo.Hello; 
 import service.demo.HelloServiceImpl; 

 public class HelloServiceAsyncServer { 
    //启动 Thrift 异步服务器 
    public static void main(String[] args) { 
        TNonblockingServerTransport serverTransport; 
        try { 
            serverTransport = new TNonblockingServerSocket(10005); 
            Hello.Processor processor = new Hello.Processor(new HelloServiceImpl()); 
            TServer server = new TNonblockingServer(processor, serverTransport); 
            System.out.println("Start server on port 10005 ..."); 
            server.serve(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } 
    } 
}

创建异步客户端实现代码,调用 Hello.AsyncClient 访问服务端的逻辑实现,将 MethodCallback 对象作为参数传入调用方法中:

 package service.client; 
 import java.io.IOException; 
 import org.apache.thrift.async.AsyncMethodCallback; 
 import org.apache.thrift.async.TAsyncClientManager; 
 import org.apache.thrift.protocol.TBinaryProtocol; 
 import org.apache.thrift.protocol.TProtocolFactory; 
 import org.apache.thrift.transport.TNonblockingSocket; 
 import org.apache.thrift.transport.TNonblockingTransport; 
 import service.callback.MethodCallback; 
 import service.demo.Hello; 

 public class HelloServiceAsyncClient { 
    //调用 Hello 服务
    public static void main(String[] args) throws Exception { 
        try { 
            TAsyncClientManager clientManager = new TAsyncClientManager(); 
            TNonblockingTransport transport = new TNonblockingSocket("localhost", 10005); 
            TProtocolFactory protocol = new TBinaryProtocol.Factory(); 
            Hello.AsyncClient asyncClient = new Hello.AsyncClient(protocol, clientManager, transport); 
            System.out.println("Client calls ....."); 
            MethodCallback callBack = new MethodCallback(); 
            asyncClient.helloString("Hello World", callBack); 
            Object res = callBack.getResult(); 
            //等待服务调用后的返回结果
            while (res == null) { 
                res = callBack.getResult(); //异步客户端实现同步效果代码段
            } 
            System.out.println(((Hello.AsyncClient.helloString_call) res).getResult()); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
    } 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值