• RPC框架之Thrift简单使用


    前言

    Thrift是一个可以跨平台,跨语言的RPC(远程过程调用)框架,通过IDL(接口描述语言)来定义数据类型和接口,相当于Protobuf和gRPC的结合体。Thrift最开始由Facebook开源,后来贡献给了Apache。

    下载编译器

    官网,这里我们下载windows版本的编译器thrift-0.16.0.exe

    定义thrift文件

    namespace java com.imooc.sourcecode.java.google.thrift.test1
    
    typedef i16 short
    typedef i32 int
    typedef i64 long
    typedef bool boolean
    typedef string String
    
    struct Person {
      1: optional String username
      2: optional int age
      3: optional boolean married
    }
    
    exception DataException {
      1: optional String message
      2: optional String callStack
      3: optional String date
    }
    
    service PersonService {
      Person getPersonByUsername(1: required String username) throws (1: DataException dataException),
      void savePerson(1: required Person person) throws (1: DataException dataException),
    }
    

    namespace 定义包名,类似ProtoBuf的java_package。
    typedef 定义类型的别名。
    optional 表示当前字段可选,非必填。
    1: optional String message 每个字段需要有一个唯一的号码,必须大于0。
    exception 定义异常,可以看做Java中的异常。
    service 定义服务,可以看做Java中的接口。
    struct 可以看做一个Java类,类似ProtoBuf的message。

    Java处理

    添加maven依赖

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

    根据thrift文件创建Java数据类型和接口代码

    thrift -out $DST_DIR --gen java $SRC_DIR/data.proto
    

    -out表示生成代码的路径,java表示生成Java语言的代码,实际命令为

    .\thrift-0.16.0.exe -out D:\java\code_resp\github_resp\source_code\src\main\java --gen java D:\java\code_resp\github_resp\source_code\src\main\java\com\imooc\sourcecode\java\google\thrift\test1\data.thrift
    

    -out值不要包含具体的包路径,thrift文件中已经配置了。

    序列化和反序列化

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import org.apache.thrift.TDeserializer;
    import org.apache.thrift.TSerializer;
    import org.apache.thrift.protocol.TCompactProtocol.Factory;
    
    public class TestSerialize {
    
      public static void main(String[] args) throws Exception {
        testSerialize();
        testDeserialize();
      }
    
      private static void testSerialize() throws Exception {
        Person person = new Person();
        person.setUsername("李四");
        person.setAge(23);
        person.setMarried(true);
        TSerializer serializer = new TSerializer(new Factory());
        byte[] bytes = serializer.serialize(person);
        new ByteArrayInputStream(bytes)
            .transferTo(new FileOutputStream("D:/testjar/thrift_java_serialize_person"));
      }
    
      private static void testDeserialize() throws Exception {
        TDeserializer deserializer = new TDeserializer(new Factory());
        Person person = new Person();
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        new FileInputStream("D:/testjar/thrift_java_serialize_person").transferTo(output);
        deserializer.deserialize(person, output.toByteArray());
        System.out.println(person);
      }
    }
    

    序列化和反序列化要使用相同的TProtocolFactory类型。

    服务器端代码

    import com.imooc.sourcecode.java.google.thrift.test1.PersonService.Processor;
    import org.apache.thrift.TException;
    import org.apache.thrift.TProcessorFactory;
    import org.apache.thrift.protocol.TCompactProtocol;
    import org.apache.thrift.server.THsHaServer;
    import org.apache.thrift.server.THsHaServer.Args;
    import org.apache.thrift.transport.TNonblockingServerSocket;
    import org.apache.thrift.transport.TTransportException;
    import org.apache.thrift.transport.layered.TFramedTransport;
    
    public class MyServer {
    
      public static void main(String[] args) throws TTransportException {
        TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(8883);
        THsHaServer.Args serverArgs = new Args(serverSocket).minWorkerThreads(2).maxWorkerThreads(4);
        Processor<PersonServiceImpl> processor = new Processor<>(new PersonServiceImpl());
        serverArgs.protocolFactory(new TCompactProtocol.Factory());
        serverArgs.transportFactory(new TFramedTransport.Factory());
        serverArgs.processorFactory(new TProcessorFactory(processor));
        System.out.println("Server start");
        THsHaServer server = new THsHaServer(serverArgs);
        server.serve();
      }
    
      public static class PersonServiceImpl implements PersonService.Iface {
    
        @Override
        public Person getPersonByUsername(String username) throws DataException, TException {
          System.out.println("param: " + username);
          Person person = new Person();
          person.setUsername(username);
          person.setAge(23);
          person.setMarried(true);
          return person;
        }
    
        @Override
        public void savePerson(Person person) throws DataException, TException {
          System.out.println("param: " + person);
          System.out.println(person);
        }
      }
    
    }
    

    定义PersonServiceImpl实现类,来处理客户端请求。

    客户端代码

    import com.imooc.sourcecode.java.google.thrift.test1.PersonService.Client;
    import org.apache.thrift.TException;
    import org.apache.thrift.protocol.TCompactProtocol;
    import org.apache.thrift.transport.TSocket;
    import org.apache.thrift.transport.layered.TFramedTransport;
    
    public class MyClient {
    
      public static void main(String[] args) throws TException {
        TFramedTransport transport = new TFramedTransport(new TSocket("localhost", 8883));
    
        TCompactProtocol protocol = new TCompactProtocol(transport);
        Client client = new Client(protocol);
        transport.open();
        Person queryPerson = client.getPersonByUsername("张三");
        System.out.println(queryPerson);
        Person savePerson = new Person();
        savePerson.setUsername("李四");
        savePerson.setAge(21);
        savePerson.setMarried(false);
        client.savePerson(savePerson);
        transport.close();
      }
    
    }
    

    可以看到,客户端调用服务器端接口就像调用本地方法一样简单。

    Python处理

    安装依赖

    pip install thrift
    

    PyCharm中可以这样安装

    根据thrift文件创建Python数据类型和接口代码

    thrift -out $DST_DIR --gen py $SRC_DIR/data.proto
    

    -out表示生成代码的路径,py表示生成Python语言的代码,实际命令为

    .\thrift-0.16.0.exe -out D:\java\code_resp\PycharmProjects\test_protobuf\testthrift --gen py D:\java\code_resp\PycharmProjects\test_protobuf\testthrift\data.thrift
    

    代码结构如下,data目录为生成的目录,包含数据类型和服务接口。

    序列化和反序列化

    from data import ttypes
    from thrift import TSerialization
    from thrift.protocol import TCompactProtocol
    
    def test_serialize():
        p = ttypes.Person()
        p.username = "小明"
        p.age = 27
        p.married = False
        bytes = TSerialization.serialize(p,TCompactProtocol.TCompactProtocolFactory())
        with open("D:/testjar/thrift_python_serialize_person", "wb") as f:
            f.write(bytes)
        return
    
    def test_deserialize():
        with open("D:/testjar/thrift_python_serialize_person", "rb") as rf:
            bytes_read = rf.read()
            p = ttypes.Person()
            TSerialization.deserialize(p, bytes_read, TCompactProtocol.TCompactProtocolFactory())
            print(p)
        return
    
    
    if __name__ == '__main__':
        test_deserialize()
        pass
    

    服务器端代码

    from PersonServiceImpl import PersonServiceImpl
    from data import PersonService
    from thrift.protocol import TCompactProtocol
    from thrift.server import TServer
    from thrift.transport import TSocket
    from thrift.transport import TTransport
    
    
    def test():
        # Make socket
        personServiceImpl = PersonServiceImpl()
        processor = PersonService.Processor(personServiceImpl)
        serverSocket = TSocket.TServerSocket(port=8883)
        transportFactory = TTransport.TFramedTransportFactory()
        protocolFactory = TCompactProtocol.TCompactProtocolFactory()
        server = TServer.TThreadPoolServer(processor, serverSocket, transportFactory, protocolFactory)
        server.serve()
        pass
    
    
    if __name__ == '__main__':
        test()
        pass
    

    客户端代码

    from thrift.transport import TSocket
    from thrift.transport import TTransport
    from thrift.protocol import TCompactProtocol
    from data import PersonService
    
    
    def test():
        # Make socket
        transport = TSocket.TSocket('localhost', 8883)
        # Buffering is critical. Raw sockets are very slow
        transport = TTransport.TFramedTransport(transport)
        # Wrap in a protocol
        protocol = TCompactProtocol.TCompactProtocol(transport)
        # Create a client to use the protocol encoder
        client = PersonService.Client(protocol)
        # Connect!
        transport.open()
        p = client.getPersonByUsername("李四")
        print(p)
        pass
    
    if __name__ == '__main__':
        test()
        pass
    

    总结

    更多Thrift相关用法,请查看官方文档

    参考

    Apache Thrift
    如何给老婆解释什么是RPC
    Thrift 简易入门与实战
    【Thrift】Thrift的那些服务模型
    Thrift(一):快速入门

  • 相关阅读:
    Ubuntu安装GTK+教程
    Qt 错误GL/gl.h: No such file or directory的解决方法
    Qt 解决Could not start process "make" qmake_all问题
    Feign解决服务之间调用传递token
    python闭包和装饰器
    python高阶函数
    ping 和 traceroute 的区别
    ICMP协议
    OSPF协议
    RIP协议
  • 原文地址:https://www.cnblogs.com/strongmore/p/16128157.html
Copyright © 2020-2023  润新知