本案例使用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
------------------