自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

gRPC 這項(xiàng)技術(shù)真是太棒了,爆贊

開(kāi)發(fā) 前端
gRPC 這項(xiàng)技術(shù)真是太棒了,接口約束嚴(yán)格,性能還高,在 k8s 和很多微服務(wù)框架中都有應(yīng)用。

[[428427]]

gRPC 這項(xiàng)技術(shù)真是太棒了,接口約束嚴(yán)格,性能還高,在 k8s 和很多微服務(wù)框架中都有應(yīng)用。

作為一名程序員,學(xué)就對(duì)了。

之前用 Python 寫過(guò)一些 gRPC 服務(wù),現(xiàn)在準(zhǔn)備用 Go 來(lái)感受一下原汁原味的 gRPC 程序開(kāi)發(fā)。

本文的特點(diǎn)是直接用代碼說(shuō)話,通過(guò)開(kāi)箱即用的完整代碼,來(lái)介紹 gRPC 的各種使用方法。

代碼已經(jīng)上傳到 GitHub,下面正式開(kāi)始。

介紹

gRPC 是 Google 公司基于 Protobuf 開(kāi)發(fā)的跨語(yǔ)言的開(kāi)源 RPC 框架。gRPC 基于 HTTP/2 協(xié)議設(shè)計(jì),可以基于一個(gè) HTTP/2 鏈接提供多個(gè)服務(wù),對(duì)于移動(dòng)設(shè)備更加友好。

入門

首先來(lái)看一個(gè)最簡(jiǎn)單的 gRPC 服務(wù),第一步是定義 proto 文件,因?yàn)?gRPC 也是 C/S 架構(gòu),這一步相當(dāng)于明確接口規(guī)范。

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 內(nèi)置的 gRPC 插件生成 gRPC 代碼:

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

執(zhí)行完這個(gè)命令之后,會(huì)在當(dāng)前目錄生成一個(gè) helloworld.pb.go 文件,文件中分別定義了服務(wù)端和客戶端的接口:

  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) 

接下來(lái)就是寫服務(wù)端和客戶端的代碼,分別實(shí)現(xiàn)對(duì)應(yīng)的接口。

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.     // 注冊(cè) grpcurl 所需的 reflection 服務(wù) 
  28.     reflection.Register(server) 
  29.     // 注冊(cè)業(yè)務(wù)服務(wù) 
  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) 

這樣就完成了最基礎(chǔ)的 gRPC 服務(wù)的開(kāi)發(fā),接下來(lái)我們就在這個(gè)「基礎(chǔ)模板」上不斷豐富,學(xué)習(xí)更多特性。

流方式

接下來(lái)看看流的方式,顧名思義,數(shù)據(jù)可以源源不斷的發(fā)送和接收。

流的話分單向流和雙向流,這里我們直接通過(guò)雙向流來(lái)舉例。

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) {} 

增加一個(gè)流函數(shù) SayHelloStream,通過(guò) stream 關(guān)鍵詞來(lái)指定流特性。

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

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.     } 

在「基礎(chǔ)模板」上增加 SayHelloStream 函數(shù),其他都不需要變。

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. // 發(fā)送消息 
  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) 

通過(guò)一個(gè) goroutine 發(fā)送消息,主程序的 for 循環(huán)接收消息。

執(zhí)行程序會(huì)發(fā)現(xiàn),服務(wù)端和客戶端都不斷有打印輸出。

驗(yàn)證器

接下來(lái)是驗(yàn)證器,這個(gè)需求是很自然會(huì)想到的,因?yàn)樯婕暗浇涌谥g的請(qǐng)求,那么對(duì)參數(shù)進(jìn)行適當(dāng)?shù)男r?yàn)是很有必要的。

在這里我們使用 protoc-gen-govalidators 和 go-grpc-middleware 來(lái)實(shí)現(xiàn)。

先安裝:

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

接下來(lái)修改 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.     ]; 

在這里對(duì) name 參數(shù)進(jìn)行校驗(yàn),需要符合正則的要求才可以正常請(qǐng)求。

還有其他驗(yàn)證規(guī)則,比如對(duì)數(shù)字大小進(jìn)行驗(yàn)證等,這里不做過(guò)多介紹。

接下來(lái)生成 *.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 

