Eureka注册中心 1、什么是注册中心 打个比方,注册中心就好比手机中的通讯录,所有的联系人的联系方式就在这个通讯录中储存。当需要打电话的时候,只需要查询通讯录就可以获取某个联系人的联系方式。
注册中心类似于通信录,只不过注册中心储存的不是联系人的联系方式,而是每个服务的信息,从注册中心获取服务就好比通讯录的查询联系人,向注册中心注册服务,就好比通讯录的保存联系人,先有注册,才能查找。
注:注册中心只不过是用来注册和获取服务,并不会用来调取服务,具体的调取服务有获取服务方实现。就好比通讯录只负责储存和查询联系人,查到联系人后使用拨号软件拨打查询到的联系人电话。
2、CAP原则和BASE理论 2.1、CAP原则
名称
描述
Consistency
一致性 。也叫做原子性。系统在执行某些操作后数据仍然处于一致的状态。在分布式系统中,更新操作执行成功后所有的用户都应该读到最新的值,这样的系统被认为是具有强一致性的。等同于所有节点访问同一份最新的数据副本。
Availability
可用性 。每一个操作总是能够在一定的时间内返回结果,这里需要注意的是”一定时间内”和”返回结果”。一定时间内指的是在可以容忍的范围内返回结果,结果可以是成功或者是失败,且不保证获取的数据为最新数据。
Partition tolerance
分区容错性 。分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。
CAP原则又称CAP定理。值得是在系统中,Consistency
、Availability
和Partition tolerance
三者不可兼得。最多只能同时满足其中两者。
CAP 由 Eric Brewer 在 2000 年 PODC 会议上提出。该猜想在提出两年后被证明成立,成为我们熟知的 CAP
定理。CAP
三者不可兼得。
CA:一致性和可用性。如果同时要满足一致性和可用性,那么只有单体应用可以实现。因为如果是分布式应用的话,如果要保证多个系统的一致性,可定要消耗时间。那么可用性肯定就达不到要求。只有单体应用,可以满足CA
。
CP:一致性和分区容错性。这种典型的案例就是涉及到金钱的系统。涉及到金钱的系统肯定要保证数据一致,在分布式中多台服务器要保证数据一致,肯定不能再一定的时间内返回数据。好比在进行金钱转账的时候,宁愿牺牲可用性,也要保证数据一致性。
AP:可用性和分区容容错性。这种的经典案例就是抢购性的系统。例如春运的火车票,有时候明明看到还有票,但是在下单的时候却被告知没票了,这就是因为需要满足可用性,在一定的时间内返回数据,从而放弃了数据的一致性。但是这种情况在最后会有一个最终一致性。好比购票,显示的是还有车票,但是下单时却显示失败,就是因为最终一致性。在最后的时候会进行一致性判断。
如今,对于大多数互联网应用场景,主机众多,部署分散,并且规模也越来越大,节点只会越来越多,所以节点故障、网络故障时常态,分区容错性也就成为了一个分布式系统必然要面对的问题。那么就只能在C和A之前进行取舍。但是对于设计金钱的系统却不同,涉及到金钱的是非常重要的,宁愿牺牲A,也要保证C。如果出现机器故障的话,宁愿停止服务。
没有最好的策略,只有最符合当前系统的策略。
2.2、BASE理论 CAP 理论已经提出好多年了,难道真的没有办法解决这个问题吗?也许可以做些改变。比如 C 不必使用那么强的一致性,可以先将数据存起来,稍后再更新,实现所谓的 “最终一致性”。这个思路又是一个庞大的问题,同时也引出了第二个理论 BASE 理论。
BASE全称为 Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语句的缩写,来自 ebay 的架构师提出。
BASE 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大型互联网分布式实践的总结,是基于 CAP 定理逐步演化而来的。其核心思想是:既然无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
2.2.1、Basically Available(基本可用) 基本可用是指分布式系统在出现故障时,允许损失部分可用性(例如响应时间、功能上的可用性)。需要注意的是,基本可用绝不等价于系统不可用。
响应时间上的丢失:例如原先查询数据只需要0.5秒,但是现在由于机器故障可以运行返回响应时间变为1~2秒。
功能上的可用性:例如双十一购物的时候,为了满足非常庞大的流量冲击,为了保证系统的大方向的稳定,允许对一些服务降级处理(缩减服务器集群,给别的访问量大的使用)或者停止一些非必要的服务。
2.2.2、Soft state(软状态) 相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种 “硬状态”。
软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据会有多个副本,允许不同副本数据同步的延时就是软状态的体现。
2.2.3、Eventually consistent(最终一致性) 系统不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保持数据一致性。从而达到数据的最终一致性。这个时间期限取决于网络延时,系统负载,数据复制方案设计等等因素。
实际上,不只是分布式系统使用最终一致性,关系型数据库在某个功能上,也是使用最终一致性的,比如备份,数据库的复制都是需要时间的,这个复制过程中,业务读取到的值就是旧值。当然,最终还是达成了数据一致性。这也算是一个最终一致性的经典案例。
3、为什么需要注册中心 在分布式系统中,不仅仅是需要在注册中心找到服务和服务地址的映射关系那么简单,还需要考虑很多的复杂问题:
服务注册后,如何被及时的发现
服务宕机后,如何及时处理下线
服务如何有效的水平扩展
服务发现时,如何进行路由
服务异常时,如何进行降级
注册溪红心如果实现自身的高可用
这些问题的解决都依赖于注册中心。简单看,注册中心的功能有点类似于 DNS 服务器或者负载均衡器,而实际上,注册中心作为微服务的基础组件,可能要更加复杂,也需要更多的灵活性和时效性。当然上面的问题,单单使用注册中心是无法完成的,还需要使用SpringCloud的其他组件共同完成。
注册中心解决了一下问题:
4、Eureka 4.1、Eureka介绍 Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
4.2、Eureka注册中心的各个角色
Eureka Server: 通过 Register、Get、Renew 等接口提供服务的注册和发现。
Service Provider(Eureka Client): 服务提供方,把自身的服务实例注册到 Eureka Server 中。
Service Consumer(Eureka Client): 服务调用方,通过 Eureka Server 获取服务列表,消费服务。
4.3、Eureka入门案例 4.3.1、创建Mave聚合项目
server01:注册中心
server02:注册中心(后期搭建集群版准备)
provider:服务提供者
consumer:服务消费者
4.3.2、添加依赖,配置文件 4.3.2.1、父项目 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.0.RELEASE</version > </parent > <properties > <spring-cloud.version > Hoxton.SR5</spring-cloud.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-dependencies</artifactId > <version > ${spring-cloud.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement >
4.3.2.2、server02 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-server</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > </dependencies >
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 server: port: 8761 spring: application: name: eureka-server eureka: instance: hostname: localhost client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
如果是单节点需要把register-with-eureka
和fetch-registry
设置为false,否则会报错
4.3.3、单节点启动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package cn.yanghuisen;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication @EnableEurekaServer public class EurekaServer01Application { public static void main (String[] args) { SpringApplication.run(EurekaServer01Application.class); } }
访问:http://localhost:8761/
4.3.4、高可用(集群) server02
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-server</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > cn.yanghuisen</groupId > <artifactId > eureka-server01</artifactId > <version > 1.0-SNAPSHOT</version > <scope > compile</scope > </dependency > </dependencies >
server02:application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 server: port: 8762 spring: application: name: eureka-server eureka: instance: hostname: eureka02 client: service-url: defaultZone: http://localhost:8761/eureka/
server01:application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 server: port: 8761 spring: application: name: eureka-server eureka: instance: hostname: eureka01 client: service-url: defaultZone: http://localhost:8762/eureka/
server02:启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package cn.yanghuisen;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication @EnableEurekaServer public class EurekaServer02Application { public static void main (String[] args) { SpringApplication.run(EurekaServer01Application.class); } }
访问:http://localhost:8761/ 或者 http://localhost:8762/
4.3.5、显示IP+端口 (server01和server02):application.yml
1 2 3 4 eureka: instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${server.port}
4.3.6、provider pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-client</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > </dependencies >
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server: port: 7070 spring: application: name: service-provider eureka: instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${server.port} client: service-url: defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.yanghuisen.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;@Data @NoArgsConstructor @AllArgsConstructor public class Product implements Serializable { private Integer id; private String productName; private Integer productNum; private Double productPrice; }
控制层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package cn.yanghuisen.controller;import cn.yanghuisen.pojo.Product;import cn.yanghuisen.service.ProductService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @GetMapping("/list") public List<Product> selectProductList () { return productService.selectProductList(); } }
服务类:接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package cn.yanghuisen.service;import cn.yanghuisen.pojo.Product;import java.util.List;public interface ProductService { List<Product> selectProductList () ; }
服务类:实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.example.service.impl;import com.example.pojo.Product;import com.example.service.ProductService;import org.springframework.stereotype.Service;import java.util.Arrays;import java.util.List;@Service public class ProductServiceImpl implements ProductService { @Override public List<Product> selectProductList () { return Arrays.asList( new Product(1 , "华为手机" , 2 , 5888D ), new Product(2 , "联想笔记本" , 1 , 6888D ), new Product(3 , "小米平板" , 5 , 2666D ) ); } }
启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package cn.yanghuisen;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class ServiceProviderApplication { public static void main (String[] args) { SpringApplication.run(ServiceProviderApplication.class, args); } }
访问:http://localhost:8761/ 或者 http://localhost:8762/。查看服务提供者是否已经注册到注册中心
4.3.7、consumer pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-client</artifactId > <exclusions > <exclusion > <groupId > com.fasterxml.jackson.dataformat</groupId > <artifactId > jackson-dataformat-xml</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > </dependencies >
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 server: port: 9090 spring: application: name: service-consumer eureka: client: register-with-eureka: false registry-fetch-interval-seconds: 10 service-url: defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.yanghuisen.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;@Data @NoArgsConstructor @AllArgsConstructor public class Product implements Serializable { private Integer id; private String productName; private Integer productNum; private Double productPrice; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package cn.yanghuisen.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;import java.util.List;@Data @NoArgsConstructor @AllArgsConstructor public class Order implements Serializable { private Integer id; private String orderNo; private String orderAddress; private Double totalPrice; private List<Product> productList; }
消费服务:接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package cn.yanghuisen.service;import cn.yanghuisen.pojo.Order;public interface OrderService { Order selectOrderById (Integer id) ; }
控制层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package cn.yanghuisen.controller;import cn.yanghuisen.pojo.Order;import cn.yanghuisen.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; @GetMapping("/{id}") public Order selectOrderById (@PathVariable("id") Integer id) { return orderService.selectOrderById(id); } }
消费服务:方式一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package cn.yanghuisen.service.impl;import cn.yanghuisen.pojo.Order;import cn.yanghuisen.pojo.Product;import cn.yanghuisen.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.core.ParameterizedTypeReference;import org.springframework.http.HttpMethod;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import org.springframework.web.client.RestTemplate;import java.util.List;@Service public class OrderServiceImpl implements OrderService { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @Override public Order selectOrderById (Integer id) { return new Order(id, "order-001" , "中国" , 31994D , selectProductListByDiscoveryClient()); } private List<Product> selectProductListByDiscoveryClient () { StringBuffer sb = null ; List<String> serviceIds = discoveryClient.getServices(); if (CollectionUtils.isEmpty(serviceIds)) return null ; List<ServiceInstance> serviceInstances = discoveryClient.getInstances("service-provider" ); if (CollectionUtils.isEmpty(serviceInstances)) return null ; ServiceInstance si = serviceInstances.get(0 ); sb = new StringBuffer(); sb.append("http://" + si.getHost() + ":" + si.getPort() + "/product/list" ); ResponseEntity<List<Product>> response = restTemplate.exchange( sb.toString(), HttpMethod.GET, null , new ParameterizedTypeReference<List<Product>>() {}); return response.getBody(); } }
消费服务:方式二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package cn.yanghuisen.service.impl;import cn.yanghuisen.pojo.Order;import cn.yanghuisen.pojo.Product;import cn.yanghuisen.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;import org.springframework.core.ParameterizedTypeReference;import org.springframework.http.HttpMethod;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Service;import org.springframework.web.client.RestTemplate;import java.util.List;@Service public class OrderServiceImpl implements OrderService { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @Override public Order selectOrderById (Integer id) { return new Order(id, "order-001" , "中国" , 31994D , selectProductListByLoadBalancerClient()); } private List<Product> selectProductListByLoadBalancerClient () { StringBuffer sb = null ; ServiceInstance si = loadBalancerClient.choose("service-provider" ); if (null == si) return null ; sb = new StringBuffer(); sb.append("http://" + si.getHost() + ":" + si.getPort() + "/product/list" ); ResponseEntity<List<Product>> response = restTemplate.exchange( sb.toString(), HttpMethod.GET, null , new ParameterizedTypeReference<List<Product>>() {}); return response.getBody(); } }
消费服务:方式三
启动类
1 2 3 4 5 @Bean @LoadBalanced public RestTemplate restTemplate () { return new RestTemplate(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package cn.yanghuisen.service.impl;import cn.yanghuisen.pojo.Order;import cn.yanghuisen.pojo.Product;import cn.yanghuisen.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;import org.springframework.core.ParameterizedTypeReference;import org.springframework.http.HttpMethod;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Service;import org.springframework.web.client.RestTemplate;import java.util.List;@Service public class OrderServiceImpl implements OrderService { @Autowired private RestTemplate restTemplate; @Override public Order selectOrderById (Integer id) { return new Order(id, "order-001" , "中国" , 31994D , selectProductListByLoadBalancerAnnotation()); } private List<Product> selectProductListByLoadBalancerAnnotation () { ResponseEntity<List<Product>> response = restTemplate.exchange( "http://service-provider/product/list" , HttpMethod.GET, null , new ParameterizedTypeReference<List<Product>>() {}); return response.getBody(); } }
访问:http://localhost:9090/order/1
4.3.8、自我保护 一般情况下,服务在 Eureka 上注册后,会每 30 秒发送心跳包,Eureka 通过心跳来判断服务是否健康,同时会定期删除超过 90 秒没有发送心跳的服务。
有两种情况会导致 Eureka Server 收不到微服务的心跳
微服务自身的原因
微服务与 Eureka 之间的网络故障
自我保护模式
Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,同时提示一个警告。这种算法叫做 Eureka Server 的自我保护模式。
为什么要启动自我保护
因为同时保留”好数据”与”坏数据”总比丢掉任何数据要更好,当网络故障恢复后,这个 Eureka 节点会退出”自我保护模式”。
Eureka 还有客户端缓存功能(也就是微服务的缓存功能)。即使 Eureka 集群中所有节点都宕机失效,微服务的 Provider 和 Consumer 都能正常通信。
微服务的负载均衡策略会自动剔除死亡的微服务节点。
关闭自我保护
server01和server02:application.yml
1 2 3 4 5 eureka: server: enable-self-preservation: false eviction-interval-timer-in-ms: 60000
4.3.9、优雅停服 配置了优雅停服以后,将不需要 Eureka Server 中配置关闭自我保护。本文使用 actuator 实现。
1、添加依赖
provider:pom.xml
1 2 3 4 5 6 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency >
2、配置文件
provider:application.yml
1 2 3 4 5 6 7 8 9 10 management: endpoints: web: exposure: include: shutdown endpoint: shutdown: enabled: true
使用 POST 请求访问:http://localhost:7070/actuator/shutdown
4.3.10、Eureka安全认证 1、添加依赖
server01和server02:pom.xml
1 2 3 4 5 6 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency >
2、配置文件
server01和server02:application.yml
1 2 3 4 5 6 7 spring: security: user: name: root password: 123456
3、修改访问集群节点的 url
server01和server02:application.yml
1 2 3 4 5 6 7 8 9 10 11 eureka: instance: hostname: eureka01 prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${server.port} client: service-url: defaultZone: http://root:123456@localhost:8762/eureka/
provider:application.yml
1 2 3 4 5 6 7 8 9 eureka: instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${server.port} client: service-url: defaultZone: http://root:123456@localhost:8761/eureka/,http://root:123456@localhost:8762/eureka/
consumer:application.yml
1 2 3 4 5 6 7 8 eureka: client: register-with-eureka: false registry-fetch-interval-seconds: 10 service-url: defaultZone: http://root:123456@localhost:8761/eureka/,http://root:123456@localhost:8762/eureka/
4.3.11、过滤CSRF Eureka 会自动化配置 CSRF 防御机制,Spring Security 认为 POST, PUT, and DELETE http methods 都是有风险的,如果这些 method 发送过程中没有带上 CSRF token 的话,会被直接拦截并返回 403 forbidden。
官方给出了解决的方法,具体可以参考 spring cloud issue 2754 ,里面有大量的讨论,这里提供两种解决方案。
首先注册中心配置一个 @EnableWebSecurity
配置类,继承 org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
,然后重写 configure
方法。
方案一:忽略/eureka/的所有请求
server01和server02:配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.yanghuisen.config;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (HttpSecurity http) throws Exception { super .configure(http); http.csrf().ignoringAntMatchers("/eureka/**" ); } }
方案二:保持密码验证的同时禁用 CSRF 防御机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package cn.yanghuisen.config;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); } }