基於 Twirp RPC 的簡易 JSON Api Gateway 實現
Twirp 是 Twichtv 2018 年開源的一套極簡 RPC 框架,當時官方的介紹文章 Twirp: a sweet new RPC framework for Go。功能上比不上大名鼎鼎的 grpc,但是勝在小巧靈活,相容 Go 原生的 http.Handler
,可以與現有的任意 Router
和中介軟體搭配使用;同時支援 protobuf 和 json 格式傳輸,除錯方便;另外我個人很喜歡它的 Error
實現,直觀並且可擴充套件性還不錯。
Twirp 支援 json 格式的 api 請求,前端可以直接接入使用;但是請求全部是統一的 POST 請求,url 是 /twirp/package.name/method 這種格式。就算能說服自己公司的前端接受這種接入方式,也不太好公開出去給第三方合作方使用。所以需要類似 grpc gateway 的東西,能提供 restful 風格的 api 出去。並且由於專案歷史原因,希望返回的資料按照如下格式返回:
// ok
{
"data": { // twirp 返回的資料沒有 data 這層包裝
"foo":"bar"
}
}
// error
{
"code": 10002, // twirp 返回的 error code 是 'permission_denied' 這種 string
"msg": "error msg"
}
需求明確之後剩下的實現也就清晰了。我們需要類似 httputil.ReverseProxy 那樣對收到的 request 按照 twirp 的要求進行改造,然後交給實現好的 twirp rpc service 去處理。對於 body 的處理,我們自定義一個 http.ResponsWriter 對 body 進行調整之後再寫入真正的 Writer。 下面我將使用 go-chi/chi 作為 Router 來實現一個簡單的 twirp api gateway 的例子。這個例子的完整程式碼開源在 yiplee/twirp-gateway-example。
/reversetwirp.go
將 url path params 以及 query params 打包到 body
修改 request method 為 POST
修改 url path 為對應的 /twirp/package.name/method
轉發給 twirp rpc handler 處理請求
使用示例見 /api/books
/render/response.go 包裝 Response
攔截 response status
如果是成功的返回,將 body 用 data 包裝後寫入真正的 ResponseWriter
如果是失敗的返回,還原 Twirp Error 然後構造新的 errorResponse 寫入 ResponseWriter
Server
Server 實現了 rpc 和 rest api 的 Handler
Route RPC
使用 path 字首匹配的方式將 rpc 請求路由到對應的實現上去
func (s Server) HandleRpc() http.Handler {
r := chi.NewRouter()
r.Use(resetRoutePath)
// book service
{
svc := book.New(s.Books)
r.Mount(svc.PathPrefix(), svc)
}
return r
}
Route Rest Api
提供 restful 風格的 api 請求,由 ReverseTwirp 實現
func (s Server) HandleApi() http.Handler {
r := chi.NewRouter()
r.Use(render.WrapResponse(true))
r.Route("/books", func(r chi.Router) {
svc := book.New(s.Books)
rt := reversetwirp.NewSingleTwirpServerProxy(svc)
r.Post("/", rt.Handle("Create", nil))
r.Get("/{id}", rt.Handle("Find", nil))
})
return r
}
啟動 Server
r := chi.NewMux()
r.Use(middleware.Recoverer)
r.Use(middleware.Logger)
books := book.New()
svr := handler.New(books)
r.Mount("/twirp", svr.HandleRpc())
r.Mount("/api", svr.HandleApi())
r.Mount("/hc", hc.Handler())
addr := fmt.Sprintf(":%d", cfg.port)
if err := http.ListenAndServe(addr, r); err != nil {
log.Fatal(err)
}
最後,我們既提供了由 twirp code generator 生成的 rpc 客戶端程式碼 可以被內部服務直接使用,也提供了
GET /api/books/:id
POST /api/books
這兩個 rest api 供前端使用。
完整程式碼見 yiplee/twirp-gateway-example。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 簡易RPC框架實現RPC框架
- Twirp:一個很酷的基於Go的新RPC框架GoRPC框架
- 基於Vue的簡易MVVM實現VueMVVM
- 基於Websocket的簡易webshell實現Webshell
- Go 實現簡易 RPC 框架GoRPC框架
- 基於react的hash路由簡易實現React路由
- 基於vue全家桶實現的簡易商城Vue
- 構建api gateway之 基於etcd實現動態配置同步APIGateway
- twirp: 支援protobuf服務定義的簡單RPC框架RPC框架
- 基於 WPF 的酷炫 GUI 視窗的簡易實現GUI
- 基於akka與scala實現一個簡單rpc框架RPC框架
- VNPY 一種基於統計的交易策略簡易實現
- 基於AOP和Redis實現的簡易版分散式鎖Redis分散式
- 基於 Mysql 實現一個簡易版搜尋引擎MySql
- Python基於Socket實現簡易多人聊天室Python
- 基於FFmpeg和Qt實現簡易影片播放器QT播放器
- 基於long pull實現簡易的訊息系統參考
- 簡易JSONJSON
- 基於Redis實現Spring Cloud Gateway的動態管理RedisSpringCloudGateway
- 簡易ApiAPI
- 實現基於json的級聯選單JSON
- 基於json資料格式實現的簡單資料庫——jsonDBJSON資料庫
- 構建api gateway之 http路由實現APIGatewayHTTP路由
- 簡述RPC原理實現RPC
- 基於“結構體”實現簡易版學生管理系統(Golang)結構體Golang
- namedtuple簡易實現
- 如何使用NodeJS構建基於RPC的API系統NodeJSRPCAPI
- netty 實現簡單的rpc呼叫NettyRPC
- Java實現最簡單的RPC框架JavaRPC框架
- 基於Redis的簡易延時佇列Redis佇列
- 手寫實現java棧結構,並實現簡易的計算器(基於字尾演算法)Java演算法
- 基於Spring-Cloud-Gateway開發API閘道器的思路SpringCloudGatewayAPI
- 簡易版 vue實現Vue
- 實現一個簡易的vueVue
- 造個輪子之基於 Netty 實現自己的 RPC 框架NettyRPC框架
- 「譯」如何使用 NodeJS 構建基於 RPC 的 API 系統NodeJSRPCAPI
- Java使用Netty實現簡單的RPCJavaNettyRPC
- 基於 git 打造簡易的 npm 私有倉庫GitNPM