執(zhí)行成功之后,目錄下會(huì)多一個(gè) helloworld.validator.pb.go 文件。

這里需要特別注意一下,使用之前的簡(jiǎn)單命令是不行的,需要使用多個(gè) proto_path 參數(shù)指定導(dǎo)入 proto 文件的目錄。

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

即使使用上面的命令,也有可能會(huì)遇到這個(gè)報(bào)錯(cuò):

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

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

最后是服務(wù)端代碼改造:

引入包:

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

然后在初始化的時(shí)候增加驗(yàn)證器功能:

  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.     ), 

啟動(dòng)程序之后,我們?cè)儆弥暗目蛻舳舜a來(lái)請(qǐng)求,會(huì)收到報(bào)錯(cuò):

  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 

因?yàn)?name: zhangsan 是不符合服務(wù)端正則要求的,但是如果傳參 name: zzz,就可以正常返回了。

Token 認(rèn)證

終于到認(rèn)證環(huán)節(jié)了,先看 Token 認(rèn)證方式,然后再介紹證書認(rèn)證。

先改造服務(wù)端,有了上文驗(yàn)證器的經(jīng)驗(yàn),那么可以采用同樣的方式,寫一個(gè)攔截器,然后在初始化 server 時(shí)候注入。

認(rèn)證函數(shù):

  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 從上下文讀取用戶名和密碼,然后和實(shí)際數(shù)據(jù)進(jìn)行比較,判斷是否通過(guò)認(rèn)證。

攔截器:

  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.     //攔截普通方法請(qǐng)求,驗(yàn)證 Token 
  6.     err = Auth(ctx) 
  7.     if err != nil { 
  8.         return 
  9.     } 
  10.     // 繼續(xù)處理請(qǐng)求 
  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.     ), 

除了上文的驗(yàn)證器,又多了 Token 認(rèn)證攔截器 authInterceptor。

最后是客戶端改造,客戶端需要實(shí)現(xiàn) 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 方法返回認(rèn)證需要的必要信息,RequireTransportSecurity 方法表示是否啟用安全鏈接,在生產(chǎn)環(huán)境中,一般都是啟用的,但為了測(cè)試方便,暫時(shí)這里不啟用了。

實(shí)現(xiàn)接口:

  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)) 

好了,現(xiàn)在我們的服務(wù)就有 Token 認(rèn)證功能了。如果用戶名或密碼錯(cuò)誤,客戶端就會(huì)收到:

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

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

單向證書認(rèn)證

證書認(rèn)證分兩種方式:

  1. 單向認(rèn)證
  2. 雙向認(rèn)證

先看一下單向認(rèn)證方式:

生成證書

首先通過(guò) 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 

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

  • key: 服務(wù)器上的私鑰文件,用于對(duì)發(fā)送給客戶端數(shù)據(jù)的加密,以及對(duì)從客戶端接收到數(shù)據(jù)的解密。
  • csr: 證書簽名請(qǐng)求文件,用于提交給證書頒發(fā)機(jī)構(gòu)(CA)對(duì)證書簽名。
  • crt: 由證書頒發(fā)機(jī)構(gòu)(CA)簽名后的證書,或者是開(kāi)發(fā)者自簽名的證書,包含證書持有人的信息,持有人的公鑰,以及簽署者的簽名等信息。

gRPC 代碼

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

  1. // 證書認(rèn)證-單向認(rèn)證 
  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)) 

只有幾行代碼需要修改,很簡(jiǎn)單,接下來(lái)是客戶端。

由于是單向認(rèn)證,不需要為客戶端單獨(dú)生成證書,只需要把服務(wù)端的 crt 文件拷貝到客戶端對(duì)應(yīng)目錄下即可。

  1. // 證書認(rèn)證-單向認(rèn)證 
  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)) 

好了,現(xiàn)在我們的服務(wù)就支持單向證書認(rèn)證了。

但是還沒(méi)完,這里可能會(huì)遇到一個(gè)問(wèn)題:

  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 開(kāi)始廢棄了 CommonName,推薦使用 SAN 證書。如果想要兼容之前的方式,可以通過(guò)設(shè)置環(huán)境變量的方式支持,如下:

  1. export GODEBUG="x509ignoreCN=0" 

