服务注册:每个服务单元都向注册中心登记自己提供的服务,将主机、端口号、版本号、通信协议等一些附加信息告诉注册中心,注册中心按照服务名分类组织注册清单。
服务发现:服务间的调用不在通过制定具体的实例地址实现,而是通过向服务名发起请求调用实现,所以服务调用方在服务调用提供方接口的时候,并不知道具体的服务实
例位置,调用方需要向服务注册中心咨询服务,并获取所有的服务实例清单,以实现对具体的服务实例的访问。
Netflix Eureka:
服务端:服务注册中心,支持高可用的配置,如果eureka以集群的方式部署,当集群中有分片出现故障时,那么eureka就转入自我保护模式,它允许在分片故障的时候继续提供服务的发现与注册,当故障分片恢复运行时,集群中的其他分片会把他们的状态再次同步回来。
客户端:主要处理服务的注册与发现,客户端通过注解和参数配置的方式,在应用程序运行时,eureka客户端向注册中心自身提供的服务并周期性的发送心跳来更新它的服务租约,同时,也能从服务端查询当前注册服务的信息并把他们缓存到本地并周期性的刷新的刷新服务状态。
高可用注册中心:在Erueka的服务治理体系中,所有节点既是服务的提供方又是服务的消费方。
Eureka server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就形成一组互相注册的服务注册中心。
常用注解:@EnableEurekaServer
开启一个注册中心
@EnableEurekaClient
开启一个服务注册/服务发现
@EnableDiscoveryClient
激活eureka中的DiscoveryClient实现
常用配置:eureka.client.register-with-erueka
如果为true表示向注册中心注册自己,false表示不向注册中心注册自己。
eureka.clinet.fetch-registery
true:检索服务 false:不去检索服务
spring.application.name
指定服务名
eureka.client.serviceUrl.defaultZone
指定服务注册中心的地址
eureka.instance.prefer-ip-address=true
使用ip的格式来定义注册中心的地址
服务注册中心:erueka提供的服务端,提供法务注册与发现的功能。
失效剔除:当服务实例不是正常下线的时候,比如内存溢出,网络故障等原因,而服务注册中心并未收到下线的请求,为了从服务列表中将这些无法提供服务的实例剔除,eureka server在启动的时候会创建一个定时任务默认每隔一段时间(默认60s)将当前清单中超时(默认是90s)没有续约的服务剔除出去。
自我保护:eurekaserver 在运行期间会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况下,eureka server会将当前的实例注册信息保护起来,让这些实例不会过期,尽可能保护这些注册信息,但是如果保护期间内实例若出现问题,那么客户端很容易拿到实际已经不存在的服务实例,出现调用失败的情况,那么客户端必须要有容错机制,比如可以使请求重试,断路器等。在本地调式很容易出发注册中心的保护机制,这会使得注册中心维护的实例不是那么准确,可以使用eureka.server.enable-self-preservation=false来关闭保护机制,以确保注册中心可以将不可用的实例剔除掉
服务提供者:提供服务的的应用。
服务注册:服务提供者在服务启动的时候会通过发送rest请求的方式将自己注册到Eureka server中,同时带上了自身服务的元数据信息,eureka server 介绍到rest请求,将元数据信息存储到一个双层map中,其中第一层key是服务名,第二层key是具体服务的实例名。
服务同步:如果服务注册中心之间因互相注册为服务,当服务提供者发送请求到一个服务注册中心时,它会将请求转发给集群中相连的其他服务注册中心,从而实现注册中心之间的服务同步。
服务续约:在注册服务之后,服务提供者会维护一个心跳用来持续告诉erueka server 我还活着,防止erueka server 的剔除任务,将服务实例从服务列表中排除去除。
eureka.instance.less-renewal-interval-in-seconds=30
用于定义服务续约任务的调用间隔时间
eureka.instance.lease-expiration-duration-in-seconds=90
用于定义服务失效的时间
服务消费者:消费者应用从服务注册中心获取服务列表,从而使消费者可以知道去何处调用其他服务。
获取服务:首先会发送一个rest请求给服务注册中心,获取上面注册的服务清单,erueka server 会维护一份只读的服务清单来返回给客户端,同时该缓存清单每30s更新一次。
如果修改缓存清单的更新时间,通过eureka.client.registry-fetch-interval-seconds=30参数进行修改。
服务调用:服务消费者获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息,因为有这些服务实例的详情信息,所以客户端可以根据自己的需要决定具体调用那个实例,在ribbon中会默认采用轮询的方式进行调用,从而实现负载均衡。
服务下线:在客户端程序中,当服务实例进行正常的关闭操作时,他会触发一个服务下线的rest请求给eurekaserver,告诉服务注册中心,我要下线了,服务端接收到请求后,将该服务状态设置为down,并把该下线事件传播出去。
源码分析:
@EnableDiscoveryClient 用于开启DiscoveryClient实例
com.netflix.discovery.DiscoveryClient 正真实现发现服务的类
作用:向eurekaserver 注册服务实例
向eureka server服务租约
当服务关闭期间,向eureka server取消租约
查询eureka server中的服务实例列表
eureka.client.serviceUrl.defaultZone:用于对eureka server的url列表进行配置,也就是将该应用注册到那个注册中心的地址配置。
但是在SR5中被标准不再建议使用,并且找到了替代类com.netflix.discovery.endpoint.EndpointUtils。
public static Map> getServiceUrlsMapFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
Map> orderedUrls = new LinkedHashMap<>();
String region = getRegion(clientConfig);
String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
if (availZones == null || availZones.length == 0) {
availZones = new String[1];
availZones[0] = DEFAULT_ZONE;
}
logger.debug("The availability zone for the given region {} are {}", region, Arrays.toString(availZones));
int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
String zone = availZones[myZoneOffset];
List serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
if (serviceUrls != null) {
orderedUrls.put(zone, serviceUrls);
}
int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
while (currentOffset != myZoneOffset) {
zone = availZones[currentOffset];
serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
if (serviceUrls != null) {
orderedUrls.put(zone, serviceUrls);
}
if (currentOffset == (availZones.length - 1)) {
currentOffset = 0;
} else {
currentOffset++;
}
}
if (orderedUrls.size() < 1) {
throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
}
return orderedUrls;
}
Region: public static String getRegion(EurekaClientConfig clientConfig) {
String region = clientConfig.getRegion();
if (region == null) {
region = DEFAULT_REGION;
}
region = region.trim().toLowerCase();
return region;
}
通过getRegion函数,我们可以看到它从配置中获取了一个region返回,所以一个微服务应用只属于一个region,如果不特别配置默认为default,若想自己设置,可以通过eureka.client.region属性定义.
Zone:@Override
public String[] getAvailabilityZones(String region) {
String value = this.availabilityZones.get(region);
if (value == null) {
value = DEFAULT_ZONE;
}
return value.split(",");
}
`当我们没有特别为region配置zone的时候,默认采用defaultzone,就是我们之前配置过的eureka.client.serviceUrl.defaultZone,如果要为应用指定zone,可以通过eureka.client.availability-zones属性来设置,从该函数的return内容,我们知道zone能设置多个,并用逗号隔开,因此我们可以判断region和zone是一对多的关系。``
ServicecUrls:
int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
String zone = availZones[myZoneOffset];
List serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
getEurekaSeverSericeUrls()方法位于EurekaClientConfigBean,该类是EurekaClientConfig和EurekaConstants接口的实现,用来加载配置文件的中的内容。
public List getEurekaServerServiceUrls(String myZone) {
String serviceUrls = this.serviceUrl.get(myZone);
if (serviceUrls == null || serviceUrls.isEmpty()) {
serviceUrls = this.serviceUrl.get(DEFAULT_ZONE);
}
if (!StringUtils.isEmpty(serviceUrls)) {
final String[] serviceUrlsSplit = StringUtils.commaDelimitedListToStringArray(serviceUrls);
List eurekaServiceUrls = new ArrayList<>(serviceUrlsSplit.length);
for (String eurekaServiceUrl : serviceUrlsSplit) {
if (!endsWithSlash(eurekaServiceUrl)) {
eurekaServiceUrl += "/";
}
eurekaServiceUrls.add(eurekaServiceUrl);
}
return eurekaServiceUrls;
}
未完待续。。
配置详解
eurek客户端:
服务注册类配置(EurekaClientConfigBean):
registryFetchIntervalSeconds
从eureka服务端获取注册信息的间隔时间 30s
instanceInfoReplicationIntervalSeconds
更新实例信息的变化到eureka服务端的间隔时间 30s
initialInstanceInfoReplcationIntervalSeconds
初始化实例信息到Eureka服务端的间隔时间 40
eurekaServiceUrlPollIntervalSeconds
轮询Eureka服务端地址更改的时间,单位为秒, 300s
eurekaServerReadTimeOutSeconds
读取EurekaServer的超时时间 8s
eurekaServerConnectTimeoutSeconds
连接EureakaServer的超时时间 5s
eurekaServerTotalConnections
从eureka客户端到每个eureka服务端的连接总数 200
enabled
启用eureka客户端
eurekaServerTotalConnectionsPerHost
从eureka客户端到每个eureka服务端主机的连接总数 50
eurekaConnectionIdleTimeoutSeconds
eureka服务端连接的空闲关闭时间 30s
heartbeatExecutorThreadPoolSIze
心跳连接池的初始化线程数 2
registerWithEureka
是否将自身的实例信息注册到Eureka服务端 true
preferSameZoneEureka
是否偏好使用处于相同的zone的eureka服务端 true
filterOnlyUpInstances
获取实例时是否过滤,仅保留UP状态的实例 true
fetchRegistry
是否从服务端获取注册信息 true
服务实例类配置(EurekaInstanceConfigBean):
元数据:它是Eureka客户端向服务注册中心发送注册请求时,用来描述自身服务信息的对象,其中包含了一些标准化的元数据,比如服务名称、实例名称、实例ip、实例端口等。可以通过,eureka.instance.=的格式对标准化元数据进行配置。对于自定义元数据可以通过eureka.instace.metadataMap.=的格式进行配置。
实例名配置:即InstanceInfo中的instanceId参数,他是区分同一服务中不同实例名的唯一标识。
默认配置规则 s p r i n g . c l o u d . c l i e n t . h o s t n a m e : {spring.cloud.client.hostname}: spring.cloud.client.hostname:{spring.application.name}{spring.application.instance_id:{server.port}}。
端点配置:eureka.instance.statusPageUrlPath=/ . . . . / i n f o e u r e k a . i n s t a n c e . h e a l t h C h e c k U r l P a t h = / {....}/info eureka.instance.healthCheckUrlPath=/ ..../infoeureka.instance.healthCheckUrlPath=/{…}/health
eureka.instance.hostPageUrl=/${…}/
健康检测:把eureka客户端的健康检测交给spring-boot-actuator模块的/health端点以实现更加全面的健康状态维护。
配置步骤
1、在pom中引入spring-boot-starter-actuator模块的依赖
2、在application.propertites中增加配置eureka.client.healthcheck.enabled=true
其他配置:
preferIpAddress
是否优先使用IP地址作为主机名的标识 false
appname
服务名默认使用spring.application.name的配置值
hostname
主机名