thrift 是 facebook 於2007年開發的一款跨平臺 RPC(Remote Procedure Call) 軟件框架,
它能夠在多種平臺上進行無縫交互,數據傳輸使用二進制的方式,比XML和JSON體積更小,適合於內網的之間的數據進行交互。javascript
(參見https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/)html
thrift 是由傳輸層、協議層和業務組成。用戶選定的傳輸層類型、協議層類型以後,只須要關注業務代碼便可,無需關注底層實現。vue
當生成了一套協議後,由客戶端和和服務端根據協議文件生成 thrift 的接口庫,
接口庫會提供定義的service方法,直接調用方法,遠程服務端會返回數據。java
接口定義地址;
http://thrift.apache.org/docs/typesnode
基本類型:webpack
與Js對應的對象類型,ios
struct ListItem { 1: i32 id, 2: string content = '', 3: Status status = 1, 4: string author, 5: i32 textLength }
容器類型,是不能直接在外層定義的,須要在 struct 中定義或者在 service 中定義,
主要包括:
與Js的數組對應的類型:git
與Js中set對應的類型:github
與Js中Map對應的類型web
其餘類型;
異常類型
可調用接口
service Todo { list<ListItem> getTodoList(), i32 getTotalLength(1: string author), i8 postTodo(1: PostItem item) ListItem doneArtical(1: i32 id) ListItem deleteArtical(1: i32 id) }
爲方便操做,使用vue進行html的開發。首先,須要安裝thrift環境(在mac環境下,其餘環境請參考http://thrift.apache.org/tutorial/):
brew install thrift
同時安裝vue開發的環境,vue/cli,用於直接對單個文件進行開發(實際上是爲了省事,不想搭webpack環境)。
npm install -g @vue/cli npm install -g @vue/cli-service-global
接口文件是咱們根據 thrift 定義的類型進行書寫。其中除service類型外,其餘定義都至關於定義成全局的類型,要注意名字的惟一性,service 是供咱們調用的類型,就是接口。
建立的thrift文件以下:
enum Status { NORMAL = 1, DONE = 2, DELETED = 3 } struct PostItem { 1: string content = '', 2: string author, } exception CodeError { 1: i32 code = 0, 2: string message = '' } struct ListItem { 1: i32 id, 2: string content = '', 3: Status status = 1, 4: string author, 5: i32 textLength } service Todo { list<ListItem> getTodoList(), i32 getTotalLength(1: string author), i8 postTodo(1: PostItem item) ListItem doneArtical(1: i32 id) ListItem deleteArtical(1: i32 id) }
Todo就是咱們須要使用的類。
thrift -r --gen js:node todo.thrift && thrift -r --gen js todo.thrift
js:node 是供 Nodejs 調用的庫文件,js 是瀏覽器環境的文件(貌似是須要使用grunt進行打包,反正我是用不習慣,只是個示例,直接中在html經過腳本引入了)。生成的文件保存在gen-js
和 gen/nodejs
兩個文件夾下,一個是類型文件,一個是接口文件。
因爲瀏覽器和後臺交互目前只支持 ajax 的方式,因此咱們的服務端是須要搭建http服務器的。
使用 thrift 的 createWebServer便可(注意不要使用示例中的createServer,那個建立的是socket服務,不是Http服務)。同時設置好傳輸協議爲json格式,傳輸層類型爲buffer模式。爲接口中的每一個 service 添加實現方式。
const thrift = require('thrift') const Todo = require('./gen-nodejs/Todo') const tTypes = require('./gen-nodejs/todo_types') const data = [] let gid = 0 const actions = { getTodoList () { return data }, getTotalLength () { return data.length }, postTodo (item) { const result = new tTypes.ListItem({ content: item.content, author: item.author, status: tTypes.Status.NORMAL, textLength: item.content.length, id: ++gid }) data.push(result) return 0 }, doneArtical (id) { const result = data.find(item => item.id === id) if (!result) { throw new tTypes.CodeError({code: 1, message: '請選擇條目!'}) } result.status = tTypes.Status.DONE return result }, deleteArtical (id) { const index = data.findIndex(item => item.id === id) const result = data[index] if (!~result) { throw new tTypes.CodeError({code: 1, message: '請選擇條目!'}) } data.splice(index, 1) return result } } const serverOptions = { // 靜態文件服務器路徑 files: '.', // 設置跨域請求 cors: { '*': true }, services: { // 設置service '/': { // 傳輸層類型爲buffer模式 transport: thrift.TBufferedTransport, // 協議類型爲json格式 protocol: thrift.TJSONProtocol, processor: Todo, handler: actions, } } } const server = thrift.createWebServer(serverOptions) server.listen(7878, () => { console.log(`監聽端口:${7878}`) })
瀏覽器代碼就是寫網頁了。爲了使用 vue 的 serve 功能,網頁的名稱須要設置爲 App.vue,同時添加個自定義的 html 文件,添加引入 thrift 庫腳本:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>新增文章</title> <meta name="viewport" id="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <link rel="shortcut icon" href="/favicon.ico"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="format-detection" content="telephone=no"> </head> <body> <div id="app"></div> <script type='text/javascript' src='http://localhost:7878/thrift-bundle.js'></script> </body> </html>
vue文件內容爲:
<template> <div> <section class="artical-list"> <ul> <li v-for="(item, index) in list" :key="index"> <p>{{item.content}}</p> <p>做者: {{item.author}}, 當前狀態:{{item.status | status}}</p> <button @click="doneArtical(item)">設置爲已閱</button> <button @click="deleteArtical(item)">刪除</button> </li> </ul> </section> <section class="form-data"> <textarea name="artical" v-model="artical" cols="30" rows="10"></textarea> <input type="text" name="author" v-model="author"/> <button @click="postArtical">提交</button> </section> </div> </template> <script> /* eslint-disable */ export default { data () { return { list: [], artical: '', author: '', } }, created () { this.init() }, filters: { status (value) { const status = ['無', '正常', '已閱', '刪除'] return status[value] }, }, methods: { init () { const transport = new Thrift.Transport('http://localhost:7878') const protocol = new Thrift.Protocol(transport) const client = new TodoClient(protocol) this.client = client this.getList() }, getList () { this.client.getTodoList((result) => { this.list = result }) }, postArtical () { const result = new PostItem() result.content = this.artical result.author = this.author this.client.postTodo(result, (result) => { this.getList() }) }, doneArtical (item) { this.client.doneArtical(item.id, (result) => { if (result instanceof Thrift.TApplicationException) { alert(result.message) return } this.getList() }) }, deleteArtical (item) { this.client.deleteArtical(item.id, (result) => { if (result instanceof Thrift.TApplicationException) { alert(result.message) return } this.getList() }) }, }, } </script>
主要思路是在初始化先建立接口的實例,設置 transport 的請求地址,而後使用咱們定義的 service,
綁定實例在this上。每次要操做時直接調用實例的方法便可。看起來是否是和咱們寫普通封裝好的axios同樣?
爲方便使用,咱們使用 nodemon 進行服務器開發自動重啓,咱們在 npm 包中添加如下腳本:
"scripts": { "start": "vue serve & node server.js", "dev": "vue serve & npm run compile && nodemon server.js", "compile": "npm run gen && npm run concat", "gen": "thrift -r --gen js:node todo.thrift && thrift -r --gen js todo.thrift", "concat": "concat -o thrift-bundle.js ./thrift.js ./gen-js/*.js" },
這樣,咱們使用 npm start
啓動已經構建好的服務,使用 npm run dev
進行開發,
使用 npm run compile
在改動了 thrift 接口文件後進行從新編譯。
這樣咱們的網頁就作好了:
搭建一個簡單的 thrift 項目仍是很容易的,全部的代碼已經放在個人github上https://github.com/wenlonghuo/code-test/tree/master/004_thrift。 其餘原理和總結有待後續挖掘。