Golang中四种gRPC模式

文章目录

  • 1. Unary RPC
  • 2. Server-side streaming RPC
  • 3. Client-side streaming RPC
  • 4. Bidirectional streaming RPC
  • 4. ALTS
    • 4.1 ALTS的介绍
    • 4.2 gRPC客户端使用ALTS传输安全协议
    • 4.3 Server Authorization

本博客需要你有一点基本的gRPC的常识,如果你完全是新手建议访问官网全面了解。

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

使用命令(注意命令路径和自己的对应):

protoc -I . --go-grpc_out=require_unimplemented_servers=false:. --go_out=.  *.proto

对应目录上有xx.pb.goxx_grpc.pb.go
然后对应目录实现服务端接口

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这种跨语言调用的感觉,可以尝试使用python编写client端代码,直接复制proto文件,在python中使用以下命令生成对应的proto文件(注意命令和自己的对应):

python -m grpc_tools.protoc -I . --python_out=. --pyi_out=. --grpc_python_out=. *.proto

使用python实现的客户端代码如下:

# 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文件,服务端将以流式数据的形式发送给客户端数据

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

同理,生成对应的proto文件后,在对应的文件先生成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),
	}
	// 连续发送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()
		// 读到结束标志
		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

对应的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命令生成对应的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)
}

对应得完成服务端的代码:

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)
		}
		// 发送结束的消息
		if err := stream.CloseSend(); err != nil {
			log.Fatalf("failed to close stream: %v", err)
		}
	}()

	<-waitc
}

一定要注意关闭发送或者避免针对一个已经关闭stream进行发送消息,读取消息是被允许的,这里有一点类似chan

4. ALTS

4.1 ALTS的介绍

应用层传输安全(ALTS)是谷歌开发的一种相互验证和传输加密系统。它用于确保谷歌基础设施内 RPC 通信的安全。ALTS 类似于相互 TLS,但经过设计和优化,可满足 Google 生产环境的需要。ALTS在gRPC中有以下的特征:

  • 使用ALTS作为传输协议创建gRPC的服务端和客户端;
  • ALSTS是一个端到端的保护,具有隐私性和完成性;
  • 应用可以访问对等信息比如对等服务账户;
  • 支持客户端和服务端的认知;
  • 最小的代码更改就能使用ALTS;

值得注意的是ALTS被全部发挥作用如果应用程序运行在CE或者GKE中

4.2 gRPC客户端使用ALTS传输安全协议

gRPC客户端使用ALTS认证去连接服务端,正如下面代码中所描述的:

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服务端能够使用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 内置了使用 ALTS 的服务器授权支持。使用 ALTS 的 gRPC 客户端可以在建立连接前设置预期的服务器服务账户。然后,在握手结束时,服务器授权会保证服务器身份与客户端指定的服务账户之一相匹配。否则,连接将失败。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/499583.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

速成软件书是神器还是焦虑?

一、背景 "速成软件书"通常是指那些宣称能帮助读者在短时间内掌握某种软件操作或编程技能的书籍。这类书籍往往以其高效、快捷的学习路径吸引读者&#xff0c;尤其适合有一定基础或者急需短期内提升特定技能的人群。 然而&#xff0c;“神器”之称则带有主观性和一…

双端队列deque和vector以及list的优缺点比较

参考:https://blog.csdn.net/TWRenHao/article/details/123483085 一、vector vector具体用法详情点这里 优点&#xff1a; 支持随机访问 CPU高速环缓存命中率很高 缺点&#xff1a; 空间不够&#xff0c;便需要增容。而增容代价很大&#xff0c;还存在一定的空间浪费。 头部…

数据可视化为什么能在智慧港口中发挥作用?

随着全球贸易活动日益频繁&#xff0c;港口作为国际贸易的重要节点&#xff0c;其运营效率与智能化程度直接影响着整个物流链的效能。在此背景下&#xff0c;智慧港口的概念应运而生&#xff0c;它借助先进的信息技术手段对传统港口进行改造升级&#xff0c;其中&#xff0c;数…

基于Arduino IDE 野火ESP8266模块 文件系统LittleFS 的开发

一、文件系统LittleFS的介绍 LittleFS是一个为微控制器设计的轻量级、可靠且高性能的文件系统。它专为嵌入式设备打造&#xff0c;拥有占用空间小、对硬件要求低的特点&#xff0c;同时保证在断电情况下数据的完整性和稳定性。 1.设计与特点 LittleFS的设计旨在提供嵌入式系统所…

第三十二天-Django模板-DTL模板引擎

目录 1.介绍 2. 使用 1.配置jinja2 2.DTL模板变量使用 3.与jinja2区别 4.模板标签使用 1.循环 2.条件控制 3.注释 4.url解析 5.显示时间 5.模板的基础与包含 6.过滤器 内置过滤器 自定义过滤器 1.介绍 2. 使用 1.配置jinja2 2.DTL模板变量使用 与jinja2语法相似…

PHP图床程序优化版:图片外链服务、图床API服务、图片CDN加速与破解防盗链

图片免费上传 支持本地储存、FTP储存、第三方云储存&#xff08;阿里云 OSS、腾讯云 COS、七牛云等&#xff09;。 图片外链加速 一键转换第三方网站的图片外链地址为图床可分享的图片地址&#xff08;支持CDN&#xff09;。 图片解析服务 直接将第三方外链图片地址显示为…

