I.背景
眾所周知,Thrift
是一個RPC的框架,其可用於不同語言之間的服務相互呼叫。比如最近接觸到的一個運用環境:
*前端使用Node.Js重構了部分我們的老舊程式碼(前後端未分離的SpringBoot專案),我們後端使用zookeeper+Thrift為新的Node.Js前端專案提供基本的DAO層服務支援*
所以基於這個專案,我大概瞭解了一下Thrift,該文章則均以Java
為基礎語言。
II.如何入門
因為後端已經有一套服務註冊和暴露機制,所以服務已經是RPC的形式,所以我們僅需要使用Thrift IDL
來重寫一遍我們需要暴露的方法即可,Thrift IDL
有兩個比較好的參考選擇:
- Thrift 型別說明可以參考官方文件: Thrift Type,簡單來說,
thrift
基本支援所有的Java基本型別以及引用型別:bool(boolean)、byte(byte)、i16(short)、i32(int)、i64(long)、double(double)、string(String)、binary(byte[])以及一些常用容器和自建型別(Struct); - Thrift IDL的書寫規範則可以參考Thrift: The Missing Guide,這個文件相較於官方文件有更多的例子可以參考。
III.TIPS
- 很多參考和學習文件,都將
services
和struct
放在一個.thrift
檔案中,這種方式將service
和其所需的struct
繫結在一起,會導致個別struct
重複出現在多個.thrift
檔案中,導致大量的程式碼重複。所以,應該與Java的編碼風格保持一致,採用POJO類(struct)
+介面(services)
的方案,利用include
關鍵字,將struct引進services中使用。 - Thrift支援基本所有的
Java基本型別
,但是注意是基本型別,類似於Java中的Integer、Boolean、Long、Double等基本型別包裝類是不支援的,或許你可以使用struct
實現一個類似的包裝類結構進行資料承載。 - Thrift支援
enum
列舉型別,但是如果沒有description
的列舉型別也可以直接使用string
來承接。 - Thrift是通過序列化和反序列化來獲取對應
struct
結構體的資料的,所以struct
中的資料順序一定要和java
檔案中的一致,否則可能會出現資料對應關係錯亂或者資料丟失。 - 在編寫
struct
體時,要注意java物件
的父級,如果父級中含有變數,不要忘記其變數的書寫,且順序一定在前面。 - 如非必要,
java物件
中的常量可以不出現在thrift idl
的struct
結構體中。 - 時間
Date
我們固定使用timestamp
時間戳的形式傳遞,即long
型。
IV. 例子
我們現在有一個Account
物件,需要將其變為thrift
檔案,Account
的結構如下:
public class Account extends BaseEntity implements SecurityUser{
private static final long serialVersionUID = 1L;
public static final String PASSWORD_TIME = "passwordTime";
private String password; // 密碼
private Date createTime; // 建立時間
private Date lastLoginTime; // 最後登入時間
private int loginCount = 0; // 登入次數
private boolean enabled = true;
private Date passwordTime; //密碼修改時間
private boolean freeze; //賬戶是否被凍結
//該賬戶的繫結資訊,非持久化欄位
@Transient
private Set<Binding> bindings = new HashSet();
@Transient
private String name;//儲存登入時用的使用者名稱,非持久化欄位
//省略getter和setter
}
複製程式碼
根據上述結構我們可以書寫如下的Account.thrift
:
/**
* 賬戶資訊
*/
struct Account{
1: string password, //密碼
2: i64 createTime, //建立時間
3: i64 lastLoginTime, //最後登入時間
4: i32 loginCount, //登陸次數
5: bool enabled=true,
6: i64 passwordTime, //密碼修改時間
7: bool freeze, //賬戶是否被凍結
}
複製程式碼
但是經過測試前端呼叫介面獲取到的Account
資訊,要麼資料錯位,要麼資料丟失,問題出在哪裡呢?這時,我們發現Account
物件繼承了BaseEntity
,實現了SecurityUser
,我們去檢視一下繼承的BaseEntity
物件:
public abstract class BaseEntity extends IdEntity implements Cacheable, TypeAliases{
private static final long serialVersionUID = 1L;
private static final String CACHE_NAMESPACE = "entity" ;
public BaseEntity() {
super();
}
public BaseEntity(Long id) {
super(id);
}
//省略下方程式碼
}
複製程式碼
我們發現其中並沒有非常量變數,但是BaseEntity
又繼承了IdEntity
,所以我們得再去看一看IdEntity
:
public abstract class IdEntity implements Serializable{
private static final long serialVersionUID = -1L;
/**
* Hibernate JPA環境中使用@GenericGenerator註解生成主鍵
*/
@Id
@GeneratedValue(generator = "longIdGenerator")
@GenericGenerator(name = "longIdGenerator", strategy = "net.qiyuesuo.framework.id.LongIdGenerator")
protected Long id;
public IdEntity() {
super();
}
public IdEntity(Long id) {
super();
this.id = id;
}
//省略getter和setter
}
複製程式碼
這時我們發現IdEntity
中含有一個Id
的變數,所以我們需要重構一下剛剛書寫的Account.thrift
:
/**
* 賬戶資訊
*/
struct Account{
1: i64 id, //賬戶Id
2: string password, //密碼
3: i64 createTime, //建立時間
4: i64 lastLoginTime, //最後登入時間
5: i32 loginCount, //登陸次數
6: bool enabled,
7: i64 passwordTime, //密碼修改時間
8: bool freeze, //賬戶是否被凍結
}
複製程式碼
書寫完Account.thrift
後,我們需要寫其相應的介面即service
,檢視Interface AccountService
:
public interface AccountService {
/**
* 建立賬戶
* @param account
* @return 返回建立後的Account物件
*/
Account create(Account account);
/**
* 更新賬戶資訊
* @param account
* @return
*/
boolean update(Account account);
/**
* 修改開放平臺密碼 傳入的 密碼是未加密的
* @param username
* @param newPassword
* @return
*/
boolean updatePasswd(String username, String newPassword);
/**
* 重置密碼
* @param username 使用者名稱
* @param newPassword 新密碼
*/
void resetPasswd(String username, String newPassword);
/**
* 驗證使用者名稱和密碼是否匹配
* @param username
* @param password
*/
boolean matches(String username,String password);
}
複製程式碼
有很多方法,但是如果前端只需要用到校驗使用者名稱和密碼的方法,那麼我就只需要暴露建立賬戶的方法,即:
include "Account.thrift"
service AccountService{
/*
* 校驗使用者名稱和密碼
*/
bool matches(1: string username,2: string password),
}
複製程式碼
這樣我們就完成了一個關於使用者名稱和密碼的校驗方法的thrift idl
文件的書寫,前端需要執行thrift
的一條語句對檔案進行編譯,以node.js
為例(具體可參考:Apache Thrift Tutorial)
thrift --gen <language> <Thrift filename>
thrift -r --gen js:node Account.thrift
thrift -r --gen js:node AccountService.thrift複製程式碼