背景
要看4個thrift接口半夷。thrift是一種RPC框架,先看一下怎么一回事极颓。
與我做的這層的關(guān)系
API這層其實是兩端服務(wù)盅藻,一個是作為客戶端調(diào)用后端服務(wù)糜颠,一個是作為服務(wù)端為前端提供接口汹族。在調(diào)用后端服務(wù)這一端上面,95%以上的接口都是RPC其兴,為前端提供接口這部分顶瞒,如果是native調(diào)用,那么多為http元旬,如果是H5榴徐,RPC調(diào)用越來越多了。(關(guān)于native和H5匀归,請參考這篇和這篇坑资,這里不做詳細(xì)介紹)
微服務(wù)通訊
從通訊模式角度考慮
1對1 | 1對多 | |
---|---|---|
同步 | 請求響應(yīng)模式,最常見 | |
異步 | 通知/請求異步響應(yīng) | 發(fā)布訂閱/發(fā)布異步響應(yīng) |
從通訊協(xié)議角度考慮:
- REST
- RPC
- MQ
RPC框架
選擇RPC框架考慮的東西:
I/O(NIO/BIO)穆端,線程調(diào)度模型(多線程/調(diào)度方式)
-
序列化方式(很影響RPC通訊效率):
-
可讀的:
XML: e.g. webservice, soap
JSON, e.g. fastjson, json-rpc
binary: e.g. thrift, JDK自帶的序列化方式
-
是否需要服務(wù)治理: e.g. 部署袱贮,HA,etc.
用的多的RPC框架包括Dubbo, Thrift, gRPC
Thrift原理
框架如下圖:(source: Thrift Wikipedia)
Thrift RPC調(diào)用過程:
客戶端調(diào)用本地代理
本地代理對客戶端的請求進(jìn)行序列化并發(fā)起網(wǎng)絡(luò)請求体啰,等待返回結(jié)果
服務(wù)端也有個代理攒巍,它接受到網(wǎng)絡(luò)請求之后會對請求參數(shù)進(jìn)行反序列化,并對真實server application方法進(jìn)行調(diào)用荒勇,對返回結(jié)果進(jìn)行序列化并發(fā)出網(wǎng)絡(luò)請求返回結(jié)果
客戶端代理接受到返回結(jié)果后對返回結(jié)果進(jìn)行反序列化并將結(jié)果交給客戶端發(fā)起調(diào)用的方法
可以看出很重要的一個地方: client application和client柒莉,server application和server,都是分開的沽翔;也就是說兢孝,真正的client和server都是有代理的(兩個橙色的service client)
Protocol和Transport層
從上圖看出,除開input code這個自己寫的代碼部分仅偎,和Generated code這個thrift框架生成的代碼跨蟹,下面還有2層: TProtocol和TTransport: 實際上這就是RPC中最重要的兩層(TProtocol和TTransport中的T前綴代表Thrift)。一旦這兩層具體方式確定了橘沥,那么就可以進(jìn)行RPC通訊了窗轩。
-
Protocol: 序列化協(xié)議:Client和Server兩端需要遵循相同的序列化協(xié)議,才能進(jìn)行RPC通訊威恼。支持的protocol有
TBinaryProtocol
TCompactProtocol
TJSONProtocol
TSimpleJSONProtocol
-
Transport: 傳輸層:如何傳輸序列化字節(jié)流品姓。支持的傳輸方式有
TSimpleFileTransport
TFramedTransport
TMemoryTransport
TSocket
TZlibTransport
補(bǔ)充一下:Server也有幾種: TNonblockingServer, TSimpleServer, TThreadedServer, TThreadPoolServer
Thrift框架使用
如果不看Thrift的原理而只是看client和server之間進(jìn)行RPC通訊的過程寝并,那么流程如下:
所以用這個框架分3步:
IDL語法寫demo.thrift文件
thrift --gen <language> <demo.thrift>生成接口
有了接口箫措,自己實現(xiàn)這個接口
Demo:
Thrift文件:
service Calculator extends shared.SharedService {
/**
* A method definition looks like C code. It has a return type, arguments,
* and optionally a list of exceptions that it may throw. Note that argument
* lists and exception lists are specified using the exact same syntax as
* field lists in struct or exception definitions.
*/
void ping(),
i32 add(1:i32 num1, 2:i32 num2),
i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
/**
* This method has a oneway modifier. That means the client only makes
* a request and does not listen for any response at all. Oneway methods
* must be void.
*/
oneway void zip()
}
Python client:
def main():
# socket傳輸,后面用buffer封裝socket,協(xié)議用binary,上層用client封裝,然后就可以調(diào)用了
transport = TSocket.TSocket('localhost', 9090)
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
client = Calculator.Client(protocol)
# Connect!
transport.open()
client.ping()
print('ping()')
sum_ = client.add(1, 1)
Java Server (注意Calculator.java這個自動生成的接口文件沒寫)
// initialize server
try {
TServerTransport serverTransport = new TServerSocket(9090);
TServer server = new TSimpleServer(new Args(serverTransport).processor(processor));
// Use this for a multithreaded server
// TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor));
System.out.println("Starting the simple server...");
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
// 自定義實現(xiàn)類
public class CalculatorHandler implements Calculator.Iface {
// ...
}