注册中心选型比较

848 阅读9分钟
原文链接: mp.weixin.qq.com

Zookeeper

maven依赖

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>

</dependency>

 

<dependency>

    <groupId>org.apache.zookeeper</groupId>

    <artifactId>zookeeper</artifactId>

    <version>3.4.10</version>

</dependency>

配置信息

spring.cloud.zookeeper.enabled=true

spring.cloud.zookeeper.connect-string=192.168.56.2:2181

spring.cloud.zookeeper.discovery.enabled=true

 

注册元数据

{

    "name": "zkreg-client",

    "id": "bdf16c2b-5d0b-462a-a532-bf9bc311004d",

    "address": "DESKTOP-94SCF9A",

    "port": 10100,

    "sslPort": null,

    "payload": {

        "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",

        "id": "application-1",

        "name": "zkreg-client",

        "metadata": {}

    },

    "registrationTimeUTC": 1543550254396,

    "serviceType": "DYNAMIC",

    "uriSpec": {

        "parts": [{

            "value": "scheme",

            "variable": true

        },

        {

            "value": "://",

            "variable": false

        },

        {

            "value": "address",

            "variable": true

        },

        {

            "value": ":",

            "variable": false

        },

        {

            "value": "port",

            "variable": true

        }]

    }

}

缺点

Zookeeper作为注册中心,主要是满足了CAP中的CP 要求,P没什么好说的分区容错性必须要保证,然后需要保证可用性,而zk的可用性在大并发情况下它的选举算法势必会导致部分请求丢失。优点是一致性,但是一致性像在这种情况显得没有必要。而 Eureka即是AP的例子,比较适合做注册中心。

 

Eureka

服务端

maven配置

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

</dependency>

配置信息

# 关闭自我保护模式

eureka.server.enable-self-preservation=false

 

# 设置清理无效节点的时间间隔,默认60000,即是60s

eureka.server.eviction-interval-timer-in-ms=5000

 

# 使用ip进行发布

eureka.instance.prefer-ip-address=true

 

# eureka服务器的地址(注意:地址最后面的 /eureka/ 这个是固定值)

eureka.client.serviceUrl.defaultZone=http://192.168.56.1:${server.port}/eureka/

 

# 是否从eureka获取注册信息, 因为本身所以不需要

eureka.client.fetch-registry=false

 

# 是否注册到eureka,如果是集群就需要配置成true

# 并且上面的defaultZone需要配置集群的另外的节点

eureka.client.register-with-eureka=false

 

备注:

ureka Server通过“自我保护模式”来解决这个问题----当Eureka Server 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server 节点会自动退出自我保护模式。

 

注解

@EnableEurekaServer

 

注册元数据

GET http://192.168.56.1:10200/eureka/apps/EUREKAREG-CLIENT-SPRING

 

<application>

  <name>EUREKAREG-CLIENT-SPRING</name>

  <instance>

    <instanceId>DESKTOP-94SCF9A:eurekareg-client-spring:10300</instanceId>

    <hostName>192.168.56.1</hostName>

    <app>EUREKAREG-CLIENT-SPRING</app>

    <ipAddr>192.168.56.1</ipAddr>

    <status>UP</status>

    <overriddenstatus>UNKNOWN</overriddenstatus>

    <port enabled="true">10300</port>

    <securePort enabled="false">443</securePort>

    <countryId>1</countryId>

    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">

      <name>MyOwn</name>

    </dataCenterInfo>

    <leaseInfo>

      <renewalIntervalInSecs>5</renewalIntervalInSecs>

      <durationInSecs>10</durationInSecs>

      <registrationTimestamp>1543565263391</registrationTimestamp>

      <lastRenewalTimestamp>1543565338399</lastRenewalTimestamp>

      <evictionTimestamp>0</evictionTimestamp>

      <serviceUpTimestamp>1543565263391</serviceUpTimestamp>

    </leaseInfo>

    <metadata>

      <management.port>10300</management.port>

    </metadata>

    <homePageUrl>http://192.168.56.1:10300/</homePageUrl>

    <statusPageUrl>http://192.168.56.1:10300/actuator/info</statusPageUrl>

    <healthCheckUrl>http://192.168.56.1:10300/actuator/health</healthCheckUrl>

    <vipAddress>eurekareg-client-spring</vipAddress>

    <secureVipAddress>eurekareg-client-spring</secureVipAddress>

    <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>

    <lastUpdatedTimestamp>1543565263391</lastUpdatedTimestamp>

    <lastDirtyTimestamp>1543565263335</lastDirtyTimestamp>

    <actionType>ADDED</actionType>

  </instance>

</application>

 

客户端

spring客户端

maven配置

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

</dependency>

配置信息

# Eureka启用Security后客户端使用http://qb:123456@192.168.56.1:10200/eureka/ 连接上来会报错下面的错误

# com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

# eureka服务器的地址(注意:地址最后面的 /eureka/ 这个是固定值)

