什么是服务注册中心

服务注册中心是服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来存储服务信息,譬如提供者url 串、路由信息等。 服务注册中心是微服务架构中最基础的设施之一。 注册中心可以说是微服务架构中的“通讯录”,它记录了服务和服务地址的映射关系。

注册中心选型

序号比较项EurekazookeeperNacosConsul
1集群结构平级主从支持平级和主从主从
2集群角色主人Leader、follower observerleader、follower、candidateserver-leader、server以及client
3是否可以及时知道服务状态变化不能及时知道会及时知道不能及时知道不能及时知道
4一致性协议(CAP****)注重可用性(AP)注重一致性(CP)支持CP\AP 默认AP注重一致性(CP)
5雪崩保护没有没有
6社区是否活跃Eureka2.0不再维护了持续维护持续维护持续维护
7管理端有现成的eureka管理端没有现成的管理端有现成的管理端有现成的管理端
8负载均衡策略使用ribbon实现一般可以直接采用RPC的负载均衡权重/metadata/SelectorFabio
9权限控制使用ACL实现节点权限控制RBAC-用户、角色、权限ACL
10Spring Cloud集成支持支持支持支持
11健康检查Client BeatKeep AliveTCP/HTTP/MYSQL/Client BeatTCP/HTTP/gRPC/Cmd
12自动注销实例支持支持支持不支持
13访问协议HTTPTCPHTTP/DNSHTTP/DNS
14是否可用作配置中心
15多数据中心不支持不支持不支持支持
16跨注册中心同步不支持不支持支持支持
17Dubbo集成不支持支持支持不支持
18K8S集成支持支持支持支持

架构图

原理

主从集群

Raft协议

优雅停机主动从Nacos下线实例

问题场景

服务注册到 Nacos 上,部署采用的是 k8s + docker 容器部署。

当pod发布升级时,新的 pod 被创建——Nacos 上服务实例加 1,旧的 pod 被关闭——Nacos 上服务实例减 1。

Nacos 通过心跳检测机制,将旧的 pod 实例下线。

在关闭旧的 pod 到下线 pod 之间,存在一定的时间差,导致服务消费方调用接口会发生报错的情况,对业务操作造成一定影响。

解决方案

在 Pod 关闭前设置一个 preStop 钩子,在 preStop 脚本中执行主动从 Nacos 下线本机实例, sleep 25 秒后再执行 Pod 的销毁,从而实现优雅停机。

# 调用从nacos下线接口
curl http://localhost:8080/api/nacos/deregister
# 延迟发送关闭信号到容器进程
sleep 25

代码示例

// 创建clientConfig
clientConnfig := constant.ClientConfig{
        TimeoutMs:           5000, // 超时时间
        NamespaceId:         "95d1f5d2-cc16-4b65-86e0-9ac0357d623d",
        NotLoadCacheAtStart: true,
        CacheDir:            "/tmp/nacos/cache",
        LogDir:              "/tmp/nacos/log",
        RotateTime:          "1h",    // 日志轮转周期,比如:30m, 1h, 24h, 默认是24h
        MaxAge:              3,       // 日志最大文件数,默认3
        LogLevel:            "debug", // 日志默认级别,值必须是:debug,info,warn,error,默认值是info
    }

// 创建ServerConfig 至少一个ServerConfig
    serverConfigs := []constant.ServerConfig{
        {
            IpAddr:      "172.16.213.134",
            ContextPath: "/nacos",
            Port:        8848,
            Scheme:      "http",
        },
    }

// 创建服务发现客户端
    namingClient, err := clients.CreateNamingClient(map[string]interface{}{
        "serverConfigs": serverConfigs,
        "clientConfig":  clientConnfig,
    })

// 注册实例
success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{
        Ip:          "172.16.231.1",
        Port:        8080,
        ServiceName: "user-service",
        Weight:      1,
        Enable:      true,
        Healthy:     true,
        Ephemeral:   true,
        Metadata:    map[string]string{"idc": "shanghai"},
        ClusterName: "cluster-a",
        GroupName:   "group-a",
    })

// 注销实例
success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{
        Ip:          "172.16.231.1",
        Port:        8080,
        ServiceName: "user-service",
        Ephemeral:   true,
        Cluster:     "cluster-a",
        GroupName:   "group-a",
    })

// 获取服务信息
services, err := namingClient.GetService(vo.GetServiceParam{
        Clusters:    []string{"cluster-a"},
        ServiceName: "user-service",
        GroupName:   "group-a",
    })

// 获取所有的实例列表
instances, err := namingClient.SelectAllInstances(vo.SelectAllInstancesParam{
        Clusters:    []string{"cluster-a"},
        ServiceName: "user-service",
        GroupName:   "group_a",
    })

// 获取实例列表
instances, err := namingClient.SelectInstances(vo.SelectInstancesParam{
        Clusters:    []string{"cluster-a"},
        ServiceName: "user-service",
        GroupName:   "group-a",
        HealthyOnly: true,
    })

// 获取一个健康的实例(加权随机轮询)
instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{
        Clusters:    []string{"cluster-a"},
        ServiceName: "user-service",
        GroupName:   "group-a",
    })

// 监听服务变化
err := namingClient.Subscribe(&vo.SubscribeParam{
        ServiceName: "service-user",
        GroupName:   "group-a",
        Clusters:    []string{"cluster-a"},
        SubscribeCallback: func(services []model.SubscribeService, err error) {
            log.Printf("\n\n callback return services:%s \n\n", util.ToJsonString(services))
        },
    })

// 取消服务监听
err := namingClient.Unsubscribe(&vo.SubscribeParam{
        ServiceName: "user-service",
        Clusters:    []string{"cluster-a"},
        GroupName:   "group-a",
        SubscribeCallback: func(services []model.SubscribeService, err error) {
            log.Printf("\n\n callback return services:%s \n\n", util.ToJsonString(services))
        },
    })

// 获取服务名列表
serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
        NameSpace: "95d1f5d2-cc16-4b65-86e0-9ac0357d623d",
        GroupName: "group-a",
        PageNo:    1,
        PageSize:  10,
    })

Scroll to Top