【go微服務】如何快速掌握grpc開發

在這里插入圖片描述

?? 歡迎大家來到景天科技苑??

🎈🎈 養成好習慣,先贊后看哦~🎈🎈

🏆 作者簡介:景天科技苑
🏆《頭銜》:大廠架構師,華為云開發者社區專家博主,阿里云開發者社區專家博主,CSDN全棧領域優質創作者,掘金優秀博主,51CTO博客專家等。
🏆《博客》:Python全棧,Golang開發,PyQt5和Tkinter桌面開發,小程序開發,人工智能,js逆向,App逆向,網絡系統安全,數據分析,Django,fastapi,flask等框架,云原生K8S,linux,shell腳本等實操經驗,網站搭建,數據庫等分享。

所屬的專欄:Go語言微服務
景天的主頁:景天科技苑

在這里插入圖片描述

文章目錄

  • grpc框架
    • 1、gRPC 是什么?
    • 2、grpc環境安裝
      • 1)安裝grpc包
      • 2)生成grpc代碼
    • 3、grpc的使用
      • 1)定義proto文件
      • 2)生成grpc代碼
      • 3)編寫grpc服務端代碼
      • 4)編寫grpc客戶端
    • 4、grpc其他特性
      • 1)同步阻塞
      • 2)設置收發數據大小
      • 3)grpc recover
      • 4)grpc攔截器
        • 1. 服務端攔截器
        • 2. 客戶端攔截器
    • 5、grpc安全與認證
      • 1. go v1.15以下版本
      • 2. go v1.15以后版本
        • 1)生成SAN證書
        • 2) 生成根證書
        • 3) 生成SNA的服務端證書
      • 3. grpc加密代碼實現
        • 1)server端
        • 4)客戶端

grpc框架

GRPC是Google公司基于Protobuf開發的跨語言的開源RPC框架
GRPC基于HTTP/2協議設計,可以基于一個HTTP/2鏈接提供多個服務,對于移動設備更加友好。
目前提供 C、Java 和 Go 語言版本,分別是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.

1、gRPC 是什么?

在 gRPC 里客戶端應用可以像調用本地對象一樣直接調用另一臺不同的機器上服務端應用的方法,使得您能夠更容易地創建分布式應用和服務。
與許多 RPC 系統類似,gRPC 也是基于以下理念:定義一個服務,指定其能夠被遠程調用的方法(包含參數和返回類型)。
在服務端實現這個接口,并運行一個 gRPC 服務器來處理客戶端調用。在客戶端擁有一個存根能夠像服務端一樣的方法。
在這里插入圖片描述

gRPC 客戶端和服務端可以在多種環境中運行和交互 - 從 google 內部的服務器到你自己的筆記本,并且可以用任何 gRPC 支持的語言來編寫。
所以,你可以很容易地用 Java 創建一個 gRPC 服務端,用 Go、Python、Ruby 來創建客戶端。
此外,Google 最新 API 將有 gRPC 版本的接口,使你很容易地將 Google 的功能集成到你的應用里。

使用 protocol buffers
gRPC 默認使用 protocol buffers,這是 Google 開源的一套成熟的結構數據序列化機制(當然也可以使用其他數據格式如 JSON)。
正如你將在下方例子里所看到的,你用 proto files 創建 gRPC 服務,用 protocol buffers 消息類型來定義方法參數和返回類型。
你可以在 Protocol Buffers 文檔找到更多關于 Protocol Buffers 的資料。

Protocol buffers 版本
盡管 protocol buffers 對于開源用戶來說已經存在了一段時間,例子內使用的卻一種名叫 proto3 的新風格的 protocol buffers,它擁有輕量簡化的語法、一些有用的新功能,并且支持更多新語言。
當前針對 Java 和 C++ 發布了 beta 版本,針對 JavaNano(即 Android Java)發布 alpha 版本,在protocol buffers Github 源碼庫里有 Ruby 支持, 在golang/protobuf Github 源碼庫里還有針對 Go 語言的生成器, 對更多語言的支持正在開發中。 你可以在 proto3 語言指南里找到更多內容, 在與當前默認版本的發布說明比較,看到兩者的主要不同點。
更多關于 proto3 的文檔很快就會出現。雖然你可以使用 proto2 (當前默認的 protocol buffers 版本), 我們通常建議你在 gRPC 里使用 proto3,因為這樣你可以使用 gRPC 支持全部范圍的的語言,并且能避免 proto2 客戶端與 proto3 服務端交互時出現的兼容性問題,反之亦然。

