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示例.

原文地址:https://www.cnblogs.com/albizzia/p/10885786.html

时间: 2024-10-29 19:12:03

thrift简单示例 (go语言)的相关文章

thrift简单示例 (基于C++)

这个thrift的简单示例, 来源于官网 (http://thrift.apache.org/tutorial/cpp), 因为我觉得官网的例子已经很简单了, 所以没有写新的示例, 关于安装的教程, 可以参考https://www.cnblogs.com/albizzia/p/10838646.html, 关于thrift文件的语法, 可以参考: https://www.cnblogs.com/albizzia/p/10838646.html. thrift文件 首先给出shared.thrif

RPC学习----Thrift快速入门和Java简单示例

一.什么是RPC? RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据.在OSI网络通信模型中,RPC跨越了传输层和应用层.RPC使得开发包括网络分布式多程序在内的应用程序更加容易. 二.什么是Thrift? thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发.它结合了功能强大的软件堆栈和

HMM的维特比算法简单示例

今天读了一位大牛的关于HMM的技术博客,读完之后,写了一个关于维特比算法的简单示例,用scala和java语言混合编写的.现在上传之. package com.txq.hmm import java.utilimport scala.collection._ /** * HMM维特比算法,根据显示状态链条估计隐式链条 * @param states 隐式states * @param observations 显式states * @param start_probability 初始概率向量

asp.net WebService的一个简单示例

不同的系统之间经常会需要数据的交换对接,而Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的.专门的第三方软件或硬件, 就可相互交换数据或集成.依据Web Service规范实施的应用之间, 无论它们所使用的语言. 平台或内部协议是什么, 都可以相互交换数据.Web Service是自描述. 自包含的可用网络模块, 可以执行具体的业务功能.Web Service也很容易部署, 因为它们基于一些常规的产业标准以及已有的一些技术,诸如标准通用标记语言下的子集XML.HTTP

【转】Apache Thrift - 可伸缩的跨语言服务开发框架

Apache Thrift - 可伸缩的跨语言服务开发框架 Apache Thrift 是 Facebook 实现的一种高效的.支持多种编程语言的远程服务调用的框架.本文将从 Java 开发人员角度详细介绍 Apache Thrift 的架构.开发和部署,并且针对不同的传输协议和服务类型给出相应的 Java 实例,同时详细介绍 Thrift 异步客户端的实现,最后提出使用 Thrift 需要注意的事项. 12 评论 黄 晓军, 实习生, IBM 张 静, 软件工程师, IBM 张 凯, 高级软件

xcode江湖录-第04章 风水宝地--界面生成器之StoryBoard简单示例 与 约束

第04章风水宝地--界面生成器之StoryBoard简单示例 ??如何设置转场动画?? ??如何在参与到转场动作中?? ??如何让页面跳转到自定义VC?? ??如何设置自定义跳转模式?? ??如何用StoryBoard中的VC生成对象?? 01:新建SingleViewApplication,命名为StoryBoardTest. 02:打开Main.Storyboard,如下: 03:通过Object Library选中View Controller(A controller that mana

AMQP消息队列之RabbitMQ简单示例

前面一篇文章讲了如何快速搭建一个ActiveMQ的示例程序,ActiveMQ是JMS的实现,那这篇文章就再看下另外一种消息队列AMQP的代表实现RabbitMQ的简单示例吧.在具体讲解之前,先通过一个图来概览下: 1.添加Maven依赖 <!-- rabbitmq begin --> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit

spring-servlet.xml简单示例

spring-servlet.xml简单示例 某个项目中的spring-servlet.xml 记下来以后研究用 1 <!-- springMVC简单配置 --> 2 <?xml version="1.0" encoding="UTF-8"?> 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://w

递归学习(一)最简单的C语言递归求年龄算法

递归是我们在学习编程中,必须要去学习的,虽然递归晦涩难懂 ,但是很多时候,递归的思想会很有用,但是在实际开发中,不建议使用递归,要用循环来代替递归,不然bug无穷. ----------------------------------------------------------- 问题描述: 有5个人坐在一起, 问第5个人,他说比第4个人大2岁, 问第4个人,他说比第3个人大2岁, 问第3个人,他说比第2个人大2岁, 问第2个人,他说比第1个人大2岁, 问最后一个人,他说10岁 第5个人多大