SpringCloud中的Eureka服务注册中心

Eureka服务注册中心

1.服务注册中心

​ 服务注册中心是服务实现服务化管理的核心组件类似于目录服务的作用主要用来存储服务信
息,譬如提供者 url 串、路由信息等。服务注册中心是微服务架构中最基础的设施之一
在微服务架构流行之前,注册中心就已经开始出现在分布式架构的系统中。Dubbo 是一个在国内比
较流行的分布式框架,被大量的中小型互联网公司所采用,它提供了比较完善的服务治理功能,而服务治理的实现主要依靠的就是注册中心。

2.什么是注册中心

注册中心可以说是微服务架构中的“通讯录”,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。

理解:demo 手机通讯录

当我想给李四打电话时,那我需要在通讯录中按照名字找到李四,然后就可以找到他的手机号拨
打电话。—— 服务发现

小花办了手机号并把手机号告诉了我,我把小花的号码存进通讯录,后续,我就可以从通讯录找
到他。—— 服务注册

总结:服务注册中心的作用就是服务的注册和服务发现。
3.常见的注册中心
  • Netflix Eureka
  • Alibaba Nacos
  • HashiCorp Consul
  • Apache ZooKeeper
  • CoreOS Etcd
  • CNCF CoreDNS
特性EurekaNacosConsulZookeeper
CAPAPCP + APCPCP
健康检查Client BeatTCP/HTTP/MYSQL/Client BeatTCP/HTTP/gRPC/CmdKeep Alive
雪崩保护
自动注销实例支持支持不支持支持
访问协议HTTPHTTP/DNSHTTP/DNSTCP
监听支持支持支持支持支持
多数据中心支持支持支持不支持
跨注册中心同 步不支持支持支持不支持
SpringCloud集 成支持支持支持支持
4.为什么需要注册中心&解决什么问题

在分布式系统中,我们不仅仅是需要在注册中心找到服务和服务地址的映射关系这么简单,我们还需要考虑更多更复杂的问题:

  1. 服务注册后,如何被及时发现
  2. 服务宕机后,如何及时下线
  3. 服务如何有效的水平扩展
  4. 服务发现时,如何进行路由
  5. 服务异常时,如何进行降级
  6. 注册中心如何实现自身的高可用

这些问题的解决都依赖于注册中心。简单看,注册中心的功能有点类似于 DNS 服务器或者负载均衡

**器,而实际上,注册中心作为微服务的基础组件,可能要更加复杂,也需要更多的灵活性和时效性。**所以我们还需要学习更多 Spring Cloud 微服务组件协同完成应用开发

注册中心解决问题
  • 服务管理
  • 服务的依赖关系管理
5.什么是Eureka注册中心

​ Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud 将它集成在其子项目 Spring Cloud Netflix 中,实现 Spring Cloud 的服务注册与发现,同时还提供了负载均衡、故障转移等能力。

6.Eureka注册中心三种角色在这里插入图片描述

在这里插入图片描述

7.Eureka入门案例
1.创建项目

创建聚合项目,创建一个pom父工程

2.添加依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.shsxt</groupId>
    <artifactId>eureka-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>eureka-server</module>
        <module>eureka-server02</module>
        <module>service-provider</module>
        <module>service-consumer</module>
    </modules>

    <!-- 继承 spring-boot-starter-parent 依赖 -->
    <!-- 使用继承方式,实现复用,符合继承的都可以被使用 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>

    <properties>
        <!-- Spring Cloud Hoxton.SR1 依赖 -->
        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
    </properties>

    <!-- 项目依赖管理 父项目只是声明依赖,子项目需要写明需要的依赖(可以省略版本信息) -->
    <dependencyManagement>
        <dependencies>
            <!-- spring cloud 依赖 -->
            <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>