Linux网络配置(超详细)

Linux网络配置大全 Linux网络配置一.网络地址配置网络地址查看–ifconfig使用网络配置命令设置网络接口参数-ifconfig禁用(临时)或者重新激活网卡设置虚拟网络接口 修改网络配置文件网络接口配置文件 IP命令详解OPTIONS选项OBJECT对象 ip link 二、获取和修改主机名hostname查看…

数对 离散化BIT

先把公式变个形&#xff0c;然后直接BIT 枚举右端点查询左端点累加答案 离散化好题&#xff0c;注意BIT写的时候右端点的范围是离散化区间的大小 #include<bits/stdc.h> using namespace std; #define int long long using ll long long; using pii pair<int,int&…

【IC前端虚拟项目】write_path子模块DS与RTL编码

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 read_path的代码完成之后,就可以开始整个项目里复杂度最高、bug最多、时序收敛最为困难的模块——write_path的开发了!我自己写过两次这个虚拟项目,每次都是在这里耗时最久,所以大家也可以挑战一下自…

MAC上好用的文件查找软件

和windows上的everything很像&#xff0c;不过就是要钱&#xff0c;我简单测试了一下还可以蛮好用的&#xff0c;

【QT入门】 QTabWidget各种常见用法详解

往期回顾&#xff1a; 【QT入门】 Qt代码创建布局之分裂器布局详解-CSDN博客 【QT入门】 Qt代码创建布局之setLayout使用-CSDN博客 【QT入门】 Qt代码创建布局之多重布局变换与布局删除技巧-CSDN博客 【QT入门】 QTabWidget各种常见用法详解 一般来说&#xff0c;学一个新的控…

关系型数据库mysql(7)sql高级语句①

目录 一.MySQL常用查询 1.按关键字&#xff08;字段&#xff09;进行升降排序 按分数排序 &#xff08;默认为升序&#xff09; 按分数升序显示 按分数降序显示 根据条件进行排序&#xff08;加上where&#xff09; 根据多个字段进行排序 ​编辑 2.用或&#xff08;or&…

以新质生产力引领智能锁行业腾飞,凯迪仕打造全球最大智能安防产业园

凯迪仕&#xff0c;作为智能锁行业的领军企业&#xff0c;今日在温州举行了凯迪仕全球超级工厂落成庆典。积极拥抱新质生产力&#xff0c;大力发展智能制造&#xff0c;凯迪仕在全球制造业科技创新的制高点上迈出了坚实的步伐。 浙江省温州市瓯海区委副书记、区长刘云峰&#x…

作者开发的爬取妹子图片Python项目,值得你收藏拥有

最好的学习方法在于实践&#xff0c;学习编程语言Python&#xff0c;也是同样的道理。本文讲解自己开发的一个项目&#xff0c;实现爬取妹子图片&#xff0c;所用的Python知识点以及模块&#xff0c;可以关注参考作者公众号的Python语言合集。 —、前情介绍 1.1 涉及模块 本…

泛微E-Office10 < 10.0_20240222 远程代码执行漏洞

一、软件背景 泛微e-office是一套企业级电子办公解决方案&#xff0c;提供文档管理、流程审批、协同办公等功能&#xff0c;帮助企业实现数字化办公、提高工作效率。 二、影响版本 e-office10[10.0_20180516, 10.0_20240222) 三、漏洞分析 在受影响版本中&#xff0c;由于…

缺省和重载.引用——初识c++

. 个人主页&#xff1a;晓风飞 专栏&#xff1a;数据结构|Linux|C语言 路漫漫其修远兮&#xff0c;吾将上下而求索 文章目录 C输入&输出cout 和cin<<>> 缺省参数全缺省半缺省应用场景声明和定义分离的情况 函数重载1.参数的类型不同2.参数的个数不同3.参数的顺…

【AIGC】如何在Windows/Linux上部署stable diffusion

文章目录 整体安装步骤windows10安装stable diffusion环境要求安装步骤注意事项参考博客其他事项安装显卡驱动安装cuda卸载cuda安装对应版本pytorch安装git上的python包Q&A linux安装stable diffusion安装anaconda安装cudagit 加速配置虚拟环境挂载oss&#xff08;optional…

实时渲染是什么意思?实时渲染和离线渲染的区别

一、实时渲染是什么意思&#xff1f; 实时渲染是指在计算机程序运行时即时地生成图像和动画的过程&#xff0c;这种渲染技术通常用于网络游戏、虚拟现实和增强现实等需要实时交互的XR应用中。实时渲染需要在每秒内渲染数百万到数十亿个像素&#xff0c;以呈现出平滑的动画和交…

位运算算法(2)

目录 面试题 01.01. 判断字符是否唯一 一、题目描述 二、思路解析 三、代码 268.丢失的数字 一、题目描述 二、思路解析 三、代码 371.两整数之和 一、题目描述 二、思路解析 三、代码 137.只出现一次的数字 II 一、题目描述 二、思路解析 三、代码 面试题 01.0…

consul集群部署三server一client

环境&#xff1a; consul&#xff1a;consul_1.16.2_linux_amd64.zip centos7.9 server:192.168.50.154 192.168.50.155 192.168.50.156 client:192.168.70.64 安装目录&#xff1a; [rootrabbit4-64 consul]# pwd /app/consul [rootrabbit4-64 consul]# ls consul consul_1…
最新文章