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。 其余原理和总结有待后续挖掘。