Thrift 框架的使用

2017-04-08  本文已影响1360人  如弦

Thrift 框架主要用于跨语言服务的开发。一般场景下,服务端对于接口的互相调用可以直接使用 http 请求来完成,这种方式简单并且方便,但是,数据的传输速度并不是很快,并且很难规范出一种统一的调用方式。

比较理想的调用方式应该是这样,对于客户端来说,服务端的实现方式是透明的,客户端只需要知道自己可以调用哪些方法,以及如何调用这些方法,所谓如何调用,是指相关方法需要传入什么参数,以及其返回值格式。在thrift框架中,这种约定可以通过接口描述来实现。

在确定接口描述之后,实际上对于任何需要与服务端通信的客户端来说,调用方式就已经确定。而客户端可以由其它各种语言自己实现。

数据类型

基础类型

结构体

通其它语言中一样,IDL中的结构体,定义一种数据类型的集合,这个变量可以在其他地方被使用。对应于具体的语言,类似于类的概念。

容器

在IDL中,这种存放数据的结构称为容器,在编译之后的具体语言文件中,将会对应到这种语言所支持的数据结构。

Services

定义 service 相当于定义接口,或者纯虚抽象类,定义的方法相当于面向对象语言中的纯虚函数。thrift 的编译器将根据相关定义来实现客户端和服务端的代码。
service 由一组函数组成,每个函数都有一组参数定义和返回值的定义。

IDL

以官网 tutorial.thrift 示例来做说明

常量定义
// 定义32位整型
const i32 INT32CONSTANT = 9853
//定义一个map结构
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
//定义一个结构体,每个字段前面都有一个整数作为标记,字段可设置默认值
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}
//定义一个service
service Calculator extends shared.SharedService {

   void ping(),

  //返回值,参数列表同样用数字作为标记
   i32 add(1:i32 num1, 2:i32 num2),

   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),

   /**
    * This method has a oneway modifier. That means the client only makes
    * a request and does not listen for any response at all. Oneway methods
    * must be void.
    */
   oneway void zip()

}

编译

可直接在本地安装 thrift 编译器,或者用docker来运行编译命令。docker 运行可能更方便一些,还可以避免本机环境和部署环境的不同而导致代码无法运行。
运行编译命令:thrift --gen <language> <Thrift filename>

以 go 语言为例:thrift --gen go tutorial.thrift
编译过程中,会生成 go-gen 目录,编译好的包也会自动放入这个目录。上述 thrift 文件则生成了包名为 tutorial 的 go 语言包。然后将生成的包移动到 GOPATH 目录下,就可以直接调用了。

运行

以官网示例作为说明:
https://thrift.apache.org/tutorial/go

代码分为三部分,server handler client。
server.go 启动了一个服务端程序,handler.go 则具体实现了service里面定义的纯虚函数,client.go 则是远程调用的实现方式。

运行 server.go 需要向其添加入口函数,完整代码如下:

package main

import (
    "crypto/tls"
    "fmt"
    "tutorial"

    "./handler"
    "git.apache.org/thrift.git/lib/go/thrift"
)

func runServer(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string, secure bool) error {
    var transport thrift.TServerTransport
    var err error
    if secure {
        cfg := new(tls.Config)
        if cert, err := tls.LoadX509KeyPair("server.crt", "server.key"); err == nil {
            cfg.Certificates = append(cfg.Certificates, cert)
        } else {
            return err
        }
        transport, err = thrift.NewTSSLServerSocket(addr, cfg)
    } else {
        transport, err = thrift.NewTServerSocket(addr)
    }

    if err != nil {
        return err
    }
    fmt.Printf("%T\n", transport)
        //引入 handler 并传入,此处相当于返回一个对象实例,对象的方法就是纯虚函数的具体实现
    handler := handler.NewCalculatorHandler()
    processor := tutorial.NewCalculatorProcessor(handler)
    server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory)

    fmt.Println("Starting the simple server... on ", addr)
    return server.Serve()
}

func main() {
    transportFactory := thrift.NewTBufferedTransportFactory(1024)
    protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
//监听 1234 端口
    addr := "127.0.0.1:1234"
    runServer(transportFactory, protocolFactory, addr, false)
}

运行 client.go 同样需要添加入口函数 main ,并初始化连接,步骤和上述相同。

上一篇 下一篇

猜你喜欢

热点阅读