eureka.client.serviceUrl.defaultZone=http://192.168.56.1:10200/eureka/

 

#设置拉取服务注册信息时间,默认60s

eureka.client.registry-fetch-interval-seconds=30

 

#自定义实例ID

#eureka.instance.instanceId=${spring.application.name}:${random.value}

 

#显示IP地址

eureka.instance.prefer-ip-address=true

 

#指定续约更新频率,默认是30s

eureka.instance.lease-renewal-interval-in-seconds=5

 

#设置过期剔除时间,默认90s

eureka.instance.lease-expiration-duration-in-seconds=10

 

注解

@EnableEurekaClient

 

非spring客户端

这里以java模拟http请求作为示例,当然其他异构语言只要使用 http协议调用各api同样可以实现向eureka注册

 

以java作为示例需要依赖一下工具包

<dependency>

    <groupId>com.squareup.okhttp3</groupId>

    <artifactId>okhttp</artifactId>

    <version>3.11.0</version>

</dependency>

<dependency>

    <groupId>commons-io</groupId>

    <artifactId>commons-io</artifactId>

    <version>2.5</version>

</dependency>

<dependency>

    <groupId>com.alibaba</groupId>

    <artifactId>fastjson</artifactId>

    <version>1.1.41</version>

</dependency>

<dependency>

    <groupId>com.google.guava</groupId>

    <artifactId>guava</artifactId>

    <version>20.0</version>

</dependency>

注册实例

l 接口代码

MediaType JSON = MediaType.get("application/json; charset=utf-8");

URL url = ResourceUtils.getURL("classpath:reg-instance.json");

RequestBody body = RequestBody.create(JSON, IOUtils.toString(url, Charset.defaultCharset()));

String regAPI = "http://192.168.56.1:10200/eureka/apps/eurekareg-client-rest";

Request request = new Request.Builder().url(regAPI).post(body).build();

Response response = client.newCall(request).execute();

 

l 请求报文reg-instance.json

其中红色部分注册信息同properties/yml 中的eureka.instance.*前缀属性

{

  "instance": {

    "instanceId": "DESKTOP-94SCF9A:eurekareg-client-rest:10301",

    "app": "eurekareg-client-rest",

    "appGroutName": null,

    "ipAddr": "192.168.56.1",

    "sid": "na",

    "homePageUrl": null,

    "statusPageUrl": null,

    "healthCheckUrl": null,

    "secureHealthCheckUrl": null,

    "vipAddress": "eurekareg-client-rest2",

    "secureVipAddress": "eurekareg-client-rest2",

    "countryId": 1,

    "dataCenterInfo": {

      "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",

      "name": "MyOwn"

    },

    "hostName": "192.168.56.2",

    "status": "UP",

    "leaseInfo": null,

    "isCoordinatingDiscoveryServer": false,

    "lastUpdatedTimestamp": 1529391461000,

    "lastDirtyTimestamp": 1529391461000,

    "actionType": null,

    "asgName": null,

    "overridden_status": "UNKNOWN",

    "port": {

      "$": 10301,

      "@enabled": "false"

    },

    "securePort": {

      "$": 10302,

      "@enabled": "false"

    },

    "metadata": {

      "@class": "java.util.Collections$EmptyMap"

    }

  }

}

 

销毁实例

l 接口代码

OkHttpClient client=new OkHttpClient();

String deleteAPI="http://192.168.56.1:10200/eureka/apps/eurekareg-client-rest/DESKTOP-94SCF9A:eurekareg-client-rest:10301";

Request request=new Request.Builder().url(deleteAPI).delete().build();

Response response=client.newCall(request).execute();

log.info("\n{}",response.body().string());

 

实例暂停/下线/脱网

l 接口代码

OkHttpClient client=new OkHttpClient();

String outServiceAPI="http://192.168.56.1:10200/eureka/apps/eurekareg-client-rest/DESKTOP-94SCF9A:eurekareg-client-rest:10301/status?value=OUT_OF_SERVICE ";

FormBody body=new FormBody.Builder().build();

Request request=new Request.Builder().url(outServiceAPI).put(body).build();

Response response=client.newCall(request).execute();

log.info("\n{}",response.body().string());

 

 

恢复实例

l 接口代码

OkHttpClient client=new OkHttpClient();

String outServiceAPI="http://192.168.56.1:10200/eureka/apps/eurekareg-client-rest/DESKTOP-94SCF9A:eurekareg-client-rest:10301/status?value=UP ";

FormBody body=new FormBody.Builder().build();

Request request=new Request.Builder().url(outServiceAPI).delete(body).build();

Response response=client.newCall(request).execute();

log.info("\n{}",response.body().string());

 

 

应用实例发送心跳

l 接口代码

OkHttpClient client=new OkHttpClient();

String outServiceAPI="http://192.168.56.1:10200/eureka/apps/eurekareg-client-rest/DESKTOP-94SCF9A:eurekareg-client-rest:10301";

