gRPC 這項(xiàng)技術(shù)真是太棒了,爆贊
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
- syntax = "proto3";
- package proto;
- // The greeting service definition.
- service Greeter {
- // Sends a greeting
- rpc SayHello (HelloRequest) returns (HelloReply) {}
- }
- // The request message containing the user's name.
- message HelloRequest {
- string name = 1;
- }
- // The response message containing the greetings
- message HelloReply {
- string message = 1;
- }
使用 protoc-gen-go 內(nèi)置的 gRPC 插件生成 gRPC 代碼:
- protoc --go_out=plugins=grpc:. helloworld.proto
執(zhí)行完這個(gè)命令之后,會(huì)在當(dāng)前目錄生成一個(gè) helloworld.pb.go 文件,文件中分別定義了服務(wù)端和客戶端的接口:
- // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
- type GreeterClient interface {
- // Sends a greeting
- SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
- }
- // GreeterServer is the server API for Greeter service.
- type GreeterServer interface {
- // Sends a greeting
- SayHello(context.Context, *HelloRequest) (*HelloReply, error)
- }
接下來(lái)就是寫服務(wù)端和客戶端的代碼,分別實(shí)現(xiàn)對(duì)應(yīng)的接口。
server
- package main
- import (
- "context"
- "fmt"
- "grpc-server/proto"
- "log"
- "net"
- "google.golang.org/grpc"
- "google.golang.org/grpc/reflection"
- )
- type greeter struct {
- }
- func (*greeter) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloReply, error) {
- fmt.Println(req)
- reply := &proto.HelloReply{Message: "hello"}
- return reply, nil
- }
- func main() {
- lis, err := net.Listen("tcp", ":50051")
- if err != nil {
- log.Fatalf("failed to listen: %v", err)
- }
- server := grpc.NewServer()
- // 注冊(cè) grpcurl 所需的 reflection 服務(wù)
- reflection.Register(server)
- // 注冊(cè)業(yè)務(wù)服務(wù)
- proto.RegisterGreeterServer(server, &greeter{})
- fmt.Println("grpc server start ...")
- if err := server.Serve(lis); err != nil {
- log.Fatalf("failed to serve: %v", err)
- }
- }
client
- package main
- import (
- "context"
- "fmt"
- "grpc-client/proto"
- "log"
- "google.golang.org/grpc"
- )
- func main() {
- conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
- if err != nil {
- log.Fatal(err)
- }
- defer conn.Close()
- client := proto.NewGreeterClient(conn)
- reply, err := client.SayHello(context.Background(), &proto.HelloRequest{Name: "zhangsan"})
- if err != nil {
- log.Fatal(err)
- }
- 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
- service Greeter {
- // Sends a greeting
- rpc SayHello (HelloRequest) returns (HelloReply) {}
- // Sends stream message
- rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply) {}
- }
增加一個(gè)流函數(shù) SayHelloStream,通過(guò) stream 關(guān)鍵詞來(lái)指定流特性。
需要重新生成 helloworld.pb.go 文件,這里不再多說(shuō)。
server
- func (*greeter) SayHelloStream(stream proto.Greeter_SayHelloStreamServer) error {
- for {
- args, err := stream.Recv()
- if err != nil {
- if err == io.EOF {
- return nil
- }
- return err
- }
- fmt.Println("Recv: " + args.Name)
- reply := &proto.HelloReply{Message: "hi " + args.Name}
- err = stream.Send(reply)
- if err != nil {
- return err
- }
- }
- }
在「基礎(chǔ)模板」上增加 SayHelloStream 函數(shù),其他都不需要變。
client
- client := proto.NewGreeterClient(conn)
- // 流處理
- stream, err := client.SayHelloStream(context.Background())
- if err != nil {
- log.Fatal(err)
- }
- // 發(fā)送消息
- go func() {
- for {
- if err := stream.Send(&proto.HelloRequest{Name: "zhangsan"}); err != nil {
- log.Fatal(err)
- }
- time.Sleep(time.Second)
- }
- }()
- // 接收消息
- for {
- reply, err := stream.Recv()
- if err != nil {
- if err == io.EOF {
- break
- }
- log.Fatal(err)
- }
- 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)。
先安裝:
- go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators
- go get github.com/grpc-ecosystem/go-grpc-middleware
接下來(lái)修改 proto 文件:
proto
- import "github.com/mwitkow/go-proto-validators@v0.3.2/validator.proto";
- message HelloRequest {
- string name = 1 [
- (validator.field) = {regex: "^[z]{2,5}$"}
- ];
- }
在這里對(duì) name 參數(shù)進(jìn)行校驗(yàn),需要符合正則的要求才可以正常請(qǐng)求。
還有其他驗(yàn)證規(guī)則,比如對(duì)數(shù)字大小進(jìn)行驗(yàn)證等,這里不做過(guò)多介紹。
接下來(lái)生成 *.pb.go 文件:
- protoc \
- --proto_path=${GOPATH}/pkg/mod \
- --proto_path=${GOPATH}/pkg/mod/github.com/gogo/protobuf@v1.3.2 \
- --proto_path=. \
- --govalidators_out=. --go_out=plugins=grpc:.\
- *.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ò):
- Import "github.com/mwitkow/go-proto-validators/validator.proto" was not found or had errors
但不要慌,大概率是引用路徑的問(wèn)題,一定要看好自己的安裝版本,以及在 GOPATH 中的具體路徑。
最后是服務(wù)端代碼改造:
引入包:
- grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
- grpc_validator "github.com/grpc-ecosystem/go-grpc-middleware/validator"
然后在初始化的時(shí)候增加驗(yàn)證器功能:
- server := grpc.NewServer(
- grpc.UnaryInterceptor(
- grpc_middleware.ChainUnaryServer(
- grpc_validator.UnaryServerInterceptor(),
- ),
- ),
- grpc.StreamInterceptor(
- grpc_middleware.ChainStreamServer(
- grpc_validator.StreamServerInterceptor(),
- ),
- ),
- )
啟動(dòng)程序之后,我們?cè)儆弥暗目蛻舳舜a來(lái)請(qǐng)求,會(huì)收到報(bào)錯(cuò):
- 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}$"
- 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ù):
- func Auth(ctx context.Context) error {
- md, ok := metadata.FromIncomingContext(ctx)
- if !ok {
- return fmt.Errorf("missing credentials")
- }
- var user string
- var password string
- if val, ok := md["user"]; ok {
- user = val[0]
- }
- if val, ok := md["password"]; ok {
- password = val[0]
- }
- if user != "admin" || password != "admin" {
- return grpc.Errorf(codes.Unauthenticated, "invalid token")
- }
- return nil
- }
metadata.FromIncomingContext 從上下文讀取用戶名和密碼,然后和實(shí)際數(shù)據(jù)進(jìn)行比較,判斷是否通過(guò)認(rèn)證。
攔截器:
- var authInterceptor grpc.UnaryServerInterceptor
- authInterceptor = func(
- ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
- ) (resp interface{}, err error) {
- //攔截普通方法請(qǐng)求,驗(yàn)證 Token
- err = Auth(ctx)
- if err != nil {
- return
- }
- // 繼續(xù)處理請(qǐng)求
- return handler(ctx, req)
- }
初始化:
- server := grpc.NewServer(
- grpc.UnaryInterceptor(
- grpc_middleware.ChainUnaryServer(
- authInterceptor,
- grpc_validator.UnaryServerInterceptor(),
- ),
- ),
- grpc.StreamInterceptor(
- grpc_middleware.ChainStreamServer(
- grpc_validator.StreamServerInterceptor(),
- ),
- ),
- )
除了上文的驗(yàn)證器,又多了 Token 認(rèn)證攔截器 authInterceptor。
最后是客戶端改造,客戶端需要實(shí)現(xiàn) PerRPCCredentials 接口。
- type PerRPCCredentials interface {
- // GetRequestMetadata gets the current request metadata, refreshing
- // tokens if required. This should be called by the transport layer on
- // each request, and the data should be populated in headers or other
- // context. If a status code is returned, it will be used as the status
- // for the RPC. uri is the URI of the entry point for the request.
- // When supported by the underlying implementation, ctx can be used for
- // timeout and cancellation.
- // TODO(zhaoq): Define the set of the qualified keys instead of leaving
- // it as an arbitrary string.
- GetRequestMetadata(ctx context.Context, uri ...string) (
- map[string]string, error,
- )
- // RequireTransportSecurity indicates whether the credentials requires
- // transport security.
- RequireTransportSecurity() bool
- }
GetRequestMetadata 方法返回認(rèn)證需要的必要信息,RequireTransportSecurity 方法表示是否啟用安全鏈接,在生產(chǎn)環(huán)境中,一般都是啟用的,但為了測(cè)試方便,暫時(shí)這里不啟用了。
實(shí)現(xiàn)接口:
- type Authentication struct {
- User string
- Password string
- }
- func (a *Authentication) GetRequestMetadata(context.Context, ...string) (
- map[string]string, error,
- ) {
- return map[string]string{"user": a.User, "password": a.Password}, nil
- }
- func (a *Authentication) RequireTransportSecurity() bool {
- return false
- }
連接:
- conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithPerRPCCredentials(&auth))
好了,現(xiàn)在我們的服務(wù)就有 Token 認(rèn)證功能了。如果用戶名或密碼錯(cuò)誤,客戶端就會(huì)收到:
- 2021/10/11 20:39:35 rpc error: code = Unauthenticated desc = invalid token
- exit status 1
如果用戶名和密碼正確,則可以正常返回。
單向證書認(rèn)證
證書認(rèn)證分兩種方式:
- 單向認(rèn)證
- 雙向認(rèn)證
先看一下單向認(rèn)證方式:
生成證書
首先通過(guò) openssl 工具生成自簽名的 SSL 證書。
1、生成私鑰:
- openssl genrsa -des3 -out server.pass.key 2048
2、去除私鑰中密碼:
- openssl rsa -in server.pass.key -out server.key
3、生成 csr 文件:
- 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、生成證書:
- 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ù)端代碼。
- // 證書認(rèn)證-單向認(rèn)證
- creds, err := credentials.NewServerTLSFromFile("keys/server.crt", "keys/server.key")
- if err != nil {
- log.Fatal(err)
- return
- }
- server := grpc.NewServer(grpc.Creds(creds))
只有幾行代碼需要修改,很簡(jiǎn)單,接下來(lái)是客戶端。
由于是單向認(rèn)證,不需要為客戶端單獨(dú)生成證書,只需要把服務(wù)端的 crt 文件拷貝到客戶端對(duì)應(yīng)目錄下即可。
- // 證書認(rèn)證-單向認(rèn)證
- creds, err := credentials.NewClientTLSFromFile("keys/server.crt", "example.grpcdev.cn")
- if err != nil {
- log.Fatal(err)
- return
- }
- conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
好了,現(xiàn)在我們的服務(wù)就支持單向證書認(rèn)證了。
但是還沒(méi)完,這里可能會(huì)遇到一個(gè)問(wèn)題:
- 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"
- exit status 1
原因是 Go 1.15 開(kāi)始廢棄了 CommonName,推薦使用 SAN 證書。如果想要兼容之前的方式,可以通過(guò)設(shè)置環(huán)境變量的方式支持,如下:
- 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)在:
- /etc/pki/tls/openssl.cnf
Mac 系統(tǒng)在:
- /System/Library/OpenSSL/openssl.cnf
修改臨時(shí)配置文件,找到 [ req ] 段落,然后將下面語(yǔ)句的注釋去掉。
- req_extensions = v3_req # The extensions to add to a certificate request
接著添加以下配置:
- [ v3_req ]
- # Extensions to add to a certificate request
- basicConstraints = CA:FALSE
- keyUsage = nonRepudiation, digitalSignature, keyEncipherment
- subjectAltName = @alt_names
- [ alt_names ]
- DNS.1 = www.example.grpcdev.cn
[ alt_names ] 位置可以配置多個(gè)域名,比如:
- [ alt_names ]
- DNS.1 = www.example.grpcdev.cn
- DNS.2 = www.test.grpcdev.cn
為了測(cè)試方便,這里只配置一個(gè)域名。
1、生成 ca 證書:
- openssl genrsa -out ca.key 2048
- openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.grpcdev.com" -days 5000 -out ca.pem
2、生成服務(wù)端證書:
- # 生成證書
- openssl req -new -nodes \
- -subj "/C=CN/ST=Beijing/L=Beijing/O=grpcdev/OU=grpcdev/CN=www.example.grpcdev.cn" \
- -config <(cat openssl.cnf \
- <(printf "[SAN]\nsubjectAltName=DNS:www.example.grpcdev.cn")) \
- -keyout server.key \
- -out server.csr
- # 簽名證書
- openssl x509 -req -days 365000 \
- -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial \
- -extfile <(printf "subjectAltName=DNS:www.example.grpcdev.cn") \
- -out server.pem
3、生成客戶端證書:
- # 生成證書
- openssl req -new -nodes \
- -subj "/C=CN/ST=Beijing/L=Beijing/O=grpcdev/OU=grpcdev/CN=www.example.grpcdev.cn" \
- -config <(cat openssl.cnf \
- <(printf "[SAN]\nsubjectAltName=DNS:www.example.grpcdev.cn")) \
- -keyout client.key \
- -out client.csr
- # 簽名證書
- openssl x509 -req -days 365000 \
- -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial \
- -extfile <(printf "subjectAltName=DNS:www.example.grpcdev.cn") \
- -out client.pem
gRPC 代碼
接下來(lái)開(kāi)始修改代碼,先看服務(wù)端:
- // 證書認(rèn)證-雙向認(rèn)證
- // 從證書相關(guān)文件中讀取和解析信息,得到證書公鑰、密鑰對(duì)
- cert, _ := tls.LoadX509KeyPair("cert/server.pem", "cert/server.key")
- // 創(chuàng)建一個(gè)新的、空的 CertPool
- certPool := x509.NewCertPool()
- ca, _ := ioutil.ReadFile("cert/ca.pem")
- // 嘗試解析所傳入的 PEM 編碼的證書。如果解析成功會(huì)將其加到 CertPool 中,便于后面的使用
- certPool.AppendCertsFromPEM(ca)
- // 構(gòu)建基于 TLS 的 TransportCredentials 選項(xiàng)
- creds := credentials.NewTLS(&tls.Config{
- // 設(shè)置證書鏈,允許包含一個(gè)或多個(gè)
- Certificates: []tls.Certificate{cert},
- // 要求必須校驗(yàn)客戶端的證書??梢愿鶕?jù)實(shí)際情況選用以下參數(shù)
- ClientAuth: tls.RequireAndVerifyClientCert,
- // 設(shè)置根證書的集合,校驗(yàn)方式使用 ClientAuth 中設(shè)定的模式
- ClientCAs: certPool,
- })
再看客戶端:
- // 證書認(rèn)證-雙向認(rèn)證
- // 從證書相關(guān)文件中讀取和解析信息,得到證書公鑰、密鑰對(duì)
- cert, _ := tls.LoadX509KeyPair("cert/client.pem", "cert/client.key")
- // 創(chuàng)建一個(gè)新的、空的 CertPool
- certPool := x509.NewCertPool()
- ca, _ := ioutil.ReadFile("cert/ca.pem")
- // 嘗試解析所傳入的 PEM 編碼的證書。如果解析成功會(huì)將其加到 CertPool 中,便于后面的使用
- certPool.AppendCertsFromPEM(ca)
- // 構(gòu)建基于 TLS 的 TransportCredentials 選項(xiàng)
- creds := credentials.NewTLS(&tls.Config{
- // 設(shè)置證書鏈,允許包含一個(gè)或多個(gè)
- Certificates: []tls.Certificate{cert},
- // 要求必須校驗(yàn)客戶端的證書??梢愿鶕?jù)實(shí)際情況選用以下參數(shù)
- ServerName: "www.example.grpcdev.cn",
- RootCAs: certPool,
- })
大功告成。
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 文件:
- syntax = "proto3";
- package proto;
- // The greeting service definition.
- service Greeter {
- // Sends a greeting
- rpc SayHello (HelloRequest) returns (HelloReply) {}
- // Sends stream message
- rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply) {}
- }
- // The request message containing the user's name.
- message HelloRequest {
- string name = 1;
- }
- // The response message containing the greetings
- message HelloReply {
- string message = 1;
- }
同樣的,也需要通過(guò)命令行的方式生成 pb.py 文件:
- 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ò):
- ModuleNotFoundError: No module named 'grpc_tools'
別慌,是缺少包,安裝就好:
- pip3 install grpcio
- pip3 install grpcio-tools
最后看一下 Python 客戶端代碼:
- import grpc
- import helloworld_pb2
- import helloworld_pb2_grpc
- def main():
- channel = grpc.insecure_channel("127.0.0.1:50051")
- stub = helloworld_pb2_grpc.GreeterStub(channel)
- response = stub.SayHello(helloworld_pb2.HelloRequest(name="zhangsan"))
- print(response.message)
- if __name__ == '__main__':
- 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)。