</project>
3.注册中心 eureka-server
在刚才的父工程下创建eureka-server
添加依赖
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.shsxt</groupId>
  <artifactId>eureka-server</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>eureka-server</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

    <!-- 继承父依赖 -->
    <parent>
        <groupId>com.shsxt</groupId>
        <artifactId>eureka-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

  <dependencies>
      <!-- spring boot security 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
      <!-- netflix eureka server 依赖 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
      </dependency>
      <!-- spring boot web 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <!-- spring boot test 依赖 -->
      <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>
</project>
配置文件

application.yml

spring:
  application:
    name: eureka-server # 应用名称(集群下相同) 
# 端口
server:
  port: 8761

# 配置 Eureka Server 注册中心
eureka:
  instance:
    hostname: localhost # 主机名,不配置的时候将根据操作系统的主机名来获取
  client:
  	register-with-eureka: false # 是否将自己注册到注册中心,默认为 true
	fetch-registry: false # 是否从注册中心获取服务注册信息,默认为 true
    # 设置服务注册中心地址,指向另一个注册中心
    service-url: # 注册中心对外暴露的注册地址
      	defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

此时如果直接启动项目是会报错的,错误信息:
com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException:
Connection refused: connect ,这是因为 Eureka 默认开启了将自己注册至注册中心和从注册中心
获取服务注册信息的配置,如果该应用的角色是注册中心并是单节点的话,要关闭这两个配置项。

启动类
@SpringBootApplication
//开启EurekaServer注解
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

访问:http://localhost:8761/

4.高可用Eureka注册中心
创建项目eureka-server02

​ 在刚才的父工程下再创建一个 eureka-server02 注册中心的项目,如果是多机器部署不用修改
端口,通过 IP 区分服务,如果在一台机器上演示需要修改端口区分服务。

添加依赖
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.shsxt</groupId>
  <artifactId>eureka-server</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>eureka-server</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

    <!-- 继承父依赖 -->
    <parent>
        <groupId>com.shsxt</groupId>
        <artifactId>eureka-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

  <dependencies>
      <!-- spring boot security 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
      <!-- netflix eureka server 依赖 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
      </dependency>
      <!-- spring boot web 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <!-- spring boot test 依赖 -->
      <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>
</project>
配置文件

集群配置下,注册中心需要相互注册实现信息的同步。
eureka-server 的 application.yml

spring:
  application:
    name: eureka-server # 应用名称(集群下相同) 
# 端口
server:
  port: 8761

# 配置 Eureka Server 注册中心
eureka:
  instance:
    hostname: localhost # 主机名,不配置的时候将根据操作系统的主机名来获取
  client:
   	# 设置服务注册中心地址,指向另一个注册中心
    service-url: # 注册中心对外暴露的注册地址
      	defaultZone: http://localhost:8762/eureka/

eureka-server02 的 application.ym

spring:
  application:
    name: eureka-server # 应用名称(集群下相同) 
# 端口
server:
  port: 8762

# 配置 Eureka Server 注册中心
eureka:
  instance:
    hostname: localhost # 主机名,不配置的时候将根据操作系统的主机名来获取
  client:
   	# 设置服务注册中心地址,指向另一个注册中心
    service-url: # 注册中心对外暴露的注册地址
      	defaultZone: http://localhost:8761/eureka/
启动类

启动二个server 启动类不变内容

访问:http://localhost:8761/ 或者 http://localhost:8762/

显示IP+端口

一个普通的 Netflix Eureka 实例注册的 ID 等于其主机名(即,每个主机仅提供一项服务)。 Spring
Cloud Eureka 提供了合理的默认值,定义如下:
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.i
nstance_id:${server.port}}} ,也就是:主机名:应用名:应用端口。
我们也可以可以自定义进行修改