再詳細了解使用GRPC之前先來了解一下上面定義中的一些關鍵詞。
首先我們來看一下HTTP/2是什么內容?
其實本質上就是http2.0版本,http目前為止主要有四個版本,分別為http1.0、http1.1、http2.0、https。
http1.0是最原始的版本,不支持持久連接,效率也比較低
http1.1針對http1.0版本做了優化,可以連接一次,多次使用,效率比http1.0高
http2.0實現了多路復用,對http1.1再次進行了優化。http2.0也被稱為下一代http協議,是在2013年8月進行首次測試,所以現在用的不是很廣。
https其實是在http協議上多加了一層SSL協議,具體如下圖:
在這里插入圖片描述

所以本質上,http1.0、http1.1、http2.0都可以添加SSL協議。

2、grpc環境安裝

1)安裝grpc包

go get -u google.golang.org/protobuf
go install google.golang.org/protobuf/cmd/protoc-gen-go
go get google.golang.org/grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

安裝gogofaster,用于加速編譯

go get github.com/gogo/protobuf
go install github.com/gogo/protobuf/protoc-gen-gogofaster

2)生成grpc代碼

最常見的有兩種方法:

官方版本:protoc-go_out=.--go-grpc_out=.student.proto  //除了生成student.pb.go文件外還會生成student_grpc.pb.go
早些年使用: github.com/golang/protobuf/protoc-gen-go時,使用的命令是: protoc--go_out=plugins=grpc:. student.proto

gogofaster版本:

protoc --gogofaster_out=./micro_service/grpc  --proto_path=./micro_service/grpc student.proto    生成的go代碼僅對message完成序列化,不包含service
protoc--gogofaster_out=plugins=grpc:. student.proto  //包含對service的序列化。只生成student.pb.go一個文件

3、grpc的使用

如果從Protobuf的角度看,GRPC只不過是一個針對service接口生成代碼的生成器。接著我們來學習一下GRPC的用法。這里我們創建一個簡單的proto文件,定義一個HelloService接口:

1)定義proto文件

syntax = "proto3";  //定義proto版本
package pb;//消息體 一個package中不允許有兩個同名的message
message Doctor{int32 age = 1;string name = 2;}//定義服務
service SayName{rpc SayHello(Doctor) returns (Doctor);
}

在這里插入圖片描述

2)生成grpc代碼

我們使用gogofaster編譯

protoc -I="F:/goworks\src/jingtian/myrpc" --gogofaster_out=plugins=grpc:./pb/mygrpc --proto_path=./pb/mygrpc/ person.proto

在這里插入圖片描述

追求極致的性能,使用gogofaster。
如果業務邏輯比較耗時,序列化數據不大,建議使用官方的序列化方式

查看生成的grpc代碼
在這里插入圖片描述

3)編寫grpc服務端代碼

生成的grpc代碼中,生成了注冊函數
在這里插入圖片描述

服務端代碼:

package mainimport ("context""fmt""google.golang.org/grpc"pb "jingtian/myrpc/pb/mygrpc""net"
)// Children 定義類
type Children struct {
}// SayHello 類方法
func (children *Children) SayHello(ctx context.Context, doctor *pb.Doctor) (*pb.Doctor, error) {doctor.Name += "is Sleeping"return doctor, nil
}
func main() {//服務端創建步驟//1. 初始一個 grpc 對象//func NewServer(opt ...ServerOption) *Servergrpcserver := grpc.NewServer()//2. 注冊服務//之前,我們是直接自己注冊,現在通過proto文件生成的代碼中,生成了注冊函數pb.RegisterSayNameServer(grpcserver, new(Children))//3. 設置監聽, 指定 IP、portlistener, err := net.Listen("tcp", ":8080")if err != nil {panic("監聽失敗: " + err.Error())}fmt.Println("開始監聽...")defer listener.Close()//4. 啟動服務。---- serve()//這里就不需要再去建立conn連接了。直接通過grpcserver.Serve(listener)啟動服務//func (s *Server) Serve(lis net.Listener) errorerr = grpcserver.Serve(listener)if err != nil {panic("grpcserver啟動報錯: " + err.Error())}}

在這里插入圖片描述

4)編寫grpc客戶端

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"pb "jingtian/myrpc/pb/mygrpc""time"
)func main() {//1. 連接 grpc 服務,新版的不能用grpc.Dial了。使用grpc.NewClientconn, err := grpc.NewClient("127.0.0.1:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {panic(err)}defer conn.Close()//2. 初始化 grpc 客戶端client := pb.NewSayNameClient(conn)//3. 調用遠程服務。// 執行RPC調用并打印收到的響應數據,指定1秒超時ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()//創建Doctor對象,用來傳參var doctor pb.Doctordoctor.Name = "景天"doctor.Age = 18resp, err := client.SayHello(ctx, &doctor)if err != nil {panic(err)}fmt.Println("從grpc服務端獲取的數據: ", resp.Name)
}

