精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

gRPC 這項技術真是太棒了,爆贊

開發 前端
gRPC 這項技術真是太棒了,接口約束嚴格,性能還高,在 k8s 和很多微服務框架中都有應用。

[[428427]]

gRPC 這項技術真是太棒了,接口約束嚴格,性能還高,在 k8s 和很多微服務框架中都有應用。

作為一名程序員,學就對了。

之前用 Python 寫過一些 gRPC 服務,現在準備用 Go 來感受一下原汁原味的 gRPC 程序開發。

本文的特點是直接用代碼說話,通過開箱即用的完整代碼,來介紹 gRPC 的各種使用方法。

代碼已經上傳到 GitHub,下面正式開始。

介紹

gRPC 是 Google 公司基于 Protobuf 開發的跨語言的開源 RPC 框架。gRPC 基于 HTTP/2 協議設計,可以基于一個 HTTP/2 鏈接提供多個服務,對于移動設備更加友好。

入門

首先來看一個最簡單的 gRPC 服務,第一步是定義 proto 文件,因為 gRPC 也是 C/S 架構,這一步相當于明確接口規范。

proto

  1. syntax = "proto3"
  2.  
  3. package proto; 
  4.  
  5. // The greeting service definition. 
  6. service Greeter { 
  7.     // Sends a greeting 
  8.     rpc SayHello (HelloRequest) returns (HelloReply) {} 
  9.  
  10. // The request message containing the user's name
  11. message HelloRequest { 
  12.     string name = 1; 
  13.  
  14. // The response message containing the greetings 
  15. message HelloReply { 
  16.     string message = 1; 

使用 protoc-gen-go 內置的 gRPC 插件生成 gRPC 代碼:

  1. protoc --go_out=plugins=grpc:. helloworld.proto 

執行完這個命令之后,會在當前目錄生成一個 helloworld.pb.go 文件,文件中分別定義了服務端和客戶端的接口:

  1. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 
  2. type GreeterClient interface { 
  3.     // Sends a greeting 
  4.     SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) 
  5.  
  6. // GreeterServer is the server API for Greeter service. 
  7. type GreeterServer interface { 
  8.     // Sends a greeting 
  9.     SayHello(context.Context, *HelloRequest) (*HelloReply, error) 

接下來就是寫服務端和客戶端的代碼,分別實現對應的接口。

server

  1. package main 
  2.  
  3. import ( 
  4.     "context" 
  5.     "fmt" 
  6.     "grpc-server/proto" 
  7.     "log" 
  8.     "net" 
  9.  
  10.     "google.golang.org/grpc" 
  11.     "google.golang.org/grpc/reflection" 
  12.  
  13. type greeter struct { 
  14.  
  15. func (*greeter) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloReply, error) { 
  16.     fmt.Println(req) 
  17.     reply := &proto.HelloReply{Message: "hello"
  18.     return reply, nil 
  19.  
  20. func main() { 
  21.     lis, err := net.Listen("tcp"":50051"
  22.     if err != nil { 
  23.         log.Fatalf("failed to listen: %v", err) 
  24.     } 
  25.  
  26.     server := grpc.NewServer() 
  27.     // 注冊 grpcurl 所需的 reflection 服務 
  28.     reflection.Register(server) 
  29.     // 注冊業務服務 
  30.     proto.RegisterGreeterServer(server, &greeter{}) 
  31.  
  32.     fmt.Println("grpc server start ..."
  33.     if err := server.Serve(lis); err != nil { 
  34.         log.Fatalf("failed to serve: %v", err) 
  35.     } 

client

  1. package main 
  2.  
  3. import ( 
  4.     "context" 
  5.     "fmt" 
  6.     "grpc-client/proto" 
  7.     "log" 
  8.  
  9.     "google.golang.org/grpc" 
  10.  
  11. func main() { 
  12.     conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) 
  13.     if err != nil { 
  14.         log.Fatal(err) 
  15.     } 
  16.     defer conn.Close() 
  17.  
  18.     client := proto.NewGreeterClient(conn) 
  19.     reply, err := client.SayHello(context.Background(), &proto.HelloRequest{Name"zhangsan"}) 
  20.     if err != nil { 
  21.         log.Fatal(err) 
  22.     } 
  23.     fmt.Println(reply.Message) 

這樣就完成了最基礎的 gRPC 服務的開發,接下來我們就在這個「基礎模板」上不斷豐富,學習更多特性。

流方式

接下來看看流的方式,顧名思義,數據可以源源不斷的發送和接收。

流的話分單向流和雙向流,這里我們直接通過雙向流來舉例。

proto

  1. service Greeter { 
  2.     // Sends a greeting 
  3.     rpc SayHello (HelloRequest) returns (HelloReply) {} 
  4.     // Sends stream message 
  5.     rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply) {} 

增加一個流函數 SayHelloStream,通過 stream 關鍵詞來指定流特性。

需要重新生成 helloworld.pb.go 文件,這里不再多說。

server

  1. func (*greeter) SayHelloStream(stream proto.Greeter_SayHelloStreamServer) error { 
  2.     for { 
  3.         args, err := stream.Recv() 
  4.         if err != nil { 
  5.             if err == io.EOF { 
  6.                 return nil 
  7.             } 
  8.             return err 
  9.         } 
  10.  
  11.         fmt.Println("Recv: " + args.Name
  12.         reply := &proto.HelloReply{Message: "hi " + args.Name
  13.  
  14.         err = stream.Send(reply) 
  15.         if err != nil { 
  16.             return err 
  17.         } 
  18.     } 

在「基礎模板」上增加 SayHelloStream 函數,其他都不需要變。

client

  1. client := proto.NewGreeterClient(conn) 
  2.  
  3. // 流處理 
  4. stream, err := client.SayHelloStream(context.Background()) 
  5. if err != nil { 
  6.     log.Fatal(err) 
  7.  
  8. // 發送消息 
  9. go func() { 
  10.     for { 
  11.         if err := stream.Send(&proto.HelloRequest{Name"zhangsan"}); err != nil { 
  12.             log.Fatal(err) 
  13.         } 
  14.         time.Sleep(time.Second
  15.     } 
  16. }() 
  17.  
  18. // 接收消息 
  19. for { 
  20.     reply, err := stream.Recv() 
  21.     if err != nil { 
  22.         if err == io.EOF { 
  23.             break 
  24.         } 
  25.         log.Fatal(err) 
  26.     } 
  27.     fmt.Println(reply.Message) 

通過一個 goroutine 發送消息,主程序的 for 循環接收消息。

執行程序會發現,服務端和客戶端都不斷有打印輸出。

驗證器

接下來是驗證器,這個需求是很自然會想到的,因為涉及到接口之間的請求,那么對參數進行適當的校驗是很有必要的。

在這里我們使用 protoc-gen-govalidators 和 go-grpc-middleware 來實現。

先安裝:

  1. go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators 
  2.  
  3. go get github.com/grpc-ecosystem/go-grpc-middleware 

接下來修改 proto 文件:

proto

  1. import "github.com/mwitkow/go-proto-validators@v0.3.2/validator.proto"
  2.  
  3. message HelloRequest { 
  4.     string name = 1 [ 
  5.         (validator.field) = {regex: "^[z]{2,5}$"
  6.     ]; 

在這里對 name 參數進行校驗,需要符合正則的要求才可以正常請求。

還有其他驗證規則,比如對數字大小進行驗證等,這里不做過多介紹。

接下來生成 *.pb.go 文件:

  1. protoc  \ 
  2.     --proto_path=${GOPATH}/pkg/mod \ 
  3.     --proto_path=${GOPATH}/pkg/mod/github.com/gogo/protobuf@v1.3.2 \ 
  4.     --proto_path=. \ 
  5.     --govalidators_out=. --go_out=plugins=grpc:.\ 
  6.     *.proto 

執行成功之后,目錄下會多一個 helloworld.validator.pb.go 文件。

這里需要特別注意一下,使用之前的簡單命令是不行的,需要使用多個 proto_path 參數指定導入 proto 文件的目錄。

官方給了兩種依賴情況,一個是 google protobuf,一個是 gogo protobuf。我這里使用的是第二種。

即使使用上面的命令,也有可能會遇到這個報錯:

  1. Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors 

但不要慌,大概率是引用路徑的問題,一定要看好自己的安裝版本,以及在 GOPATH 中的具體路徑。

最后是服務端代碼改造:

引入包:

  1. grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 
  2. grpc_validator "github.com/grpc-ecosystem/go-grpc-middleware/validator" 

然后在初始化的時候增加驗證器功能:

  1. server := grpc.NewServer( 
  2.     grpc.UnaryInterceptor( 
  3.         grpc_middleware.ChainUnaryServer( 
  4.             grpc_validator.UnaryServerInterceptor(), 
  5.         ), 
  6.     ), 
  7.     grpc.StreamInterceptor( 
  8.         grpc_middleware.ChainStreamServer( 
  9.             grpc_validator.StreamServerInterceptor(), 
  10.         ), 
  11.     ), 

啟動程序之后,我們再用之前的客戶端代碼來請求,會收到報錯:

  1. 2021/10/11 18:32:59 rpc error: code = InvalidArgument desc = invalid field Name: value 'zhangsan' must be a string conforming to regex "^[z]{2,5}$" 
  2. exit status 1 

因為 name: zhangsan 是不符合服務端正則要求的,但是如果傳參 name: zzz,就可以正常返回了。

Token 認證

終于到認證環節了,先看 Token 認證方式,然后再介紹證書認證。

先改造服務端,有了上文驗證器的經驗,那么可以采用同樣的方式,寫一個攔截器,然后在初始化 server 時候注入。

認證函數:

  1. func Auth(ctx context.Context) error { 
  2.     md, ok := metadata.FromIncomingContext(ctx) 
  3.     if !ok { 
  4.         return fmt.Errorf("missing credentials"
  5.     } 
  6.  
  7.     var user string 
  8.     var password string 
  9.  
  10.     if val, ok := md["user"]; ok { 
  11.         user = val[0] 
  12.     } 
  13.     if val, ok := md["password"]; ok { 
  14.         password = val[0] 
  15.     } 
  16.  
  17.     if user != "admin" || password != "admin" { 
  18.         return grpc.Errorf(codes.Unauthenticated, "invalid token"
  19.     } 
  20.  
  21.     return nil 

metadata.FromIncomingContext 從上下文讀取用戶名和密碼,然后和實際數據進行比較,判斷是否通過認證。

攔截器:

  1. var authInterceptor grpc.UnaryServerInterceptor 
  2. authInterceptor = func( 
  3.     ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, 
  4. ) (resp interface{}, err error) { 
  5.     //攔截普通方法請求,驗證 Token 
  6.     err = Auth(ctx) 
  7.     if err != nil { 
  8.         return 
  9.     } 
  10.     // 繼續處理請求 
  11.     return handler(ctx, req) 

初始化:

  1. server := grpc.NewServer( 
  2.     grpc.UnaryInterceptor( 
  3.         grpc_middleware.ChainUnaryServer( 
  4.             authInterceptor, 
  5.             grpc_validator.UnaryServerInterceptor(), 
  6.         ), 
  7.     ), 
  8.     grpc.StreamInterceptor( 
  9.         grpc_middleware.ChainStreamServer( 
  10.             grpc_validator.StreamServerInterceptor(), 
  11.         ), 
  12.     ), 

除了上文的驗證器,又多了 Token 認證攔截器 authInterceptor。

最后是客戶端改造,客戶端需要實現 PerRPCCredentials 接口。

  1. type PerRPCCredentials interface { 
  2.     // GetRequestMetadata gets the current request metadata, refreshing 
  3.     // tokens if required. This should be called by the transport layer on 
  4.     // each request, and the data should be populated in headers or other 
  5.     // context. If a status code is returned, it will be used as the status 
  6.     // for the RPC. uri is the URI of the entry point for the request. 
  7.     // When supported by the underlying implementation, ctx can be used for 
  8.     // timeout and cancellation. 
  9.     // TODO(zhaoq): Define the set of the qualified keys instead of leaving 
  10.     // it as an arbitrary string. 
  11.     GetRequestMetadata(ctx context.Context, uri ...string) ( 
  12.         map[string]string,    error, 
  13.     ) 
  14.     // RequireTransportSecurity indicates whether the credentials requires 
  15.     // transport security. 
  16.     RequireTransportSecurity() bool 

GetRequestMetadata 方法返回認證需要的必要信息,RequireTransportSecurity 方法表示是否啟用安全鏈接,在生產環境中,一般都是啟用的,但為了測試方便,暫時這里不啟用了。

實現接口:

  1. type Authentication struct { 
  2.     User     string 
  3.     Password string 
  4.  
  5. func (a *Authentication) GetRequestMetadata(context.Context, ...string) ( 
  6.     map[string]string, error, 
  7. ) { 
  8.     return map[string]string{"user": a.User"password": a.Password}, nil 
  9.  
  10. func (a *Authentication) RequireTransportSecurity() bool { 
  11.     return false 

連接:

  1. conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth)) 

好了,現在我們的服務就有 Token 認證功能了。如果用戶名或密碼錯誤,客戶端就會收到:

  1. 2021/10/11 20:39:35 rpc error: code = Unauthenticated desc = invalid token 
  2. exit status 1 

如果用戶名和密碼正確,則可以正常返回。

單向證書認證

證書認證分兩種方式:

  1. 單向認證
  2. 雙向認證

先看一下單向認證方式:

生成證書

首先通過 openssl 工具生成自簽名的 SSL 證書。

1、生成私鑰:

  1. openssl genrsa -des3 -out server.pass.key 2048 

2、去除私鑰中密碼:

  1. openssl rsa -in server.pass.key -out server.key 

3、生成 csr 文件:

  1. openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=beijing/L=beijing/O=grpcdev/OU=grpcdev/CN=example.grpcdev.cn" 

4、生成證書:

  1. openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt 

再多說一句,分別介紹一下 X.509 證書包含的三個文件:key,csr 和 crt。

  • key: 服務器上的私鑰文件,用于對發送給客戶端數據的加密,以及對從客戶端接收到數據的解密。
  • csr: 證書簽名請求文件,用于提交給證書頒發機構(CA)對證書簽名。
  • crt: 由證書頒發機構(CA)簽名后的證書,或者是開發者自簽名的證書,包含證書持有人的信息,持有人的公鑰,以及簽署者的簽名等信息。

gRPC 代碼

證書有了之后,剩下的就是改造程序了,首先是服務端代碼。

  1. // 證書認證-單向認證 
  2. creds, err := credentials.NewServerTLSFromFile("keys/server.crt""keys/server.key"
  3. if err != nil { 
  4.     log.Fatal(err) 
  5.     return 
  6.  
  7. server := grpc.NewServer(grpc.Creds(creds)) 

只有幾行代碼需要修改,很簡單,接下來是客戶端。

由于是單向認證,不需要為客戶端單獨生成證書,只需要把服務端的 crt 文件拷貝到客戶端對應目錄下即可。

  1. // 證書認證-單向認證 
  2. creds, err := credentials.NewClientTLSFromFile("keys/server.crt""example.grpcdev.cn"
  3. if err != nil { 
  4.     log.Fatal(err) 
  5.     return 
  6. conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds)) 

好了,現在我們的服務就支持單向證書認證了。

但是還沒完,這里可能會遇到一個問題:

  1. 2021/10/11 21:32:37 rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0" 
  2. exit status 1 

原因是 Go 1.15 開始廢棄了 CommonName,推薦使用 SAN 證書。如果想要兼容之前的方式,可以通過設置環境變量的方式支持,如下:

  1. export GODEBUG="x509ignoreCN=0" 

但是需要注意,從 Go 1.17 開始,環境變量就不再生效了,必須通過 SAN 方式才行。所以,為了后續的 Go 版本升級,還是早日支持為好。

雙向證書認證

最后來看看雙向證書認證。

生成帶 SAN 的證書

還是先生成證書,但這次有一點不一樣,我們需要生成帶 SAN 擴展的證書。

什么是 SAN?

SAN(Subject Alternative Name)是 SSL 標準 x509 中定義的一個擴展。使用了 SAN 字段的 SSL 證書,可以擴展此證書支持的域名,使得一個證書可以支持多個不同域名的解析。

將默認的 OpenSSL 配置文件拷貝到當前目錄。

Linux 系統在:

  1. /etc/pki/tls/openssl.cnf 

Mac 系統在:

  1. /System/Library/OpenSSL/openssl.cnf 

修改臨時配置文件,找到 [ req ] 段落,然后將下面語句的注釋去掉。

  1. req_extensions = v3_req # The extensions to add to a certificate request 

接著添加以下配置:

  1. [ v3_req ] 
  2. # Extensions to add to a certificate request 
  3.  
  4. basicConstraints = CA:FALSE 
  5. keyUsage = nonRepudiation, digitalSignature, keyEncipherment 
  6. subjectAltName = @alt_names 
  7.  
  8. [ alt_names ] 
  9. DNS.1 = www.example.grpcdev.cn 

[ alt_names ] 位置可以配置多個域名,比如:

  1. [ alt_names ] 
  2. DNS.1 = www.example.grpcdev.cn 
  3. DNS.2 = www.test.grpcdev.cn 

為了測試方便,這里只配置一個域名。

1、生成 ca 證書:

  1. openssl genrsa -out ca.key 2048 
  2.  
  3. openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.grpcdev.com" -days 5000 -out ca.pem 

2、生成服務端證書:

  1. # 生成證書 
  2. openssl req -new -nodes \ 
  3.     -subj "/C=CN/ST=Beijing/L=Beijing/O=grpcdev/OU=grpcdev/CN=www.example.grpcdev.cn" \ 
  4.     -config <(cat openssl.cnf \ 
  5.         <(printf "[SAN]\nsubjectAltName=DNS:www.example.grpcdev.cn")) \ 
  6.     -keyout server.key \ 
  7.     -out server.csr 
  8.  
  9. # 簽名證書 
  10. openssl x509 -req -days 365000 \ 
  11.     -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial \ 
  12.     -extfile <(printf "subjectAltName=DNS:www.example.grpcdev.cn") \ 
  13.     -out server.pem 

3、生成客戶端證書:

  1. # 生成證書 
  2. openssl req -new -nodes \ 
  3.     -subj "/C=CN/ST=Beijing/L=Beijing/O=grpcdev/OU=grpcdev/CN=www.example.grpcdev.cn" \ 
  4.     -config <(cat openssl.cnf \ 
  5.         <(printf "[SAN]\nsubjectAltName=DNS:www.example.grpcdev.cn")) \ 
  6.     -keyout client.key \ 
  7.     -out client.csr 
  8.  
  9. # 簽名證書 
  10. openssl x509 -req -days 365000 \ 
  11.     -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial \ 
  12.     -extfile <(printf "subjectAltName=DNS:www.example.grpcdev.cn") \ 
  13.     -out client.pem 

gRPC 代碼

接下來開始修改代碼,先看服務端:

  1. // 證書認證-雙向認證 
  2. // 從證書相關文件中讀取和解析信息,得到證書公鑰、密鑰對 
  3. cert, _ := tls.LoadX509KeyPair("cert/server.pem""cert/server.key"
  4. // 創建一個新的、空的 CertPool 
  5. certPool := x509.NewCertPool() 
  6. ca, _ := ioutil.ReadFile("cert/ca.pem"
  7. // 嘗試解析所傳入的 PEM 編碼的證書。如果解析成功會將其加到 CertPool 中,便于后面的使用 
  8. certPool.AppendCertsFromPEM(ca) 
  9. // 構建基于 TLS 的 TransportCredentials 選項 
  10. creds := credentials.NewTLS(&tls.Config{ 
  11.     // 設置證書鏈,允許包含一個或多個 
  12.     Certificates: []tls.Certificate{cert}, 
  13.     // 要求必須校驗客戶端的證書。可以根據實際情況選用以下參數 
  14.     ClientAuth: tls.RequireAndVerifyClientCert, 
  15.     // 設置根證書的集合,校驗方式使用 ClientAuth 中設定的模式 
  16.     ClientCAs: certPool, 
  17. }) 

再看客戶端:

  1. // 證書認證-雙向認證 
  2. // 從證書相關文件中讀取和解析信息,得到證書公鑰、密鑰對 
  3. cert, _ := tls.LoadX509KeyPair("cert/client.pem""cert/client.key"
  4. // 創建一個新的、空的 CertPool 
  5. certPool := x509.NewCertPool() 
  6. ca, _ := ioutil.ReadFile("cert/ca.pem"
  7. // 嘗試解析所傳入的 PEM 編碼的證書。如果解析成功會將其加到 CertPool 中,便于后面的使用 
  8. certPool.AppendCertsFromPEM(ca) 
  9. // 構建基于 TLS 的 TransportCredentials 選項 
  10. creds := credentials.NewTLS(&tls.Config{ 
  11.     // 設置證書鏈,允許包含一個或多個 
  12.     Certificates: []tls.Certificate{cert}, 
  13.     // 要求必須校驗客戶端的證書。可以根據實際情況選用以下參數 
  14.     ServerName: "www.example.grpcdev.cn"
  15.     RootCAs:    certPool, 
  16. }) 

大功告成。

Python 客戶端

前面已經說了,gRPC 是跨語言的,那么,本文最后我們用 Python 寫一個客戶端,來請求 Go 服務端。

使用最簡單的方式來實現:

proto 文件就使用最開始的「基礎模板」的 proto 文件:

  1. syntax = "proto3"
  2.  
  3. package proto; 
  4.  
  5. // The greeting service definition. 
  6. service Greeter { 
  7.     // Sends a greeting 
  8.     rpc SayHello (HelloRequest) returns (HelloReply) {} 
  9.     // Sends stream message 
  10.     rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply) {} 
  11.  
  12. // The request message containing the user's name
  13.  message HelloRequest { 
  14.     string name = 1; 
  15.  
  16. // The response message containing the greetings 
  17. message HelloReply { 
  18.     string message = 1; 

同樣的,也需要通過命令行的方式生成 pb.py 文件:

  1. python3 -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. ./*.proto 

執行成功之后會在目錄下生成 helloworld_pb2.py 和 helloworld_pb2_grpc.py 兩個文件。

這個過程也可能會報錯:

  1. ModuleNotFoundError: No module named 'grpc_tools' 

別慌,是缺少包,安裝就好:

  1. pip3 install grpcio 
  2. pip3 install grpcio-tools 

最后看一下 Python 客戶端代碼:

  1. import grpc 
  2.  
  3. import helloworld_pb2 
  4. import helloworld_pb2_grpc 
  5.  
  6.  
  7. def main(): 
  8.     channel = grpc.insecure_channel("127.0.0.1:50051"
  9.     stub = helloworld_pb2_grpc.GreeterStub(channel) 
  10.     response = stub.SayHello(helloworld_pb2.HelloRequest(name="zhangsan")) 
  11.     print(response.message) 
  12.  
  13.  
  14. if __name__ == '__main__'
  15.     main() 

這樣,就可以通過 Python 客戶端請求 Go 啟的服務端服務了。

總結

本文通過實戰角度出發,直接用代碼說話,來說明 gRPC 的一些應用。

內容包括簡單的 gRPC 服務,流處理模式,驗證器,Token 認證和證書認證。

除此之外,還有其他值得研究的內容,比如超時控制,REST 接口和負載均衡等。以后還會抽時間繼續完善剩下這部分內容。

本文中的代碼都經過測試驗證,可以直接執行,并且已經上傳到 GitHub,小伙伴們可以一遍看源碼,一遍對照文章內容來學習。

源碼地址:

  • https://github.com/yongxinz/go-example/tree/main/grpc-example
  • https://github.com/yongxinz/gopher/tree/main/blog

本文轉載自微信公眾號「AlwaysBeta」,可以通過以下二維碼關注。轉載本文請聯系AlwaysBeta公眾號。

 

責任編輯:武曉燕 來源: AlwaysBeta
相關推薦

2013-12-23 09:44:43

2021-03-22 09:27:44

PythonEXCEL熱點推薦

2024-01-22 06:55:09

BiomeWeb 應用Prettier

2024-02-26 10:30:27

Biome開發前端

2021-12-13 01:58:58

產品經理程序員

2021-03-02 20:42:20

實戰策略

2023-11-17 14:06:43

2022-01-07 13:36:00

MySQL數據庫分頁

2009-12-02 13:56:40

Visual Stud

2022-06-06 08:51:56

PandasSQLPython

2017-11-30 13:15:34

數據中心備份云端

2023-03-02 11:44:08

AI技術

2021-02-21 00:22:32

技術團隊工具

2022-04-29 11:52:02

API代碼HTTP

2025-05-29 01:55:00

Vue3.5API性能

2009-04-03 15:21:37

2021-04-01 22:36:08

蘋果iOS系統功能

2019-01-30 18:00:21

開源Python庫

2022-10-26 17:28:41

分布式事務seata

2017-12-28 12:38:29

Windows微軟服務器
點贊
收藏

51CTO技術棧公眾號

激情视频在线观看| 免费一级全黄少妇性色生活片| 国产拍在线视频| 2欧美一区二区三区在线观看视频| 欧洲精品在线视频| 五月天婷婷丁香网| 成人免费直播在线| 色婷婷综合久久久久中文一区二区| 日韩欧美视频第二区| 国产伦精品一区二区三区四区| 激情av一区| 中文字幕在线观看亚洲| 潘金莲一级淫片aaaaa| 波多视频一区| 一区二区三区欧美亚洲| 欧洲亚洲一区二区三区四区五区| 99在线小视频| 首页国产欧美久久| 九九视频直播综合网| 3d动漫精品啪啪一区二区下载| 亚洲欧美专区| 色婷婷国产精品久久包臀| 992tv快乐视频| 国产精品99999| 成人午夜碰碰视频| 国产日韩中文字幕在线| www.com亚洲| 99在线精品免费视频九九视| 粗暴蹂躏中文一区二区三区| 97人妻人人揉人人躁人人| 噜噜噜狠狠夜夜躁精品仙踪林| 欧美久久久久久久久| 一本久道中文无码字幕av| 国产欧洲在线| 亚洲国产视频一区二区| 日韩视频一二三| 2021av在线| 国产丝袜欧美中文另类| 久久亚洲免费| 午夜成人免费影院| 精品一区二区三区的国产在线播放| 人人做人人澡人人爽欧美| 日本a在线观看| 欧美三级视频| 欧美激情视频给我| 国产av无码专区亚洲av毛网站| 久久国产成人精品| 一本久久综合亚洲鲁鲁| 国产精品高清无码在线观看| 国产免费a视频| 久久经典视频| 99精品欧美一区二区三区小说 | 亚洲男人天堂九九视频| 理论片大全免费理伦片| 电影一区二区在线观看| 日韩精品在线看片z| 美女被艹视频网站| 精品中文字幕一区二区三区四区| 欧美一区二区三区免费视频| 午夜视频在线网站| 国产一区 二区| 日韩欧美一级二级三级久久久| 一级片黄色免费| 日韩在线成人| 亚洲成人久久久| 国产肉体xxxx裸体784大胆| 伦理一区二区| 亚洲视频在线观看| 国产精品麻豆免费版现看视频| 久久视频在线| 欧美大胆在线视频| 国产五月天婷婷| 国产精品日韩久久久| 国产97免费视| 国产熟女一区二区丰满| 高清成人在线观看| 久草精品电影| 福利在线观看| 亚洲欧美国产毛片在线| 国产日韩欧美精品在线观看| 精精国产xxxx视频在线野外| 日韩欧美亚洲综合| 男人插女人下面免费视频| 久久久久毛片| 精品国产欧美一区二区| 久久国产精品无码一级毛片| 国产区精品区| 久久久精品电影| 国产一级久久久| 欧美亚洲免费| 国产在线a不卡| 全国男人的天堂网| 欧美激情一区二区三区四区| 特级黄色录像片| 岛国av在线网站| 欧美吞精做爰啪啪高潮| 性生活一级大片| 国产精品三级| 欧美成人精品一区二区| 久久久久久少妇| 激情综合色综合久久| 国产女人水真多18毛片18精品 | 99热这里只有精品首页| 亚洲视屏在线播放| 懂色av蜜桃av| av不卡在线看| 亚洲一区国产精品| 精品视频一二三| 悠悠色在线精品| 久久婷婷国产91天堂综合精品| 中文字幕一区二区三区中文字幕| 亚洲性生活视频在线观看| 国产视频123区| 一区二区三区导航| 91久久久久久久久| 久久久久久久久亚洲精品| 一区二区在线观看免费| 天天爽人人爽夜夜爽| 大香伊人久久精品一区二区 | 永久免费看mv网站入口| 久久精品综合| 久久99精品久久久久久青青日本| jizz性欧美| 欧美日韩中文精品| 全黄一级裸体片| 国产欧美高清| 国产一区二区久久久| 天堂8中文在线| 欧美精品成人一区二区三区四区| 欧美另类z0zx974| 国产欧美日本| 国产在线资源一区| 成人爽a毛片免费啪啪动漫 | 天天干天天插天天操| 亚洲精品日产精品乱码不卡| 爱豆国产剧免费观看大全剧苏畅| 欧美精美视频| 欧美视频在线不卡| 欧美成人高清视频| 亚洲婷婷综合网| 99re热这里只有精品视频| 成人午夜免费在线视频| 国产高清亚洲| 久久综合国产精品台湾中文娱乐网| www.亚洲激情| 欧美韩日一区二区三区| 成年人视频在线免费| 日韩mv欧美mv国产网站| 97av在线视频免费播放| 天天av天天翘| 日韩欧美在线观看| 免费在线观看你懂的| 免费在线欧美黄色| 欧洲精品国产| 国产a亚洲精品| 亚洲人成毛片在线播放| 超碰在线97观看| 中文av字幕一区| 日本人69视频| 久久精品欧美一区| 99re视频在线播放| 伦理av在线| 亚洲精品大尺度| 无码免费一区二区三区| 欧美激情一区在线| 欧美视频国产视频| 欧美日韩日本国产亚洲在线| 国产精品视频500部| 亚洲天堂手机| 中文字幕欧美日韩va免费视频| 亚洲综合五月天婷婷丁香| 自拍偷在线精品自拍偷无码专区| 亚洲精品中文字幕乱码无线| 激情另类综合| 欧美色图亚洲自拍| 波多野结衣视频免费观看| 成人在线视频区| 日韩在线视频播放| 亚洲h视频在线观看| 精品久久久久久亚洲精品| 国产一二三四五区| 国内精品自线一区二区三区视频| 国产传媒久久久| 亚洲理论电影片| 成人午夜激情网| 男人av在线播放| 色妞一区二区三区| 亚洲狼人综合网| 色94色欧美sute亚洲13| 成人在线观看小视频| 成人福利电影精品一区二区在线观看| 欧美s码亚洲码精品m码| 成人一区二区| 国产另类第一区| 欧美电影在线观看网站| 久久久久女教师免费一区| 狠狠色伊人亚洲综合网站l | 99在线精品视频免费观看20| 精品福利免费观看| 黄色裸体一级片| 91在线国产观看| 国产成人美女视频| 国产偷自视频区视频一区二区| 亚洲国内在线| 日韩极品少妇| 91最新在线免费观看| 精品国产免费人成网站| 欧美成人免费播放| 国产午夜在线视频| 精品999在线播放| 国产精品久久久久久久久毛片| 天天综合色天天| 欧美三级在线免费观看| 国产片一区二区| 催眠调教后宫乱淫校园| 久久99国产精品久久| 日本a级片免费观看| 欧美大片专区| 亚洲人成人77777线观看| 免费观看成人www动漫视频| 91久久久久久久久久久久久| 欧美大片免费| 久久欧美在线电影| 先锋影音在线资源站91| 中文字幕av日韩| 毛片在线能看| 亚洲精品国产精品国自产在线| 国产激情无套内精对白视频| 欧美日韩一区 二区 三区 久久精品| 久久久久久久极品| 亚洲无线码一区二区三区| 久草视频手机在线| 国产精品国产自产拍高清av王其| 国产制服丝袜在线| caoporm超碰国产精品| 中文字幕99页| 国产成人av电影在线观看| 夜夜夜夜夜夜操| 另类人妖一区二区av| 久草福利视频在线| 久久亚洲视频| 玩弄japan白嫩少妇hd| 久久久蜜桃一区二区人| 丰满少妇被猛烈进入高清播放| 99国产精品视频免费观看一公开| 男的插女的下面视频| 激情亚洲成人| 人妻少妇精品久久| 日韩网站在线| 国产裸体舞一区二区三区| 亚洲欧美日韩国产| 黄色a级片免费| 日韩成人午夜电影| 亚欧美在线观看| 精品一区二区三区在线播放| 亚洲免费黄色录像| 国内久久精品视频| 色哟哟网站在线观看| 成人网在线免费视频| 国产又粗又长又爽| 91麻豆福利精品推荐| 天天躁日日躁aaaa视频| 亚洲国产精品成人综合色在线婷婷 | 日韩综合在线观看| 日本高清视频一区二区| 久久精品国产亚洲av麻豆蜜芽| 欧美日韩一级黄| 国产精品欧美亚洲| 精品粉嫩aⅴ一区二区三区四区| 免费激情视频网站| 亚洲欧洲日本专区| 一级日本在线| 萌白酱国产一区二区| h片在线观看视频免费免费| 孩xxxx性bbbb欧美| 韩漫成人漫画| 成人黄色免费看| 老司机在线精品视频| 日韩欧美亚洲区| 午夜日韩视频| 欧美精品99久久| 久久av中文字幕片| 国产精品日日摸夜夜爽| 久久嫩草精品久久久精品一| 亚洲少妇xxx| 亚洲成av人片在线观看| 美女黄页在线观看| 欧美成人video| 成人午夜在线观看视频| 欧美伦理91i| 里番在线播放| 国产精品午夜国产小视频| 国产精品115| 欧洲久久久久久| 激情视频一区| 亚洲精品手机在线观看| jiyouzz国产精品久久| 中文国语毛片高清视频| 婷婷一区二区三区| 国产精品久久久久久久久久久久久久久久 | 91亚洲男人天堂| 亚洲色图日韩精品| 午夜精品久久久久久久99樱桃| 中文字幕日本人妻久久久免费| 亚洲高清色综合| 麻豆电影在线播放| 日本久久久久亚洲中字幕| 视频二区欧美毛片免费观看| 日韩精品无码一区二区三区| 影音先锋亚洲一区| 午夜精品久久久久久久99热影院| 91一区二区在线| 精品97人妻无码中文永久在线 | 丰满肉肉bbwwbbww| 色偷偷9999www| 午夜欧美巨大性欧美巨大 | 手机av在线网| 国产日韩欧美a| 国产成人自拍视频在线| 日韩一区二区在线看| 日韩在线资源| 国产精品国语对白| 蜜乳av综合| 男人揉女人奶房视频60分| 国产白丝网站精品污在线入口| 国产又粗又猛又爽又黄的视频四季 | 一本一道无码中文字幕精品热| 欧美xingq一区二区| av片哪里在线观看| 国产啪精品视频| 日韩欧美视频在线播放| av免费网站观看| 久久品道一品道久久精品| 国产三级av片| 亚洲九九九在线观看| 久草在线中文最新视频| 国产精品一国产精品最新章节| 国内精品美女在线观看| 久久黄色一级视频| 亚洲欧美另类久久久精品| 国产美女精品视频国产| 久久综合久中文字幕青草| 宅男噜噜噜66国产精品免费| 在线观看日韩片| 国内精品伊人久久久久av影院| 麻豆天美蜜桃91| 欧美一级日韩免费不卡| 午夜成年人在线免费视频| 91亚洲精品久久久| 综合激情网站| 日韩大尺度视频| 亚洲综合视频在线| 亚洲乱码在线观看| 韩剧1988免费观看全集| 日本成人中文| 国产精品69页| 欧美国产视频在线| 亚洲一级av毛片| 久久亚洲精品国产亚洲老地址| 精品中文在线| 国产毛片视频网站| 久久久久国产免费免费| 中文字幕 国产| 不卡av电影院| 嗯用力啊快一点好舒服小柔久久| 国产乱子伦农村叉叉叉| 国产婷婷一区二区| 国产一区二区波多野结衣| 欧美刺激性大交免费视频| 女一区二区三区| 欧美精品aaaa| 亚洲色图欧美偷拍| 三级视频在线看| 国产精品色视频| 欧美69视频| 日韩网站在线播放| 欧美日韩一区在线观看| 色婷婷av在线| 欧美在线激情| 国模大尺度一区二区三区| 日韩av在线电影| 这里精品视频免费| 亚洲精品一区在线| 已婚少妇美妙人妻系列| **网站欧美大片在线观看| 欧美熟女一区二区| 国产精品吹潮在线观看| 欧美黄色aaaa| av网站免费在线看| 日韩视频免费直播| 英国三级经典在线观看| 天天爱天天做天天操| 91碰在线视频| 国产免费黄色片| 日韩免费观看av| 国产精品mm| 五月天婷婷丁香网| 日韩黄色在线免费观看| 亚洲一区二区av| 欧美日韩在线成人|