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
    

歡迎找歪歪梯聊騷

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章