在這里插入圖片描述

如果我們不加 grpc.WithTransportCredentials(insecure.NewCredentials())。會報錯
在這里插入圖片描述

4、grpc其他特性

1)同步阻塞

我們關掉grpcserver。然后運行客戶端
我們發現報錯的地方,跟我們預想的不一樣,這是因為連接函數是異步執行的
在這里插入圖片描述

如果想把異步方式改為同步的話,只需要在連接的時候,加個參數grpc.WithBlock()
WithBlock() 只生效于 DialContext(),不會影響普通 grpc.Dial()。
必須搭配 context.WithTimeout(),否則可能永遠卡住。
grpc.WithInsecure() 在新版本中建議替換為 grpc.WithTransportCredentials(insecure.NewCredentials())(如果不加密):

package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"pb "jingtian/myrpc/pb/mygrpc""time"
)func main() {// 執行RPC調用并打印收到的響應數據,指定1秒超時ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()//1. 連接 grpc 服務,新版的不能用grpc.Dial了。使用grpc.NewClientconn, err := grpc.DialContext(ctx,"127.0.0.1:8080",grpc.WithTransportCredentials(insecure.NewCredentials()),grpc.WithBlock(), //直到連接真正建立才會返回,否則連接是異步建立的。因此grpc.WithBlock()和Timeout結合使用才有意義。server端正常的情況下使用grpc.WithBlock()得到的connection.GetState()為READY,不使用grpc.WithBlock()得到的connection.GetState()為IDEL)if err != nil {panic("建立連接失敗: " + err.Error())}defer conn.Close()//2. 初始化 grpc 客戶端client := pb.NewSayNameClient(conn)//3. 調用遠程服務。//創建Doctor對象,用來傳參var doctor pb.Doctordoctor.Name = "景天"doctor.Age = 18resp, err := client.SayHello(ctx, &doctor)if err != nil {panic("調用服務端函數失敗: " + err.Error())}fmt.Println(resp.Name)
}

此時,不啟動server,運行客戶端
就會報錯建立連接失敗
在這里插入圖片描述

2)設置收發數據大小

grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(10<<20), grpc.MaxCallRecvMsgSize(10<<20))
默認情況下SendMsg上限是MaxInt32,RecvMsg上限是4M,這里都修改為10M
在這里插入圖片描述

3)grpc recover

大多數時候,服務端在開發的時候,是正常的,但是線上通常會因為客戶端傳遞一些異常的參數,可能會導致panic的產生
這種情況很可能在測試時,也很難覆蓋到。
因此,為了避免服務端運行時異常panic,我們需要捕捉recover下,在每個接口一進來,就寫一個recover
在這里插入圖片描述

package mainimport ("context""fmt""google.golang.org/grpc"pb "jingtian/myrpc/pb/mygrpc""net"
)// Children 定義類
type Children struct {
}// SayHello 類方法
func (children *Children) SayHello(ctx context.Context, doctor *pb.Doctor) (*pb.Doctor, error) {defer func() {if r := recover(); r != nil {fmt.Println("Recovered in f", r)}}()doctor.Name += " is Sleeping"return doctor, nil
}
func main() {//服務端創建步驟//1. 初始一個 grpc 對象//func NewServer(opt ...ServerOption) *Servergrpcserver := grpc.NewServer()//2. 注冊服務//之前,我們是直接自己注冊,現在通過proto文件生成的代碼中,生成了注冊函數pb.RegisterSayNameServer(grpcserver, new(Children))//3. 設置監聽, 指定 IP、portlistener, err := net.Listen("tcp", ":8080")if err != nil {panic("監聽失敗: " + err.Error())}fmt.Println("開始監聽...")defer listener.Close()//4. 啟動服務。---- serve()//這里就不需要再去建立conn連接了。直接通過grpcserver.Serve(listener)啟動服務//func (s *Server) Serve(lis net.Listener) errorerr = grpcserver.Serve(listener)if err != nil {panic("grpcserver啟動報錯: " + err.Error())}}

4)grpc攔截器

