Netty章节十一:Thrift多语言案例

本案例使用JavaPython两种语言进行互相调用测试Thrift

IDL文件(.thrift文件)

#定义命名空间: namespace 语言名 路径
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
struct Person{
    1:optional String username,
    2:optional int age,
    3:optional boolean married
}
/*说明:
    唯一标记数:修饰符 数据类型 属性名
  修饰符: 默认就是optional
       required 必须的,必须存在,必须赋值
       optional 可选的,可以不使用
*/

#定义一个异常,数据传递时/方法调用是可能出现的异常 关键字exception
#服务端如果出现异常的话直接抛给客户端,让客户端catch处理
exception DataException{
    1:optional String message;
    2:optional String callStack;
    3:optional String date;
}

#定义服务接口 关键字service
#定义一系列方法,就是客户端于服务端进行交互所调用的方法,具体实现由服务端完成
service PersonService{
    //返回值 方法名(参数) throws (异常)
    Person getPersonByUsername(1:required String username) throws (1:DataException dataException),
    void savePersion(1:required Person person) throws (1:DataException dataException)
}

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

生成Java代码

thrift --gen java src/thrift/data.thrift

生成Python代码

thrift --gen py src/thrift/data.thrift

Java所有实现

引入依赖

使用包管理器使用Gradle

org.apache.thrift:libthrift:0.13.0

编写接口实现类

实际开发中放在服务端

//thrift生成的接口文件的实现类
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().setUsername(username)
                .setAge(18).setMarried(false);
        return person;
    }

    @Override
    public void savePersion(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());
    }
}

服务器端

/*
	服务端  实现client远程调用Server方法,这个结果说明了方法还是在Server端。
 	Client调用方法,方法走了一遍,但其实还是在server端走,最后的结果通过网络传输到Client
   	方法体里的打印值,还是在Server端打印。只有Client端自己打印的值,才会出现在Client中。
*/
public class ThriftServer {
    public static void main(String[] args) throws Exception {
        //非阻塞的socket  绑定端口号8899,表示客户端与服务端建立的连接
        TNonblockingServerSocket socket = new TNonblockingServerSocket(8899);
        //高可用的server,并设置工作线程的最大值和最小值  arg作用就是构建一系列信息
        THsHaServer.Args arg = new THsHaServer.Args(socket)
                .minWorkerThreads(2).maxWorkerThreads(4);
        //设置处理器(Processor),将实现接口作为泛型,因为客户端那边调用的就是这个,
        //所以后面传输的也是这个对象new PersonServiceImpl()
        PersonService.Processor<PersonServiceImpl> processor =
                new PersonService.Processor<>(new PersonServiceImpl());

        //设置协议工厂
        //协议层:表示数据传输格式,这里TCompactProtocol(二进制压缩协议)表示压缩格式,速率很快
        arg.protocolFactory(new TCompactProtocol.Factory());
        //传输层:表示数据的传输方式,这里TFramedTransport是以frame为单位传输,非阻塞式传输
        arg.transportFactory(new TFramedTransport.Factory());
        arg.processorFactory(new TProcessorFactory(processor));

        //启动server  支持的服务模型:THsHaServer半同步,半异步Server
        TServer server = new THsHaServer(arg);

        System.out.println("Thrift Server Started!");

        //一个异步死循环,永远不会退出
        server.serve();
    }
}

客户端

public class ThriftClient {
    public static void main(String[] args) {
        //传输层/传输协议:要和服务端的传输协议保持一致,设置地址,端口号,和超时时间,是一个连接/socket
        TTransport transport = new TFramedTransport(
                new TSocket("localhost",8899),600);
        //协议层设置,设置数据传输格式,传入传输层,要与服务端保持一致
        TProtocol protocol = new TCompactProtocol(transport);
        //获得thrift自动生成的Client对象,可以与服务端进行远程调用的对象
        PersonService.Client client = new PersonService.Client(protocol);
        try {
            //打开socket
            transport.open();
            //关键:client本来就没有getPersonByUsername方法,这是通过网络传输调用
            Person person = client.getPersonByUsername("星空");

            System.out.println(person.getUsername());
            System.out.println(person.getAge());
            //对于boolean型,不是get,而是is开头.但是set都一样
            System.out.println(person.isMarried());

            System.out.println("------------------");

            Person person1 = new Person().setUsername("测试")
                .setAge(18).setMarried(false);
            client.savePersion(person1);

        }catch (Exception e){
            throw new RuntimeException(e.getMessage(),e);
        }finally {
            //最后关闭transport
            transport.close();
        }
    }
}

