gRPC之grpc自定义负载均衡(resolver)

1、grpc自定义负载均衡(resolver)

1.1 proto编写和编译

syntax = "proto3";
package pb;
option go_package = "./;pb";service Greeter {rpc SayHello (HelloRequest) returns (HelloReply) {}
}message HelloRequest {string name = 1;
}message HelloReply {string message = 1;
}
$ protoc -I . --go_out=plugins=grpc:. ./helloword.proto

1.2 服务端

这里编写两个服务端:

package mainimport ("context""demo/pb""google.golang.org/grpc""log""net"
)const (port = ":50051"
)type server struct {pb.UnimplementedGreeterServer
}// 该函数定义必须与helloworld.pb.go定义的SayHello一致
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {//打印客户端传入HelloRequest请求的Name参数log.Printf("Received: %v", in.GetName())//将name参数作为返回值,返回给客户端return &pb.HelloReply{Message: "Service1: Hello " + in.GetName()}, nil
}// main方法函数开始执行的地方
func main() {// 调用标准库,监听50051端口的tcp连接lis, err := net.Listen("tcp", port)if err != nil {log.Fatalf("failed to listen: %v", err)}//创建grpc服务s := grpc.NewServer()//将server对象,也就是实现SayHello方法的对象,与grpc服务绑定pb.RegisterGreeterServer(s, &server{})// grpc服务开始接收访问50051端口的tcp连接数据if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}
}
package mainimport ("context""demo/pb""google.golang.org/grpc""log""net"
)const (port2 = ":50052"
)type server2 struct {pb.UnimplementedGreeterServer
}// 该函数定义必须与helloworld.pb.go 定义的SayHello一致
func (s *server2) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {//打印客户端传入HelloRequest请求的Name参数log.Printf("Received: %v", in.GetName())//将name参数作为返回值,返回给客户端return &pb.HelloReply{Message: "Service2: Hello " + in.GetName()}, nil
}// main方法 函数开始执行的地方
func main() {// 调用标准库,监听50052端口的tcp连接lis, err := net.Listen("tcp", port2)if err != nil {log.Fatalf("failed to listen: %v", err)}//创建grpc服务s := grpc.NewServer()//将server对象,也就是实现SayHello方法的对象,与grpc服务绑定pb.RegisterGreeterServer(s, &server2{})// grpc服务开始接收访问50051端口的tcp连接数据if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}
}

1.3 客户端