在這里插入圖片描述

1. 服務端攔截器

如果使用多個攔截器,需要下載第三方包
go get github.com/grpc-ecosystem/go-grpc-middleware

// 計時攔截器
func timerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface {
}, error) {begin := time.Now()resp, err := handler(ctx, req)fmt.Printf("%s finished in %d ms\n", info.FullMethod, time.Since(begin).Microseconds())return resp, err
}// 限流攔截器。比如同時限制10個請求
var limitAtServer = make(chan struct{}, 10)func limitInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {limitAtServer <- struct{}{}resp, err := handler(ctx, req)fmt.Printf("concurrency %d\n", len(limitAtServer)) //打印瞬時并發<-limitAtServerreturn resp, err
}在創建grpc服務的時候,把攔截器傳進去//使用中間件。默認grpc.NewServer()只支持一個中間件grpcserver := grpc.NewServer(//grpc.UnaryInterceptor(timerInterceptor), //grpc.UnaryInterceptor只能使用一次,即server端只能用一個攔截器//grpcmiddleware可以把多個攔截器,封裝成一個攔截器grpc.UnaryInterceptor(grpcmiddleware.ChainUnaryServer(timerInterceptor, limitInterceptor)),)
2. 客戶端攔截器

客戶端也可以加,比如計時攔截器,客戶端訪問很多個接口,可以隨時了解,訪問的接口的快與慢

var limitAtClient = make(chan struct{}, 10) //瞬間并發度限制為10// 計時攔截器
func timerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {begin := time.Now()err := invoker(ctx, method, req, reply, cc, opts...)fmt.Printf("use time %d ms\n", time.Since(begin).Milliseconds())return err
}// 限流攔截器
func limitInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {limitAtClient <- struct{}{}err := invoker(ctx, method, req, reply, cc, opts...)fmt.Printf("concurrency %d\n", len(limitAtClient)) //打印瞬間并發度<-limitAtClientreturn err
}應用// 連接到GRPC服務端conn, err := grpc.Dial("127.0.0.1:5678",grpc.WithTransportCredentials(insecure.NewCredentials()), //無需使用安全傳輸//在GRPC客戶端使用攔截器grpc.WithChainUnaryInterceptor(timerInterceptor, limitInterceptor), //不定長參數,第一個攔截器在最外層,最后一個攔截器最靠近真實的業務調用)

5、grpc安全與認證

服務端與客戶端之間數據傳輸進行加密認證
證書有兩種方式

  1. 在公司內部,小組與小組之間互相調用接口可以使用自簽證書
  2. 跨公司,跨外網,就需要購買證書了

1. go v1.15以下版本

我們采用自簽證書形式
在go1.15版本以下可以采用以下方式:
1.生成server的私鑰(由私鑰可以生成公鑰)
需要先安裝openssl
openssl genrsa -out server.key 2048

2.生成自簽名證書,有效其10年。證書里包含server的公鑰和簽發者2.信息
openssl req -x509 -new -nodes -key server.key -subj “/CN=localhost” -days 3650 -out server.crt
/CN指定Common Name,實際中替換成自己網站的域名在內網環境,server的證書可以直接傳送給client。在公網環境下需要由CA統一管理所有的證書。

grpc數據安全傳輸

Server端代碼:

creds, err := credentials.NewServerTLSFromFile("server.crt", server.key")server:= grpc.NewServer(grpc.Creds(creds))

Client端代碼:

creds, err := credentials.NewClientTLSFromFile("server.crt", "")conn, err := grpc.Dial("localhost:5678",grpc.WithTransportCredentials(creds),)

Client端從CA拿到server的證書server.crt,證書里包含了server的公鑰。
在TLS握手階段,Client先生成自己的密鑰(對稱加密算法),用server的公鑰加密自己的密鑰,然后發給Server。
Server用自己的私鑰(server.key)解密后獲得Client的密鑰。
此后所有的grpc數據傳輸都會先使用Client的密鑰進行加密

2. go v1.15以后版本

在go v1.15以后,如果使用一般的證書通信,則會報錯:

rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs"

在這里插入圖片描述

總的來說,就是:
身份驗證失敗了,因為GO 1.15以后X509 不能用了,提示我們有兩個選擇:

  1. 需要使用SAN 證書
  2. 改變環境變量:GODEBUG=x509ignoreCN=0

這里,我們選擇使用SAN證書,進行通信

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

1)生成SAN證書