Python所有实现

本项目的Python代码基于Python2

pip安装

Archlinux安装pip

sudo pacman -S python-pip

python-pip包会吧当前系统存在的python版本对应的pip都装上

使用pip或pip2安装thrift源码

pip2 install thrift

如果是python专用软件则会自动被扫描到thrift,如果使用vscode指定pip安装的文件目录,会更容易识别

例如:

 "python.autoComplete.extraPaths": [
        "/home/sakura/.local/lib/python2.7/site-packages/"  //这里填自己的
  ],

查看pip安装包安装路经

如果不知道pip安装的位置可以运行python2/python3,然后import要查看路径的包,再输入包的名称,即可查看安装路径了

❯ python2                                                               
Python 2.7.18 (default, Apr 23 2020, 22:32:06) 
[GCC 9.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import thrift
>>> thrift
<module 'thrift' from '/home/sakura/.local/lib/python2.7/site-packages/thrift/__init__.pyc'>

编写接口实现类

# -*- coding:utf-8 -*-

from py.thrift.generated import *

class PersonServiceImpl:
    def getPersonByUsername(self,username):
        print "Python Got client param:" + username

        person = ttypes.Person()
        person.username = username
        person.age = 18
        person.married = False

        return person
    
    def savePersion(self,person):
        print "Python Got client param:"

        print person.username
        print person.age
        print person.married

服务端

# -*- coding: utf-8 -*-

from py.thrift.generated import PersonService
from PersonServiceImpl import PersonServiceImpl

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TCompactProtocol
from thrift.server import TServer
import sys

#修改字符编码 解决thrift无法传输中文问题
reload(sys)
sys.setdefaultencoding('utf-8')


try:
    personServiceHandler = PersonServiceImpl()
    #设置Processor
    processor = PersonService.Processor(personServiceHandler)
    #绑定端口号
    serverSocket = TSocket.TServerSocket(port=8899)
    #服务端使用Factory 客户端使用Transport
    transportFactory = TTransport.TFramedTransportFactory()
    #服务端使用Factory 客户端使用Protocol
    protocolFactory = TCompactProtocol.TCompactProtocolFactory()

    server = TServer.TSimpleServer(processor,serverSocket,transportFactory,protocolFactory)
    
    server.serve()

except Thrift.TException, ex:
    print '%s' % ex.message

客户端

# -*- coding: utf-8 -*-

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TCompactProtocol
import sys

#修改字符编码 解决thrift无法传输中文问题
reload(sys)
sys.setdefaultencoding('utf-8')

from py.thrift.generated import *
try:
    #设置连接 指定地址以及端口号
    tSocket = TSocket.TSocket('localhost',8899)
    #设置超时时间
    tSocket.setTimeout(600)
    #设置数据传输格式,设为TFramedTransport并传入tSocket
    transport = TTransport.TFramedTransport(tSocket)
    #设置数据的传输方式,设为TCompactProtocol并传入transport
    protocol = TCompactProtocol.TCompactProtocol(transport)
    #创建client 于服务端交互的接口
    client = PersonService.Client(protocol)
    #开启连接
    transport.open()

    #调用相应的方法
    person = client.getPersonByUsername("三木")

    print person.username
    print person.age
    print person.married

    print "------------"

    newPerson = ttypes.Person()
    newPerson.username = "星空"    
    newPerson.age = 18
    newPerson.married = True

    client.savePersion(newPerson)
    #关闭连接
    transport.close()
    

except Thrift.TException, tx:
    print '%s' % tx.message

测试

Python作为服务端,Java作为客户端
服务端打印:

Python Got client param:星空
Python Got client param:
测试
18
False

客户端打印:

星空
18
false
------------------
原文地址:https://www.cnblogs.com/mikisakura/p/12983570.html