微服务通讯:RPC框架和thrift

背景

要看4个thrift接口。thrift是一种RPC框架,先看一下怎么一回事。

与我做的这层的关系

API这层其实是两端服务,一个是作为客户端调用后端服务,一个是作为服务端为前端提供接口。在调用后端服务这一端上面,95%以上的接口都是RPC,为前端提供接口这部分,如果是native调用,那么多为http,如果是H5,RPC调用越来越多了。(关于native和H5,请参考这篇和这篇,这里不做详细介绍)

微服务通讯

从通讯模式角度考虑

1对1 1对多
同步 请求响应模式,最常见
异步 通知/请求异步响应 发布订阅/发布异步响应

从通讯协议角度考虑:

  • REST
  • RPC
  • MQ

RPC框架

选择RPC框架考虑的东西:

  1. I/O(NIO/BIO),线程调度模型(多线程/调度方式)

  2. 序列化方式(很影响RPC通讯效率):

    1. 可读的:

      1. XML: e.g. webservice, soap

      2. JSON, e.g. fastjson, json-rpc

    2. binary: e.g. thrift, JDK自带的序列化方式

  3. 是否需要服务治理: e.g. 部署,HA,etc.

用的多的RPC框架包括Dubbo, Thrift, gRPC

Thrift原理

框架如下图:(source: Thrift Wikipedia)

architect.png

Thrift RPC调用过程:

  1. 客户端调用本地代理

  2. 本地代理对客户端的请求进行序列化并发起网络请求,等待返回结果

  3. 服务端也有个代理,它接受到网络请求之后会对请求参数进行反序列化,并对真实server application方法进行调用,对返回结果进行序列化并发出网络请求返回结果

  4. 客户端代理接受到返回结果后对返回结果进行反序列化并将结果交给客户端发起调用的方法

可以看出很重要的一个地方: client application和client,server application和server,都是分开的;也就是说,真正的client和server都是有代理的(两个橙色的service client)

Protocol和Transport层

从上图看出,除开input code这个自己写的代码部分,和Generated code这个thrift框架生成的代码,下面还有2层: TProtocol和TTransport: 实际上这就是RPC中最重要的两层(TProtocol和TTransport中的T前缀代表Thrift)。一旦这两层具体方式确定了,那么就可以进行RPC通讯了。

  1. Protocol: 序列化协议:Client和Server两端需要遵循相同的序列化协议,才能进行RPC通讯。支持的protocol有

    1. TBinaryProtocol

    2. TCompactProtocol

    3. TJSONProtocol

    4. TSimpleJSONProtocol

  2. Transport: 传输层:如何传输序列化字节流。支持的传输方式有

    1. TSimpleFileTransport

    2. TFramedTransport

    3. TMemoryTransport

    4. TSocket

    5. TZlibTransport

  3. 补充一下:Server也有几种: TNonblockingServer, TSimpleServer, TThreadedServer, TThreadPoolServer

Thrift框架使用

t.png

如果不看Thrift的原理而只是看client和server之间进行RPC通讯的过程,那么流程如下:

所以用这个框架分3步:

  1. IDL语法写demo.thrift文件

  2. thrift --gen 生成接口

  3. 有了接口,自己实现这个接口

Demo:

Thrift文件:

service Calculator extends shared.SharedService {

  /**
   * A method definition looks like C code. It has a return type, arguments,
   * and optionally a list of exceptions that it may throw. Note that argument
   * lists and exception lists are specified using the exact same syntax as
   * field lists in struct or exception definitions.
   */
   void ping(),

   i32 add(1:i32 num1, 2:i32 num2),

   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),

   /**
    * This method has a oneway modifier. That means the client only makes
    * a request and does not listen for any response at all. Oneway methods
    * must be void.
    */
   oneway void zip()

}

Python client:

def main():
    # socket传输,后面用buffer封装socket,协议用binary,上层用client封装,然后就可以调用了
    transport = TSocket.TSocket('localhost', 9090)

    # Buffering is critical. Raw sockets are very slow
    transport = TTransport.TBufferedTransport(transport)

    # Wrap in a protocol
    protocol = TBinaryProtocol.TBinaryProtocol(transport)

    # Create a client to use the protocol encoder
    client = Calculator.Client(protocol)

    # Connect!
    transport.open()

    client.ping()
    print('ping()')

    sum_ = client.add(1, 1)

Java Server (注意Calculator.java这个自动生成的接口文件没写)

// initialize server
try {
      TServerTransport serverTransport = new TServerSocket(9090);
      TServer server = new TSimpleServer(new Args(serverTransport).processor(processor));

      // Use this for a multithreaded server
      // TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor));

      System.out.println("Starting the simple server...");
      server.serve();
    } catch (Exception e) {
      e.printStackTrace();
    }
// 自定义实现类
public class CalculatorHandler implements Calculator.Iface {
        // ...
}

参考

  1. https://en.wikipedia.org/wiki/Apache_Thrift

  2. https://thrift.apache.org/

  3. https://coding.imooc.com/learn/list/198.html

  4. https://sunchao.github.io/posts/2015-09-22-understanding-how-thrift-works.html

你可能感兴趣的:(微服务通讯:RPC框架和thrift)