要生成SAN證書,需要以下幾步:

  1. 修改openssl.cfg
    如果是Linux系統的話,應該修改的是openssl.cnf文件,我這個是windos,所以就是openssl.cfg。
    將openssl.cfg文件拷貝到需要生成證書的那個目錄,進行修改。將一些默認注釋的參數取消注釋
    在這里插入圖片描述

1)打開copy_extensions 在CA_default節

# Extension copying option: use with caution.
copy_extensions = copy

2)打開req_extensions 在req中修改

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

這段配置表示在生成 CSR 文件時讀取名叫 v3_req 的段落的配置信息,因此我們再在此配置文件中加入一段名為 v3_req 的配置:

3)增加subjectAltName 在v3_req里面

[ v3_req ]
...
subjectAltName = @alt_names
## 這段配置中最重要的是在最后導入名為 alt_names 的配置段,
## 因此我們還需要添加一個名為 [ alt_names ] 的配置段,這可以定義多個服務
[alt_names]
DNS.1 = *.org.example.com 
DNS.2 = *.example.com
2) 生成根證書

現在,開始生成證書,首先是生成CA證書:
生成CA私鑰(.key)–>生成CA證書請求(.csr)–>自簽名得到根證書(.crt)(CA給自已頒發的證書)。
生成根證書私鑰

openssl genrsa -out ca.key 2048 

生成CA證書請求

openssl req -new -key ca.key -out ca.csr -subj "/C=cn/OU=myorg/O=mytest/CN=myname"

自簽名得到根證書

openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt

其中:
genrsa:使用RSA算法產生私鑰
-in:要輸入的csr文件
-out:輸出文件的路徑
-subj:證書相關的用戶信息(subject的縮寫)
-key:指定私鑰路徑
-new:新證書簽發請求
-req:輸入csr文件
-days:證書的有效期(天)

3) 生成SNA的服務端證書

生成服務端私鑰(serve.key)–>生成服務端證書請求(server.csr)–>CA對服務端請求文件簽名,生成服務端證書(server.pem)

生成服務端證書私鑰

openssl genrsa -out server.key 2048

根據私鑰server.key生成證書請求文件server.crt

openssl req -new -nodes -key server.key -out server.crt -subj "/C=cn/OU=myserver/O=servercomp/CN=servername"  -config /etc/pki/tls/openssl.cnf -extensions v3_req

請求CA對證書請求文件簽名,生成最終證書文件

openssl x509 -req -days 365 -in server.crt -out server.pem -CA ca.crt -CAkey ca.key -CAcreateserial  -extfile  /etc/pki/tls/openssl.cnf -extensions v3_req
Signature ok
subject=C = cn, OU = myserver, O = servercomp, CN = servername
Getting CA Private Key

在這里插入圖片描述

驗證:

openssl x509 -noout -text -in server.pem
[root@redis01 cert ]#openssl x509 -noout -text -in server.pem
Certificate:Data:Version: 3 (0x2)Serial Number:82:af:4a:7d:58:5f:9f:02
...X509v3 extensions:X509v3 Basic Constraints: CA:FALSEX509v3 Key Usage: Digital Signature, Non Repudiation, Key EnciphermentX509v3 Subject Alternative Name: DNS:*.org.example.com, DNS:*.example.com

生成的證書
在這里插入圖片描述

3. grpc加密代碼實現

