目录
Eureka介绍
Eureka由多个实例组成,这些实例可以分为两种:Eureka Server和Eureka Client。Eureka Server作为服务的注册中心,提供服务注册和发现
在下面的例子中,我们将Eureka Client再分为Provider Client(服务提供者客户端)和Consumer Client(服务消费者客户端)。
Provider Client 作为服务提供方,将自身注册到Eureka,从而使服务消费方Consumer Client能够从注册中心获取到。服务消费方会从Eureka获取注册服务列表,从而能够消费服务。然而在实际的工作中是不会出现某个微服务仅仅只对外提供服务或者说只作为服务消费方。这里仅仅是方便理解而设计成这样。
Eureak注册中心介绍
修改配置host
host文件目录:C:\Windows\System32\drivers\etc
。方便后面配置注册中心高可用集群。
127.0.0.1 www.eurekaServer7000.com
127.0.0.1 www.eurekaServer7001.com
127.0.0.1 www.eurekaServer7002.com
127.0.0.1 www.eurekaServer7003.com
1、部署Eureka Server
1.1 单机部署
1.1.1 引入依赖
<!--eureka-server 服务端 旧版本使用spring-cloud-starter-eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
1.1.2 application.yml中添加配置
server:
port: 7000
servlet:
context-path: /study
#eureka 服务端配置
eureka:
instance:
hostname: 127.0.0.1 #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。没有做集群不需要自己注册自己
fetch-registry: false #是否从Eureka Server获取注册的服务信息。false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/study/eureka/ # 单节点配置
server:
enable-self-preservation: true # 关闭自我保护模式(缺省为打开)
eviction-interval-timer-in-ms: 60000 # 续期时间,即扫描失效服务的间隔时间(缺省为60*1000ms)
1.1.4 启动类添加@EnableEurekaServer
注解
添加@EnableEurekaServer
注解,用于启动注册中心。启动成功后,访问http://127.0.0.1:7000/study/
@SpringBootApplication
@EnableEurekaServer // 开启Eureka服务注册中心
public class EurekaServer7000Start {
public static void main(String[] args) {
// 启动之后访问 http://127.0.0.1:7000/study/
SpringApplication.run(EurekaServer7000Start.class,args);
}
}
1.1.3 完整demo地址
gitee地址中的cloud-eureka-server7000
模块
1.2 单机集群部署
1.2.1 在单机的基础上修改application.yml
#eureka 服务端配置
eureka:
instance:
hostname: www.eurekaServer7001.com #eureka服务端的实例名称 修改了hosts文件,单节点直接使用127.0.0.1
client:
register-with-eureka: true #false表示不向注册中心注册自己。没有做集群不需要自己注册自己
fetch-registry: true #是否从Eureka Server获取注册的服务信息。false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
# 单节点配置 defaultZone: http://${eureka.instance.hostname}:${server.port}/study/eureka/
# 集群配置从节点的Eureka
defaultZone: http://www.eurekaServer7002.com:7002/study/eureka/,http://www.eurekaServer7003.com:7003/study/eureka/
server:
enable-self-preservation: true # 关闭自我保护模式(缺省为打开)
eviction-interval-timer-in-ms: 60000 # 续期时间,即扫描失效服务的间隔时间(缺省为60*1000ms)
-
与单机部署的差别
eureka.client.register-with-eureka:true
:向注册中心注册自己。
eureka.client.fetch-registry:true
:从Eureka Server获取注册的服务信息设置为true。
eureka.client.service-url.defaultZone
:配置集群中其他机器的地址,用逗号分隔。
例如7001端口的集群,就需要配置7002和7003机器的地址(如果你将本机也配置,也是可以的)。 -
其他事项说明
eureka.client.register-with-eureka
和eureka.client.fetch-registry
即使都设置为false。集群中所有注册中心节点的服务列表也会是一致的。因为当一个Eureka Client在向集群中注册的时候,当前集群会将注册表根据eureka.client.service-url.defaultZone
同步发送到其他的集群,进行信息同步。
只不过eureka.client.register-with-eureka
配置为false。集群的其他节点会出现在该节点的【不可用副本】中:
1.2.2 配置注意事项
注意:必须使用配置的host域名,否则使用127.0.0.1或者是localhost,集群会无法通信。
原因:
public class PeerEurekaNodes {
........省略.......
protected List<String> resolvePeerUrls() {
InstanceInfo myInfo = applicationInfoManager.getInfo();
String zone = InstanceInfo.getZone(clientConfig.getAvailabilityZones(clientConfig.getRegion()), myInfo);
List<String> replicaUrls = EndpointUtils
.getDiscoveryServiceUrls(clientConfig, zone, new EndpointUtils.InstanceInfoBasedUrlRandomizer(myInfo));
int idx = 0;
while (idx < replicaUrls.size()) {
if (isThisMyUrl(replicaUrls.get(idx))) { // isThisMyUrl这个方法,如果传入的url地址的域名部分和本节点相同,则会返回true
replicaUrls.remove(idx);
} else {
idx++;
}
}
return replicaUrls;
}
........省略.......
}
1.2.3 启动集群
idea修改运行并行运行,通过3个不同端口的配置文件启动3个集群
1.2.4 完整demo地址
gitee地址中的cloud-eureka-server7001-3
模块
1.3 多机集群部署
http://www.heartthinkdo.com/?p=2968
2、部署Eureka Client
2.1、部署provider-client
2.1.1 引入依赖
<!--eureka-client 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.1.2 application.yml中添加配置
eureka.client.service-url.defaultZone
:需要配置所有的注册中心地址。
# eureka 客户端配置
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
service-url:
# 单点注册中心配置
defaultZone: http://127.0.0.1:7000/study/eureka
# 注册中心集群
#defaultZone: http://www.eurekaServer7001.com:7001/study/eureka/,http://www.eurekaServer7002.com:7002/study/eureka/,http://www.eurekaServer7003.com:7003/study/eureka/
healthcheck:
enabled: true # 开启健康检查(依赖spring-boot-starter-actuator)
instance:
appName: ${spring.application.name} # 在eureka注册的应用程序的名称,不配置默认使用spring.application.name
instance-id: provider-payment-${server.port} # 服务名称
hostname: localhost # 实例的主机名,其他模块调用时,使用该主机名调用作为域名调用该服务
prefer-ip-address: true # 在获取hostname时优先使用ip-address
ip-address: 127.0.0.1 # 注册的实例ip,当使用的是docker容器部署时,则需要配置宿主机的ip,则可以启用用该配置
lease-renewal-interval-in-seconds: 30 # 向服务端发送心跳的时间间隔,即服务续约间隔时间(缺省为30s)
lease-expiration-duration-in-seconds: 90 # 发呆时间,即服务续约到期时间(缺省为90s)
依照这个配置3台服务提供者客户端:
2.1.3 添加插入和查询的测试接口
// 插入数据 127.0.0.1:8001/study/provider/payment/insertPayment?serial=yanlei
@PostMapping(value = "/provider/payment/insertPayment")
public CommonResult<Integer> insertPayment(@RequestBody Payment payment)
{
int result = paymentService.insertPayment(payment);
log.info("*****插入操作返回结果:" + result);
if(result > 0)
{
return new CommonResult<>(200,"插入数据库成功,端口:"+serverPort,result);
}else{
return new CommonResult<>(444,"插入数据库失败,端口:"+serverPort,null);
}
}
// 根据id查询 127.0.0.1:8001/study/provider/payment/get/1
@GetMapping(value = "/provider/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
{
log.info("参数为id:"+id);
Payment payment = paymentService.getPaymentById(id);
log.info("*****查询结果:{}",payment);
if (payment != null) {
return new CommonResult<>(200,"查询成功,端口:"+serverPort,payment);
}else{
return new CommonResult<>(444,"没有对应记录,查询ID: "+id+",端口:"+serverPort,null);
}
}
2.1.4 启动类添加@EnableEurekaClient注解,并启动3台服务提供者客户端
@SpringBootApplication
@EnableEurekaClient
public class EurekaPayment8001_3Start {
public static void main(String[] args) {
SpringApplication.run(EurekaPayment8001_3Start.class, args);
}
}
启动后可以在注册中心看到:
2.1.5 完整demo地址
gitee地址中的cloud-eureka-provider-payment8001-3
模块
2.2、部署consumer-client
2.2.1 引入依赖
<!--eureka-client 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.2.2 application.yml中添加配置
eureka.client.service-url.defaultZone
:需要配置所有的注册中心地址。
eureka.client.register-with-eureka:true
:将自己注册进注册中心
eureka.client.fetch-registry:true
:从注册中心抓取注册的服务列表
eureka.client.registry-fetch-interval-seconds:30
:从注册中心获取注册表信息的间隔时间,单位:秒
# eureka 客户端配置
eureka:
client:
register-with-eureka: true #表示是否将自己注册进EurekaServer默认为true。
fetchRegistry: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
registry-fetch-interval-seconds: 30 #从注册中心获取注册表信息的间隔时间,单位:秒
service-url:
# 单点注册中心配置
#defaultZone: http://127.0.0.1:7000/study/eureka
# 注册中心集群
defaultZone: http://www.eurekaServer7001.com:7001/study/eureka/,http://www.eurekaServer7002.com:7002/study/eureka/,http://www.eurekaServer7003.com:7003/study/eureka/
instance:
instance-id: consumer-order-80 #服务名称
prefer-ip-address: true #访问路径可以显示IP地址
lease-renewal-interval-in-seconds: 30 # 向服务端发送心跳的时间间隔,即服务续约间隔时间(缺省为30s)
lease-expiration-duration-in-seconds: 90 # 发呆时间,即服务续约到期时间(缺省为90s)
2.2.3 模块编码,通过Robin进行客户端负载均衡,调用服务提供者。
2.2.3.1 配置Robin负载均衡规则:随机
/**
* @Description 自定义ribbon的轮询规则,不能配置在@ComponentScan注解的扫描包下,不然,会对所有的@RibbonClient生效
*/
@Configuration
public class MyRobinRuleConfig {
@Bean
public IRule myRule(){
// ribbon定义好的轮询规则:
// com.netflix.loadbalancer.RoundRobinRule 轮询
// com.netflix.loadbalancer.RandomRule 随机
// com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
// WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
// BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
// AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
// ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
return new RandomRule();
}
}
轮询规则的接口实现继承关系:
2.2.3.2 配置RestTemplate
使用@LoadBalanced
注解赋予RestTemplate负载均衡的能力。关于@LoadBalanced原理,查看文章中的@LoadBalanced 注解作用原理简单讲解
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
// 不加 @LoadBalanced 注解 报java.net.UnknownHostException: PROVIDER-PAYMENT-8001-3异常
public RestTemplate createRestTemplate(){
return new RestTemplate();
}
}
2.2.3.3 添加Controller调用服务提供者
这里针对RestTemplate的写了Object和Entity两种调用方式调用服务提供者。
并结合Robin,可以直接通过微服务集群的名称(spring.application.name)的大写(这个是因为eureka服务注册到注册中心时会自动变为大写,之后使用zookeeper时,就不会。)直接调用服务提供者。
@RestController
@Slf4j
public class OrderController {
// 固定服务调用地址。若使用固定地址调用,则不能使用ribbon的负载均衡的功能,
// 所以需要将com.study.springcloud.config.ApplicationContextConfig.createRestTemplate方法上面的@LoadBalanced注解删掉,
// 否则报异常"No instances available for 127.0.0.1"
//public static final String PAYMENT_SRV = "http://127.0.0.1:8001/study";
// 通过在eureka上注册过的微服务名称调用
public static final String PAYMENT_SRV = "http://PROVIDER-PAYMENT-8001-3/study";
@Resource
private RestTemplate restTemplate;
// 127.0.0.1/study/consumer/payment/create?serial=xiaohong
@GetMapping("/consumer/payment/create") //客户端用浏览器是get请求,但是底层实质发送post调用服务端8001
public CommonResult create(Payment payment)
{
return restTemplate.postForObject(PAYMENT_SRV + "/provider/payment/insertPayment",payment,CommonResult.class);
}
// 127.0.0.1/study/consumer/payment/createResultEntity?serial=xiaohong
@GetMapping("/consumer/payment/createResultEntity")
public CommonResult createResultEntity(Payment payment)
{
ResponseEntity<CommonResult> result = restTemplate.postForEntity(PAYMENT_SRV + "/provider/payment/insertPayment",payment,CommonResult.class);
if(result.getStatusCode().is2xxSuccessful()){
return result.getBody();
} else {
return new CommonResult(-999,"操作失败");
}
}
// 127.0.0.1/study/consumer/payment/get/1
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getPayment(@PathVariable Long id)
{
return restTemplate.getForObject(PAYMENT_SRV + "/provider/payment/get/"+id, CommonResult.class, id);
}
// 127.0.0.1/study/consumer/payment/getPaymentResultEntity/1
@GetMapping("/consumer/payment/getPaymentResultEntity/{id}")
public CommonResult getPaymentResultEntity(@PathVariable Long id)
{
ResponseEntity<CommonResult> result = restTemplate.getForEntity(PAYMENT_SRV + "/provider/payment/get/"+id,CommonResult.class);
if(result.getStatusCode().is2xxSuccessful()){
return result.getBody();
} else {
return new CommonResult(-999,"操作失败");
}
}
}
2.2.4 启动类
添加@EnableEurekaClient
注解启动服务发现
添加@RibbonClients
注解,给指定的服务提供者集群设置轮询规则,个不同的服务使用不同的规则,则在@RibbonClients中配置多个@RibbonClient
@SpringBootApplication
@EnableEurekaClient
// 修改针对PROVIDER-PAYMENT-8001-3服务的轮训规则,多个不同的服务使用不同的规则,则在@RibbonClients中配置多个@RibbonClient
@RibbonClients(value = {@RibbonClient(name="PROVIDER-PAYMENT-8001-3",configuration = MyRobinRuleConfig.class)})
public class EurekaOrder80Start {
public static void main(String[] args) {
SpringApplication.run(EurekaOrder80Start.class,args);
}
}
启动后可以在注册中心看到:
2.2.5 负载均衡验证
多次调用http://127.0.0.1/study/consumer/payment/get/1
,可以观察端口不同,说明负载均衡起作用,对3台服务提供者进行随机调用
2.2.6 完整demo地址
gitee地址中的cloud-eureka-consumer-order80
模块
3、服务注册中心Eureka Server功能介绍
3.1 Eureka管理控制台参数说明
3.2 保存所有客户端注册的服务信息
这个不用多说,作为服务注册中心的基础功能,注册中心需要保存所有服务的注册信息。但是这些注册信息仅仅只会保存在缓存中,eureka并不会持久化这些信息。
3.3 剔除失效服务
有些时候服务实例因为一些特殊原因而不能正常工作,而注册中心并未收到“服务下线”的请求。为了从服务列表中将这些无法提供服务的实例剔除,Eureka Server在启动的时候会创建一个定时任务,默认每隔60秒会扫描一次,将服务清单中超时( eureka.instance.lease-expiration-duration-in-seconds=90秒
)没有续约的服务剔除出去。
3.4 自我保护机制
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护机制”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(在15分钟内超过85%的客户端节点都没有正常的心跳),那么Eureka就认为客户端与注册中心出现了网络故障,那么这个节点就会进入自我保护模式。
一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
4、服务提供者客户端provider-client功能介绍
4.1 服务注册
服务提供者在启动的时候会通过发送请求将自己注册到Eureka Server上,同时带上了自身服务的一些元数据信息。
客户端也可以选择是否注册到Eureka Server。通过eureka.client.register-with-eureka
控制。true表示将当前客户端注册进注册中心,默认为true。
4.2 服务续约
在注册完服务后,服务提供者会维持一个心跳用来持续告诉Eureka Server我还活着,以防止Eureka Server的“剔除任务”将服务实例从服务列表中排除出去,我们将该操作称为服务续约。
关于服务续约有两个重要的属性,我们可以关注并根据需要来进行调整:
eureka.instance.lease-renewal-interval-in-seconds=30
:参数用于定义服务续约任务的调用间隔时间,默认30秒
eureka.instance.lease-expiration-duration-in-seconds=90
:用于定义服务失效的时间,默认90秒。服务器自收到最后一次心跳后会再等待该配置的时间,若还是没有收到心跳,则会将该服务剔除。
一个服务下线后,在不触发自我保护机制的情况下最多经过eureka.instance.lease-renewal-interval-in-seconds
+eureka.instance.lease-expiration-duration-in-seconds
+eureka.server.eviction-interval-timer-in-ms
=心跳间隔时间
+注册中心自收到最后一次心跳后等待的时间
+扫描失效服务的间隔时间
= 30s
+90s
+60000ms
=180秒
会在注册中心被剔除。
然而一个服务在注册中心被剔除后,并不会立马在服务消费客户端被剔除。因为缓存在本地的服务列表每隔30秒更新才更新一次。
5、服务消费者客户端consumer-client功能介绍
5.1 服务列表同步
provider-client会从注册中心拉取服务列表。因为为了降低注册中心的压力,该服务列表会缓存在客户端,每隔一段时间从注册中心拉取更新本地服务列表缓存,间隔时间通过eureka.client.registry-fetch-interval-seconds=30
配置(单位:秒)。
通过eureka.client.fetch-registry
控制是否从EurekaServer抓取已有的注册信息,默认为true
5.2 服务调用
服务消费者从注册中心拉取服务提供者列表后,就可以调用到服务提供者provider-client。
并通过Robin对3个服务提供者进行负载均衡。
6、自己关于Eureka配置的理解
6.1 client配置
-
eureka.client.register-with-eureka
表示是否将自己注册到Eureka Server,供其他服务发现 -
eureka.client.fetch-registry
表示此客户端是否应从 eureka 注册中心获取所有eureka服务的注册表信息 -
eureka.client.service-url.defaultZone
eureka.client.service-url.defaultZone
:eureka服务器通信的完全限定URL列表(按我自己的理解,就是需要需其他eureka注册中心进行通信的服务列表)
单机eureka.client.service-url.defaultZone
可以使用127.0.0.1或localhost,因为单机不用和其他节点进行通信。也可以不填,不填默认为http://localhost:8761/eureka/
,这个可以在EurekaClientConfigBean类中看到:
当单机情况下eureka.client.service-url.defaultZone
如果配置本机地址,【注册副本】项为空,如果不填则为http://localhost:8761/eureka/
。
-
eureka.client.healthcheck.enabled
开启健康检查(依赖spring-boot-starter-actuator)
6.2 instance配置
-
eureka.instance.appname和eureka.instance.instance-id
eureka.instance.appname
和eureka.instance.instance-id
在eureka注册中心对应位置的名称: -
eureka.instance.prefer-ip-address
eureka.instance.prefer-ip-address
配置为true后可以将鼠标悬浮,查看ip:
-
eureka.instance.lease-renewal-interval-in-seconds
客户端向注册中心发送心跳的时间间隔,即服务续约间隔时间(缺省为30s) -
eureka.instance.lease-expiration-duration-in-seconds
eureka 服务器自收到最后一次收到该客户端的心跳后删除此实例的等待时长(以秒为单位),并通过禁止流向此实例的流量
6.3 server配置
- eureka.server.enable-self-preservation
自我保护模式是否开启(上面有对自我保护进行说明) - eureka.server.eviction-interval-timer-in-ms
扫描失效服务的间隔时间
6.4 关于配置自己的理解
服务端:
1、服务端保护机制默认开启,通过eureka.server.enable-self-preservation设置
2、在关闭保护机制后,服务端会间隔60秒。清理失效的服务(没有收到续约心跳的服务),可以通过eureka.server.eviction-interval-timer-in-ms设置
客户端:
1、客户端每隔30秒向服务端发送一次续约心跳,通过eureka.instance.lease-renewal-interval-in-seconds设置
2、Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务。通过eureka.instance.lease-expiration-duration-in-seconds设置
起初,看到这两个配置的描述,很傻的认为这个配置应该放在服务端,为什么将其配置在客户端呢?
① 心跳是一个发送与接收的过程,客户端也可以记录心跳是否发送成功,来判断服务端是否收到心跳。
② 如果配置在服务端,则针对的是所有客户端生效,这样做不够灵活,将这个配置放在客户端,则每个客户端都可以根据自身需要配置。
③ eureka是使用的客户端的负载均衡,在客户端剔除掉实例后,即可关闭对该节点的调用。
④ eureka服务端若是开启的保护模式,则注册后,服务不会被剔除,即使该服务已经下线
7、EurekaServer 关键API
Operation | HTTP action |
---|---|
注册新服务实例或者修改实例基本信息(就是InstanceInfo类) | POST /eureka/apps/appID |
撤销删除服务实例 | DELETE /eureka/apps/appID/instanceID |
实例心跳 | PUT /eureka/apps/appID/instanceID |
查询所有服务实例列表 | GET /eureka/apps |
查询某个服务的实例列表 | GET /eureka/apps/appID |
查询某个服务某个实例信息 | GET /eureka/apps/appID/instanceID |
查询某个实例信息 | GET /eureka/instances/instanceID |
将某个实例设置为下线,这个和删除不同,如果你手动调用删除,但如果客户端还活着,定时任务还是会将实例注册上去。但是改成这个状态,定时任务更新不了这个状态 | PUT /eureka/apps/appID/instanceID/status?value=OUT_OF_SERVICE |
下线状态恢复 | DELETE /eureka/apps/appID/instanceID/status?value=UP |
更新元数据(这个不是InstanceInfo,是自己可以往里面自定义的数据) | PUT /eureka/apps/appID/instanceID/metadata?key=value |
查询某个VIP下的所有实例 | GET /eureka/vips/vipAddress |
查询某个SVIP下的所有实例 | GET /eureka/svips/svipAddress |
例如: