Eureka有个自我保护机制。
这里的自我保护机制不是对eureka来说的,而是对每个服务来说。从上一篇文章中,我们知道在默认情况下,Eureka Server在一定时间内,没有接收到某个微服务心跳,会将某个微服务注销(90S)。但是当网络故障时,微服务与Server之间无法正常通信,上述行为就非常危险,因为微服务正常,不应该注销。
Eureka Server通过自我保护模式来解决整个问题,当Server在短时间内丢失过多客户端时,那么Server会进入自我保护模式,会保护注册表中的微服务不被注销掉。当网络故障恢复后,退出自我保护模式。思想:宁可保留健康的和不健康的,也不盲目注销任何健康的服务。
自我保护机制在每分钟续约数量小于客户端总数的85%时会触发保护机制。也就是说假设:服务实例数:10个,期望每分钟续约数:10 * 2=20,期望阈值:20*0.85=17,自我保护少于17时 触发。
默认是把他开启,认为是网络的抖动造成的服务不可用,可以防止错误的注销掉微服务。还有另外一种原因,服务真的不可用了,但是对于eureka来讲,不知道服务是否可用。这里用到了Actuator监控,在服务端自己控制本服务是否出现异常,再给Eureka server端。
下面的都是在provider机器中加入。
下面是Actuator的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
但是,spring Boot 2.0 的Actuator只暴露了health和info端点,提供的监控信息无法满足我们的需求,无法知道内存、down机的信息。所以需要在properties配置文件中额外配置一些信息,将actuator完善。
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
开启手动控制,将自己的健康状态传给server。Health是UP或者DOWM传给server。
eureka.client.healthcheck.enabled=true
再编写一个测试的seervice类
@Service
public class HealthStatusService implements HealthIndicator {
private Boolean status = true;
public void setStatus(Boolean status) {
this.status = status;
}
@Override
public Health health() {
// TODO Auto-generated method stub
if(status)
return new Health.Builder().up().build();
return new Health.Builder().down().build();
}
public String getStatus() {
// TODO Auto-generated method stub
return this.status.toString();
}
}
再controller层也需要编写代码。
@Autowired
HealthStatusService healthStatusSrv;
@GetMapping("/health")
public String health(@RequestParam("status") Boolean status) {
healthStatusSrv.setStatus(status);
return healthStatusSrv.getStatus();
}
然后输入网址,将status=false,测试一下。
看下provider的健康状态。可以看到为down的情况。
看下Eureka server状态。
这里是在provider手动的把自己服务从eureka中下线了,如果发现异常的话(及时上报自己的健康状态)。而不是用心跳的方式去确认,心跳的方式eureka还会有自我保护机制,小于心跳的85%认为是或者是大规模网络瘫痪,而不是服务真正的不可用的情况。
这里测试完毕。
在Eureka server加入spring security依赖,需要在properties加入:
spring.security.user.name=admin
spring.security.user.password=123
然后重启server。
这里注意,当provider把自己注册到eureka时,会报错,因为spring security自带了跨域名攻击,所以需要在eure server加个配置类。
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.csrf().disable();
super.configure(http);
}
}
这个时候,可以运行server机器,已经配置完毕。在运行provider时,注意此时已经加入了spring security,所以provider需要加用户名与密码。
eureka.client.service-url.defaultZone=http://admin:123@euk1.com:7001/eureka/
以后所有的provider或者consumer都需要加上面的用户名与密码,否则会报错。
什么是客户端负载均衡?什么是服务端负载均衡?
假设我们有个服务列表,记录了后面服务器所有的IP + 端口 + 服务 + 状态。个服务列表在客户端,client根据自己的负载均衡策略,轮询或者随机访问IP,也就是Client拿着服务列表,去选择一个服务器去访问。这就是客户端的负载均衡。
而服务端的负载均衡,也有个列表,只不过列表在服务端,client只需要调一个IP就行,而不是像客户端负载均衡,有多个IP。比如nginx。这种方式对客户端没有感知。
Eureka client 与 ribbon 与restTemplate都是在一个机器上的。
ribbon从哪里拉取服务呢?她是从eureka client中拉取,所以需要集成eureka client。ribbon是从服务列表中choose一个服务器。Choose方法有负载均衡策略。6个服务器提供相同服务,默认的是区域权衡策略。RestTemplate 从ribbon里面要具体的一台服务器并向其发送http请求。
这里设了两台provider机器,一台端口80,一台端口81,然后consumer从erureka中去获取这两台机器,并发送请求。下面是consummer的测试代码。
@Autowired
RestTemplate restTemplate;
@Autowired
LoadBalancerClient lb;
@GetMapping("/client6")
public String client6(){
//rebbon完成客户端的负载均衡,由于provider有多个,后面由策略选择,并且过滤掉挂的机器。
ServiceInstance instance = lb.choose("provider");
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/getHi";
String resStr = restTemplate.getForObject(url,String.class);
return resStr;
}
这里的restTemplate将其设置了单例模式,所以在配置类中创建restTemplate,这里在启动类下设置了。
@Bean
@LoadBalanced
RestTemplate gerRestTemplate(){
return new RestTemplate();
}
怎么可以不用拼URL呢?直接provider/getHi
然后配置类中配,加个loadbalanced,这样把ribbon里的url转换成真正的服务器地址了。
@GetMapping("/client7")
public String client7(){
String url = "http://provider/getHi";
String resStr = restTemplate.getForObject(url,String.class);
return resStr;
}
如果把eureka关了,那么ribbon需要从本地获取。配置properties:
Ribbon.eureka.enabled=false
Ribbon.listOfServers=localhost:80