zoukankan      html  css  js  c++  java
  • 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()
    ")
    	return nil
    }
    
    func (p *CalculatorHandler) Add(ctx context.Context, num1 int32, num2 int32) (retval17 int32, err error) {
    	fmt.Print("add(", num1, ",", num2, ")
    ")
    	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, "})
    ")
    	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, "
    ")
    	   } else {
    	     fmt.Print("Adding ", entry, " for key ", k, "
    ")
    	   }
    	*/
    	p.log[k] = entry
    	return val, err
    }
    
    func (p *CalculatorHandler) GetStruct(ctx context.Context, key int32) (*shared.SharedStruct, error) {
    	fmt.Print("getStruct(", key, ")
    ")
    	v, _ := p.log[int(key)]
    	return v, nil
    }
    
    func (p *CalculatorHandler) Zip(ctx context.Context) (err error) {
    	fmt.Print("zip()
    ")
    	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
    ", 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, "
    ")
    
    	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, "
    ")
    	}
    
    	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], ":
    ")
    	flag.PrintDefaults()
    	fmt.Fprint(os.Stderr, "
    ")
    }
    
    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, "
    ")
    		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示例.

  • 相关阅读:
    java2: HttpClient,实现登录,请求等操作,session保持
    bat使用1
    java4: 读取配置文件
    HTML+CSS学习笔记(一)
    第1章:JavaScript简介
    第2章:在HTML中使用JavaScript
    HTML+CSS学习笔记(二)
    ASP.NET防止页面刷新的方法
    上下左右无空隙不间断图片连续滚动代码
    SQL Server2005新加的功能排名函数
  • 原文地址:https://www.cnblogs.com/albizzia/p/10885786.html
Copyright © 2011-2022 走看看