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名稱爲名
主要方法有
其中,twirp.ClientOptions的Hooks成員類型爲ClientHookstype 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.ServerHooks恰好是他的一個實現type ClientHooks struct { RequestPrepared func(context.Context, *http.Request) (context.Context, error) ResponseReceived func(context.Context) Error func(context.Context, Error) }
而TwirpServer 是一個繼承http.Handler的接口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 }
所以核心就在NewTestServer這個方法type TwirpServer interface { http.Handler ServiceDescriptor() ([]byte, int) ProtocGenTwirpVersion() string PathPrefix() string }
他的第一個參數傳遞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