eureka:
instance:
prefer-ip-address: true # 是否使用 ip 地址注册
instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
5.服务提供者service-provider
创建项目
添加依赖
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.shsxt</groupId>
  <artifactId>service-provider</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>service-provider</name>

    <!-- 继承父依赖 -->
    <parent>
        <groupId>com.shsxt</groupId>
        <artifactId>eureka-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
      <!-- netflix eureka client 依赖 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <!-- spring boot web 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <!-- lombok 依赖 -->
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <scope>provided</scope>
      </dependency>
      <!-- spring boot test 依赖 -->
      <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>
</project>
配置文件

application.yml

spring:
  application:
    name: service-provider # 应用名称(集群下相同)

# 端口
server:
  port: 7070

# 配置 Eureka Server 注册中心
eureka:
  instance:
    prefer-ip-address: true       # 是否使用 ip 地址注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
  client:
    service-url:                  # 设置服务注册中心地址
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
编写服务

User.java

@Data
@NoArgsConstructor
/*@AllArgsConstructor*/
public class User implements Serializable {

    private Integer id;
    private String username;
    private Integer age;

    public User(Integer id, String username, Integer age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }
}
/**
 * 用户服务
 */
public interface UserService {

    /**
     * 查询用户列表
     *
     * @return
     */
    List<User> selectUserList();

}


/**
 * 用户服务
 */
@Service
public class UserServiceImpl implements UserService {


    @Override
    public List<User> selectUserList() {
        return Arrays.asList(
                new User(1, "张三", 20),
                new User(2, "李四", 21),
                new User(3, "王五", 22)
        );
    }
}
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 查询用户列表
     *
     * @return
     */
    @GetMapping("/list")
    public List<User> selectUserList() {
        return userService.selectUserList();
    }

}

启动类

@SpringBootApplication
// 开启 EurekaClient 注解,目前版本如果配置了 Eureka 注册中心,默认会开启该注解
//@EnableEurekaClient
public class ServiceProviderApp {
    public static void main(String[] args) {
        SpringApplication.run(ServiceProviderApp.class,args);
    }
}

访问:http://localhost:8761/ http://localhost:7070/user/list

6.服务消费者
创建项目
添加依赖
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.shsxt</groupId>
  <artifactId>service-consumer</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>service-consumer</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

    <!-- 继承父依赖 -->
    <parent>
        <groupId>com.shsxt</groupId>
        <artifactId>eureka-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

  <dependencies>
      <!-- netflix eureka client 依赖 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <!-- netflix ribbon 依赖 -->
      <!-- Ribbon 点对点直连 -->
      <!--
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      </dependency>
      -->
      <!-- spring boot web 依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <!-- lombok 依赖 -->
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <scope>provided</scope>
      </dependency>

      <!-- spring boot test 依赖 -->
      <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>
</project>
配置文件
# 应用名称
spring:
  application:
    name: service-consumer

# 端口
server:
  port: 8080

# 配置 Eureka Server 注册中心
eureka:
  client:
    register-with-eureka: false         # 是否将自己注册到注册中心,默认为 true
    registry-fetch-interval-seconds: 10 # 表示 Eureka Client 间隔多久去服务器拉取注册信息,默认为 30 秒
    service-url:                        # 设置服务注册中心地址
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
消费服务

User.java

@Data
@NoArgsConstructor
@AllArgsConstructor

public class User implements Serializable {
    private Integer id;
    private String username;
    private Integer age;
}
public interface UserService {
    /**
     * 查询用户列表
     *
     * @return
     */
    List<User> selectUserList();
}

