概述
本文是入門教程祖很,想要了解thrift的源碼實(shí)現(xiàn)可以移步我的CSDN專欄thrift源碼解析
Thrift最初由Facebook研發(fā)名斟,主要用于各個服務(wù)之間的RPC通信恤磷,支持跨語言危号,常用的語言比如C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml都支持。Thrift是一個典型的CS(客戶端/服務(wù)端)結(jié)構(gòu)舆吮,客戶端和服務(wù)端可以使用不同的語言開發(fā)。既然客戶端和服務(wù)端能使用不同的語言開發(fā)队贱,那么一定就要有一種中間語言來關(guān)聯(lián)客戶端和服務(wù)端的語言色冀,沒錯,這種語言就是IDL(Interface Description Language)柱嫌。
Thrift IDL
本節(jié)介紹Thrift的接口定義語言锋恬,Thrift IDL支持的數(shù)據(jù)類型包含:
基本類型
thrift不支持無符號類型,因?yàn)楹芏嗑幊陶Z言不存在無符號類型编丘,比如java
- byte: 有符號字節(jié)
- i16: 16位有符號整數(shù)
- i32: 32位有符號整數(shù)
- i64: 64位有符號整數(shù)
- double: 64位浮點(diǎn)數(shù)
- string: 字符串
容器類型
集合中的元素可以是除了service之外的任何類型伶氢,包括exception。
- list<T>: 一系列由T類型的數(shù)據(jù)組成的有序列表瘪吏,元素可以重復(fù)
- set<T>: 一系列由T類型的數(shù)據(jù)組成的無序集合癣防,元素不可重復(fù)
- map<K, V>: 一個字典結(jié)構(gòu),key為K類型掌眠,value為V類型蕾盯,相當(dāng)于Java中的HMap<K,V>
結(jié)構(gòu)體(struct)
就像C語言一樣,thrift也支持struct類型蓝丙,目的就是將一些數(shù)據(jù)聚合在一起级遭,方便傳輸管理。struct的定 義形式如下:
struct People {
1: string name;
2: i32 age;
3: string sex;
}
枚舉(enum)
枚舉的定義形式和Java的Enum定義差不多渺尘,例如:
enum Sex {
MALE,
FEMALE
}
異常(exception)
thrift支持自定義exception挫鸽,規(guī)則和struct一樣,如下:
exception RequestException {
1: i32 code;
2: string reason;
}
服務(wù)(service)
thrift定義服務(wù)相當(dāng)于Java中創(chuàng)建Interface一樣鸥跟,創(chuàng)建的service經(jīng)過代碼生成命令之后就會生成客戶端和服務(wù)端的框架代碼丢郊。定義形式如下:
service HelloWordService {
// service中定義的函數(shù)盔沫,相當(dāng)于Java interface中定義的函數(shù)
string doAction(1: string name, 2: i32 age);
}
類型定義
thrift支持類似C++一樣的typedef定義,比如:
typedef i32 Integer
typedef i64 Long
注意枫匾,末尾沒有逗號或者分號
常量(const)
thrift也支持常量定義架诞,使用const關(guān)鍵字,例如:
const i32 MAX_RETRIES_TIME = 10
const string MY_WEBSITE = "http://qifuguang.me";
末尾的分號是可選的干茉,可有可無谴忧,并且支持16進(jìn)制賦值
命名空間
thrift的命名空間相當(dāng)于Java中的package的意思,主要目的是組織代碼角虫。thrift使用關(guān)鍵字namespace定義命名空間沾谓,例如:
namespace java com.winwill.thrift
格式是:namespace 語言名 路徑, 注意末尾不能有分號戳鹅。
文件包含
thrift也支持文件包含均驶,相當(dāng)于C/C++中的include,Java中的import粉楚。使用關(guān)鍵字include定義辣恋,例 如:
include "global.thrift"
注釋
thrift注釋方式支持shell風(fēng)格的注釋,支持C/C++風(fēng)格的注釋模软,即#和//開頭的語句都單當(dāng)做注釋伟骨,/**/包裹的語句也是注釋。
可選與必選
thrift提供兩個關(guān)鍵字required燃异,optional携狭,分別用于表示對應(yīng)的字段時必填的還是可選的。例如:
struct People {
1: required string name;
2: optional i32 age;
}
表示name是必填的回俐,age是可選的逛腿。
生成代碼
知道了怎么定義thirtf文件之后,我們需要用定義好的thrift文件生成我們需要的目標(biāo)語言的源碼仅颇,本文以生成java源碼為例单默。假設(shè)現(xiàn)在定義了如下一個thrift文件:
namespace java com.winwill.thrift
enum RequestType {
SAY_HELLO, //問好
QUERY_TIME, //詢問時間
}
struct Request {
1: required RequestType type; // 請求的類型,必選
2: required string name; // 發(fā)起請求的人的名字忘瓦,必選
3: optional i32 age; // 發(fā)起請求的人的年齡搁廓,可選
}
exception RequestException {
1: required i32 code;
2: optional string reason;
}
// 服務(wù)名
service HelloWordService {
string doAction(1: Request request) throws (1:RequestException qe); // 可能拋出異常。
}
在終端運(yùn)行如下命令(前提是已經(jīng)安裝thrift):
thrift --gen java Test.thrift
則在當(dāng)前目錄會生成一個gen-java目錄耕皮,該目錄下會按照namespace定義的路徑名一次一層層生成文件夾境蜕,到gen-java/com/winwill/thrift/目錄下可以看到生成的4個Java類:
可以看到,thrift文件中定義的enum凌停,struct粱年,exception,service都相應(yīng)地生成了一個Java類罚拟,這就是能支持Java語言的基本的框架代碼台诗。
服務(wù)端實(shí)現(xiàn)
上面代碼生成這一步已經(jīng)將接口代碼生成了完箩,現(xiàn)在需要做的是實(shí)現(xiàn)HelloWordService的具體邏輯,實(shí)現(xiàn)的方式就是創(chuàng)建一個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 {
// 實(shí)現(xiàn)這個方法完成具體的邏輯秃励。
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;
}
}
啟動服務(wù)
上面這個就是服務(wù)端的具體實(shí)現(xiàn)類氏仗,現(xiàn)在需要啟動這個服務(wù),所以需要一個啟動類夺鲜,啟動類的代碼如下:
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();
}
}
運(yùn)行之后看到控制臺的輸出為:
Running server...
客戶端請求
現(xiàn)在服務(wù)已經(jīng)啟動皆尔,可以通過客戶端向服務(wù)端發(fā)送請求了,客戶端的代碼如下:
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);
// 創(chuàng)建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(); // 請求結(jié)束币励,斷開連接
}
}
運(yùn)行客戶端代碼慷蠕,得到結(jié)果:
Hello, winwill2012, Welcome!
Hello, winwill2012, Now is 2015-9-11 16:37:22
并且此時,服務(wù)端會有請求日志:
Running server...
Get request: Request(type:SAY_HELLO, name:winwill2012, age:24)
Get request: Request(type:QUERY_TIME, name:winwill2012, age:24)
可以看到食呻,客戶端成功將請求發(fā)到了服務(wù)端流炕,服務(wù)端成功地將請求結(jié)果返回給客戶端,整個通信過程完成仅胞。
注意事項(xiàng)
- 本文為作者個人理解每辟,如理解有誤,請留言相告干旧,感激不盡渠欺;
- 本文是入門教程,想要了解thrift的源碼實(shí)現(xiàn)可以移步我的CSDN專欄thrift源碼解析