1)server端
package mainimport ("context""fmt"grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware""google.golang.org/grpc""google.golang.org/grpc/credentials"pb "jingtian/myrpc/pb/mygrpc""net""time"
)// Children 定義類
type Children struct {
}// SayHello 類方法
func (children *Children) SayHello(ctx context.Context, doctor *pb.Doctor) (*pb.Doctor, error) {//recover捕獲異常defer func() {if r := recover(); r != nil {fmt.Println("Recovered in f", r)}}()doctor.Name += " is Sleeping"return doctor, nil
}// 計時攔截器
func timerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface {
}, error) {begin := time.Now()resp, err := handler(ctx, req)fmt.Printf("%s finished in %d ms\n", info.FullMethod, time.Since(begin).Microseconds())return resp, err
}// 限流攔截器。比如同時限制10個請求
var limitAtServer = make(chan struct{}, 10)func limitInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {limitAtServer <- struct{}{}resp, err := handler(ctx, req)fmt.Printf("concurrency %d\n", len(limitAtServer)) //打印瞬時并發<-limitAtServerreturn resp, err
}
func main() {//grpc加密傳輸creds, err := credentials.NewServerTLSFromFile("server.pem", "server.key")if err != nil {fmt.Println("credentials.NewServerTLSFromFile err:", err)}fmt.Println("加密后是啥:", creds.Info())//服務端創建步驟//1. 初始一個 grpc 對象//func NewServer(opt ...ServerOption) *Server//使用中間件。默認grpc.NewServer()只支持一個中間件grpcserver := grpc.NewServer(//應用證書grpc.Creds(creds),//grpc.UnaryInterceptor(timerInterceptor), //grpc.UnaryInterceptor只能使用一次,即server端只能用一個攔截器//grpcmiddleware可以把多個攔截器,封裝成一個攔截器grpc.UnaryInterceptor(grpcmiddleware.ChainUnaryServer(timerInterceptor, limitInterceptor)),)//2. 注冊服務//之前,我們是直接自己注冊,現在通過proto文件生成的代碼中,生成了注冊函數pb.RegisterSayNameServer(grpcserver, new(Children))//3. 設置監聽, 指定 IP、portlistener, err := net.Listen("tcp", ":8080")if err != nil {panic("監聽失敗: " + err.Error())}fmt.Println("開始監聽...")defer listener.Close()//4. 啟動服務。---- serve()//這里就不需要再去建立conn連接了。直接通過grpcserver.Serve(listener)啟動服務//func (s *Server) Serve(lis net.Listener) errorerr = grpcserver.Serve(listener)if err != nil {panic("grpcserver啟動報錯: " + err.Error())}}
4)客戶端
package mainimport ("context""fmt""google.golang.org/grpc""google.golang.org/grpc/credentials"pb "jingtian/myrpc/pb/mygrpc""time"
)func main() {//證書加密傳輸 注意,第二個參數serverNameOverride 為服務名稱。是我們在openssl配置文件中配置的,不能亂填creds, err := credentials.NewClientTLSFromFile("server.pem", "jingtian.example.com")if err != nil {fmt.Println("證書文件沒找到", err)}// 執行RPC調用并打印收到的響應數據,指定1秒超時ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()//1. 連接 grpc 服務,新版的不能用grpc.Dial了。使用grpc.NewClientconn, err := grpc.NewClient(//ctx,"127.0.0.1:8080",//加密傳輸grpc.WithTransportCredentials(creds),//grpc.WithTransportCredentials(insecure.NewCredentials()),//grpc.WithBlock(), //直到連接真正建立才會返回,否則連接是異步建立的。因此grpc.WithBlock()和Timeout結合使用才有意義。server端正常的情況下使用grpc.WithBlock()得到的connection.GetState()為READY,不使用grpc.WithBlock()得到的connection.GetState()為IDELgrpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(10<<20), grpc.MaxCallRecvMsgSize(10<<20)), //默認情況下SendMsg上限是MaxInt32,RecvMsg上限是4M,這里都修改為10M)if err != nil {panic("建立連接失敗: " + err.Error())}defer conn.Close()//2. 初始化 grpc 客戶端client := pb.NewSayNameClient(conn)//3. 調用遠程服務。//創建Doctor對象,用來傳參var doctor pb.Doctordoctor.Name = "景天"doctor.Age = 18resp, err := client.SayHello(ctx, &doctor)if err != nil {panic("調用服務端函數失敗: " + err.Error())}fmt.Println(resp.Name)
}

運行服務端,客戶端,拿到數據,加密傳輸成功
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/75855.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/75855.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/75855.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【區塊鏈 + 文化版權】基于 FISCO BCOS 的方言大數據語料庫 | FISCO BCOS 應用案例

