前言
之前介紹了Apache Thrift的快速入門婶恼,我們使用java作為客戶端廊营,使用java作為服務(wù)器端Apache Thrift及其入門哼凯,我們知道RPC框架的一個基本特征就是支持異構(gòu)語言之間的調(diào)用锌妻,本篇博客介紹異構(gòu)語言之間的調(diào)用。
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可以在文件中定義數(shù)據(jù)類型和服務(wù)接口沸呐。編譯器根據(jù)idl(接口定義文件)文件可以生成消息傳輸類型的Message對象醇王,也生成RPC語言網(wǎng)絡(luò)傳輸?shù)拇a,使得我們可以跨語言跨平臺的調(diào)用服務(wù)垂谢。
Python作為Client厦画,Java作為Server
idl文件(接口描述文件),定義了結(jié)構(gòu)體(struct)滥朱,異常(exception)和服務(wù)(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
加入Thrift依賴,pom文件:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.10.0</version>
</dependency>
python依賴要自己編譯一下官方的包下載地址,到解壓后的lib目錄下進(jìn)入py目錄下進(jìn)行編譯得到Apache Thrift 項目中所使用的python庫(lib目錄下有Apache Thrift 支持的各種語言的庫):
? py cd /Users/naeshihiroshi/study/studySummarize/netty/thrift-0.10.0/lib/py
? py ll
安裝python相應(yīng)的包
? 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服務(wù)器端實現(xiàn)代碼(一般服務(wù)器端要編寫一個業(yè)務(wù)代碼實現(xiàn)供客戶端調(diào)用徙邻,還有一個服務(wù)代碼):
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());
}
}
服務(wù)端:
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());
//表示協(xié)議層次(壓縮協(xié)議)
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項目中,
py_client.py代碼:
# _*_ coding:utf-8 _*_
__author__ = '作者'
# 導(dǎo)入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服務(wù)器一樣使用相同的傳輸協(xié)議畸裳,數(shù)據(jù)的傳輸方式缰犁,服務(wù)模型
transport = TTransport.TFramedTransport(tSocket)
protocal = TCompactProtocol.TCompactProtocol(transport)
client = PersonService.Client(protocal)
transport.open()
# 調(diào)用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服務(wù)器端和python客戶端,
python客戶端打硬篮:
張三
32
True
--------------
java服務(wù)器端的打印結(jié)果:
Got client Param:張三
Got Client Param:
lisi
30
true
java客戶端帅容,python服務(wù)器端
服務(wù)器端要編寫服務(wù)接口的實現(xiàn)和服務(wù)器端代碼:
# _*_ coding:utf-8 _*_
__author__ = '作者'
# 導(dǎo)入包
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 服務(wù)器端代碼
# _*_ coding:utf-8 _*_
__author__ = '作者'
# 導(dǎo)入包
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()
# 協(xié)議工廠
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;
//服務(wù)端的協(xié)議和客戶端的協(xié)議要一致
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服務(wù)器端和java客戶端,python服務(wù)器端控制臺打游樯恕:
Got client param: 張三
Got client param:
李四
30
True
java客戶端代碼:
Received 1
張三
20
false
............
Received 2
總結(jié)
比較一下Apache thrift與Protobuf之間的區(qū)別:
單純的只看Protobuf本身并徘,Protobuf只是一個序列化與反序列化的一個庫而已,而Protobuf并沒有提供網(wǎng)絡(luò)傳輸載體扰魂,我們之前的netty與Protobuf結(jié)合就是使用netty作為網(wǎng)絡(luò)傳輸組件(也就是說netty作為RPC框架中的Transport(傳輸方式)組件)麦乞,而Protobuf作為Protocal(傳輸協(xié)議)組件。而Apache thrift本身就是一個跨語言的RPC框架劝评,可以直接通過thrift就可以客戶端與服務(wù)器之間的通信(通過idl文件生成的代碼即提供了傳輸協(xié)議姐直,也提供了網(wǎng)絡(luò)傳輸方式)〗螅基于Protobuf沒有提供網(wǎng)絡(luò)傳輸組件声畏,google又推出了自己的RPC框架GRPC,GRPC是基于Protobuf 3.0版本姻成,基于.proto文件不僅能生成序列化反序列化程序代碼插龄,也可以生成傳輸層次的代碼。