2025年3月21日 星期五 甲辰(龙)年 月廿 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Other

grpc实现流量染色

时间:10-29来源:作者:点击数:26
城东书院 www.cdsy.xyz
什么是流量染色

流量染色是指根据流量协议设置对应的流量染色规则,对指定的流量进行染色标记,并在整个调用链中携带该标记。通过染色流量可以对特定的流量进行跟踪和路由,所以流量染色功能常被用于灰度发布的场景。在业务系统迭代过程中会不断有新版本发布,在正式发布前,可以使用流量染色控制先进行小规模验证,通过收集使用体验的数据,对应用新版本的功能、性能、稳定性等指标进行评判,然后再全量升级。即使某个新版本出现问题,也只会影响已染色流量,不会将问题蔓延至整个系统,保证整个系统的正常运行。

同理,流量染色功能还可以用于大促前的性能压测。在线上压测的场景中,为了让压测数据和正式的线上数据实现隔离,常用的方法是对于消息队列,缓存,数据库使用影子的方式。这就需要流量染色的技术,带一个tag进去,说明这个请求是测试数据,还是真实数据。

此外,流量染色功能还可以用于多测试环境的治理。在大规模微服务场景下,不可能每个部门部署一套完整的环境,因为耗费的资源量实在是太大了。这时候就需要合理规划测试环境,可以建立一个基准测试环境,对应Master分支,里面部署全量的应用。每一个分支对应有更新的模块,比如说你修改了五个工程,测试的时候,不需要部署全量的应用,只需要把这五个工程去创建一个Delta测试环境就可以了。

当客户端进行测试的时候,通过流量染色标记不同的测试分支流量,将该流量路由至测试版本。当这五个服务之内相互调用的时候,微服务框架就会选择这五个服务的实例进行调用,如果需要调用五个服务之外的其他服务的时候,微服务框架会到Master环境里面,选择服务实例进行调用。有了流量染色的环境治理机制,测试环境数量会大大减少。

染色信息的传递

给入站请求绑定上下文(如: http header), in-process 使用 context 传递,跨服务使用 metadata 传递在这个架构中每一个基础组件都能够理解染色信息,并且能够基于染色路由隔离流量

grpc怎么实现流量染色

依赖resolver注册组件以及balancer负载均衡组件

一.首先需要将染色信息注册到etcd等注册发现中心

  • ip := "127.0.0.1" // NOTE: 必须拿到您实例节点的真实IP,
  • port := "9000" // NOTE: 必须拿到您实例grpc监听的真实端口
  • hn, _ := os.Hostname()
  • dis,err := etcd.New(&clientv3.Config{Endpoints:[]string{"127.0.0.1"}})
  • if err != nil {
  • panic(err)
  • }
  • ins := &naming.Instfszance{
  • AppID: "test",
  • Addrs: []string{
  • "grpc://" + ip + ":" + port,
  • },
  • Metadata : map[string]string{"color":env.Color},
  • }
  • cancel, err := etcd.Register(context.Background(), ins)

完成服务注册,如test服务,如果有不同测试分支,则数据有多条,同一个appid不用addr和color

二.实现resolver组件Build方法

  • addrs := make([]resolver.Address, 0, len(instances))
  • for _, ins := range instances {
  • var rpc string
  • for _, a := range ins.Addrs {
  • u, err := url.Parse(a)
  • if err == nil && u.Scheme == Scheme {
  • rpc = u.Host
  • }
  • }
  • addr := resolver.Address{
  • Addr: rpc,
  • Type: resolver.Backend,
  • ServerName: ins.AppID,
  • Metadata: wmeta.MD{Weight: uint64(weight), Color: ins.Metadata[naming.MetaColor]},
  • }
  • addrs = append(addrs, addr)
  • }
  • log.Info("resolver: finally get %d instances", len(addrs))
  • r.cc.NewAddress(addrs)

主要逻辑思想为从etcd发现信息,而后更新到grpc,resolver.Address里自此存在了color信息

三.实现balancer的Build和Pick方法,从中挑选出你想要的Conn

  • func (*p2cPickerBuilder) Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker {
  • p := &p2cPicker{
  • colors: make(map[string]*p2cPicker),
  • r: rand.New(rand.NewSource(time.Now().UnixNano())),
  • }
  • for addr, sc := range readySCs {
  • meta, ok := addr.Metadata.(wmd.MD)
  • if !ok {
  • meta = wmd.MD{
  • Weight: 10,
  • }
  • }
  • subc := &subConn{
  • conn: sc,
  • addr: addr,
  • meta: meta,
  • svrCPU: 500,
  • lag: 0,
  • success: 1000,
  • inflight: 1,
  • }
  • if meta.Color == "" {
  • p.subConns = append(p.subConns, subc)
  • continue
  • }
  • // if color not empty, use color picker
  • cp, ok := p.colors[meta.Color]
  • if !ok {
  • cp = &p2cPicker{r: rand.New(rand.NewSource(time.Now().UnixNano()))}
  • p.colors[meta.Color] = cp
  • }
  • cp.subConns = append(cp.subConns, subc)
  • }
  • return p
  • }
  • type p2cPicker struct {
  • // subConns is the snapshot of the weighted-roundrobin balancer when this picker was
  • // created. The slice is immutable. Each Get() will do a round robin
  • // selection from it and return the selected SubConn.
  • subConns []*subConn
  • colors map[string]*p2cPicker
  • logTs int64
  • r *rand.Rand
  • lk sync.Mutex
  • }
  • func (p *p2cPicker) Pick(ctx context.Context, opts balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) {
  • // FIXME refactor to unify the color logic
  • color := nmd.String(ctx, nmd.Color)
  • if color == "" && env.Color != "" {
  • color = env.Color
  • }
  • if color != "" {
  • if cp, ok := p.colors[color]; ok {
  • return cp.pick(ctx, opts)
  • }
  • }
  • return p.pick(ctx, opts)
  • }

自此则实现了根据染色信息进而路由的功能,那么最后从网关入口处根据不通的流量,标记不通的染色信息,带入go中的context,流量染色整体的功能就实现了。

城东书院 www.cdsy.xyz
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