Golang中四種gRPC模式舉例詳解
本博客需要你有一點基本的gRPC的常識,如果你完全是新手建議訪問官網(wǎng)全面了解。
1. Unary RPC
proto文件如下
syntax = "proto3";
option go_package=".;service";
message HelloRequest {
// Name of the person to greet
string name = 1;
}
message HelloResponse {
// Greeting message
string greeting = 1;
}
service HelloService {
// RPC method to say hello
rpc SayHello (HelloRequest) returns (HelloResponse){}
}
使用命令(注意命令路徑和自己的對應(yīng)):
protoc -I . --go-grpc_out=require_unimplemented_servers=false:. --go_out=. *.proto
對應(yīng)目錄上有xx.pb.go和xx_grpc.pb.go然后對應(yīng)目錄實現(xiàn)服務(wù)端接口
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"net"
"test_grpc/service"
)
type HelloService struct {
}
func (hs *HelloService) SayHello(ctx context.Context, req *service.HelloRequest) (*service.HelloResponse, error) {
resp := &service.HelloResponse{
Greeting: fmt.Sprintf("hello %s --from Golang Server", req.Name),
}
return resp, nil
}
func main() {
// listen on 127.0.0.1:50051
listen, err := net.Listen("tcp", "127.0.0.1:50051")
if err != nil {
fmt.Println("Error happened when listen on 127.0.0.1:50051:", err)
return
}
// grpc server
s := grpc.NewServer()
// register HelloService in grpc server
service.RegisterHelloServiceServer(s, &HelloService{})
// start rpc server
fmt.Println("Golang rpc server is waiting messages......")
if err = s.Serve(listen); err != nil {
fmt.Println("Error happened when start rpc server:", err)
return
}
}
客戶端接口如下:
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"test_grpc/service"
"time"
)
func main() {
// connect to server
conn, err := grpc.Dial("127.0.0.1:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println("Connect to rpc server err:", err)
return
}
defer conn.Close()
// init service client
c := service.NewHelloServiceClient(conn)
// init context with timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// send message
req := &service.HelloRequest{Name: "Golang"}
r, err := c.SayHello(ctx, req)
if err != nil {
fmt.Println("Send message err:", err)
return
}
fmt.Println("Client:", r.Greeting)
}
實際上為了更好得感受gRPC這種跨語言調(diào)用的感覺,可以嘗試使用python編寫client端代碼,直接復(fù)制proto文件,在python中使用以下命令生成對應(yīng)的proto文件(注意命令和自己的對應(yīng)):
python -m grpc_tools.protoc -I . --python_out=. --pyi_out=. --grpc_python_out=. *.proto
使用python實現(xiàn)的客戶端代碼如下:
# client template
# grpc server address
channel = grpc.insecure_channel("127.0.0.1:50051")
stub = hello_pb2_grpc.HelloServiceStub(channel)
# send request
response = stub.SayHello(hello_pb2.HelloRequest(name="Python"))
print(response.greeting)
# hello Python --from Golang Server
2. Server-side streaming RPC
重新給出這個的proto文件,服務(wù)端將以流式數(shù)據(jù)的形式發(fā)送給客戶端數(shù)據(jù)
syntax = "proto3";
option go_package=".;service";
message HelloRequest {
// Name of the person to greet
string name = 1;
}
message HelloResponse {
// Greeting message
string greeting = 1;
}
service HelloService {
// RPC method to say hello
rpc SayHello (HelloRequest) returns (stream HelloResponse){}
}
同理,生成對應(yīng)的proto文件后,在對應(yīng)的文件先生成server端的代碼:
package main
import (
"fmt"
"google.golang.org/grpc"
"net"
"test_grpc/service"
)
type HelloService struct {
}
func (hs *HelloService) SayHello(req *service.HelloRequest, stream service.HelloService_SayHelloServer) error {
resp := &service.HelloResponse{
Greeting: fmt.Sprintf("hello %s --from Golang Server", req.Name),
}
// 連續(xù)發(fā)送5次
for i := 0; i < 5; i++ {
if err := stream.Send(resp); err != nil {
return err
}
}
return nil
}
func main() {
// listen on 127.0.0.1:50051
listen, err := net.Listen("tcp", "127.0.0.1:50051")
if err != nil {
fmt.Println("Error happened when listen on 127.0.0.1:50051:", err)
return
}
// grpc server
s := grpc.NewServer()
// register HelloService in grpc server
service.RegisterHelloServiceServer(s, &HelloService{})
// start rpc server
fmt.Println("Golang rpc server is waiting messages......")
if err = s.Serve(listen); err != nil {
fmt.Println("Error happened when start rpc server:", err)
return
}
}
同理給出客戶端的代碼:
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"io"
"log"
"test_grpc/service"
"time"
)
func main() {
// connect to server
conn, err := grpc.Dial("127.0.0.1:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println("Connect to rpc server err:", err)
return
}
defer conn.Close()
// init service client
c := service.NewHelloServiceClient(conn)
// init context with timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// send message
req := &service.HelloRequest{Name: "Golang"}
stream, err := c.SayHello(ctx, req)
if err != nil {
fmt.Println("Send message err:", err)
return
}
// 加載消息
for {
resp, err := stream.Recv()
// 讀到結(jié)束標(biāo)志
if err == io.EOF {
log.Fatalf("end.....")
break
}
if err != nil {
log.Fatalf("failed to receive response: %v", err)
}
log.Printf("Greeting: %s", resp.Greeting)
}
}
3. Client-side streaming RPC
對應(yīng)的proto文件如下
syntax = "proto3";
option go_package=".;service";
message HelloRequest {
// Name of the person to greet
string name = 1;
}
message HelloResponse {
// Greeting message
string greeting = 1;
}
service HelloService {
// RPC method to say hello
rpc SayHello (stream HelloRequest) returns (HelloResponse){}
}
同理使用protoc命令生成對應(yīng)的proto文件,后先編寫client端的代碼,如下:
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"test_grpc/service"
"time"
)
func main() {
// connect to server
conn, err := grpc.Dial("127.0.0.1:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println("Connect to rpc server err:", err)
return
}
defer conn.Close()
// init service client
c := service.NewHelloServiceClient(conn)
// init context with timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// create stream
stream, err := c.SayHello(ctx)
if err != nil {
log.Fatalf("could not greet: %v", err)
}
names := []string{"World", "Gophers", "Anthropic"}
for _, name := range names {
// request body
req := &service.HelloRequest{Name: name}
if err := stream.Send(req); err != nil {
log.Fatalf("faild to send request: %v", err)
}
}
resp, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
}
log.Printf("Greeting: %s", resp.Greeting)
}
對應(yīng)得完成服務(wù)端的代碼:
package main
import (
"fmt"
"google.golang.org/grpc"
"io"
"net"
"strings"
"test_grpc/service"
)
type HelloService struct {
}
func (hs *HelloService) SayHello(stream service.HelloService_SayHelloServer) error {
var strs []string
for {
msg, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
strs = append(strs, msg.Name)
}
resp := &service.HelloResponse{Greeting: strings.Join(strs, " ")}
err := stream.SendAndClose(resp)
if err != nil {
return err
}
return nil
}
func main() {
// listen on 127.0.0.1:50051
listen, err := net.Listen("tcp", "127.0.0.1:50051")
if err != nil {
fmt.Println("Error happened when listen on 127.0.0.1:50051:", err)
return
}
// grpc server
s := grpc.NewServer()
// register HelloService in grpc server
service.RegisterHelloServiceServer(s, &HelloService{})
// start rpc server
fmt.Println("Golang rpc server is waiting messages......")
if err = s.Serve(listen); err != nil {
fmt.Println("Error happened when start rpc server:", err)
return
}
}
4. Bidirectional streaming RPC
新的proto文件被如下給出:
syntax = "proto3";
option go_package=".;service";
message HelloRequest {
// Name of the person to greet
string name = 1;
}
message HelloResponse {
// Greeting message
string greeting = 1;
}
service HelloService {
// RPC method to say hello
rpc SayHello (stream HelloRequest) returns (stream HelloResponse){}
}
和上文中的操作一致,同時給出server端的代碼:
package main
import (
"fmt"
"google.golang.org/grpc"
"io"
"log"
"net"
"test_grpc/service"
)
type HelloService struct {
}
func (hs *HelloService) SayHello(stream service.HelloService_SayHelloServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
name := msg.Name
resp := &service.HelloResponse{Greeting: name}
if err = stream.Send(resp); err != nil {
log.Fatalf("Failed to send a resp:%s", err)
}
}
return nil
}
func main() {
// listen on 127.0.0.1:50051
listen, err := net.Listen("tcp", "127.0.0.1:50051")
if err != nil {
fmt.Println("Error happened when listen on 127.0.0.1:50051:", err)
return
}
// grpc server
s := grpc.NewServer()
// register HelloService in grpc server
service.RegisterHelloServiceServer(s, &HelloService{})
// start rpc server
fmt.Println("Golang rpc server is waiting messages......")
if err = s.Serve(listen); err != nil {
fmt.Println("Error happened when start rpc server:", err)
return
}
}
同時給出下面的client端的代碼:
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"io"
"log"
"test_grpc/service"
"time"
)
func main() {
// connect to server
conn, err := grpc.Dial("127.0.0.1:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println("Connect to rpc server err:", err)
return
}
defer conn.Close()
// init service client
c := service.NewHelloServiceClient(conn)
// init context with timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// create stream
stream, err := c.SayHello(ctx)
if err != nil {
log.Fatalf("could not greet: %v", err)
}
names := []string{"World", "Gophers", "Anthropic"}
waitc := make(chan struct{})
go func() {
for {
resp, err := stream.Recv()
if err == io.EOF {
close(waitc)
return
}
if err != nil {
log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
}
log.Printf("Greeting: %s", resp.Greeting)
}
}()
go func() {
for _, name := range names {
// request body
req := &service.HelloRequest{Name: name}
if err := stream.Send(req); err != nil {
log.Fatalf("faild to send request: %v", err)
}
// send delay
time.Sleep(1)
}
// 發(fā)送結(jié)束的消息
if err := stream.CloseSend(); err != nil {
log.Fatalf("failed to close stream: %v", err)
}
}()
<-waitc
}
一定要注意關(guān)閉發(fā)送或者避免針對一個已經(jīng)關(guān)閉stream進行發(fā)送消息,讀取消息是被允許的,這里有一點類似chan
4. ALTS
4.1 ALTS的介紹
應(yīng)用層傳輸安全(ALTS)是谷歌開發(fā)的一種相互驗證和傳輸加密系統(tǒng)。它用于確保谷歌基礎(chǔ)設(shè)施內(nèi) RPC 通信的安全。ALTS 類似于相互 TLS,但經(jīng)過設(shè)計和優(yōu)化,可滿足 Google 生產(chǎn)環(huán)境的需要。ALTS在gRPC中有以下的特征:
- 使用ALTS作為傳輸協(xié)議創(chuàng)建gRPC的服務(wù)端和客戶端;
- ALSTS是一個端到端的保護,具有隱私性和完成性;
- 應(yīng)用可以訪問對等信息比如對等服務(wù)賬戶;
- 支持客戶端和服務(wù)端的認知;
- 最小的代碼更改就能使用ALTS;
值得注意的是ALTS被全部發(fā)揮作用如果應(yīng)用程序運行在CE或者GKE中
4.2 gRPC客戶端使用ALTS傳輸安全協(xié)議
gRPC客戶端使用ALTS認證去連接服務(wù)端,正如下面代碼中所描述的:
import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/alts"
)
altsTC := alts.NewClientCreds(alts.DefaultClientOptions())
// connect to server
conn, err := grpc.Dial("127.0.0.1:50051", grpc.WithTransportCredentials(altsTC))
gRPC服務(wù)端能夠使用ALTS認證來運行客戶端連接到它,正如下面的描述:
import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/alts" ) altsTC := alts.NewServerCreds(alts.DefaultServerOptions()) server := grpc.NewServer(grpc.Creds(altsTC))
4.3 Server Authorization
gRPC 內(nèi)置了使用 ALTS 的服務(wù)器授權(quán)支持。使用 ALTS 的 gRPC 客戶端可以在建立連接前設(shè)置預(yù)期的服務(wù)器服務(wù)賬戶。然后,在握手結(jié)束時,服務(wù)器授權(quán)會保證服務(wù)器身份與客戶端指定的服務(wù)賬戶之一相匹配。否則,連接將失敗。
import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/alts"
)
clientOpts := alts.DefaultClientOptions()
clientOpts.TargetServiceAccounts = []string{expectedServerSA}
altsTC := alts.NewClientCreds(clientOpts)
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(altsTC))總結(jié)
到此這篇關(guān)于Golang中四種gRPC模式的文章就介紹到這了,更多相關(guān)Golang中g(shù)RPC模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang中為什么Response.Body需要被關(guān)閉詳解
這篇文章主要給大家介紹了關(guān)于golang中為什么Response.Body需要被關(guān)閉的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
GO語言協(xié)程創(chuàng)建使用并通過channel解決資源競爭
這篇文章主要為大家介紹了GO語言協(xié)程創(chuàng)建使用并通過channel解決資源競爭,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04
Go中阻塞以及非阻塞操作實現(xiàn)(Goroutine和main Goroutine)
本文主要介紹了Go中阻塞以及非阻塞操作實現(xiàn)(Goroutine和main Goroutine),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05
golang使用bcrypt包對密碼進行加密的方法實現(xiàn)
本文主要介紹了golang使用bcrypt包對密碼進行加密的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Qt6.5 grpc組件使用 + golang grpc server
這篇文章主要介紹了Qt6.5 grpc組件使用+golang grpc server示例,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
解決Golang小數(shù)float64在實際工程中加減乘除的精度問題
這篇文章主要介紹了解決Golang小數(shù)float64在實際工程中加減乘除的精度問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
詳解Go語言中用 os/exec 執(zhí)行命令的五種方法
這篇文章主要介紹了Go語言中用 os/exec 執(zhí)行命令的五種方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11