对于服务的消费我们这里讲三种实现方式:

  1. DiscoveryClient:通过元数据获取服务信息
  2. LoadBalancerClient:Ribbon 的负载均衡器
  3. @LoadBalanced:通过注解开启 Ribbon 的负载均衡器
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient; //获取元素法

    @Autowired
    private LoadBalancerClient loadBalancerClient;//Ribbon 负载均衡器


    /**
     * 查询用户列表
     * *
     @return
     */
    @Override
    public List<User> selectUserList() {
        return selectUserListByLoadBalancerAnnotation();
    }

    //第三种方法 注解 @LoadBalanced //负载均衡器注解
    private List<User> selectUserListByLoadBalancerAnnotation(){
        // ResponseEntity: 封装了返回数据
        ResponseEntity<List<User>> response = restTemplate.exchange(
                "http://service-provider/user/list",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<User>>() {});
        return response.getBody();
    }

    //第二种方法  Ribbon 负载均衡器
    private List<User> selectUserListByLoadBalancerClient(){
        StringBuffer sb=null;
        //根据服务名称获取服务
        ServiceInstance si=loadBalancerClient.choose("service-provider");
        if(null==si){
            return null;
        }
        sb=new StringBuffer();
        sb.append("http://"+si.getHost()+":"+si.getPort()+"/user/list");
        System.out.println(sb.toString());//打印服务地址
        //ResponseEntity:封装了返回数据
        ResponseEntity<List<User>> response=restTemplate.exchange(
                sb.toString(),
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<User>>() {
                });
               return response.getBody();

    }

    //第一种方法
    private List<User> selectUserListByDiscoveryClient() {
        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() + "/user/list");
        // ResponseEntity: 封装了返回数据
        ResponseEntity<List<User>> response = restTemplate.exchange(
                sb.toString(),
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<User>>() {
                });
        return response.getBody();
    }
}
启动类
@SpringBootApplication
// 开启 Eureka Client 注解,目前版本如果配置了 Eureka 注册中心,默认会开启该注解
//@EnableEurekaClient
public class ServiceConsumerApp {

    @Bean
    @LoadBalanced //负载均衡注解
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ServiceConsumerApp.class,args);
    }
}

访问:http://localhost:8080/user/manage

8.Eureka架构原理

在这里插入图片描述

9.CAP原则

在这里插入图片描述
CAP 原则又称 CAP 定理,指的是在一个分布式系统中具有以下其中两个特性:

  • Consistency (一致性)
  • Availability (可用性)
  • Partition tolerance(分区容错性)
特性定理
Consistency也叫做数据原子性,系统在执行某项操作后仍然处于一致的状态。在分布式系统 中,更新操作执行成功后所有的用户都应该读到最新的值,这样的系统被认为是具 有强一致性的。等同于所有节点访问同一份最新的数据副本。
Availability每一个操作总是能够在一定的时间内返回结果,这里需要注意的是"一定时间 内"和"返回结果"。一定时间内指的是,在可以容忍的范围内返回结果,结果可以 是成功或者是失败。
Partition tolerance在网络分区的情况下,被分隔的节点仍能正常对外提供服务(分布式集群,数据被 分布存储在不同的服务器上,无论什么情况,服务器都能正常被访问)。

CAP 三个特性只能满足其中两个,那么取舍的策略就共有三种:
CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃 P 的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是
违背分布式系统设计的初衷的。
CP without A:如果不要求A(可用),相当于每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统。设计成 CP 的系统其实不少,最典型的就是分布式数据库,如 Redis、HBase 等。对于这些分布式数据库来说,数据的一致性是最基本的要求,因为如果连这个标准都达不到,那么直接采用关系型数据库
就好,没必要再浪费资源来部署分布式数据库。
AP without C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联
系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。典型的
应用就如某米的抢购手机场景,可能前几秒你浏览商品的时候页面提示是有库存的,当你选择完商
品准备下单的时候,系统提示你下单失败,商品已售完。这其实就是先在 A(可用性)方面保证系
统可以正常的服务,然后在数据的一致性方面做了些牺牲,虽然多少会影响一些用户体验,但也不
至于造成用户购物流程的严重阻塞。

总结

现如今,对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,
节点只会越来越多,所以节点故障、网络故障是常态,因此分区容错性也就成为了一个分布式系统必然
要面对的问题。那么就只能在 C 和 A 之间进行取舍。但对于传统的项目就可能有所不同,拿银行的转账
系统来说,涉及到金钱的对于数据一致性不能做出一丝的让步,C 必须保证,出现网络故障的话,宁可
停止服务,可以在 A 和 P 之间做取舍。
​ 总而言之,没有最好的策略,好的系统应该是根据业务场景来进行架构设计的,只有适合的才是最
好的。