package mainimport ("context""demo/pb""fmt""google.golang.org/grpc""google.golang.org/grpc/balancer""google.golang.org/grpc/connectivity""google.golang.org/grpc/resolver""log""time"
)// 全局注册Scheme为myservice的Resolver Build
func init() {log.Println("&myServiceBuilder!")resolver.Register(&myServiceBuilder{})
}type myServiceBuilder struct {
}func (*myServiceBuilder) Scheme() string {log.Println("myServiceBuilder Scheme()!")return "myservice"
}// 创建Resolver实例
func (*myServiceBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {log.Println("myServiceBuilder Build()!")r := &myServiceResolver{target: target,cc:     cc,}r.start()return r, nil
}type myServiceResolver struct {target resolver.Targetcc     resolver.ClientConn
}// 根据target不同,解析出不同的端口
func (r *myServiceResolver) start() {log.Println("myServiceResolver start()!")// 模拟myservice解析出两个Addressr.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: ":50051"}, {Addr: ":50052"}}})
}// 再次解析使用的解析方式不变
func (r *myServiceResolver) ResolveNow(o resolver.ResolveNowOptions) {log.Println("myServiceResolver ResolveNow()!")r.start()
}func (*myServiceResolver) Close() {log.Println("myServiceResolver Close()!")
}const (address1 = "myservice:///abc"
)// 自定义负载均衡
// 初始化中进行注册
func init() {balancer.Register(newMyPickBuilder())
}func newMyPickBuilder() balancer.Builder {log.Println("newMyPickBuilder()!")return &myPickBuilder{}
}type myPickBuilder struct{}func (*myPickBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {log.Println("myPickBuilder Build()!")return &myPickBalancer{state:     0,cc:        cc,subConns:  make(map[resolver.Address]balancer.SubConn),subConns1: make(map[balancer.SubConn]resolver.Address),}
}func (*myPickBuilder) Name() string {log.Println("myPickBuilder Name()!")return "mypickBalance"
}type myPickBalancer struct {state     connectivity.Statecc        balancer.ClientConnsubConns  map[resolver.Address]balancer.SubConnsubConns1 map[balancer.SubConn]resolver.Address
}func (b *myPickBalancer) ResolverError(err error) {log.Println("myPickBalancer ResolverError()!")// TODO 需要剔除无效连接
}func (b *myPickBalancer) UpdateClientConnState(s balancer.ClientConnState) error {log.Println("myPickBalancer UpdateClientConnState()!")addrsSet := make(map[resolver.Address]struct{})for _, a := range s.ResolverState.Addresses {addrsSet[a] = struct{}{}if _, ok := b.subConns[a]; !ok {sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{})if err != nil {continue}b.subConns[a] = scsc.Connect()}}return nil
}func (b *myPickBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {log.Println("myPickBalancer UpdateSubConnState()!")// TODO 需要剔除无效连接,增加有效连接//if s.ConnectivityState == connectivity.Ready {//	b.subConns[b.subConns1[sc]] = sc//}log.Println("b.subConns", b.subConns)var scs []balancer.SubConnfor _, sc := range b.subConns {scs = append(scs, sc)}if len(b.subConns) == 2 {b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Ready, Picker: &myPicker{scs}})}
}func (b *myPickBalancer) Close() {log.Println("myPickBalancer Close()!")
}type myPicker struct {subConns []balancer.SubConn
}func (p *myPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {log.Println("myPicker Pick()!")//获取当前时间second := time.Now().Second()fmt.Printf("Current Time Second:%d\n", second)if second%2 == 0 {return balancer.PickResult{SubConn: p.subConns[0]}, nil}return balancer.PickResult{SubConn: p.subConns[1]}, nil
}func main() {// 访问服务端address,创建连接conn,地址格式"myservice:///abc"conn, err := grpc.Dial(address1, grpc.WithInsecure(), grpc.WithBlock(),grpc.WithDefaultServiceConfig(`{"loadBalancingConfig": [{"mypickBalance":{}}]}`))if err != nil {log.Fatalf("did not connect: %v", err)}time.Sleep(100 * time.Millisecond)defer conn.Close()c := pb.NewGreeterClient(conn)// 设置客户端访问超时时间1秒ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()// 客户端调用服务端 SayHello 请求,传入Name 为 "world", 返回值为服务端返回参数r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})if err != nil {log.Fatalf("could not greet: %v", err)}// 根据服务端处理逻辑,返回值也为"world"log.Printf("Greeting: %s", r.GetMessage())time.Sleep(time.Second)ctx2, cancel2 := context.WithTimeout(context.Background(), time.Second)defer cancel2()// 客户端调用服务端 SayHello 请求,传入Name 为 "world", 返回值为服务端返回参数r2, err2 := c.SayHello(ctx2, &pb.HelloRequest{Name: "world"})if err2 != nil {log.Fatalf("could not greet: %v", err2)}// 根据服务端处理逻辑,返回值也为"world"log.Printf("Greeting: %s", r2.GetMessage())
}

1.4 测试

[root@zsx demo]# go run server/server1.go
2023/02/18 09:41:22 Received: world
[root@zsx demo]# go run server/server2.go
2023/02/18 09:41:21 Received: world
[root@zsx demo]# go run client/client.go
2023/02/18 09:41:21 &myServiceBuilder!
2023/02/18 09:41:21 myServiceBuilder Scheme()!
2023/02/18 09:41:21 newMyPickBuilder()!
2023/02/18 09:41:21 myPickBuilder Name()!
2023/02/18 09:41:21 myServiceBuilder Build()!
2023/02/18 09:41:21 myServiceResolver start()!
2023/02/18 09:41:21 myPickBuilder Build()!
2023/02/18 09:41:21 myPickBuilder Name()!
2023/02/18 09:41:21 myPickBalancer UpdateClientConnState()!
2023/02/18 09:41:21 myPickBalancer UpdateSubConnState()!
2023/02/18 09:41:21 myPickBalancer UpdateSubConnState()!
2023/02/18 09:41:21 myPickBalancer UpdateSubConnState()!
2023/02/18 09:41:21 myPickBalancer UpdateSubConnState()!
2023/02/18 09:41:21 myPicker Pick()!
Current Time Second:21
2023/02/18 09:41:21 Greeting: Service2: Hello world
2023/02/18 09:41:22 myPicker Pick()!
Current Time Second:22
2023/02/18 09:41:22 Greeting: Service1: Hello world
2023/02/18 09:41:22 myPickBalancer Close()!
2023/02/18 09:41:22 myServiceResolver Close()!
# 项目结构
$ tree demo
demo
├── client
│   └── client.go
├── go.mod
├── go.sum
├── pb
│   ├── helloword.pb.go
│   └── helloword.proto
└── server├── server1.go└── server2.go

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle免费资源 终止实例 以及新建一台实例的折腾记录

事情的背景是这样的,我的一台oracle小鸡,不太好用的样子,有时候SSH连不上,有时候莫名其妙卡住。所以我就想把它重新安装一下系统,恢复成最初的样子。 然后在网上查资料,是有办法把系统重装一下的。但是略微…

抖去推--短视频账号矩阵系统saas工具源码技术开发

一、短视频矩阵系统搭建常见问题? 1、抖去推的短视频AI矩阵营销软件需要一定的技术水平吗? 答:不需要。产品简单易用,不需要具备专业的技术水平,即使是初学者,也能够轻松上手操作。 3、抖去推的短视频AI矩…

MySQL巧用公用表表达式(CTE)处理递归查询

概述 根据《MySQL 8.0 Reference Manual》的描述,Common Table Expressions(简称CTE)是一种名为临时结果集的表达式。它可以用来构造复杂的查询语句,并且可以在多个查询中重复使用同一个结果集。CTE的优点是可以使查询语句更加简…

神经网络可视化——基于torchviz绘制模型的计算图

神经网络可视化——基于torchviz绘制模型的计算图 第一步、安装 graphviz 和 torchviz 库 第二步、编写代码生成计算图 第三步、安装graphviz软件 在深入理解深度学习模型时,可视化网络结构是一个非常有用的手段。今天介绍如何使用 torchviz 和 graphviz 来生成网…

很清楚展示GPT插件的调用过程,人工智能(AI)的潜在危险与好处 超级智能 未来

好处,未来 很清楚展示GPT插件的调用过程: 把请求和要求发chatGPT chatGPT返回markdown格式发给插件 插件返回结果给用户。 你不用别人用。 人工智能(AI)的最危险之处通常与以下几个方面有关: 自主决策能力过强&…

数据挖掘 朴素贝叶斯

直入正题,直接看代码: 这是一段判断是不是藏话的代码 import numpy as np# 数据采集(定义函数加载数据集) def load_dataset():sent_list [[my, name, is, Devin],[you, are, stupid],[my, boyfriend, is, SB],[you, looks, ver…

元宇宙3d服装数字化交互展示营销平台大幅提高客户满意度和口碑

web3D云展营销平台是以web3d开发、VR虚拟现实和计算机技术,以展品3D展示、数字人,AI,社交等技术打造,为 Web3D可视化提供了丰富的展示形式和效果,实现将线下展厅、展品在线上1:1复刻呈现的线上场景营销。 w…

企业软件手机app定制开发新趋势|网站小程序搭建

企业软件手机app定制开发新趋势|网站小程序搭建 随着移动互联网的快速发展和企业数字化转型的加速,企业软件手机App定制开发正成为一个新的趋势。这种趋势主要是由于企业对于手机App的需求增长以及现有的通用应用不能满足企业特定需求的情况下而产生的。 首先&#…

使用char.js 柱形方式显示 一年12个月的最高气温与最低气温

<!DOCTYPE html> <html> <head><title>气温图表</title><script src"https://cdn.jsdelivr.net/npm/chart.js"></script><style>#myChart{width:800px;height: 400px;}</style> </head> <body>&l…

2023全球数字贸易大赛-web3,区块链,诺威信,浪潮云,微众区块链,福建中科星泰,瓴羊区块链,联想-元宇宙,硅基智能-

目录 诺威信B隐私计算平台 浪潮云=星火连-澳优码 HyperChain 产品介绍 CA认证即电子认证服务

离线安装python包,遇到is not a supported wheel on this platform

离线安装python包&#xff0c;遇到is not a supported wheel on this platform 行走_ 于 2022-10-14 23:11:27 发布 阅读量2.7k 收藏 8 点赞数6 分类专栏&#xff1a; Python 文章标签&#xff1a; python 开发语言 版权 Python 专栏收录该内容 46 篇文章2 订阅 订阅专栏…

【一文带你读懂docker,从入门到精通!】

dockerfile 是啥?dockerfile 用来构建 docker 镜像的文件。 前言 Docker 入门到精通 1、DockerFile 介绍 dockerfile 是啥?dockerfile 用来构建 docker 镜像的文件。 具体步骤&#xff1a; 1、编写一个 dockerfile 文件 2、docker build 构造一个镜像 3、docker run 运行…

Swift下如何使用#if条件编译

一、OC使用条件编译 OC中可以使用宏定义&#xff0c;再使用条件编译 #define USER_CUSTOM使用 #if USER_CUSTOM //其他代码 #endif二、Swift使用条件编译 Swift 不像ObjectC一样&#xff0c;通过定义一个变量&#xff0c;然后使用**#if #endif** 方法。swift需要设置一下才能…

计算机网络(超详解!) 第一节计算机网络的性能指标

1.速率 比特&#xff08;bit&#xff09;是计算机中数据量的单位&#xff0c;也是信息论中使用的信息量的单位。 比特&#xff08;bit&#xff09;来源于 binary digit&#xff0c;意思是一个“二进制数字”&#xff0c;因此一个比特就是二进制数字中的一个 1 或 0。 速率是…

aPEAR包绘制功能富集网络图

本期教程 前言 今天学习aPEAR包&#xff0c;绘制KEGG和GO功能富集网络图&#xff0c;用起来还是比较方便的&#xff0c;直接将clusterProfiler富集结果进行绘制&#xff0c;对人类、动物等分析结果非常方便。对于模式植物&#xff0c;使用自己制作的GO或KEGG背景文件进行富集分…

QT linux下应用程序打包

一、应用程序app 1、应用程序的pro文件 2、 程序工作函数 3、app的UI界面 二、动态库lib 1、Lib类头文件 2、.cpp文件 三、对应用程序和动态库进行构建 1、对动态库进行qmake,然后进行构建 2、对应用程序进行qmake&#xff0c;然后进行构建 3、查看构建目录 四、编写脚本 …

一键删除多余内容,批量处理HTML文本,轻松省时!

亲爱的用户们&#xff0c;您是否曾经为了删除HTML文本中的多余内容而烦恼&#xff1f;是否曾经为了批量处理文本而感到困扰&#xff1f;现在&#xff0c;我们为您带来了一款全新的HTML文本处理工具&#xff0c;它可以轻松解决您的问题&#xff01; 首先&#xff0c;在首助编辑…

【python】python基础速通系列2-python程序中的积木块

【组成Python的几个单位】 变量:指向值的名称。或者说变量是一个名称,这个名称指向一个具体的指。比如n=17,就说这个叫做n的变量的值是17。表达式:是值,变量和运算符的组合。如果把变量理解为名词,那么表达式就是把名词连起来的动词形容词。比如:n+25。语句:代码的基本…

设计模式 创建者模式

设计模式 创建者模式 前言原来代码使用设计模式总结Builder模式在源码中的应用&#xff1a;其他代码 前言 “对象创建”模式——绕开new 工厂模式 抽象工厂 原型模式 构建器 动机与背景 目前需要建造一个房子&#xff0c;建造房子需要一系列特定的步骤&#xff0c;但是房子的类…