thrift简单示例 (go语言)
这个thrift的简单示例来自于官网 (http://thrift.apache.org/tutorial/go), 因为官方提供的例子简单易懂, 所以没有必要额外考虑新的例子. 关于安装的教程, 可以参考https://www.cnblogs.com/albizzia/p/10838646.html, 关于thrift文件的语法, 可以参考: https://www.cnblogs.com/albizzia/p/10838646.html.
运行下面的示例, 除了需要安装thrift外, 还有一些要求:
(1) go需要版本达到1.7或者更高.
(2) GOPATH可能需要调整, 或者人工将go thrift的库文件放置到合适的位置.
(3) thrift库和编译器需要是相同的版本. 如果版本不一致, 程序可能也可以运行, 但是这个不是官方保证的. 为了使用一个特定版本的库, 要么克隆那个版本的仓库, 要么使用dep(https://golang.github.io/dep/)或者go modules(https://github.com/golang/go/wiki/Modules)一类的包管理器.
(4) 需要调用如下命令:
go get github.com/apache/thrift/lib/go/thrift
首先给出shared.thrift文件的定义:
/**
* 这个Thrift文件包含一些共享定义
*/ namespace go shared struct SharedStruct {
1: i32 key
2: string value
} service SharedService {
SharedStruct getStruct(1: i32 key)
}
然后给出tutorial.thrift的定义:
/**
* Thrift引用其他thrift文件, 这些文件可以从当前目录中找到, 或者使用-I的编译器参数指示.
* 引入的thrift文件中的对象, 使用被引入thrift文件的名字作为前缀, 例如shared.SharedStruct.
*/
include "shared.thrift" namespace go tutorial // 定义别名
typedef i32 MyInteger /**
* 定义常量. 复杂的类型和结构体使用JSON表示法.
*/
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} /**
* 枚举是32位数字, 如果没有显式指定值,从1开始.
*/
enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
} /**
* 结构体由一组成员来组成, 一个成员包括数字标识符, 类型, 符号名, 和一个可选的默认值.
* 成员可以加"optional"修饰符, 用来表明如果这个值没有被设置, 那么他们不会被串行化到
* 结果中. 不过这个在有些语言中, 需要显式控制.
*/
struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: optional string comment,
} // 结构体也可以作为异常
exception InvalidOperation {
1: i32 whatOp,
2: string why
} /**
* 服务需要一个服务名, 加上一个可选的继承, 使用extends关键字
*/
service Calculator extends shared.SharedService { /**
* 方法定义和C语言一样, 有返回值, 参数或者一些它可能抛出的异常, 参数列表和异常列表的
* 写法与结构体中的成员列表定义一致.
*/ void ping(), i32 add(1:i32 num1, 2:i32 num2), i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), /**
* 这个方法有oneway修饰符, 表示客户段发送一个请求, 然后不会等待回应. Oneway方法
* 的返回值必须是void
*/
oneway void zip() }
将上述文件放置在同一个文件夹, 然后编译上述的thrift文件:
$ thrift -r --gen go tutorial.thrift
然后在gen-go的子文件夹中, 可以看到编译生成的go文件. 下面, 我们来分析一下生成的go文件, 这里, 我们只分析调用如下命令生成的go文件:
$ thrift --gen go shared.thrift
调用上述命令, 在gen-go子文件夹中会有个文件夹叫做shared, 这个文件夹对应go中的包名, shared文件夹中有shared-consts.go, shared.go, GoUnusedProtection__.go, 以及一个shared_service-remote文件夹.
关于GoUnusedProtection__.go文件, 具体用处不太清楚.
关于shared-consts.go文件, 这个文件用来定义thrift中的常量.
关于shared.go文件, 我们从上到下, 简单地看一下:
(1) SharedStruct结构体, 这个结构体对应于thrift中的SharedStruct, 这个结构体中的成员都是大写开头的, 表示可以额直接访问, 还有以下函数:
1) 生成结构体的函数, NewSharedStruct
2) 获取结构体中成员的函数, 包括 GetKey和GetValue
3) 从Protocol中读取数据, 设置自身值的Read函数
4) 从Protocol中读取数据, 设置第N个字段的ReadField1和ReadField2函数
5) 将自身值写入到Protocol中的Write函数
6) 将自身第N个字段写入到Protocol中的writeField1和writeField2函数 (这两个函数在包外不可见)
7) 返回结构体的字符串表示的String函数
(2) thrift文件中SharedService服务对应的接口:
type SharedService interface {
// Parameters:
// - Key
GetStruct(ctx context.Context, key int32) (r *SharedStruct, err error)
}
(3) SharedServiceClient结构体, 及其相关函数
type SharedServiceClient struct {
c thrift.TClient
}
1) 构造函数, 包括如下方式:
func NewSharedServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SharedServiceClient
func NewSharedServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SharedServiceClient
func NewSharedServiceClient(c thrift.TClient) *SharedServiceClient
2) SharedServiceClient实现SharedService接口的函数:
func (p *SharedServiceClient) GetStruct(ctx context.Context, key int32) (r *SharedStruct, err error) {
var _args0 SharedServiceGetStructArgs
_args0.Key = key
var _result1 SharedServiceGetStructResult
if err = p.Client_().Call(ctx, "getStruct", &_args0, &_result1); err != nil {
return
}
return _result1.GetSuccess(), nil
}
(3) SharedServiceProcessor结构体, 及其函数:
type SharedServiceProcessor struct {
processorMap map[string]thrift.TProcessorFunction
handler SharedService
} // A processor is a generic object which operates upon an input stream and
// writes to some output stream.
type TProcessor interface {
Process(ctx context.Context, in, out TProtocol) (bool, TException)
}
1) 构造函数
func NewSharedServiceProcessor(handler SharedService) *SharedServiceProcessor { self2 := &SharedServiceProcessor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)}
self2.processorMap["getStruct"] = &sharedServiceProcessorGetStruct{handler:handler}
return self2
}
2)操作处理函数(processorMap)的方法
func (p *SharedServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction)
func (p *SharedServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool)
func (p *SharedServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction
3) 事件循环处理函数
func (p *SharedServiceProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
name, _, seqId, err := iprot.ReadMessageBegin()
if err != nil { return false, err }
if processor, ok := p.GetProcessorFunction(name); ok {
return processor.Process(ctx, seqId, iprot, oprot)
}
iprot.Skip(thrift.STRUCT)
iprot.ReadMessageEnd()
x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function " + name)
oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId)
x3.Write(oprot)
oprot.WriteMessageEnd()
oprot.Flush(ctx)
return false, x3 }
(4) sharedServiceProcessorGetStruct 用来实现SharedService的GetStruct函数, 这个结构体需要实现TProcessorFunction接口, 所以需要实现TProcessorFunction的Process函数.
type sharedServiceProcessorGetStruct struct {
handler SharedService
} type TProcessorFunction interface {
Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException)
}
sharedServiceProcessorGetStruct需要实现TProcessorFunction接口中的Process函数.
(5) SharedServiceGetStructArgs是传入thrift中GetStruct函数的参数, 实现细节类似于上面的SharedStruct结构体.
(6) SharedServiceGetStructResult是thrift中GetStruct函数的结果, 实现细节类似于上面的SharedStruct结构体.
关于shared_service-remote中的文件, 是shared.go文件中结构体和函数的使用示例, 可以参考这个go文件写出使用thrift的程序.
服务端服务结构体
import (
"context"
"fmt"
"strconv"
"shared"
"tutorial"
) type CalculatorHandler struct {
log map[int]*shared.SharedStruct
} func NewCalculatorHandler() *CalculatorHandler {
return &CalculatorHandler{log: make(map[int]*shared.SharedStruct)}
} func (p *CalculatorHandler) Ping(ctx context.Context) (err error) {
fmt.Print("ping()\n")
return nil
} func (p *CalculatorHandler) Add(ctx context.Context, num1 int32, num2 int32) (retval17 int32, err error) {
fmt.Print("add(", num1, ",", num2, ")\n")
return num1 + num2, nil
} func (p *CalculatorHandler) Calculate(ctx context.Context, logid int32, w *tutorial.Work) (val int32, err error) {
fmt.Print("calculate(", logid, ", {", w.Op, ",", w.Num1, ",", w.Num2, "})\n")
switch w.Op {
case tutorial.Operation_ADD:
val = w.Num1 + w.Num2
break
case tutorial.Operation_SUBTRACT:
val = w.Num1 - w.Num2
break
case tutorial.Operation_MULTIPLY:
val = w.Num1 * w.Num2
break
case tutorial.Operation_DIVIDE:
if w.Num2 == 0 {
ouch := tutorial.NewInvalidOperation()
ouch.WhatOp = int32(w.Op)
ouch.Why = "Cannot divide by 0"
err = ouch
return
}
val = w.Num1 / w.Num2
break
default:
ouch := tutorial.NewInvalidOperation()
ouch.WhatOp = int32(w.Op)
ouch.Why = "Unknown operation"
err = ouch
return
}
entry := shared.NewSharedStruct()
entry.Key = logid
entry.Value = strconv.Itoa(int(val))
k := int(logid)
/*
oldvalue, exists := p.log[k]
if exists {
fmt.Print("Replacing ", oldvalue, " with ", entry, " for key ", k, "\n")
} else {
fmt.Print("Adding ", entry, " for key ", k, "\n")
}
*/
p.log[k] = entry
return val, err
} func (p *CalculatorHandler) GetStruct(ctx context.Context, key int32) (*shared.SharedStruct, error) {
fmt.Print("getStruct(", key, ")\n")
v, _ := p.log[int(key)]
return v, nil
} func (p *CalculatorHandler) Zip(ctx context.Context) (err error) {
fmt.Print("zip()\n")
return nil
}
服务端代码
import (
"crypto/tls"
"fmt"
"github.com/apache/thrift/lib/go/thrift"
"tutorial"
) 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 := NewCalculatorHandler()
processor := tutorial.NewCalculatorProcessor(handler)
server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory) fmt.Println("Starting the simple server... on ", addr)
return server.Serve()
}
客户端代码
import (
"context"
"crypto/tls"
"fmt"
"tutorial" "github.com/apache/thrift/lib/go/thrift"
) var defaultCtx = context.Background() func handleClient(client *tutorial.CalculatorClient) (err error) {
client.Ping(defaultCtx)
fmt.Println("ping()") sum, _ := client.Add(defaultCtx, 1, 1)
fmt.Print("1+1=", sum, "\n") work := tutorial.NewWork()
work.Op = tutorial.Operation_DIVIDE
work.Num1 = 1
work.Num2 = 0
quotient, err := client.Calculate(defaultCtx, 1, work)
if err != nil {
switch v := err.(type) {
case *tutorial.InvalidOperation:
fmt.Println("Invalid operation:", v)
default:
fmt.Println("Error during operation:", err)
}
return err
} else {
fmt.Println("Whoa we can divide by 0 with new value:", quotient)
} work.Op = tutorial.Operation_SUBTRACT
work.Num1 = 15
work.Num2 = 10
diff, err := client.Calculate(defaultCtx, 1, work)
if err != nil {
switch v := err.(type) {
case *tutorial.InvalidOperation:
fmt.Println("Invalid operation:", v)
default:
fmt.Println("Error during operation:", err)
}
return err
} else {
fmt.Print("15-10=", diff, "\n")
} log, err := client.GetStruct(defaultCtx, 1)
if err != nil {
fmt.Println("Unable to get struct:", err)
return err
} else {
fmt.Println("Check log:", log.Value)
}
return err
} func runClient(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string, secure bool) error {
var transport thrift.TTransport
var err error
if secure {
cfg := new(tls.Config)
cfg.InsecureSkipVerify = true
transport, err = thrift.NewTSSLSocket(addr, cfg)
} else {
transport, err = thrift.NewTSocket(addr)
}
if err != nil {
fmt.Println("Error opening socket:", err)
return err
}
transport, err = transportFactory.GetTransport(transport)
if err != nil {
return err
}
defer transport.Close()
if err := transport.Open(); err != nil {
return err
}
iprot := protocolFactory.GetProtocol(transport)
oprot := protocolFactory.GetProtocol(transport)
return handleClient(tutorial.NewCalculatorClient(thrift.NewTStandardClient(iprot, oprot)))
}
生成应用代码
import (
"flag"
"fmt"
"github.com/apache/thrift/lib/go/thrift"
"os"
) func Usage() {
fmt.Fprint(os.Stderr, "Usage of ", os.Args[0], ":\n")
flag.PrintDefaults()
fmt.Fprint(os.Stderr, "\n")
} func main() {
flag.Usage = Usage
server := flag.Bool("server", false, "Run server")
protocol := flag.String("P", "binary", "Specify the protocol (binary, compact, json, simplejson)")
framed := flag.Bool("framed", false, "Use framed transport")
buffered := flag.Bool("buffered", false, "Use buffered transport")
addr := flag.String("addr", "localhost:9090", "Address to listen to")
secure := flag.Bool("secure", false, "Use tls secure transport") flag.Parse() var protocolFactory thrift.TProtocolFactory
switch *protocol {
case "compact":
protocolFactory = thrift.NewTCompactProtocolFactory()
case "simplejson":
protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
case "json":
protocolFactory = thrift.NewTJSONProtocolFactory()
case "binary", "":
protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
default:
fmt.Fprint(os.Stderr, "Invalid protocol specified", protocol, "\n")
Usage()
os.Exit(1)
} var transportFactory thrift.TTransportFactory
if *buffered {
transportFactory = thrift.NewTBufferedTransportFactory(8192)
} else {
transportFactory = thrift.NewTTransportFactory()
} if *framed {
transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
} if *server {
if err := runServer(transportFactory, protocolFactory, *addr, *secure); err != nil {
fmt.Println("error running server:", err)
}
} else {
if err := runClient(transportFactory, protocolFactory, *addr, *secure); err != nil {
fmt.Println("error running client:", err)
}
}
}
启动程序命令
$./thrift -server
$ ./thrift
以上就是使用go语言的thrift示例.
thrift简单示例 (go语言)的更多相关文章
- Python Thrift 简单示例
本文基于Thrift-0.10,使用Python实现服务器端,使用Java实现客户端,演示了Thrift RPC调用示例.Java客户端提供两个字符串参数,Python服务器端计算这两个字符串的相似度 ...
- thrift简单示例 (基于C++)
这个thrift的简单示例, 来源于官网 (http://thrift.apache.org/tutorial/cpp), 因为我觉得官网的例子已经很简单了, 所以没有写新的示例, 关于安装的教程, ...
- Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例
目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...
- [转]Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例
本文转自:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html 目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装 ...
- SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论 SignalR 简单示例 通过三个DEMO学会SignalR的三种实现方式 SignalR推送框架两个项目永久连接通讯使用 SignalR 集线器简单实例2 用SignalR创建实时永久长连接异步网络应用程序
SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论 异常汇总:http://www ...
- C#调用Python脚本的简单示例
C#调用Python脚本的简单示例 分类:Python (2311) (0) 举报 收藏 IronPython是一种在 .NET及 Mono上的 Python实现,由微软的 Jim Huguni ...
- Nodejs学习笔记(十五)—Node.js + Koa2 构建网站简单示例
前言 前面一有写到一篇Node.js+Express构建网站简单示例:http://www.cnblogs.com/zhongweiv/p/nodejs_express_webapp.html 这篇还 ...
- asp.net WebService的一个简单示例
不同的系统之间经常会需要数据的交换对接,而Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的.专门的第三方软件或硬件, 就可相互交换数据或集成.依据Web Service规范 ...
- 【转】Apache Thrift - 可伸缩的跨语言服务开发框架
Apache Thrift - 可伸缩的跨语言服务开发框架 Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的远程服务调用的框架.本文将从 Java 开发人员角度详 ...
随机推荐
- 在 json4s 中自定义CustomSerializer
到目前为止,Scala 环境下至少存在6种 Json 解析的类库,这里面不包括 Java 语言实现的 Json 类库.所有这些库都有一个非常相似的抽象语法树(AST).而 json4s 项目旨在提供一 ...
- 深入理解JVM虚拟机
JVM平台上还可以运行其他语言,运行的是Class字节码.只要能翻译成Class的语言就OK了.挺强大的. JVM厂商很多 垃圾收集器.收集算法 JVM检测工具 关于类的加载: Java代码中,类型( ...
- IntelliJ IDEA 安装使用 FindBugs 代码分析详述
1 下载 2 重启idea 选中文件,右键 附:一些常见的错误信息 Bad practice 代码中的一些坏习惯 Class names should start with an upper case ...
- 中国大互联网公司在github上的开源项目
公司名 账号数 账号名 总项目数 非fork项目数 百度 13 baidu.ApolloAuto. brpc. mipengine.Clouda-team.mesalock-linux. ecomfe ...
- 【APM】Pinpoint 安装部署(一)
Pinpoint简介 Pinpoint是用Java / PHP编写的大规模分布式系统的APM(应用程序性能管理)工具.受Dapper的启发,Pinpoint提供了一种解决方案,可通过跟踪跨分布式应用程 ...
- hackbar简单安装使用教程
安装hackbar: 在火狐的附加组件中搜索“hackbar”,将它添加到火狐浏览器中, 重启后Firefox后安装完成,按F9键打开我们就会看到在地址栏下面会出现一个大框框就是hackbar了 框框 ...
- [Sw] Swoole 生态迷局,基于 Swoole 的第 109 框架
这两天,又一全栈式 Swoole 协程框架面世了 - hyperf,实现思路是我内心点了赞同的,就集成现有 PHP 生态优质组件到 Swoole 的协程中来. 有人想到,为什么不是 Swoole 集成 ...
- hbase 安装(集群模式)
环境:jdk 1.8 + hadoop2.7.6+zookeeper3.4.9+centos7 一.安装zookeeper(集群模式) 0.安装机器 ip ...
- OTP详解
OTP(One Time Programmable)是单片机的一种存储器类型,意思是一次性可编程:程序烧入单片机后,将不可再次更改和清除. 随着嵌入式应用的越来越广泛,产品的安全也显得越来越重要.一方 ...
- SQL IN 子查询返回多对值
我们常用的IN 操作是这样的: select * from tab twhere t.col1 in ('value1''value2');12但是如果是多个列的取值来自同一个子查询呢? 我们是不是要 ...