Go:gRPC-Gateway 完全指南
大家好,我是程序員幽鬼。
gRPC 越來越流行,相關的插件也很多,今天介紹的就是一個 gRPC 插件。
gRPC-Gateway 是一個插件,它爲 gRPC 服務生成反向代理服務器,將 Restful/JSON 轉換爲 gRPC,反之亦然。
換句話說,gRPC-Gateway 將在你的 gRPC 服務上創建一個層,該層將充當客戶端的 Restful/JSON 服務。gRPC-Gateway 從 gRPC 服務的 Protocol Buffer 的定義生成代碼。
1、介紹
gRPC-Gateway 是 protoc 的插件,將從 gRPC 定義生成 Go 代碼。
生成的代碼可以用作獨立服務器或安裝在現有代碼庫上。gRPC-Gateway 是高度可定製的,支持從 protoc 文件生成開放 API 文檔。
在本教程指南中,我們將詳細介紹獨立服務器以及與現有代碼的集成。查看此流程圖以瞭解 gRPC 網關的工作原理。
gRPC-Gateway flowchart diagram
2、爲什麼選擇 gRPC-Gateway?
gRPC 網關爲 gRPC 服務構建代理,該代理充當客戶端的 Restful/JSON 應用程序。它開啓了使用相同代碼庫同時支持 Restful/JSON 和 gRPC 的可能性。有兩個主要的場景:
-
舊版客戶端可能不支持 gRPC 並需要 Restful/JSON 接口
-
瀏覽器可能不支持開箱即用的 gRPC;因此對於想要與 gRPC 服務交互的 Web 客戶端,gRPC-Gateway 是首選選項。
最常見的 gRPC-Gateway 模式是創建單個 gRPC 網關服務器(可能在多臺機器上運行),作爲客戶端的代理與多個 gRPC 服務交互。
下圖解釋了此服務的工作原理。
gRPC-Gateway and service requests flowchart diagram
gRPC 網關生成的反向代理被水平擴展以在多臺機器上運行,並且在這些實例之前使用負載均衡器。單個實例可以託管多個 gRPC 服務的反向代理。
3、設置 gRPC 網關
gRPC-Gateway 是 protoc 的插件。在使用它之前,必須在系統上安裝 protocol buffer 編譯器。按照官方 gRPC 網站 [1] 上的指南,根據你使用的操作系統在你的系統上安裝 protoc。
gRPC-Gateway 使用並生成 Go 代碼。要安裝 Go,請按照官方 [2] 網站上的指南進行操作。只要你的系統上安裝了 Go,你就可以安裝 gRPC-Gateway 插件了。
創建一個名爲 grpc-gateway-demo
的目錄,該目錄將保存 gRPC-Gateway 項目。爲了構建 protocol buffer 和生成 gRPC 網關反向代理,將使用 Buf。你可以按照官方網站 [3] 上的指南安裝 Buf 。
項目結構
所有的 Protocol Buffers 文件都將在 proto
目錄中,而 Go 文件將在 root
。要設置 Go 項目,請使用 go mod init grpc-gateway-demo
並創建一個 main.go
文件。你的項目應如下所示:
├── main.go
├── go.mod
└── proto
配置 Buf
Buf 需要三個不同的文件來生成存根和反向代理。
buf.gen.yaml
這些文件指定編譯器應該使用的所有插件和相關選項。
使用 Buf,你可以簡單地在 YAML 文件中指定名稱和選項。Buf 還允許構建代碼使用遠程插件(即,指定的插件將在構建過程中由 Buf 自動下載並由本地系統上的 Buf 維護)。
version: v1
plugins:
# generate go structs for protocol buffer defination
- remote: buf.build/library/plugins/go:v1.27.1-1
out: gen/go
opt:
- paths=source_relative
# generate gRPC stubs in golang
- remote: buf.build/library/plugins/go-grpc:v1.1.0-2
out: gen/go
opt:
- paths=source_relative
# generate reverse proxy from protocol definations
- remote: buf.build/grpc-ecosystem/plugins/grpc-gateway:v2.6.0-1
out: gen/go
opt:
- paths=source_relative
# generate openapi documentation for api
- remote: buf.build/grpc-ecosystem/plugins/openapiv2:v2.6.0-1
out: gen/openapiv2
buf.yaml
該文件應位於所有 proto 文件的根目錄中。這些文件指定編譯 proto 文件(例如 Google API)所需的依賴項。
version: v1
deps:
# adding well known types by google
- buf.build/googleapis/googleapis
buf.work.yaml
此文件指定工作空間中包含 Protocol Buffer 定義的所有文件夾 / 目錄。
version: v1
directories:
- proto
完成後,你的項目結構應與此類似:
├── buf.gen.yaml
├── buf.work.yaml
├── go.mod
├── main.go
└── proto
├── buf.yaml
在項目根目錄中你可以通過運行 buf build
命令來測試你的配置。
4、使用 gRPC 網關
到目前爲止,你已將 gRPC-Gateway 設置爲插件,但現在出現的問題是如何定義基本的 API 規範,如 HTTP 方法、URL 或請求正文。
爲了定義這些規範選項在 Protocol Buffers 上的 rpc
方法定義中使用的 service
內容,下面的示例將使其更加清晰。
proto/hello/hello_world.proto
:
// define syntax used in proto file
syntax = "proto3";
// options used by gRPC golang plugin(not related to gRPC gateway)
option go_package = "github.com/anshulrgoyal/grpc-gateway-demo;grpc_gateway_demo";
// well know type by google, gRPC gateway uses HTTP annotation.
import "google/api/annotations.proto";
package hello_world;
// simple message
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
// a gRPC service
service Greeter {
// SayHello is a rpc call and a option is defined for it
rpc SayHello (HelloRequest) returns (HelloReply) {
// option type is http
option (google.api.http) = {
// this is url, for RESTfull/JSON api and method
// this line means when a HTTP post request comes with "/v1/sayHello" call this rpc method over this service
post: "/v1/sayHello"
body: "*"
};
}
}
option
關鍵字用於爲 Rest 請求添加規範。選擇該 option
方法並指定該請求的路徑。
在上面的例子中,post
是請求的 HTTP 方法,/v1/sayHello
是響應。
你現在可以在項目根目錄中使用 buf generate
命令來構建代碼。
命令完成後,項目的根目錄中應該有一個 gen
目錄,裏面有 Go 代碼。這些文件包含 gRPC 和 gRPC 網關反向代理的存根。openapiv2
包含 Swagger UI 的開放 API 文檔。
gen
|-- go
| `-- hello
| |-- hello_world.pb.go
| |-- hello_world.pb.gw.go
| `-- hello_world_grpc.pb.go
`-- openapiv2
`-- hello
`-- hello_world.swagger.json
實現服務
本教程將在 Go 中實現 gRPC 服務器。任何 gRPC 實現對於 gRPC 網關都可以正常工作。
使用 Go 的優點是你可以在同一進程中運行 gRPC 服務和 gRPC-Gateway 生成的代碼。這是 Go 的 Greeter
服務實現。
sever/main.go:
package main
import (
"context"
"fmt"
"log"
"net"
// importing generated stubs
gen "grpc-gateway-demo/gen/go/hello"
"google.golang.org/grpc"
)
// GreeterServerImpl will implement the service defined in protocol buffer definitions
type GreeterServerImpl struct {
gen.UnimplementedGreeterServer
}
// SayHello is the implementation of RPC call defined in protocol definitions.
// This will take HelloRequest message and return HelloReply
func (g *GreeterServerImpl) SayHello(ctx context.Context, request *gen.HelloRequest) (*gen.HelloReply, error) {
return &gen.HelloReply{
Message: fmt.Sprintf("hello %s",request.Name),
},nil
}
func main() {
// create new gRPC server
server := grpc.NewServer()
// register the GreeterServerImpl on the gRPC server
gen.RegisterGreeterServer(server, &GreeterServerImpl{})
// start listening on port :8080 for a tcp connection
if l, err := net.Listen("tcp", ":8080"); err != nil {
log.Fatal("error in listening on port :8080", err)
} else {
// the gRPC server
if err:=server.Serve(l);err!=nil {
log.Fatal("unable to start server",err)
}
}
}
上述文件是 gRPC 服務的基本實現。它偵聽端口 8080。你可以在任何 gRPC 客戶端上對其進行測試。
在 gRPC 網關代理上註冊服務
gRPC 網關代理支持的每個 gRPC 服務器都需要在其上進行註冊。
在底層,gRPC 網關服務器將創建一個 gRPC 客戶端並使用它向提供的端點發出 gRPC 請求。你可以提供各種 DailOptions
註冊函數。
proxy/main.go
package main
import (
"context"
"log"
"net"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
gen "grpc-gateway-demo/gen/go/hello"
)
func main() {
// creating mux for gRPC gateway. This will multiplex or route request different gRPC service
mux:=runtime.NewServeMux()
// setting up a dail up for gRPC service by specifying endpoint/target url
err := gen.RegisterGreeterHandlerFromEndpoint(context.Background(), mux, "localhost:8080", []grpc.DialOption{grpc.WithInsecure()})
if err != nil {
log.Fatal(err)
}
// Creating a normal HTTP server
server:=http.Server{
Handler: mux,
}
// creating a listener for server
l,err:=net.Listen("tcp",":8081")
if err!=nil {
log.Fatal(err)
}
// start server
err = server.Serve(l)
if err != nil {
log.Fatal(err)
}
}
ServerMux
是一個多路複用器,它將根據 JSON/Restful 請求的路徑將請求路由到各種註冊服務。
grpc.WithInsecure()
dial 選項用於允許服務在不使用身份驗證的情況下連接到 gRPC 。localhost:8080
是一個 gPRC 服務正在運行的 URL - 因爲 Greet
(gRPC 服務構建之前看到)服務正在端口 8080 上運行,所以 localhost:8080
被使用。
一旦註冊了處理程序,mux
就可以處理 HTTP 請求了。在這裏,http
包中的 Go 標準 HTTP 服務器被使用。你也可以自由地使用其他實現,本文稍後將通過 gRPC 網關代理使用 Gin 來演示這一點 [4]。
ServerMux
實現 ServeHTTP
接口——它可以像Handler
在 HTTP 服務器中一樣使用。服務在 8081 端口上運行。
要啓動服務,只需在項目目錄的根目錄中運行 go run proxy/main.go
。
使用路徑參數
現在,如果你想讓 v1/sayHello
API 在 POST 調用中成爲 GET 調用並將數據作爲路徑參數傳遞,那麼在完成 gRPC 網關設置後,你無需更改代碼中的任何內容 — 只需更改協議緩衝區定義並重新生成存根,你都可以使用新的 API。
message HelloRequest {
string name = 1;
}
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get:"/v1/sayHello/{name}"
};
}
}
上述代碼段中提供的路徑是 /v1/sayHello/{name}
。你可以使用請求有效負載(HelloRequest
在本例中)中的任何鍵作爲路徑參數。如果你使用帶有 path 的 GET 請求/v1/sayHello/jane
,該請求將被路由到 Greeter.sayHello
gRPC 調用。你可以在 URL 中使用任意數量的路徑參數。
現在你對 gRPC 網關及其設置有了一些基本的瞭解。
我們使用的示例只是對 gRPC 網關的介紹,但要在生產環境中運行某些東西,你需要進行日誌記錄、跟蹤和錯誤處理。
5、常見的使用模式
對於任何準備好用於生產的系統,它都應該有一些錯誤處理並允許某種錯誤日誌記錄。
添加日誌記錄
本文的這一部分將演示如何將中間件與 gRPC 網關生成的代理一起使用。
ServerMux
實現了一個 Handler
接口,因此你可以使用任何中間件來包裝 ServerMux
和記錄傳入和輸出請求。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
要創建用於日誌記錄的中間件,你可以從 *Request
中提取與 HTTP 請求相關的信息,並從正在使用的 httpsnoop
包中提取有關響應的信息。
func withLogger(handler http.Handler) http.Handler {
// the create a handler
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
// pass the handler to httpsnoop to get http status and latency
m:=httpsnoop.CaptureMetrics(handler,writer,request)
// printing exracted data
log.Printf("http[%d]-- %s -- %s\n",m.Code,m.Duration,request.URL.Path)
})
}
該 withLogger
方法將封裝 Handler 接口並調用 snoop 以提取信息。在後臺,該 ServerHTTP
方法由 httpsnoop
包調用。
server := http.Server{
Handler: withLogger(mux),
}
這與 Go 生態系統中使用的任何其他處理程序沒有什麼不同。由於 ServerMux
是一個普通的處理程序,任何可用的中間件也可以與 gRPC 網關生成的反向代理一起使用。
錯誤處理
gRPC 網關已經帶有將 gRPC 錯誤代碼轉換爲客戶端使用的 HTTP 狀態的映射。例如,它會自動將衆所周知的和使用過的 gRPC 代碼映射到 HTTP 狀態。
InvalidArgument
轉換爲 400
(錯誤請求)。如需完整列表,你可以查看此鏈接 [5]。如果你有自定義要求,例如需要非常規狀態代碼,則可以使用 WithErrorhandler
帶有錯誤處理函數的選項——所有錯誤都將通過請求和響應編寫器傳遞給該函數。
runtime.WithErrorHandler(
func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, writer http.ResponseWriter, request *http.Request, err error) {}
)
錯誤處理函數獲取以下參數:
-
ctx
:上下文,保存有關執行的元數據 -
mux
:這是ServerMux
;它保存有關服務器的配置數據,例如應將哪個標頭傳遞給響應 -
marshaler
:將 Protocol Buffer 響應轉換爲 JSON 響應 -
writer
:這是客戶端的響應編寫器 -
request
:這請求包含客戶端發送的信息的對象 -
err
:gRPC 服務發送的錯誤
這是一個簡單的 WithErrorHandler
例子。在此示例中,無論錯誤如何,發生錯誤時請求的 HTTP 狀態都會更改爲 400
。
mux: = runtime.NewServeMux(
runtime.WithErrorHandler(func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, writer http.ResponseWriter, request *http.Request, err error) {
//creating a new HTTTPStatusError with a custom status, and passing error
newError:=runtime.HTTPStatusError{
HTTPStatus: 400,
Err: err,
}
// using default handler to do the rest of heavy lifting of marshaling error and adding headers
runtime.DefaultHTTPErrorHandler(ctx,mux,marshaler,writer,request,&newError)
}))
通過創建一個新錯誤並將其傳遞給 DefaultHTTPErrorHandler
。重要的是要注意,DefaultHTTPErrorHandler
在將錯誤轉換爲有效的 JSON 響應時,在後臺執行了大量工作——儘可能嘗試使用它。
HTTP 頭和 gRPC 元數據
gRPC 和 Restful/JSON 以不同的方式傳遞元數據。
在 Restful/JSON HTTP 中,標頭用於發送 HTTP 頭,而 gRPC 通過根據所使用的語言提供元數據接口來抽象發送元數據。
gRPC 網關提供了一個簡單的映射接口來將 gRPC 元數據轉換爲 HTTP 標頭,反之亦然。它還允許使用兩種不同的方法來處理標頭到元數據的轉換。
首先,WithOutgoingHeaderMatcher
處理從 gRPC 網關返回到客戶端的標頭。它將元數據轉換爲 HTTP 標頭(即,任何由 gRPC 服務傳遞的元數據都將作爲 HTTP 標頭髮送回客戶端)。
var allowedHeaders=map[string]struct{}{
"x-request-id": {},
}
func isHeaderAllowed(s string)( string,bool) {
// check if allowedHeaders contain the header
if _,isAllowed:=allowedHeaders[s];isAllowed {
// send uppercase header
return strings.ToUpper(s),true
}
// if not in the allowed header, don't send the header
return s, false
}
// usage
mux:=runtime.NewServeMux(
// convert header in response(going from gateway) from metadata received.
runtime.WithOutgoingHeaderMatcher(isHeaderAllowed))
此方法接受一個字符串,如果將標頭傳遞給客戶端,則返回 true,否則返回 false。
其次,WithMetadata
處理傳入的 HTTP 標頭(即 cookie、內容類型等)。它最常見的用例是獲取身份驗證令牌並將其傳遞給元數據。此處提取的 HTTP 標頭將在元數據中發送到 gRPC 服務。
mux := runtime.NewServeMux(
// handle incoming headers
runtime.WithMetadata(func(ctx context.Context, request *http.Request) metadata.MD {
header := request.Header.Get("Authorization")
// send all the headers received from the client
md := metadata.Pairs("auth",header)
return md
}),
它接受一個請求並返回元數據的函數。請注意轉換爲元數據的標頭,因爲客戶端、瀏覽器、負載均衡器和 CDN 都在其中。gRPC 的密鑰也有一些限制。
這是一個完整的例子:
package main
import (
"context"
"log"
"net"
"net/http"
"strings"
"github.com/felixge/httpsnoop"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
gen "grpc-gateway-demo/gen/go/hello"
)
func withLogger(handler http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
m:=httpsnoop.CaptureMetrics(handler,writer,request)
log.Printf("http[%d]-- %s -- %s\n",m.Code,m.Duration,request.URL.Path)
})
}
var allowedHeaders=map[string]struct{}{
"x-request-id": {},
}
func isHeaderAllowed(s string)( string,bool) {
// check if allowedHeaders contain the header
if _,isAllowed:=allowedHeaders[s];isAllowed {
// send uppercase header
return strings.ToUpper(s),true
}
// if not in the allowed header, don't send the header
return s, false
}
func main() {
// creating mux for gRPC gateway. This will multiplex or route request different gRPC service
mux := runtime.NewServeMux(
// convert header in response(going from gateway) from metadata received.
runtime.WithOutgoingHeaderMatcher(isHeaderAllowed),
runtime.WithMetadata(func(ctx context.Context, request *http.Request) metadata.MD {
header:=request.Header.Get("Authorization")
// send all the headers received from the client
md:=metadata.Pairs("auth",header)
return md
}),
runtime.WithErrorHandler(func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, writer http.ResponseWriter, request *http.Request, err error) {
//creating a new HTTTPStatusError with a custom status, and passing error
newError:=runtime.HTTPStatusError{
HTTPStatus: 400,
Err: err,
}
// using default handler to do the rest of heavy lifting of marshaling error and adding headers
runtime.DefaultHTTPErrorHandler(ctx,mux,marshaler,writer,request,&newError)
}))
// setting up a dail up for gRPC service by specifying endpoint/target url
err := gen.RegisterGreeterHandlerFromEndpoint(context.Background(), mux, "localhost:8080", []grpc.DialOption{grpc.WithInsecure()})
if err != nil {
log.Fatal(err)
}
// Creating a normal HTTP server
server:=http.Server{
Handler: withLogger(mux),
}
// creating a listener for server
l,err:=net.Listen("tcp",":8081")
if err!=nil {
log.Fatal(err)
}
// start server
err = server.Serve(l)
if err != nil {
log.Fatal(err)
}
}
查詢參數
默認支持查詢參數。你可以使用消息定義中的相同鍵將它們添加到路徑中。因此,如果你 HelloResponse
中有一個名爲 last_name
的密鑰,你可以輸入路徑 v1/sayHello/anshul?last_name=goyal
而無需更改網關代碼中的任何內容。
自定義響應
gRPC-Gateway 允許你在原始案例或 camelCase
中自定義響應中的鍵。。默認情況下它是 camelCase
,但你可以編輯 Marshaler 配置來更改它。
mux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{
Marshaler: &runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
UseProtoNames: true,
EmitUnpopulated: true,
},
UnmarshalOptions: protojson.UnmarshalOptions{
DiscardUnknown: true,
},
},
}),)
6、將 gRPC-Gateway 與 Gin 一起使用
Gin 是一個非常流行的 Go web 框架。你可以將 gRPC-Gateway 與 Gin 一起使用,因爲它只是一個處理程序。它將允許你在你的服務器上添加可能不是由 gRPC-Gateway 生成的其他路由。
package main
import (
"context"
"log"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
gen "grpc-gateway-demo/gen/go/hello"
)
var allowedHeaders=map[string]struct{}{
"x-request-id": {},
}
func isHeaderAllowed(s string)( string,bool) {
// check if allowedHeaders contain the header
if _,isAllowed:=allowedHeaders[s];isAllowed {
// send uppercase header
return strings.ToUpper(s),true
}
// if not in the allowed header, don't send the header
return s, false
}
func main() {
// creating mux for gRPC gateway. This will multiplex or route request different gRPC service
mux:=runtime.NewServeMux(
// convert header in response(going from gateway) from metadata received.
runtime.WithOutgoingHeaderMatcher(isHeaderAllowed),
runtime.WithMetadata(func(ctx context.Context, request *http.Request) metadata.MD {
header:=request.Header.Get("Authorization")
// send all the headers received from the client
md:=metadata.Pairs("auth",header)
return md
}),
runtime.WithErrorHandler(func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, writer http.ResponseWriter, request *http.Request, err error) {
//creating a new HTTTPStatusError with a custom status, and passing error
newError:=runtime.HTTPStatusError{
HTTPStatus: 400,
Err: err,
}
// using default handler to do the rest of heavy lifting of marshaling error and adding headers
runtime.DefaultHTTPErrorHandler(ctx,mux,marshaler,writer,request,&newError)
}))
// setting up a dail up for gRPC service by specifying endpoint/target url
err := gen.RegisterGreeterHandlerFromEndpoint(context.Background(), mux, "localhost:8080", []grpc.DialOption{grpc.WithInsecure()})
if err != nil {
log.Fatal(err)
}
// Creating a normal HTTP server
server:=gin.New()
server.Use(gin.Logger())
server.Group("v1/*{grpc_gateway}").Any("",gin.WrapH(mux))
// additonal route
server.GET("/test", func(c *gin.Context) {
c.String(http.StatusOK,"Ok")
})
// start server
err = server.Run(":8081")
if err != nil {
log.Fatal(err)
}
}
只需使用 gin.WrapH
帶有通配符路徑的方法,你就可以在服務器上使用 gin 了。如果需要,它允許你添加到服務器的路由。你還可以使用 HandlePath
將路由直接添加到 ServerMux 。
err = mux.HandlePath("GET", "test", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
w.Write([]byte("ok")
})
7、在同一端口上運行反向代理和 gRPC 服務
可以在一個端口上運行這兩種服務。你可以通過使用 cmux
包來做到這一點。
cmux
將通過區分使用的協議來拆分 gRPC 流量和 RestFull/JSON,因爲 gRPC 將使用 HTTP2,而 RestFull/JSON 將使用 HTTP1。
package main
import (
"context"
"fmt"
"log"
"net"
"net/http"
"github.com/felixge/httpsnoop"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/soheilhy/cmux"
// importing generated stubs
gen "grpc-gateway-demo/gen/go/hello"
"google.golang.org/grpc"
)
// GreeterServerImpl will implement the service defined in protocol buffer definitions
type GreeterServerImpl struct {
gen.UnimplementedGreeterServer
}
// SayHello is the implementation of RPC call defined in protocol definitions.
// This will take HelloRequest message and return HelloReply
func (g *GreeterServerImpl) SayHello(ctx context.Context, request *gen.HelloRequest) (*gen.HelloReply, error) {
if err:=request.Validate();err!=nil {
return nil,err
}
return &gen.HelloReply{
Message: fmt.Sprintf("hello %s %s",request.Name,request.LastName),
},nil
}
func main() {
// create new gRPC server
grpcSever := grpc.NewServer()
// register the GreeterServerImpl on the gRPC server
gen.RegisterGreeterServer(grpcSever, &GreeterServerImpl{})
// creating mux for gRPC gateway. This will multiplex or route request different gRPC service
mux:=runtime.NewServeMux()
// setting up a dail up for gRPC service by specifying endpoint/target url
err := gen.RegisterGreeterHandlerFromEndpoint(context.Background(), mux, "localhost:8081", []grpc.DialOption{grpc.WithInsecure()})
if err != nil {
log.Fatal(err)
}
// Creating a normal HTTP server
server:=http.Server{
Handler: withLogger(mux),
}
// creating a listener for server
l,err:=net.Listen("tcp",":8081")
if err!=nil {
log.Fatal(err)
}
m := cmux.New(l)
// a different listener for HTTP1
httpL := m.Match(cmux.HTTP1Fast())
// a different listener for HTTP2 since gRPC uses HTTP2
grpcL := m.Match(cmux.HTTP2())
// start server
// passing dummy listener
go server.Serve(httpL)
// passing dummy listener
go grpcSever.Serve(grpcL)
// actual listener
m.Serve()
}
func withLogger(handler http.Handler) http.Handler {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
m:=httpsnoop.CaptureMetrics(handler,writer,request)
log.Printf("http[%d]-- %s -- %s\n",m.Code,m.Duration,request.URL.Path)
})
}
8、結論
本教程解釋了爲你的 gRPC 服務構建出色的 gRPC-Gateway 反向代理所需的所有要素。
自 gRPC-Gateway 以來,ServerMux 現在只是一個處理程序,你可以通過添加更多中間件(如主體壓縮、身份驗證和恐慌處理)來構建它。
你還可以使用 gRPC 網關配置。所有的代碼示例都可以在這裏 [6] 找到。
原文鏈接:https://dev.to/logrocket/an-all-in-one-guide-to-grpc-gateway-4g11
參考資料
[1]
官方 gRPC 網站: https://grpc.io/docs/protoc-installation/
[2]
官方: https://go.dev/doc/install
[3]
你可以按照官方網站: https://docs.buf.build/installation
[4]
使用 Gin 來演示這一點: https://blog.logrocket.com/building-microservices-go-gin/
[5]
鏈接: https://github.com/grpc-ecosystem/grpc-gateway/blob/7094a052b3287b9e99f52d95230789ab34d2d7c4/runtime/errors.go#L36
[6]
這裏: https://github.com/anshulrgoyal/grpc-gateway-demo
歡迎關注「幽鬼」,像她一樣做團隊的核心。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/uUj2eIoDnpss2RvzovhqKQ