FormBody body=new FormBody.Builder().build();

Request request=new Request.Builder().url(outServiceAPI).put(body).build();

Response response=client.newCall(request).execute();

log.info("\n{}",response.body().string());

 

修改实例参数

例如修改eureka.instance.lease-renewal-interval-in-seconds=5这些配置,通过删除实例后重新添加实例

 

缺点

一致性较差,eureka分区节点挂了以后,数据一致性没法保证。客户端拉取服务信息可能是过期的服务列表。另外服务信息不做持久保存。使用eureka 应付规模不大的分布式环境足够,所以应对大型的分布式环境Consul时最优解。

 

Consul

介绍

consul是google开源的一个使用go 语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper 等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要运行agent,他有两种运行模式server和 client。每个数据中心官方建议需要3或5个 server节点以保证数据安全,同时保证server-leader的选举能够正确的进行。

 

@client

CLIENT表示consul的client 模式,就是客户端模式。是consul节点的一种模式,这种模式下,所有注册到当前节点的服务会被转发到SERVER,本身是不持久化这些信息。

 

@server

SERVER表示consul的server 模式,表明这个consul是个server,这种模式下,功能和CLIENT 都一样,唯一不同的是,它会把所有的信息持久化的本地,这样遇到故障,信息是可以被保留的。

 

@server-leader

中间那个SERVER下面有LEADER的字眼,表明这个 SERVER是它们的老大,它和其它SERVER不一样的一点是,它需要负责同步注册的信息给其它的SERVER ,同时也要负责各个节点的健康监测。

 

@raft

server节点之间的数据一致性保证,一致性协议使用的是raft,而zookeeper 用的paxos,etcd采用的也是taft 。

 

@服务发现协议

consul采用http和dns 协议,etcd只支持http

 

@服务注册

consul支持两种方式实现服务注册,一种是通过consul的服务注册http API ,由服务自己调用API实现注册,另一种方式是通过json个是的配置文件实现注册,将需要注册的服务以 json格式的配置文件给出。consul官方建议使用第二种方式。

 

@服务发现

consul支持两种方式实现服务发现,一种是通过http API来查询有哪些服务,另外一种是通过 consul agent 自带的DNS(8600端口),域名是以 NAME.service.consul的形式给出,NAME即在定义的服务配置文件中,服务的名称。DNS 方式可以通过check的方式检查服务。

 

@服务间的通信协议

Consul使用gossip协议管理成员关系、广播消息到整个集群,他有两个gossip  pool (LAN pool和WAN pool),LAN pool 是同一个数据中心内部通信的,WAN pool是多个数据中心通信的,LAN pool有多个, WAN pool只有一个。

安装部署

安装golang

安装consul需要os先安装 golang环境

https://dl.google.com/go/go1.11.2.linux-amd64.tar.gz

cp go1.11.2.linux-amd64.tar.gz /usr/local

gunzip go1.11.2.linux-amd64.tar.gz

tar -xvf go1.11.2.linux-amd64.tar

export PATH=$PATH:/usr/local/go/bin

 

安装系统

需要内核 >= 2.6.23

安装介质

https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_windows_amd64.zip

https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_linux_amd64.zip

https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_solaris_amd64.zip

https://releases.hashicorp.com/consul/1.4.0/consul_1.4.0_darwin_amd64.zip

 

 

检查安装

 

 

 

 

 

开发者模式

consul agent -dev -bind=192.168.56.101  -client=0.0.0.0

 

 

 

开发模式,自带了web ui,直接http://192.168.56.101 :8500/ 即可,非常方便

 

自带UI

 

 

springboot集成

配置

spring.application.name=consulreg-client-springserver.port=10400spring.cloud.consul.host=192.168.56.101 spring.cloud.consul.port=8500spring.cloud.consul.discovery.tags=version=1.0,mail=yuanjun3@asiainfo-sec.comspring.cloud.consul.discovery.health-check-path= /heartbeatspring.cloud.consul.discovery.health-check-interval=5s

 

心跳接口

@RestControllerpublic class HeartBeatControl{   @RequestMapping(path=" /heartbeat", method=RequestMethod.GET)   public void heartbeat(){   } }

 

注册结果

 

 

 

 

替换eureka为consul注意事项

最后提醒一下:如果使用consul来替换eureka,而你的项目中又依赖了 eureka的jar包,最好将eureka的自动配置从启动类里排除掉,参考下面:

 

优点

1. 有Google大厂支持维护,可靠性有保障

2. 支持数据持久化(eureka不支持 )

3. 支持集群部署,并且支持server(专做状态维护 )和client(负载均衡)两种模式 ( 较zk可用性高)

4. 支持开发模式

5. springboot原生支持集成

6. 界面UI美观,可读性好,( 功能较eureka好一些,zookeeper只有简单的zkui 等界面 )

7. 提供rest风格的api( zk不支持)