現現在對於任何一個大型的服務,都不太多是一個單體的服務。而是由諸多的子服務構成,具體的業務邏輯經過子服務之間的相互調用來完成。這種相互的調用稱之爲遠程調用,也就是一般所說的 RPC。java
RPC(Remote Procedure Call)是指遠程調用,這個和本地調用有很大的區別。下面在 main
方法中調用 helloWorld
就是本地調用。apache
public class Main {
public static void final main(String[] args) {
helloWorld(); // 本地調用
}
public static void helloWorld() {
System.out.println("Hello World");
}
}
複製代碼
如今假設 helloWorld
這個方法不在本地,無法直接調用,那麼就須要經過遠程調用來訪問這個方法,在進行遠程調用的過程當中,就產生了客戶端-服務端(Server-Client)模式,服務的提供方就是服務端,服務的調用方就是客戶端,客戶端和服務端並非絕對不變的,要看具體的服務的調用流程來認定服務端和客戶端。編程
遠程調用的方式有不少,好比 Java 中的 RMI(Remote Method Invocation) 就是典型的 RPC, 還有 Http 也能夠認爲是一種 RPC。服務器
Thrift 是 FaceBook 實現的一種支持多語言的 RPC 框架,對於主流的編程語言 Java
,C++
, Python
等都有很好的支持。 Thrift 會經過自身的 IDL(Interface Description Language)
語言來對接口進行定義,而後 Thrift 經過定義好的 IDL 文件生成相應的接口腳手架,而後只須要分別實現接口腳手架的 Server 端和 Client 端。並將 Server 端進行部署,Client 就能夠訪問 Server端的服務。網絡
下圖是 Thrift 官方給出的架構圖。Thrift 支持的語言多達 28 種,對於各種操做系統也都提供了支持,Thrift 將自身劃爲4層(不要和網絡的四層協議混淆),並且對於各個層已經作好乾淨的分層和實現。對於不一樣的業務場景就能夠選擇不一樣的協議進行組合。數據結構
Thrift 總體上能夠分紅如下四層:架構
Thrift 的性能在現有的 RPC 框架中屬於前列,這裏有詳細的 Benchmark,若是特別關注性能,那麼 Thrift 是一個很好的選擇。併發
並且 Thrift 提供了多種網絡模型,支持阻塞服務模型和非阻塞服務模型,全部的網絡處理模型都繼承自 TServer。框架
阻塞服務模型:異步
非阻塞服務模型:
數據在網絡的傳輸過程當中,序列化和反序列化是一個很重要的過程,對於不一樣的系統,對序列化有着不一樣的要求,Thrift 提供了多種序列化的方式來知足不一樣的要求。傳輸協議整體上能夠分紅文本和二進制的兩種協議。
Thrift 有以下的傳輸協議:
Thrift 的安裝在官方文檔已經介紹的很清楚了。
Thrift 使用 IDL 來定義(Client-Server)之間的接口。假如須要定義一個 hello 的接口,那麼就能夠定義一個 hello.thrift
文件,文件的內容以下:
service HelloWorldService {
string hello(1: string name)
}
複製代碼
上面定義了一個名爲 HelloWorldService
的服務,在這個服務器中有一個叫 hello
的接口,這個接口接受一個 string
類型的參數。
service
和 string
都是 IDL 的關鍵字,IDL 中的關鍵字包括:
這些都是 IDL 的基本數據結構,也還有命名空間、異常、常量等關鍵字。完整的 IDL文檔看這裏
上面的 IDL 代碼定義好了一個 Thrift 接口的規則,Thrift 就能夠根據這個規則生成相應的接口文件。經過這些接口文件,就能夠方便的實現相應的業務邏輯,至關於 Thrift 經過 IDL 文件搭出了服務腳手架。
在生成的腳手架文件中,有兩個 interface 很是重要:
AsyncIface
和 AsyncClient
是這兩個 interface 的異步版本。
下面使用 SimpleServer
來實現這個服務:
Iface 實現:
public class HelloWorldServiceImpl implements HelloWorldService.Iface{
@Override
public String say(String username) throws TException {
return "Hello " + username;
}
}
/** *建立一個服務器,向外暴露服務 */
public class HelloWorldServer {
public static void main(String[] args) throws TTransportException {
TServerSocket serverSocket = new TServerSocket(9090);
HelloWorldService.Processor processor = new HelloWorldService.Processor(new HelloWorldServiceImpl());
TSimpleServer.Args tArgs = new TSimpleServer.Args(serverSocket);
tArgs.processor(processor);
TServer server = new TSimpleServer(tArgs);
server.serve();
}
}
複製代碼
Client實現
/** * 客戶端,鏈接服務器,而且發送消息 */
public class HelloWorldClient {
public static void main(String[] args) throws TTransportException, TException {
TTransport transport = new TSocket("127.0.0.1", 9090, 3000);
TProtocol protocol = new TBinaryProtocol(transport);
HelloWorldService.Client client = new HelloWorldService.Client(protocol);
try {
transport.open();
String result = client.say("Ray");
System.out.println(result);
} finally {
if (null != transport) {
transport.close();
}
}
}
}
複製代碼
一個簡單的 Thrift 的服務也就實現了。
(完)