什么是服务注册中心

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

注册中心选型

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

架构图

原理

主从集群

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,
    })

滚动至顶部