美文网首页netty学习
Apache Thrift 支持异构语言之间的调用

Apache Thrift 支持异构语言之间的调用

作者: 二月_春风 | 来源:发表于2017-10-17 23:36 被阅读69次

    前言

    之前介绍了Apache Thrift的快速入门,我们使用java作为客户端,使用java作为服务器端Apache Thrift及其入门,我们知道RPC框架的一个基本特征就是支持异构语言之间的调用,本篇博客介绍异构语言之间的调用。

    Apache Thrift allows you to define data types and service interfaces in a simple definition file. Taking that file as input, the compiler generates code to be used to easily build RPC clients and servers that communicate seamlessly across programming languages. Instead of writing a load of boilerplate code to serialize and transport your objects and invoke remote methods, you can get right down to business.

    Apache Thrift可以在文件中定义数据类型和服务接口。编译器根据idl(接口定义文件)文件可以生成消息传输类型的Message对象,也生成RPC语言网络传输的代码,使得我们可以跨语言跨平台的调用服务。

    Python作为Client,Java作为Server

    idl文件(接口描述文件),定义了结构体(struct),异常(exception)和服务(service):

    namespace java thrift.generated
    namespace py py.thrift.generated
    
    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 dateException),
    
        void savePerson(1: required Person person) throws (1: DataException dataException)
    }
    

    使用thrift编译器生成编译文件

    thrift --gen java src/thrift/data.thrift
    thrift --gen py src/thrift/data.thrift 
    
    java生成的代码 python生成的代码

    加入Thrift依赖,pom文件:

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

    python依赖要自己编译一下官方的包下载地址,到解压后的lib目录下进入py目录下进行编译得到Apache Thrift 项目中所使用的python库(lib目录下有Apache Thrift 支持的各种语言的库):

    ➜  py  cd /Users/naeshihiroshi/study/studySummarize/netty/thrift-0.10.0/lib/py
    ➜  py  ll
    

    安装python相应的包

    ➜  py  sudo python setup.py install
    

    生成的依赖位于/Library/Python/2.7/site-packages/

    ➜  py cd /Library/Python/2.7/site-packages/
    ➜  site-packages ll
    total 480
    -rwxr-xr-x  1 root  wheel   157B  7 31  2016 Extras.pth
    -rw-r--r--  1 root  wheel   119B  7 31  2016 README
    -rw-r--r--  1 root  wheel   263B  6 17 19:31 easy-install.pth
    drwxr-xr-x  6 root  wheel   204B  6 17 19:31 six-1.10.0-py2.7.egg
    -rw-r--r--  1 root  wheel   235K  6 17 19:31 thrift-0.10.0-py2.7-macosx-10.12-intel.egg
    

    Python作为Client,Java作为Server

    Java Server

    编写java服务器端实现代码(一般服务器端要编写一个业务代码实现供客户端调用,还有一个服务代码):

    public class PersonServiceImpl implements PersonService.Iface{
    
        @Override
        public Person getPersonByUsername(String username) throws DataException, TException {
            System.out.println("Got client Param:" + username);
    
            Person person = new Person();
            person.setUsername(username);
            person.setAge(32);
            person.setMarried(true);
    
            return person;
        }
    
        @Override
        public void savePerson(Person person) throws DataException, TException {
            System.out.println("Got Client Param: ");
    
            System.out.println(person.getUsername());
            System.out.println(person.getAge());
            System.out.println(person.isMarried());
        }
    }
    

    服务端:

    public class ThriftServer {
        public static void main(String[] args) throws Exception{
    
            TNonblockingServerSocket socket = new TNonblockingServerSocket(8899);
            THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4);
            PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
    
            //表示协议层次(压缩协议)
            arg.protocolFactory(new TCompactProtocol.Factory());
            //表示传输层次
            arg.transportFactory(new TFramedTransport.Factory());
            arg.processorFactory(new TProcessorFactory(processor));
    
            //半同步半异步的server
            TServer server = new THsHaServer(arg);
    
            System.out.println("Thrift Server started!");
    
            server.serve();
        }
    }
    

    python的客户端:

    将上面自动生成的代码拷贝到python项目中,

    python生成的代码

    py_client.py代码:

    # _*_ coding:utf-8 _*_
    __author__ = '作者'
    
    # 导入thrift的包
    from py.thrift.generated import PersonService
    from py.thrift.generated import ttypes
    
    from thrift import Thrift
    from thrift.transport import TSocket
    from thrift.transport import TTransport
    from thrift.protocol import TCompactProtocol
    import sys
    
    # 解决中文编码问题
    reload(sys)
    sys.setdefaultencoding('utf8')
    
    try:
        tSocket = TSocket.TSocket('localhost',8899)
        tSocket.setTimeout(600)
    
        # 与java服务器一样使用相同的传输协议,数据的传输方式,服务模型
        transport = TTransport.TFramedTransport(tSocket)
        protocal = TCompactProtocol.TCompactProtocol(transport)
        client = PersonService.Client(protocal)
    
        transport.open()
    
        # 调用getPersonByUsername方法返回一个person对象
        person = client.getPersonByUsername("张三")
    
        print person.username
        print  person.age
        print person.married
    
        print '--------------'
    
        newPerson = ttypes.Person()
        newPerson.username ='lisi'
        newPerson.age = 30
        newPerson.married =True
    
        client.savePerson(newPerson)
    
        transport.close()
    
    
    except Thrift.TException,tx:
        print '%s' % tx.message
    

    启动java服务器端和python客户端,
    python客户端打印:

    张三
    32
    True
    --------------
    

    java服务器端的打印结果:

    Got client Param:张三
    Got Client Param: 
    lisi
    30
    true
    

    java客户端,python服务器端

    服务器端要编写服务接口的实现和服务器端代码:

    # _*_ coding:utf-8 _*_
    __author__ = '作者'
    
    # 导入包
    from py.thrift.generated import ttypes
    
    #处理器
    class PersonHandler :
    
        def getPersonByUsername(self,username):
            print "Got client param: "+username
    
            person = ttypes.Person()
            person.username = username
            person.age = 20
            person.married = False
    
            return person
    
        def savePerson(self,person):
            print "Got client param: "
    
            print person.username
            print person.age
            print person.married
    

    python 服务器端代码

    # _*_ coding:utf-8 _*_
    __author__ = '作者'
    
    # 导入包
    from py.thrift.generated import PersonService
    from PersonHandler import PersonHandler
    
    from thrift import Thrift
    from thrift.transport import TSocket
    from thrift.transport import TTransport
    from thrift.protocol import TCompactProtocol
    from thrift.server import TServer
    
    try:
        personHandler = PersonHandler()
        processor = PersonService.Processor(personHandler)
    
        serverSocket = TSocket.TServerSocket(port=8899)
        # 传输方式工厂
        transportFactory = TTransport.TFramedTransportFactory()
        # 协议工厂
        protocolFactory = TCompactProtocol.TCompactProtocolFactory()
    
        server = TServer.TSimpleServer(processor,serverSocket,transportFactory,protocolFactory)
        server.serve()
    
    except Thrift.TException, ex:
        print '%s' % ex.message
    

    java客户端:

    import org.apache.thrift.protocol.TCompactProtocol;
    import org.apache.thrift.protocol.TProtocol;
    import org.apache.thrift.transport.TFastFramedTransport;
    import org.apache.thrift.transport.TSocket;
    import org.apache.thrift.transport.TTransport;
    import thrift.generated.Person;
    import thrift.generated.PersonService;
    
    //服务端的协议和客户端的协议要一致
    public class ThriftClient {
        public static void main(String[] args) {
    
            TTransport tTransport = new TFastFramedTransport(new TSocket("localhost",8899),600);
            TProtocol tProtocol = new TCompactProtocol(tTransport);
            PersonService.Client client = new PersonService.Client(tProtocol);
    
            try{
                tTransport.open();
    
                Person person = client.getPersonByUsername("张三");
    
                System.out.println(person.getUsername());
                System.out.println(person.getAge());
                System.out.println(person.isMarried());
    
                System.out.println("............");
    
                Person person2 = new Person();
    
                person2.setUsername("李四");
                person2.setAge(30);
                person2.setMarried(true);
    
                client.savePerson(person2);
            }catch (Exception ex){
                throw new  RuntimeException(ex.getMessage(),ex);
            }finally {
                tTransport.close();
            }
        }
    }
    

    启动python服务器端和java客户端,python服务器端控制台打印:

    Got client param: 张三
    Got client param: 
    李四
    30
    True
    

    java客户端代码:

    Received 1
    张三
    20
    false
    ............
    Received 2
    

    总结

    比较一下Apache thrift与Protobuf之间的区别:

    单纯的只看Protobuf本身,Protobuf只是一个序列化与反序列化的一个库而已,而Protobuf并没有提供网络传输载体,我们之前的netty与Protobuf结合就是使用netty作为网络传输组件(也就是说netty作为RPC框架中的Transport(传输方式)组件),而Protobuf作为Protocal(传输协议)组件。而Apache thrift本身就是一个跨语言的RPC框架,可以直接通过thrift就可以客户端与服务器之间的通信(通过idl文件生成的代码即提供了传输协议,也提供了网络传输方式)。基于Protobuf没有提供网络传输组件,google又推出了自己的RPC框架GRPC,GRPC是基于Protobuf 3.0版本,基于.proto文件不仅能生成序列化反序列化程序代码,也可以生成传输层次的代码。

    相关文章

      网友评论

        本文标题:Apache Thrift 支持异构语言之间的调用

        本文链接:https://www.haomeiwen.com/subject/znrwqxtx.html