精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

日韩理论片中文av| 日韩精品国产欧美| 亚洲激情在线观看视频免费| 精品国产一二三四区| 国产区视频在线播放| 国内久久婷婷综合| 午夜精品一区二区三区在线视频| 亚洲成人网在线播放| 日本中文字幕视频一区| 亚洲黄色免费电影| 日韩亚洲视频在线| 黄色片网站免费在线观看| 水蜜桃久久夜色精品一区的特点| 欧美猛男性生活免费| 女~淫辱の触手3d动漫| 国产精品成人3p一区二区三区| 香蕉影视欧美成人| 黄色a级在线观看| 久久久久久久久亚洲精品| 国产精品一区二区视频| 国产精品免费电影| 国产精品23p| 91av精品| 一区二区三区视频免费在线观看| 亚洲少妇一区二区| 黄色成人小视频| 天天操天天干天天综合网| 欧美性视频在线播放| 男男电影完整版在线观看| 国产成a人无v码亚洲福利| 国产精品久久久久久久7电影| 国产在线拍揄自揄拍无码视频| 欧美精选视频在线观看| 亚洲国产精品99久久| 亚洲一区二区偷拍| 日韩三区在线| 欧美丝袜美女中出在线| 国产日韩亚洲欧美在线| 91在线中文| 国产精品久久久久三级| 日韩精品第一页| 日本v片在线免费观看| 成人小视频免费在线观看| 成人欧美在线视频| 在线观看中文字幕码| 久久在线91| 欧美中文字幕视频| 91午夜视频在线观看| 精品91视频| 欧美精品久久久久a| 黄色一级免费视频| 正在播放日韩欧美一页| 久久影院中文字幕| www.av免费| 91精品综合| 超薄丝袜一区二区| 日韩视频中文字幕在线观看| 外国成人免费视频| 久久精品最新地址| 91香蕉视频在线播放| 91精品国产调教在线观看| 日韩中文字幕网| 精品国产精品国产精品| 中文不卡在线| 久久久久久久久久久久av| 国产亚洲精品久久久久久无几年桃| 一区二区三区四区日韩| 欧美巨大黑人极品精男| 国产小视频在线观看免费| 雨宫琴音一区二区在线| 欧美一区在线直播| 中文字幕永久在线| 捆绑调教美女网站视频一区| 91九色国产社区在线观看| a在线观看免费| k8久久久一区二区三区| 久久综合中文色婷婷| 国产尤物视频在线| 中文字幕在线观看一区二区| 国产女人18毛片| 久草在线视频网站| 精品女厕一区二区三区| 成人中文字幕av| 亚洲精品乱码日韩| 欧美zozozo| 久久精品一区二区免费播放 | 亚洲久久一区二区| 日本aⅴ大伊香蕉精品视频| 中文天堂在线播放| 国产成人午夜视频| 免费精品视频一区二区三区| 92国产在线视频| 一区二区三区精品视频在线| 黄色一级在线视频| 九九九精品视频| 精品国产一区二区亚洲人成毛片 | 欧美一级视频在线观看| 中文字幕欧美人妻精品| 国产激情精品久久久第一区二区| 久久99精品久久久久久久久久| 大片免费播放在线视频| 亚洲综合激情小说| 国产一级不卡毛片| 在线观看视频一区二区三区| 亚洲新中文字幕| 久久久久黄色片| 日本亚洲一区二区| 国产精品久久精品国产 | 日本va中文字幕| 一区二区三区亚洲变态调教大结局 | 久久免费精品视频| 艳妇乳肉豪妇荡乳av| 成人激情小说网站| 国产精品jizz在线观看老狼| 涩涩网在线视频| 欧美绝品在线观看成人午夜影视| 超碰97在线资源站| 欧美在线高清| 国产精品视频一区二区高潮| 无码h黄肉3d动漫在线观看| 中文字幕亚洲一区二区av在线 | 午夜不卡影院| 日韩美女在线视频| 亚洲色图日韩精品| 美女国产一区| 精品国产福利| 日本无删减在线| 8v天堂国产在线一区二区| 无码人妻aⅴ一区二区三区69岛| 国产一区久久| 91视频8mav| 欧美精品hd| 欧美日韩中文一区| 欧美熟妇激情一区二区三区| 亚洲国产婷婷| 国产高清精品一区二区三区| a毛片在线观看| 5566中文字幕一区二区电影| 我不卡一区二区| 免费亚洲一区| 久久久久久99| 亚洲天堂手机| 日韩精品极品视频| 日韩无码精品一区二区三区| 国产69精品久久久久777| 毛片av在线播放| 精品中文字幕一区二区三区| 久久这里只有精品99| 亚洲一二区视频| 国产精品电影一区二区| 美女在线视频一区二区| 成人高清电影网站| 国产精品一区二区性色av| 国产在线一在线二| 在线视频你懂得一区二区三区| v8888av| 久久一二三四| 亚洲一二三区在线| 电影91久久久| 欧美国产亚洲视频| 日本xxxxwww| 黄色精品在线看| 成人免费毛片糖心| 欧美aaaaa成人免费观看视频| 午夜精品一区二区在线观看的| 日韩毛片一区| 久久影院免费观看| 人妻与黑人一区二区三区| 大伊人狠狠躁夜夜躁av一区| 亚洲成人网在线播放| 美女视频网站久久| 一级性生活视频| 国产欧美三级电影| 日本中文字幕不卡免费| 午夜视频在线看| 欧美一二三区精品| 日韩视频免费观看高清| 国产欧美一区二区三区在线老狼| 天天综合网日韩| 欧美日韩福利| 欧美日韩大片一区二区三区| 福利一区在线| 欧美风情在线观看| 国产在线观看精品一区| 欧美麻豆精品久久久久久| 免费在线观看国产精品| 91视频国产观看| 亚洲制服中文字幕| 99国产精品视频免费观看一公开 | 另类视频在线观看+1080p| 成人黄页网站视频| 久久成人免费视频| 香蕉视频免费看| 欧美日韩精品综合在线| 国产在线一区视频| 国产精品区一区二区三区| 自拍视频第一页| 日韩国产成人精品| 激情六月天婷婷| 精品无人区麻豆乱码久久久| 成人片在线免费看| 超碰这里只有精品| 久久久亚洲成人| 蜜桃av在线免费观看| 亚洲精品99久久久久| 96亚洲精品久久久蜜桃| 高跟丝袜欧美一区| 欧美精品一区二区蜜桃| 欧美韩国日本综合| avtt香蕉久久| 国产成人精品一区二区三区四区| 波多野结衣作品集| 国产精品av一区二区| 一级日韩一区在线观看| 天天躁日日躁成人字幕aⅴ| 亚洲综合精品一区二区| 日韩漫画puputoon| 欧美一级在线播放| 欧美xxxx视频| 精品激情国产视频| youjizz在线播放| 国产视频欧美视频| 国产91麻豆视频| 欧美精选在线播放| 特级西西444www大胆免费看| 欧美日韩一区二区在线播放| 久久国产精品波多野结衣| 国产精品第四页| 神马久久久久久久久久久 | 国产喷水在线观看| 国产欧美精品一区aⅴ影院| 精品夜夜澡人妻无码av| 成人精品在线视频观看| 99久久综合网| 极品尤物av久久免费看| 亚洲欧美国产日韩综合| 日韩高清中文字幕一区| 成年人网站大全| 久久天堂精品| 久久综合久久色| 久久欧美肥婆一二区| av免费观看网| 亚洲一区日本| 2022亚洲天堂| 欧美中文字幕| 亚洲一区二区蜜桃| 日本不卡在线视频| 亚洲黄色av网址| 麻豆精品视频在线观看视频| 一区二区xxx| 九九久久精品视频| 日韩视频在线观看一区二区三区| 久久精品国产久精国产| 天堂在线中文在线| 国产精品99久久久| 人妻互换一二三区激情视频| 大陆成人av片| 中文字幕在线免费看线人| 久久亚洲一级片| 日韩福利在线视频| 亚洲欧美电影院| 久久免费播放视频| 欧美日韩精品中文字幕| 久久久久久无码精品大片| 欧美日韩亚洲高清一区二区| 国产精品高潮呻吟AV无码| 日韩免费成人网| 视频在线观看你懂的| 一区二区国产精品视频| 巨大荫蒂视频欧美另类大| 欧美日韩国产123| 在线观看爽视频| 国产精品美女视频网站| 欧州一区二区三区| 九九九九精品| 久久人人88| av日韩一区二区三区| 丝袜a∨在线一区二区三区不卡| 久久久久久蜜桃一区二区| 国产白丝精品91爽爽久久 | 天天舔天天干天天操| 亚洲一级黄色片| 操你啦视频在线| 国产91精品久久久久久久| 91国内外精品自在线播放| 亚洲自拍偷拍色片视频| 亚洲电影一级片| 国产精品99久久久久久大便| 999亚洲国产精| 超碰在线资源站| 91碰在线视频| 久久中文免费视频| 色综合久久综合网| 精品人妻少妇AV无码专区 | av免费在线观看网址| 91av在线看| 在线免费成人| 欧美久久久久久| 国产精品扒开腿做爽爽爽软件| 国产乱子夫妻xx黑人xyx真爽| 麻豆精品国产91久久久久久| 在线天堂www在线国语对白| 国产精品久久久久久久久久免费看| 懂色av.com| 欧美一区日韩一区| 大胆av不用播放器在线播放| 久久琪琪电影院| 国产精品99久久免费| 日韩免费三级| 一区二区三区精品视频在线观看| 91丨九色丨蝌蚪| 久久久国际精品| 日韩免费一级片| 91精品国产高清一区二区三区| 第一福利在线| 538国产精品一区二区在线| 激情久久免费视频| 五月天久久狠狠| 久久蜜桃资源一区二区老牛| 在线天堂www在线国语对白| 亚洲黄色免费电影| 国产三级午夜理伦三级| 色老头一区二区三区| 欧美影视资讯| 欧美在线3区| 99精品国产在热久久下载| 最新中文字幕日本| 亚洲人成影院在线观看| 中文字幕在线观看你懂的| 亚洲欧美资源在线| gay欧美网站| 久久精品午夜一区二区福利| 亚洲九九精品| 亚洲天堂2024| 午夜视频在线观看一区二区| 亚洲精品久久久久久无码色欲四季 | 天堂在线中文网官网| 精品婷婷色一区二区三区蜜桃| 欧美午夜不卡影院在线观看完整版免费| 九九九九九国产| 国产精品激情偷乱一区二区∴| 最新中文字幕第一页| 一区二区三区四区视频| 成人亚洲网站| 一区二区精品在线| 精品一区二区三区久久久| 亚洲人做受高潮| 3d动漫精品啪啪| 午夜伦理在线视频| 国产伦精品一区二区三区视频免费| 欧美视频福利| 四虎成人免费视频| 婷婷久久综合九色综合伊人色| 人妻一区二区三区四区| 91av在线网站| 国产在线观看91一区二区三区| 不卡av免费在线| 国产欧美日韩中文久久| 中文字幕一区二区免费| 久久精品电影一区二区| 日韩精品中文字幕吗一区二区| 日本五级黄色片| 99久久99久久精品免费观看| 9i看片成人免费看片| 这里只有精品视频在线| 国产成年精品| 欧美精品久久久久久久久久久| 99精品视频在线免费观看| 四虎影院在线免费播放| 最近2019免费中文字幕视频三| 热久久久久久| 国产一区二区三区小说| 久久久久久久久久美女| 夜夜嗨aⅴ一区二区三区| 欧美激情一区二区三区高清视频 | 高清在线一区| 日本三日本三级少妇三级66| 成人久久视频在线观看| 丰满熟女人妻一区二区三| 久久伊人色综合| 综合国产视频| 爽爽爽在线观看| 欧美日韩精品国产| 二区三区四区高清视频在线观看| 国产伦精品一区二区三区免费视频| 久久午夜精品一区二区| 三级av在线免费观看| 日韩精品高清视频| 精品国产亚洲一区二区三区| 青青草原av在线播放| 1024国产精品| 日本天堂影院在线视频| 91网站在线看| 久久精品卡一| 久草资源在线视频| 菠萝蜜影院一区二区免费| 亚洲午夜久久| 亚洲午夜精品在线观看| 欧美中文字幕不卡| xxxcom在线观看|