zoukankan      html  css  js  c++  java
  • spring cloud 服务治理

    前言

    在分布式系统领域有个著名的CAP定理:

    C——数据一致性;

    A——服务可用性;

    P——服务对网络分区故障的容错性。

    这三个特性在任何分布式系统中不能同时满足,最多同时满足两个。

    Zookeeper是著名Hadoop的一个子项目,很多场景下Zookeeper也作为Service发现服务解决方案。Zookeeper保证的是CP,即任何时刻对Zookeeper的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务请求的可用性。

    而Spring Cloud Netflix在设计Eureka时遵守的就是AP原则。因为对于服务消费者来说,能消费才是最重要的——拿到可能不正确的服务实例信息后尝试消费一下,也好过因为无法获取实例信息而不去消费。所以,对于服务发现而言,可用性比数据一致性更加重要——AP胜过CP。

    1、服务注册与服务发现

    服务治理是微服务架构中最为核心和基础的模块,主要为各个微服务实例提供自动化注册与发现功能。

    服务注册:服务治理框架会构建一个注册中心,每个服务向注册中心注册登记自己提供的服务,将服务名、主机、端口号、版本号、通信协议等告知注册中心,注册中心按服务名分类组织成一个服务清单。

    例如:

    有三个提供订单服务的实例分别运行在 192.168.0.100:8081、192.168.0.101:8082、192.168.0.102:8083;

    还有四个提供用户服务的实例分别运行在 192.168.1.100:8091、192.168.1.101:8092、192.168.1.102:8093、192.168.1.103:8094;

    当这些服务全部启动并成功注册到注册中心上后,注册中心会维护一份类似下面的一个服务清单:

    服务名 服务地址
    订单服务 192.168.0.100:8081、192.168.0.101:8082、192.168.0.102:8083
    用户服务 192.168.1.100:8091、192.168.1.101:8092、192.168.1.102:8093、192.168.1.103:8094

    服务发现:在服务治理框架下,服务之间不再通过具体实例地址来相互调用,而是通过向注册中心咨询并获取服务清单,以实现(通过服务名)对具体服务的调用。

      例如:前台用户下单需要调用订单服务,首先向注册中心发起咨询订单服务的请求,注册中心返回订单服务位置清单,然后调用方通过负载策略选择其中某一个位置进行服务调用即可。

    2、单点注册中心

     创建一个空项目,pom.xml如下(后面都基于这个pom):

    <?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.springcloud.demo</groupId>
      <artifactId>springcloud-demo</artifactId>
      <packaging>pom</packaging>
      <version>1.0-SNAPSHOT</version>
      <modules>
        <module>eureka-server</module>
        <module>eureka-service-provider</module>
      </modules>
    
      <name>Maven</name>
      <!-- FIXME change it to the project's website -->
      <url>http://maven.apache.org/</url>
      <inceptionYear>2001</inceptionYear>
    
      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.8.RELEASE</version>
        <relativePath/>
      </parent>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
        </dependency>
      </dependencies>
    
      <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>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
          <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
              <failIfNoTests>true</failIfNoTests>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </project>
    View Code

    1、服务注册中心

    首先搭建一个注册中心,命名为eureka-server,引入eureka依赖包

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>

    在 Spring Boot 启动入口文件上加注解 @EnableEurekaServer

    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApplication {
        public static void main(String[] args) {
            SpringApplication.run( EurekaServerApplication.class, args );
        }
    }

    然后配置application.yml:

    server:
      port: 8081
    
    eureka:
      instance:
        hostname: eurekaserver
      client:
        registerWithEureka: false
        fetchRegistry: false
        serviceUrl:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
    spring:
      application:
        name: eurka-server

    其中: 

    eureka.client.registerWithEureka = false     设置为false表示不向注册中心注册自己,默认为true
    eureka.client.fetchRegistry = false               设置为false表示不需要去检索服务,默认为true

    启动应用并访问 http://localhost:8081/ ,如下图所示:

    可以看到注册中心还没有注册任何服务,至此注册中心已经搭建完毕。

    2、服务提供者

    搭建好注册中心后,我们再来创建一个服务提供者,然后将其注册到注册中心。新建一个项目eureka-service-provider,添加eureka依赖,在 Spring Boot 启动入口文件上加注解 @EnableEurekaClient

    @SpringBootApplication
    @EnableEurekaClient
    public class EurekaServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run( EurekaServiceApplication.class, args );
        }
    }

    配置application.yml:

    server:
      port: 8082
    
    eureka:
      instance:
        hostname: service-provider
      client:
        serviceUrl:
          defaultZone: http://localhost:8081/eureka/
    
    spring:
      application:
        name: service-1

    因为需要向注册中心注册,所以 eureka.client.registerWithEureka、eureka.client.fetchRegistry 不要配置为 false

    启动eureka-service-provider,刷新注册中心,可以看到服务已经注册成功了:

    同时eureka-server控制台会打印一行日志:Registered instance SERVICE-1/xxxx:service-1:8082 with status UP (replication=false)

    3、高可用注册中心

    上面只是一个单点注册中心,微服务场景下应用通过注册中心来访问真实服务,如果注册中心挂了会造成服务无法调用,所以注册中心也得要保持高可用,Eureka 不可能没有想到这一点,其实在Eureka里,所有结点既是服务提供方,又是服务消费方,注册中心也不例外。

    大家还记得在单节点配置里讲到的这两个配置吗:

    eureka.client.registerWithEureka = false

    eureka.client.fetchRegistry = false

    实际上 Eureka 高可用方案就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组相互注册的服务注册中心,在这组服务注册中心内部相互同步注册清单,形成一个高可用集群

    注册中心集群

    创建以下三个Eureka Server:peer1、peer2、peer3

    peer1的配置如下(peer2、peer3配置类似):

    server:
      port: 8081
    
    eureka:
      instance:
        hostname: peer1
      client:
        serviceUrl:
          defaultZone: http://peer2:8082/eureka/,http://peer3:8083/eureka/
    
    spring:
      application:
        name: eurka-server

    分别启动peer1、peer2、peer3,访问peer1:8081

    单点故障

    这时候如果peer2突然挂了,访问peer1和peer3仍然可以看到peer2,这是因为Eureka默认开启了自我保护机制(peer2挂了没有剔除),可以通过设置 eureka.server.enable-self-preservation=false 来禁用保护

    peer1的配置文件里新增eureka.server.enable-self-preservation=false设置,然后模拟peer3挂了,peer1注册中心如下:

    大概两分钟左右,从peer1注册中心可以看到 registered-replicas 有3个,unavailable-replicas 里有 peer3,available-replicas 里只有 peer1 和 peer2 可用,

    而从 peer2 里看 peer3 仍然可用(过比较长的时间,peer2 仍然会将 peer3 从 available-replicas 里剔除)

    将serverce-1注册到注册中心peer1,然后在peer1 和 peer2里都可以看到service-1。

    故障恢复

    peer3 经过运维处理恢复了,重新启动peer3自动加入了peer1和peer2集群,可用看到service-1也自动注册到peer3上了:

    4、配置

    服务注册相关配置:eureka.instance 为前缀对应的Class

    org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean
    

    服务实例相关配置:eureka.client 为前缀对应的Class

    org.springframework.cloud.netflix.eureka.EurekaClientConfigBean

    服务配置属性以eureka.server为前缀对应的Class

    org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean

    client常见配置

    # 启用Eureka客户端
    eureka.client.enabled=true
    # 从Eureka服务端获取注册信息的间隔时间(单位:秒)
    eureka.client.registry-fetch-interval-seconds=30
    # 是否从Eureka服务端获取注册信息
    eureka.client.fetch-registry=true
    # 是否要将自身的实例信息注册到Eureka服务端
    eureka.client.register-with-eureka=true
    # 初始化实例信息的变化到Eureka服务端的间隔时间(单位:秒)
    eureka.client.instance-info-replication-interval-seconds=40
    # 从Eureka客户端到Eureka服务端的连接总数
    eureka.client.eureka-server-total-connections=200
    # 获取实例时是否过滤,仅保留UP状态的实例
    eureka.client.filter-only-up-instances=true

    server常见配置

    #设为false,关闭自我保护
    eureka.server.enable-self-preservation=false
    #表示是否将自己注册到Eureka Server,默认为true
    eureka.client.register-with-eureka=false
    #表示是否从Eureka Server获取注册信息,默认为true
    eureka.client.fetch-registry=false
    # 扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒
    eureka.server.eviction-interval-timer-in-ms=5000
    #设置 eureka server同步失败的等待时间 默认 5分
    #在这期间,它不向客户端提供服务注册信息
    eureka.server.wait-time-in-ms-when-sync-empty=5
    #设置 eureka server同步失败的重试次数 默认为 5 次
    eureka.server.number-of-replication-retries=5
    #自我保护系数(默认0.85)
    eureka.server.renewal-percent-threshold=0.85

    instance常见配置

    #是否优先使用IP地址作为主机名的标识,如果不配置就是机器的主机名
    eureka.instance.prefer-ip-address=false
    #Eureka客户端向服务的发送心跳的时间间隔(单位:秒)
    eureka.instance.lease-renewal-interval-in-seconds=30
    #Eureka服务的收到最后一次心跳之后等待的时间上限(单位:秒),
    #超过这个时间之后会将该服务实例从注册中心清单里剔除
    eureka.instance.lease-expiration-duration-in-seconds=90

    附录

      本文Demo地址

      Spring Cloud Netflix官方文档

      Spring Cloud 中文社区

      spring-cloud-book

    服务治理 

    SpringCloudLesson

    更多配置参考

  • 相关阅读:
    BDOC ROUTER
    web dom api中的Selection和Range
    基于第三方vuejs库组件做适配性个性开发
    香草js侦测元素是否离开视窗viewport
    xampp windows10下xdebug调试环境安装及配置
    beyond compare全文件夹比较,仅显示变化的文件
    给定制的vuejs组件添加v-model双向绑定支持
    javascript工厂函数(factory function)vs构造函数(constructor function)
    edrawmax使用技巧备忘
    babel plugin和presets是什么,怎么用?
  • 原文地址:https://www.cnblogs.com/mr-yang-localhost/p/10461735.html
Copyright © 2011-2022 走看看