蘇州喵自在區塊鏈科技有限公司打造的基于FISCO BCOS 的粵語大數據語料庫&#xff0c; 旨在利用區塊鏈技術保護和發展粵語文化遺產。該項目利用區塊鏈的不可篡改性、分布式存儲、智能合約和激勵機制等特性&#xff0c; 為保護非物質文化遺產&#xff0c; 加強粵語研究與教育和開…

大模型在支氣管擴張預測及治療方案制定中的應用研究

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與方法 1.3 國內外研究現狀 二、大模型技術概述 2.1 大模型的基本原理與架構 2.2 適用于支氣管擴張預測的大模型類型及特點 2.3 大模型在醫療領域的應用現狀與優勢 三、支氣管擴張的相關醫學知識 3.1 支氣管擴張的病因…

亞馬遜云科技提供完全托管的DeepSeek-R1模型

近日&#xff0c;亞馬遜云科技宣布在Amazon Bedrock上線完全托管的DeepSeek-R1模型。DeepSeek是首個登陸Amazon Bedrock的國產大模型&#xff0c;自今年1月底推出以來&#xff0c;已有數千客戶使用Amazon Bedrock的自定義模型導入功能部署了DeepSeek-R1模型。 DeepSeek在過去幾…

二叉樹、排序算法與結構圖

二叉樹、排序算法與數據庫 二叉樹 二叉樹的性質 節點數與深度的關系&#xff1a;深度為 k k k的二叉樹&#xff0c;最多有 2 k ? 1 2^k - 1 2k?1個節點。例如&#xff0c;深度為 3 3 3的二叉樹&#xff0c;最多有 2 3 ? 1 7 2^3 - 1 7 23?17個節點。葉子節點與度為2節…

vmwaretools解壓失敗|vmware tools distrib cannot mkdir read only file system|bug匯總

最簡單的一條路線&#xff1a;你的解壓命令用sudo了嗎&#xff1f; 這個方法不能解決的話就看下面內容。本文提供給你全過程思路。 如需轉載&#xff0c;標記出處 背景&#xff1a; 之前虛擬機和主機的復制黏貼還能用&#xff0c;今天突然用不了&#xff0c;重新下載安裝包&am…

jEasyUI 創建自定義視圖

jEasyUI 創建自定義視圖 引言 jEasyUI 是一款流行的 jQuery UI 組件庫&#xff0c;它提供了豐富的 UI 組件和交互效果&#xff0c;極大地簡化了 Web 開發的復雜度。在 jEasyUI 中&#xff0c;我們可以通過自定義視圖來擴展其功能&#xff0c;滿足特定的業務需求。本文將詳細介…

Spring MVC配置詳解:從歷史到實戰

文章目錄 一、Java Web的發展歷程1.Model I與Model II開發模式&#xff08;1&#xff09; Model I開發模式&#xff08;2&#xff09;Model II開發模式 2.MVC設計模式Spring MVC本質MVC工作流程 二、Spring MVC快速入門實戰1.環境搭建步驟&#xff08;1&#xff09;創建Maven W…

老是忘記package.json,備忘一下 webpack 環境下 Vue Cli 和 Vite 命令行工具對比

