go使用twirp开发rpc

go使用twirp开发rpc

twirp简介

twirp是谷歌开源的rpc框架,默认支持golang并提供其他语言的实现版本,使用proto进行rpc定义开发。

安装

安装proto插件和twirp插件

go get github.com/twitchtv/twirp/protoc-gen-twirp
go get github.com/golang/protobuf/protoc-gen-go

开发

  • 编写proto文件
    //文件名test.proto
    syntax = "proto3";
    
    //包名,通过protoc生成时go文件时
    package main;
    
    message A{
    }
    message B {
    	string result = 1;
    }
    
    service Test{
    	rpc hello(A) returns(B);
    }
    
  • 生成go代码
    proto_path指定proto文件所在目录,twirp_out指定生成的rpc代码存放目录,go_out指定生成的modal代码存放目录,最后一个参数是编译的proto文件路径
    protoc --proto_path=. --twirp_out=. --go_out=. ./test.proto
    
  • server与hook
    阅读twirp_out中的代码
    默认生成的Server结构体以service名称+Server作为名称
    默认生成的service接口以service名称为名
    主要方法有
    type Test interface {
    	Hello(context.Context, *A) (*B, error)
    }
    type testJSONClient struct {
    	client HTTPClient
    	urls   [1]string
    	opts   twirp.ClientOptions
    }
    //proto定义的rpc方法
    func (c *testJSONClient) Hello(ctx context.Context, in *A) (*B, error) {
    	ctx = ctxsetters.WithPackageName(ctx, "main")
    	ctx = ctxsetters.WithServiceName(ctx, "Test")
    	ctx = ctxsetters.WithMethodName(ctx, "Hello")
    	out := new(B)
    	err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
    	if err != nil {
    		twerr, ok := err.(twirp.Error)
    		if !ok {
    			twerr = twirp.InternalErrorWith(err)
    		}
    		callClientError(ctx, c.opts.Hooks, twerr)
    		return nil, err
    	}
    	//调用钩子函数
    	callClientResponseReceived(ctx, c.opts.Hooks)
    	return out, nil
    }
    
    type testServer struct {
    	Test
    	hooks *twirp.ServerHooks
    }
    
    func NewTestServer(svc Test, hooks *twirp.ServerHooks) TwirpServer {
    	return &testServer{
    		Test:  svc,
    		hooks: hooks,
    	}
    }
    
    其中,twirp.ClientOptions的Hooks成员类型为ClientHooks
    type ClientHooks struct {
    	RequestPrepared func(context.Context, *http.Request) (context.Context, error)
    	ResponseReceived func(context.Context)
    	Error func(context.Context, Error)
    }
    
    而twirp.ServerHooks恰好是他的一个实现
    type ServerHooks struct {
    	RequestReceived func(context.Context) (context.Context, error)
    	RequestRouted func(context.Context) (context.Context, error)
    	ResponsePrepared func(context.Context) context.Context
    	ResponseSent func(context.Context)
    	Error func(context.Context, Error) context.Context
    }
    
    而TwirpServer 是一个继承http.Handler的接口
    type TwirpServer interface {
    	http.Handler
    	ServiceDescriptor() ([]byte, int)
    	ProtocGenTwirpVersion() string
    	PathPrefix() string
    }
    
    所以核心就在NewTestServer这个方法
    他的第一个参数传递service的实现对象
    第二个参数传递twirp.ServerHooks引用类型的钩子对象,实现rpc调用的拦截
    最终方法返回一个http.Handler对象,就可以通过http.ListenAndServe方法,运行这个handler

实现rpc

编写实现代码,因为proto生成的代码都在相同目录,且包名都是main,所以这里面可以直接使用生成代码中的结构而不需要导入文件

  • 服务端
    http.ListenAndServe返回值为error,如果输出为空代表启动正常,否则为出错原因
    package main
    
    import (
    	context "context"
    	"fmt"
    	"net/http"
    
    	twirp "github.com/twitchtv/twirp"
    )
    
    type TestImpl struct{}
    
    func (t TestImpl) Hello(ctx context.Context, a *A) (*B, error) {
    	println("hello")
    	//返回结果Result字段为"server result"
    	return &B{Result: "server result"}, nil
    }
    
    //重写RequestReceived,在请求发起时打印
    var testHook = &twirp.ServerHooks{
    	RequestReceived: func(ctx context.Context) (context.Context, error) {
    		println("receive ...")
    		return ctx, nil
    	},
    	ResponseSent: func(ctx context.Context) {
    		println("response ...")
    	},
    }
    
    func main() {
    	twirpHandler := NewTestServer(TestImpl{}, testHook)
    	fmt.Printf("%v\n", http.ListenAndServe(":8080", twirpHandler))
    }
    
  • 客户端
    接下来编写客户端看看结果
    //+build ignore
    
    package main
    
    import (
    	"context"
    	"net/http"
    )
    
    func main() {
    	client := NewTestProtobufClient("http://localhost:8080", &http.Client{})
    	res, _ := client.Hello(context.Background(), &A{})
    	println(res.Result)
    }
    
  • 运行
    运行服务端程序以后再启动客户端,可以看到服务端打印
    receive ...
    hello
    response ...
    
    客户端输出
    server result
    

欢迎找歪歪梯聊骚

原创文章 53 获赞 12 访问量 9129

猜你喜欢

转载自blog.csdn.net/weixin_44627989/article/details/106102978