?? 歡迎大家來到景天科技苑??
🎈🎈 養成好習慣,先贊后看哦~🎈🎈
🏆 作者簡介:景天科技苑
🏆《頭銜》:大廠架構師,華為云開發者社區專家博主,阿里云開發者社區專家博主,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. 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 不能用了,提示我們有兩個選擇:
- 需要使用SAN 證書
- 改變環境變量:GODEBUG=x509ignoreCN=0
這里,我們選擇使用SAN證書,進行通信
SAN and openssl
首先,我們看看什么是SAN證書:
SAN(Subject Alternative Name) 是 SSL 標準 x509 中定義的一個擴展。使用了 SAN 字段的 SSL 證書,可以擴展此證書支持的域名,使得一個證書可以支持多個不同域名的解析。
1)生成SAN證書
要生成SAN證書,需要以下幾步:
- 修改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)
}
運行服務端,客戶端,拿到數據,加密傳輸成功