概述
本文是入門教程,想要了解thrift的源碼實現可以移步我的CSDN專欄thrift源碼解析
Thrift最初由Facebook研發,主要用于各個服務之間的RPC通信,支持跨語言,常用的語言比如C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml都支持。Thrift是一個典型的CS(客戶端/服務端)結構,客戶端和服務端可以使用不同的語言開發。既然客戶端和服務端能使用不同的語言開發,那么一定就要有一種中間語言來關聯客戶端和服務端的語言,沒錯,這種語言就是IDL(Interface Description Language)。
Thrift IDL
本節介紹Thrift的接口定義語言,Thrift IDL支持的數據類型包含:
基本類型
thrift不支持無符號類型,因為很多編程語言不存在無符號類型,比如java
- byte: 有符號字節
- i16: 16位有符號整數
- i32: 32位有符號整數
- i64: 64位有符號整數
- double: 64位浮點數
- string: 字符串
容器類型
集合中的元素可以是除了service之外的任何類型,包括exception。
- list<T>: 一系列由T類型的數據組成的有序列表,元素可以重復
- set<T>: 一系列由T類型的數據組成的無序集合,元素不可重復
- map<K, V>: 一個字典結構,key為K類型,value為V類型,相當于Java中的HMap<K,V>
結構體(struct)
就像C語言一樣,thrift也支持struct類型,目的就是將一些數據聚合在一起,方便傳輸管理。struct的定 義形式如下:
struct People {
1: string name;
2: i32 age;
3: string sex;
}
枚舉(enum)
枚舉的定義形式和Java的Enum定義差不多,例如:
enum Sex {
MALE,
FEMALE
}
異常(exception)
thrift支持自定義exception,規則和struct一樣,如下:
exception RequestException {
1: i32 code;
2: string reason;
}
服務(service)
thrift定義服務相當于Java中創建Interface一樣,創建的service經過代碼生成命令之后就會生成客戶端和服務端的框架代碼。定義形式如下:
service HelloWordService {
// service中定義的函數,相當于Java interface中定義的函數
string doAction(1: string name, 2: i32 age);
}
類型定義
thrift支持類似C++一樣的typedef定義,比如:
typedef i32 Integer
typedef i64 Long
注意,末尾沒有逗號或者分號
常量(const)
thrift也支持常量定義,使用const關鍵字,例如:
const i32 MAX_RETRIES_TIME = 10
const string MY_WEBSITE = "http://qifuguang.me";
末尾的分號是可選的,可有可無,并且支持16進制賦值
命名空間
thrift的命名空間相當于Java中的package的意思,主要目的是組織代碼。thrift使用關鍵字namespace定義命名空間,例如:
namespace java com.winwill.thrift
格式是:namespace 語言名 路徑, 注意末尾不能有分號。
文件包含
thrift也支持文件包含,相當于C/C++中的include,Java中的import。使用關鍵字include定義,例 如:
include "global.thrift"
注釋
thrift注釋方式支持shell風格的注釋,支持C/C++風格的注釋,即#和//開頭的語句都單當做注釋,/**/包裹的語句也是注釋。
可選與必選
thrift提供兩個關鍵字required,optional,分別用于表示對應的字段時必填的還是可選的。例如:
struct People {
1: required string name;
2: optional i32 age;
}
表示name是必填的,age是可選的。
生成代碼
知道了怎么定義thirtf文件之后,我們需要用定義好的thrift文件生成我們需要的目標語言的源碼,本文以生成java源碼為例。假設現在定義了如下一個thrift文件:
namespace java com.winwill.thrift
enum RequestType {
SAY_HELLO, //問好
QUERY_TIME, //詢問時間
}
struct Request {
1: required RequestType type; // 請求的類型,必選
2: required string name; // 發起請求的人的名字,必選
3: optional i32 age; // 發起請求的人的年齡,可選
}
exception RequestException {
1: required i32 code;
2: optional string reason;
}
// 服務名
service HelloWordService {
string doAction(1: Request request) throws (1:RequestException qe); // 可能拋出異常。
}
在終端運行如下命令(前提是已經安裝thrift):
thrift --gen java Test.thrift
則在當前目錄會生成一個gen-java目錄,該目錄下會按照namespace定義的路徑名一次一層層生成文件夾,到gen-java/com/winwill/thrift/目錄下可以看到生成的4個Java類:

可以看到,thrift文件中定義的enum,struct,exception,service都相應地生成了一個Java類,這就是能支持Java語言的基本的框架代碼。
服務端實現
上面代碼生成這一步已經將接口代碼生成了,現在需要做的是實現HelloWordService的具體邏輯,實現的方式就是創建一個Java類,implements com.winwill.thrift.HelloWordService,例如:
package com.winwill.thrift;
import org.apache.commons.lang3.StringUtils;
import org.apache.thrift.TException;
import java.util.Date;
/**
* @author qifuguang
* @date 15/9/11 15:53
*/
public class HelloWordServiceImpl implements com.winwill.thrift.HelloWordService.Iface {
// 實現這個方法完成具體的邏輯。
public String doAction(com.winwill.thrift.Request request) throws com.winwill.thrift.RequestException, TException {
System.out.println("Get request: " + request);
if (StringUtils.isBlank(request.getName()) || request.getType() == null) {
throw new com.winwill.thrift.RequestException();
}
String result = "Hello, " + request.getName();
if (request.getType() == com.winwill.thrift.RequestType.SAY_HELLO) {
result += ", Welcome!";
} else {
result += ", Now is " + new Date().toLocaleString();
}
return result;
}
}
啟動服務
上面這個就是服務端的具體實現類,現在需要啟動這個服務,所以需要一個啟動類,啟動類的代碼如下:
package com.winwill.thrift;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import java.net.ServerSocket;
/**
* @author qifuguang
* @date 15/9/11 16:07
*/
public class HelloWordServer {
public static void main(String[] args) throws Exception {
ServerSocket socket = new ServerSocket(7912);
TServerSocket serverTransport = new TServerSocket(socket);
com.winwill.thrift.HelloWordService.Processor processor = new com.winwill.thrift.HelloWordService.Processor(new HelloWordServiceImpl());
TServer server = new TSimpleServer(processor, serverTransport);
System.out.println("Running server...");
server.serve();
}
}
運行之后看到控制臺的輸出為:
Running server...
客戶端請求
現在服務已經啟動,可以通過客戶端向服務端發送請求了,客戶端的代碼如下:
package com.winwill.thrift;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
/**
* @author qifuguang
* @date 15/9/11 16:13
*/
public class HelloWordClient {
public static void main(String[] args) throws Exception {
TTransport transport = new TSocket("localhost", 8888);
TProtocol protocol = new TBinaryProtocol(transport);
// 創建client
com.winwill.thrift.HelloWordService.Client client = new com.winwill.thrift.HelloWordService.Client(protocol);
transport.open(); // 建立連接
// 第一種請求類型
com.winwill.thrift.Request request = new com.winwill.thrift.Request()
.setType(com.winwill.thrift.RequestType.SAY_HELLO).setName("winwill2012").setAge(24);
System.out.println(client.doAction(request));
// 第二種請求類型
request.setType(com.winwill.thrift.RequestType.QUERY_TIME).setName("winwill2012");
System.out.println(client.doAction(request));
transport.close(); // 請求結束,斷開連接
}
}
運行客戶端代碼,得到結果:
Hello, winwill2012, Welcome!
Hello, winwill2012, Now is 2015-9-11 16:37:22
并且此時,服務端會有請求日志:
Running server...
Get request: Request(type:SAY_HELLO, name:winwill2012, age:24)
Get request: Request(type:QUERY_TIME, name:winwill2012, age:24)
可以看到,客戶端成功將請求發到了服務端,服務端成功地將請求結果返回給客戶端,整個通信過程完成。
注意事項
- 本文為作者個人理解,如理解有誤,請留言相告,感激不盡;
- 本文是入門教程,想要了解thrift的源碼實現可以移步我的CSDN專欄thrift源碼解析