首页 > 其他分享 >服务注册与发现-etcd 遇见的问题

服务注册与发现-etcd 遇见的问题

时间:2023-06-02 14:46:00浏览次数:42  
标签:key return 遇见 err gcli etcd 注册 mygrpc

服务注册与发现-etcd 遇见的问题

问题现象

grpc client 调用 server ,通过 etcd 提供服务发现能力

2023/06/02 11:25:33 scheme: etcd;
{"level":"warn","ts":"2023-06-02T11:25:33.444+0800","logger":"etcd-client","caller":"endpoints/endpoints_impl.go:91","msg":"unmarshal endpoint update failed","key":"etcd.mygrpc-svc","error":"invalid character '.' after top-level value"}
2023/06/02 11:25:33 gcli.grpcc:  etcd:///etcd.mygrpc-svc

// client/main.go 客户端代码
func newGrpcCli(host []string) *grpcli {
	gcli := &grpcli{
		host: host,
	}
	cli, err := cliv3.NewFromURL("http://localhost:2379")
	if err != nil {
		gcli.err = err
		return gcli
	}

	etcdResolver, err := resolver.NewBuilder(cli)
	if err != nil {

		gcli.err = err
		return gcli
	}
	log.Printf("scheme: %s;", etcdResolver.Scheme())

	conn, err := grpc.DialContext(context.TODO(), "etcd:///etcd.mygrpc-svc",
		grpc.WithResolvers(etcdResolver),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`))
	if err != nil {
		gcli.err = err
		return gcli
	}
	gcli.grpcc = conn
	log.Println("gcli.grpcc: ", gcli.grpcc.Target())
	return gcli
}

问题分析

可以很明显看见报错,好像是解析一个 JSON 数据失败,unmarshal 一般是反序列化的时候用到的函数。那么

func (m *endpointManager) DeleteEndpoint(ctx context.Context, key string, opts ...clientv3.OpOption) error {
        // 下面这行是源码第 91行
	return m.Update(ctx, []*UpdateWithOpts{NewDeleteUpdateOpts(key, opts...)})
}

// 调用的地址解析函数
func (m *endpointManager) Update(ctx context.Context, updates []*UpdateWithOpts) (err error) {
	ops := make([]clientv3.Op, 0, len(updates))
	for _, update := range updates {
		if !strings.HasPrefix(update.Key, m.target+"/") {
			return status.Errorf(codes.InvalidArgument, "endpoints: endpoint key should be prefixed with '%s/' got: '%s'", m.target, update.Key)
		}

		switch update.Op {
		case Add:
			internalUpdate := &internal.Update{
				Op:       internal.Add,
				Addr:     update.Endpoint.Addr,
				Metadata: update.Endpoint.Metadata,
			}

			var v []byte
                        // 在这里遇见了错误并返回
			if v, err = json.Marshal(internalUpdate); err != nil {
				return status.Error(codes.InvalidArgument, err.Error())
			}
			ops = append(ops, clientv3.OpPut(update.Key, string(v), update.Opts...))
		case Delete:
			ops = append(ops, clientv3.OpDelete(update.Key, update.Opts...))
		default:
			return status.Error(codes.InvalidArgument, "endpoints: bad update op")
		}
	}
	_, err = m.client.KV.Txn(ctx).Then(ops...).Commit()
	return err
}

我们来看一下 grpc server 服务注册代码

// main.go 
import (
    myetcdRgst "mygrpc/registry/etcd"
)
func main() {
	flag.Parse()
	addr := fmt.Sprintf("0.0.0.0:%s", port)
        ......
	etcdCli, err := myetcdRgst.NewEtcdRegister()
	if err != nil {
		log.Println("etcd register: ", err)
		return
	}
	defer etcdCli.Close()

	sn := "etcd.mygrpc-svc"
        // 这里传入的 key: etcd.mygrpc-svc ,addr: 0.0.0.0:8080
	if err := etcdCli.RegisterServer(sn, addr, 5); err != nil {
		log.Println("register service: ", err)
		return
	}
        ......
}

// registry/etcd/etcd.go
func (s *EtcdRegister) BindLease(key, value string) error {
        // 将数据放入 etcd 中
	res, err := s.etcdCli.Put(s.ctx, key, value, clientv3.WithLease(s.leaseId))
	if err != nil {
		return err
	}
	log.Println("BindLease: ", res)
	return nil
}

这时我们查看 etcd 中保存的数据格式

docker exec -it etcd-server /bin/bash
etcdctl get etcd.mygrpc-svc --prefix
# 输出
etcd.mygrpc-svc
0.0.0.0:8080

解决方式

这种格式中存在 . 这样的符号。与报错信息发生的呼应。我选择修改一下BindLease() 函数中的 value

// registry/etcd/etcd.go
func (s *EtcdRegister) BindLease(key, value string) error {
        // 将数据放入 etcd 中
        data := `{"addr":"`+value+`"}`
	res, err := s.etcdCli.Put(s.ctx, key, data, clientv3.WithLease(s.leaseId))
	if err != nil {
		return err
	}
	log.Println("BindLease: ", res)
	return nil
}
# 执行客户端代码,发现调用成功
cd client
go run main.go 
# 输出
2023/06/02 14:01:43 scheme: etcd;
2023/06/02 14:01:43 gcli.grpcc:  etcd:///etcd.mygrpc-svc                                                  
say:  Name:"My name is sober; my port: 8080"

如何更加优雅,且实现负载均衡

// 注册代码改造,新增随机哈希值
// 获取一个哈希值用于标记服务端的
func randStr() string {
	var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	b := make([]rune, 5)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func (s *EtcdRegister) BindLease(key, value string) error {
	em, err := endpoints.NewManager(s.etcdCli, key)
	if err != nil {
		return err
	}
	realKey := fmt.Sprintf("%s/%s", key, randStr())
        // 将服务端添加进 endpoints 列表中
	return em.AddEndpoint(context.TODO(), realKey, endpoints.Endpoint{Addr: value})
}
# etcd 存储表现
docker exec -it etcd-server /bin/bash
etcdctl get etcd.mygrpc-svc --prefix
# 输出
etcd.mygrpc-svc/NshHo
{"Op":0,"Addr":"0.0.0.0:8080","Metadata":null}
etcd.mygrpc-svc/OJZbS
{"Op":0,"Addr":"0.0.0.0:8081","Metadata":null}

客户端再次访问的时候 etcd 对服务端的解析就实现了负载均衡,无需我们单独实现。

参考文献

gRPC naming and discovery

标签:key,return,遇见,err,gcli,etcd,注册,mygrpc
From: https://www.cnblogs.com/shangmo/p/17451690.html

相关文章

  • Linux centos7 ppc64le编译安装MySQL8遇见问题
    一.关于Nopackagedevtoolset-7-gccavailable.的解决办法1.使用centos默认yum源2.依次执行以下命令yuminstall-ycentos-release-sclyuminstall-ydevtoolset-7 二.cmake3>=3.6.1isneededbymysql-community-8.0.18-1.el7.ppc64le安装cmake3yuminstall......
  • 把任意程序注册成windows服务, 并设置开机自动启动
    背景最近有个需求让我把原本部署在linux系统上的数据库和对数据库进行CRUD的java程序部署到windows系统上来.问题mysql和tdengine这两个数据库有windows版本,直接安装就行了,并且也已经自动注册成服务,可以自己设置开机自动启动(控制面板->本地服务)但是kafka和zookeepe......
  • 网络层协议注册
    staticDEFINE_SPINLOCK(ptype_lock);structlist_headptype_base[PTYPE_HASH_SIZE];structlist_headptype_all;/*Taps*/structpacket_type{__be16type;/*Thisisreallyhtons(ether_type).*/structnet_device*dev;/*NULLiswildcardedhere......
  • Linux 内核时钟架构之时钟事件设备注册
    voidclockevents_register_device(structclock_event_device*dev);voidclockevents_config_and_register(structclock_event_device*dev,u32freq,unsignedlongmin_delta,unsignedlongmax_delta);相关的一个是配置函数voidclocke......
  • 《kubernetes 系列》6. etcd 的租约是怎么一回事?
    楔子etcd的一个典型的应用场景是Leader选举,那么etcd为什么可以用来实现Leader选举?核心特性实现原理又是怎样的?本篇文章就来聊一聊Leader选举背后的技术点之一:租约(Lease),通过解析它的核心原理、性能优化思路,从而对Lease如何关联key、Lease如何高效续期、淘汰、什么......
  • (转)微服务注册中心:Consul——服务发现
    原文:https://xie.infoq.cn/article/4fe6f02b220fb009844861b56https://www.cnblogs.com/myitnews/p/13655000.html一概述说完了Consul的服务注册,那么就该到服务发现了。大家有过rpc框架使用经验的,例如nacos、eureka、dubbo等,就会了解服务中的角色,也就是生产者和消费者,也......
  • git 注册GitHub账号:011
    打开git官网注册地址:https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fsignup%3Fref_cta%3DSign%2Bup%26ref_loc%3Dheader%2Blogged%2Bout%26ref_page%3D%252F%26source%3Dheader-home在注册过程中,有些步骤我跳过了,根据提示来操作就行      ......
  • Tinder多账号注册怎样防关联?
    Tinder是目前除了Facebook,用户数量较多的一款海外交友软件。它采用匹配的机制,双方只有互相表示感兴趣才能开始聊天。由于它的特殊机制,一上线就吸引了很多用户,有着很强的广告效应。因此,很多人把Tinder当作一个不错的宣传平台,进行广告投放。想要更好地进行营销推广,一个Tinder账号是远......
  • windows系统如何禁用组策略(修改注册表)
    1、在电脑桌面使用快捷键win键+R唤出运行窗口,输入regedit,点击确定。2、在注册表编辑器左侧窗口中依次点击展开\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies,选中其下的Explorer。3、在右侧空白处点击右键,依次点击新建—DWORD(32位)值。4、命名为R......
  • 《kubernetes 系列》4. etcd 的安装、命令行操作,以及 etcd v2 和 v3 的差异
    楔子通过前面两篇文章,我们已经对etcd有了一个基本的了解,那么接下来就要安装etcd了。安装完之后,再来全面介绍etcd的语法,etcd的功能还是很强大的。下面就来安装etcd。安装etcd先说一下配置,首先操作系统是CentOS7,而且我们这里要搭建三个节点的集群。服务器我在阿里云......