10.Eureka自我保护
启动自我保护条件

一般情况下,服务在 Eureka 上注册后,会每 30 秒发送心跳包,Eureka 通过心跳来判断服务是否健
康,同时会定期删除超过 90 秒没有发送心跳的服务

自我保护模式

​ Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,
Eureka Server 会将这些实例保护起来,让这些实例不会过期,同时提示一个警告。这种算法叫做 Eureka
Server 的自我保护模式

为什么要启动自我保护
  • 因为同时保留"好数据"与"坏数据"总比丢掉任何数据要更好,当网络故障恢复后,这个 Eureka 节点

    会退出"自我保护模式"。

  • Eureka 还有客户端缓存功能(也就是微服务的缓存功能)。即使 Eureka 集群中所有节点都宕机失

    效,微服务的 Provider 和 Consumer 都能正常通信。

  • 微服务的负载均衡策略会自动剔除死亡的微服务节点。

如何关闭自我保护
eureka:
server:
enable-self-preservation: false # true:开启自我保护模式,false:关闭自我保护模式
eviction-interval-timer-in-ms: 60000 # 清理间隔(单位:毫秒,默认是 60*1000)
11.优雅停服

配置了优雅停服以后,将不需要 Eureka Server 中配置关闭自我保护。本文使用 actuator 实现。

添加依赖

<!-- spring boot actuator 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置文件

# 度量指标监控与健康检查
management:
	endpoints:
		web:
			exposure:
				include: shutdown # 开启 shutdown 端点访问
	endpoint:
		shutdown:
			enabled: true # 开启 shutdown 实现优雅停服

优雅停服
使用 POST 请求访问:http://localhost:7070/actuator/shutdown

12.Eureka安全认证
添加依赖
<!-- spring boot security 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置文件
spring:
	# 安全认证
	security:
		user:
			name: root
			password: 123456
修改访问集群节点的 url
spring:
  application:
    name: eureka-server # 应用名称(集群下相同)
  # 安全认证
  security:
    user:
      name: root
      password: 123456
# 端口
server:
  port: 8761

# 配置 Eureka Server 注册中心
eureka:
  instance:
    hostname: localhost # 主机名,不配置的时候将根据操作系统的主机名来获取
    prefer-ip-address: true       # 是否使用 ip 地址注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port

  client:
    # 设置服务注册中心地址,指向另一个注册中心
    service-url: # 注册中心对外暴露的注册地址
      defaultZone: http://root:123456@localhost:8762/eureka/
过滤CSRF

​ Eureka 会自动化配置 CSRF 防御机制,Spring Security 认为 POST, PUT, and DELETE http methods 都是有风险的,如果这些 method 发送过程中没有带上 CSRF token 的话,会被直接拦截并返回 403forbidden。
官方给出了解决的方法,具体可以参考 spring cloud issue 2754,里面有大量的讨论,这里提供两
种解决方案。
​ 首先注册中心配置一个 @EnableWebSecurity 配置类,继承
org.springframework.security.config.annotation.web.configuration.WebSecurityConfi
gurerAdapter ,然后重写 configure 方法

方案一:

使 CSRF 忽略 /eureka/** 的所有请求
/**
 * 安全认证配置类
 */
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http); // 加这句是为了访问 eureka 控制台和 /actuator 时能做安全控制
        http.csrf().ignoringAntMatchers("/eureka/**"); // 忽略 /eureka/** 的所有请求
    }

}

方案二:

保持密码验证的同时禁用 CSRF 防御机制
/**
* 安全认证配置类
*/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
        // 注意,如果直接 disable 的话会把安全验证也禁用掉
        http.csrf().disable().authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .httpBasic();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值