但是需要注意,從 Go 1.17 開(kāi)始,環(huán)境變量就不再生效了,必須通過(guò) SAN 方式才行。所以,為了后續(xù)的 Go 版本升級(jí),還是早日支持為好。

雙向證書認(rèn)證

最后來(lái)看看雙向證書認(rèn)證。

生成帶 SAN 的證書

還是先生成證書,但這次有一點(diǎn)不一樣,我們需要生成帶 SAN 擴(kuò)展的證書。

什么是 SAN?

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

將默認(rèn)的 OpenSSL 配置文件拷貝到當(dāng)前目錄。

Linux 系統(tǒng)在:

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

Mac 系統(tǒng)在:

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

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

  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 ] 位置可以配置多個(gè)域名,比如:

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

為了測(cè)試方便,這里只配置一個(gè)域名。

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、生成服務(wù)端證書:

  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 代碼

接下來(lái)開(kāi)始修改代碼,先看服務(wù)端:

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

再看客戶端:

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

大功告成。

Python 客戶端

前面已經(jīng)說(shuō)了,gRPC 是跨語(yǔ)言的,那么,本文最后我們用 Python 寫一個(gè)客戶端,來(lái)請(qǐng)求 Go 服務(wù)端。

使用最簡(jiǎn)單的方式來(lái)實(shí)現(xiàn):

proto 文件就使用最開(kāi)始的「基礎(chǔ)模板」的 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; 

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

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

執(zhí)行成功之后會(huì)在目錄下生成 helloworld_pb2.py 和 helloworld_pb2_grpc.py 兩個(gè)文件。

這個(gè)過(guò)程也可能會(huì)報(bào)錯(cuò):

  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() 

這樣,就可以通過(guò) Python 客戶端請(qǐng)求 Go 啟的服務(wù)端服務(wù)了。

總結(jié)

本文通過(guò)實(shí)戰(zhàn)角度出發(fā),直接用代碼說(shuō)話,來(lái)說(shuō)明 gRPC 的一些應(yīng)用。

內(nèi)容包括簡(jiǎn)單的 gRPC 服務(wù),流處理模式,驗(yàn)證器,Token 認(rèn)證和證書認(rèn)證。

除此之外,還有其他值得研究的內(nèi)容,比如超時(shí)控制,REST 接口和負(fù)載均衡等。以后還會(huì)抽時(shí)間繼續(xù)完善剩下這部分內(nèi)容。

本文中的代碼都經(jīng)過(guò)測(cè)試驗(yàn)證,可以直接執(zhí)行,并且已經(jīng)上傳到 GitHub,小伙伴們可以一遍看源碼,一遍對(duì)照文章內(nèi)容來(lái)學(xué)習(xí)。

源碼地址:

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

本文轉(zhuǎn)載自微信公眾號(hào)「AlwaysBeta」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)lwaysBeta公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: AlwaysBeta
相關(guān)推薦

2013-12-23 09:44:43

2021-03-22 09:27:44

PythonEXCEL熱點(diǎn)推薦

2024-02-26 10:30:27

Biome開(kāi)發(fā)前端

2024-01-22 06:55:09

BiomeWeb 應(yīng)用Prettier

2021-12-13 01:58:58

產(chǎn)品經(jīng)理程序員

2021-03-02 20:42:20

實(shí)戰(zhàn)策略

2023-11-17 14:06:43

2022-01-07 13:36:00

MySQL數(shù)據(jù)庫(kù)分頁(yè)

2009-12-02 13:56:40

Visual Stud

2022-06-06 08:51:56

PandasSQLPython

2023-03-02 11:44:08

AI技術(shù)

2017-11-30 13:15:34

數(shù)據(jù)中心備份云端

2021-02-21 00:22:32

技術(shù)團(tuán)隊(duì)工具

2022-04-29 11:52:02

API代碼HTTP

2021-04-01 22:36:08

蘋果iOS系統(tǒng)功能

2009-04-03 15:21:37

2019-01-30 18:00:21

開(kāi)源Python庫(kù)

2022-10-26 17:28:41

分布式事務(wù)seata

2017-12-28 12:38:29

Windows微軟服務(wù)器

2012-12-28 09:47:07

程序員代碼編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)