Thrift IDL 快速入門
眾所周知,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 的書寫規範則可以參考ofollow,noindex">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複製程式碼