Vue 2.X webpack 環境下 Vue Cli 的命令 "scripts": {"dev": "vue-cli-service serve","prod": "vue-cli-service serve --mode production","build:dev": "vue-cli-service build --mode development"…

【樹莓派Pico FreeRTOS】-Mutex(互斥體)

Mutex(互斥體) 文章目錄 Mutex(互斥體)1、硬件準備2、軟件準備3、FreeRTOS的Mutex介紹4、完整示例RP2040 由 Raspberry Pi 設計,具有雙核 Arm Cortex-M0+ 處理器和 264KB 內部 RAM,并支持高達 16MB 的片外閃存。 廣泛的靈活 I/O 選項包括 I2C、SPI 和獨特的可編程 I/O (P…

sock文件介紹--以mysql.sock為例

socket 文件 (.sock) 通常是臨時文件。 MySQL 的 socket 文件是臨時文件&#xff0c;只在服務運行時有效。可通過配置文件更改 socket 文件的存放路徑&#xff0c;常見路徑如 /tmp/mysql.sock 或指定自定義目錄。如果連接出現問題&#xff0c;可能需要檢查 MySQL 服務狀態或路…

Docker應用部署之mysql篇(day5)

文章目錄 前言一、問題描述二、解決方案1. 搜索 MySQL 鏡像2. 拉取 MySQL 鏡像3. 創建并運行 MySQL 容器參數說明&#xff1a; 4. 驗證容器是否運行5. 進入 MySQL 容器 三、總結 前言 在日常開發和部署中&#xff0c;MySQL 是最常用的關系型數據庫之一。借助 Docker&#xff0…

【Elasticsearch基礎】基本核心概念介紹

Elasticsearch作為當前最流行的分布式搜索和分析引擎&#xff0c;其強大的功能背后是一套精心設計的核心概念體系。本文將深入解析Elasticsearch的五大核心概念&#xff0c;幫助開發者構建堅實的技術基礎&#xff0c;并為高效使用ES提供理論支撐。 1 索引&#xff08;Index&…

Qt在ARM中,如何使用drmModeObjectSetProperty 設置 Plane 的 zpos 值

在 Qt 中直接使用 drmModeObjectSetProperty 設置 Plane 的 zpos 值需要結合 Linux DRM/KMS API 和 Qt 的底層窗口系統&#xff08;如 eglfs 平臺插件&#xff09;。以下是詳細步驟和代碼示例&#xff1a; 1. 原理說明 DRM/KMS 基礎&#xff1a; Plane&#xff1a;負責圖層合成…

MFC添加免費版大漠3.1233

先創建一個MFC工程&#xff0c; 添加dm.dll 方法一&#xff1a;通過類向導-添加類-類型庫中的MFC類-文件&#xff0c;選擇dm.dll&#xff0c;如果沒有"添加類型庫中的MFC類"選項就用方法二添加 方法二&#xff1a;添加-新建項-MFC-Active或TypeLib-實現接口位置選…

【Linux】應用層協議 HTTP

應用層協議 HTTP 一. HTTP 協議1. URL 地址2. urlencode 和 urldecode3. 請求與響應格式 二. HTTP 請求方法1. GET 和 POST (重點) 三. HTTP 狀態碼四. HTTP 常見報頭五. 手寫 HTTP 服務器 HTTP&#xff08;超文本傳輸協議&#xff09;是一種應用層協議&#xff0c;用于在萬維網…

【活動回顧】StarRocks Singapore Meetup #2 @Shopee

3 月 13 日&#xff0c;StarRocks 社區在新加坡成功舉辦了第二場 Meetup 活動&#xff0c;主題為“Empowering Customer-Facing Analytics”。本次活動在 Shopee 新加坡辦公室舉行&#xff0c;吸引了來自 Shopee、Grab 和 Pinterest 的專家講師以及 50 多位參會者。大家圍繞電商…

Retinexformer:基于 Retinex 的單階段 Transformer 低光照圖像增強方法

開頭發點牢騷&#xff1a;本來做的好好都都要中期了&#xff0c;導師怎么突然給我換題目啊。真是繃不住了......又要從頭開始學了&#xff0c;唉&#xff01; 原論文鏈接&#xff1a;Retinexformer: One-stage Retinex-based Transformer for Low-light Image Enhancement 低光…

后端——AOP異步日志

需求分析 在SpringBoot系統中&#xff0c;一般會對訪問系統的請求做日志記錄的需求&#xff0c;確保系統的安全維護以及查看接口的調用情況&#xff0c;可以使用AOP對controller層的接口進行增強&#xff0c;作日志記錄。日志保存在數據庫當中&#xff0c;為了避免影響接口的響…

flink廣播算子Broadcast

文章目錄 一、Broadcast二、代碼示例三.或者第二種(只讀取一個csv文件到廣播內存中)提示:以下是本篇文章正文內容,下面案例可供參考 一、Broadcast 為了關聯一個非廣播流(keyed 或者 non-keyed)與一個廣播流(BroadcastStream),我們可以調用非廣播流的方法 connect(),…

Redis 和 MySQL雙寫一致性的更新策略有哪些?常見面試題深度解答。

目錄 一. 業務數據查詢&#xff0c;更新順序簡要分析 二. 更新數據庫、查詢數據庫、更新緩存、查詢緩存耗時對比 2.1 更新數據庫&#xff08;最慢&#xff09; 2.2 查詢數據庫&#xff08;較慢&#xff09; 2.3 更新緩存&#xff08;次快&#xff09; 2.4 查詢緩存&#…