zoukankan      html  css  js  c++  java
  • 使用Vue和thrift建立前后端交互的demo

    初识thrift

    thrift 是 facebook 于2007年开发的一款跨平台 RPC(Remote Procedure Call) 软件框架,
    它可以在多种平台上进行无缝交互,数据传输使用二进制的方式,比XML和JSON体积更小,适合于内网的之间的数据进行交互。

    thrift 结构

    (参见https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/)

    thrift 是由传输层、协议层和业务组成。用户选定的传输层类型、协议层类型之后,只需要关注业务代码即可,无需关注底层实现。

    当生成了一套协议后,由客户端和和服务端根据协议文件生成 thrift 的接口库,
    接口库会提供定义的service方法,直接调用方法,远程服务端会返回数据。

    thrift类型

    接口定义地址;
    http://thrift.apache.org/docs/types

    基本类型:

    • bool 对应js中boolean
    • byte 8位无符号整数
    • i16 16位无符号整数
    • i32 32位无符号整数
    • i64 64位无符号整数
    • double 64位符点型数字
    • string utf-8类型的字符串
    • binary 一系列基于string类型的编码的字符串

    与Js对应的对象类型,

    • struct 结构体,可使用基本类型,强类型,需要定义不同的名称
      示例:
    struct ListItem {
      1: i32 id,
      2: string content = '',
      3: Status status = 1,
      4: string author,
      5: i32 textLength
    }
    

    容器类型,是不能直接在外层定义的,需要在 struct 中定义或者在 service 中定义,
    主要包括:
    与Js的数组对应的类型:

    • list 一系列的数组元素

    与Js中set对应的类型:

    • set 相当于不重复的数组

    与Js中Map对应的类型

    • map 类似于字典模式

    其他类型;
    异常类型

    • exception

    可调用接口

    • service
      示例:
    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)
    }
    

    使用thrift实现网页和nodejs服务端进行交互的实践

    安装环境

    为方便操作,使用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 两个文件夹下,一个是类型文件,一个是接口文件。

    建立 server 端代码

    由于浏览器和后台交互目前只支持 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
    其他原理和总结有待后续挖掘。

  • 相关阅读:
    用.net开发wap
    MVC3 中使用 Ajax.ActionLink Ajax.BeginForm
    收藏一下这个微软MVP的老外博客
    第三篇:Django的路由系统
    第二篇:Django自定义登录功能
    第一篇:Django简介
    json和pickle序列化模块
    oracle 11gr2 rac修改VIP
    修改监听端口号
    删除磁盘组
  • 原文地址:https://www.cnblogs.com/dreamless/p/8494551.html
Copyright © 2011-2022 走看看