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

前言

之前介绍了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 

《Apache Thrift 支持异构语言之间的调用》 java生成的代码
《Apache Thrift 支持异构语言之间的调用》 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

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

安装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项目中,

《Apache Thrift 支持异构语言之间的调用》 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文件不仅能生成序列化反序列化程序代码,也可以生成传输层次的代码。

    原文作者:二月_春风
    原文地址: https://www.jianshu.com/p/ccc608f569dd
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