当前位置: 首页 > news >正文

gRPC etcd 服务注册与发现、自定义负载均衡

本文首发在这里
考虑这种常见情景:服务多开,正常连接采用轮询负载均衡,但若服务有状态,重连则需进入之前的服务

本文其实主要在讨论以下两篇官方文档

  • gRPC naming and discovery
  • Custom Load Balancing Policies

实现依赖即将废弃的resolver.Address.Metadata,其实也仅是想复用已有的官方代码

自定义负载均衡并未依照示例,官方可能也是想展示更多细节,提供的明显不是最简单的实现方式,毕竟都是支持Service Config集成自定义配置的,也是需要熟悉endpointsharding和pickfirst相关逻辑的,所以不太适合用来入门

反观官方源码中roundrobin,基于baseBalancer仅实现Picker的方式,才真是将代码写在了刀刃上

服务启动clientv3.Put,服务关闭clientv3.Delete,创建租约并借助Lease.KeepAlive确保服务异常退出时因未自动续期而删除

通过context.WithValue携带ServiceID实现选择指定服务的连接

其余看看便知的就不赘述啦

server.go

package mainimport ("context""flag""fmt""log""log/slog""net""os""os/signal""github.com/panshiqu/golang/discovery""github.com/panshiqu/golang/utils""google.golang.org/grpc"pb "google.golang.org/grpc/examples/features/proto/echo"
)var id = flag.Int("id", 1, "id")
var env = flag.String("env", "dev", "environment")
var host = flag.String("h", "127.0.0.1", "host")type echoServer struct {pb.UnimplementedEchoServer
}func (s *echoServer) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) {return &pb.EchoResponse{Message: fmt.Sprintf("%s (from %d)", req.Message, *id)}, nil
}func main() {flag.Parse()lis, err := net.Listen("tcp", fmt.Sprintf("%s:0", *host))if err != nil {log.Fatal(utils.Wrap(err))}s := grpc.NewServer()pb.RegisterEchoServer(s, &echoServer{})addr := fmt.Sprintf("%s:%d", *host, lis.Addr().(*net.TCPAddr).Port)service, err := discovery.Register("http://127.0.0.1:2379", fmt.Sprintf("%s/game", *env), addr, *id)if err != nil {log.Fatal(utils.Wrap(err))}go func() {if err := s.Serve(lis); err != nil {slog.Error("serve", slog.Any("err", err))}}()c := make(chan os.Signal, 1)signal.Notify(c, os.Interrupt)sig := <-cslog.Info("notify", slog.Any("signal", sig))if err := service.Release(); err != nil {slog.Error("release", slog.Any("err", err))}s.GracefulStop()
}

client.go

package mainimport ("context""flag""fmt""log""time""github.com/panshiqu/golang/balancer"_ "github.com/panshiqu/golang/balancer""github.com/panshiqu/golang/utils"clientv3 "go.etcd.io/etcd/client/v3""go.etcd.io/etcd/client/v3/naming/resolver""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"pb "google.golang.org/grpc/examples/features/proto/echo"
)var id = flag.Int("id", 0, "id")func main() {flag.Parse()cli, err := clientv3.NewFromURL("http://127.0.0.1:2379")if err != nil {log.Fatal(utils.Wrap(err))}etcdResolver, err := resolver.NewBuilder(cli)if err != nil {log.Fatal(utils.Wrap(err))}cc, err := grpc.NewClient("etcd:///discovery/dev/game", grpc.WithResolvers(etcdResolver), grpc.WithDefaultServiceConfig(`{"loadBalancingConfig":[{"custom_round_robin":{}}]}`), grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatal(utils.Wrap(err))}defer cc.Close()ec := pb.NewEchoClient(cc)ctx := context.Background()if *id != 0 {ctx = context.WithValue(ctx, balancer.ServiceID, fmt.Sprint(*id))}for {r, err := ec.UnaryEcho(ctx, &pb.EchoRequest{Message: "hi"})if err != nil {log.Fatal(utils.Wrap(err))}fmt.Println(r)time.Sleep(time.Second)}
}

终端依次执行以下命令试试

go run client.go
go run server.go -id=1
go run server.go -id=2
go run client.go -id=1
# stop server 2

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C++学习,函数重载
  • GO学习笔记(4) strconv/time
  • 基于鸿蒙API10的RTSP播放器(三:底部视频滑轨进度显示)
  • 【大模型理论篇】大模型周边自然语言处理技术(NLP)原理分析及数学推导(Word2Vec、TextCNN、Gated TextCNN、FastText)
  • SQL 编程基础
  • 达梦CASE_SENSITIVE参数解析
  • 7-Python基础编程之数据类型操作——列表和元组
  • cesium.js 入门到精通(7)
  • springboot013基于SpringBoot的旅游网站的设计与实现
  • 做谷歌seo,b端跟c端有什么区别吗?
  • 【笔记】CarrierConfig 解析加载的debug和日志分析
  • 高亚科技与广东海悟携手,打造全流程电子竞标管理平台!
  • 利用高德+ArcGIS优雅获取任何感兴趣的矢量边界
  • 云服务器拉取docker镜像
  • Redis学习笔记(六)——Redis的持久化
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • cookie和session
  • CSS 三角实现
  • CSS中外联样式表代表的含义
  • input的行数自动增减
  • javascript 总结(常用工具类的封装)
  • JavaScript-Array类型
  • MySQL用户中的%到底包不包括localhost?
  • React系列之 Redux 架构模式
  • Theano - 导数
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 坑!为什么View.startAnimation不起作用?
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 使用Gradle第一次构建Java程序
  • -- 数据结构 顺序表 --Java
  • 新手搭建网站的主要流程
  • 一道面试题引发的“血案”
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #QT(QCharts绘制曲线)
  • #前后端分离# 头条发布系统
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (1)虚拟机的安装与使用,linux系统安装
  • (4)logging(日志模块)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第2章第五节(日期和时间)
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (二刷)代码随想录第15天|层序遍历 226.翻转二叉树 101.对称二叉树2
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (四)汇编语言——简单程序
  • (一)80c52学习之旅-起始篇
  • .apk 成为历史!
  • .describe() python_Python-Win32com-Excel
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET分布式缓存Memcached从入门到实战
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .net与java建立WebService再互相调用
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • [ vulhub漏洞复现篇 ] Grafana任意文件读取漏洞CVE-2021-43798