单体应用存在的问题

  • 随着业务的发展,开发变得越来越复杂。
  • 修改、新增某个功能,需要对整个系统进行测试、重新部署。
  • 一个模块出现问题,很可能导致整个系统崩溃。
  • 多个开发团队同时对数据进行管理,容易产生安全漏洞。
  • 各个模块使用同一种技术进行开发,各个模块很难根据实际情况选择更合适的技术框架,局限性很大。
  • 模块内容过于复杂,如果员工离职,可能需要很长时间才能完成工作交接。

分布式&集群

  • 集群:一台服务器无法负荷高并发的数据访问量,那么就设置十台服务器一起分担压力,十台不行就设置一百台(物理层面)。很多人干同一件事情,来分摊压力。
  • 分布式:将一个复杂问题拆分成若干个简单的小问题,将一个大型的项目架构拆分成若干个微服务来协同完成。(软件设计层面)。将一个庞大的工作拆分成若干个小步骤,分别由不同的人完成这些小步骤,最终将所有的结果进行整合实现大的需求。

服务治理

  • 服务治理的核心又三部分组成:服务提供者、服务消费者、注册中心。
  • 在分布式系统架构中,每个微服务在启动时,将自己的信息存储在注册中心,叫做服务注册
  • 服务消费者从注册中心获取服务提供者的网络信息,通过该信息调用服务,叫做服务发现

服务注册中心

  • 分布式微服务架构中,服务注册中心用于存储服务提供者地址信息、服务发布相关的属性信息
  • 消费者通过主动查询和被动通知的方式获取服务提供者的地址信息,而不再需要通过硬编码方式得到提供者的地址信。
  • 消费者只需要知道当前系统发布了那些服务,不需要知道服务具体存在于什么位置,这就是透明化路由。

  1. 启动服务提供者

  2. 服务提供者将相关服务信息主动注册到注册中心

  3. 服务消费者获取服务注册信息

    • pull模式:服务消费者可以主动拉取可用的服务提供者清单

    • push模式:服务消费者订阅服务(当服务提供者有变化时,注册中心也会主动推送更新后的服务清单给消费者

  4. 服务消费者通过注册信息直接调用服务提供者

  5. 注册中心也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除;

主流服务中心对比

  • Zookeeper 存储+监听通知
  • Eureka 服务注册与发现组件
  • Consul 分布式高可用的服务 发布和注册服务软件
  • Nacos 注册中心 + 配置中心的组合

服务注册中心组件Eureka

Eureka 交互流程及原理

  • Eureka 包含两个组件:Eureka Server 和 Eureka Client

    • Eureka Client是一个Java客户端,用于简化与Eureka Server的交互;
    • Eureka Server提供服务发现的能力,各个微服务启动时,会通过Eureka Client向Eureka Server 进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;
  • 注:Eureka通过心跳检测、健康检查和客户端缓存等机制,提高系统的灵活性、可伸缩性和可用性。

  • 图中us-east-1c、us-east-1d,us-east-1e代表不同的区也就是不同的机房

  • 图中每一个Eureka Server都是一个集群。

  • 图中Application Service作为服务提供者,向Eureka Server中注册服务,Eureka Server接受到注册事件会在集群和分区中进行数据同步,ApplicationClient作为消费端(服务消费者)可以从Eureka Server中获取到服务注册信息,进行服务调用。

  • 微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息

  • Eureka Server在一定时间内没有接收到某个微服务节点的心跳,EurekaServer将会注销该微服务节点(默认90秒)

  • 每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册列表的同步

  • Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者

单例Eureka Server搭建

  • Boot与Cloud版本的说明:https://start.spring.io/actuator/info

    1
    2
    3
    4
    5
    6
    7
    8
    9
    "spring-cloud": {
    "Hoxton.SR11": "Spring Boot >=2.2.0.RELEASE and <2.3.999.BUILD-SNAPSHOT",
    "Hoxton.BUILD-SNAPSHOT": "Spring Boot >=2.3.999.BUILD-SNAPSHOT and <2.4.0.M1",
    "2020.0.0-M3": "Spring Boot >=2.4.0.M1 and <=2.4.0.M1",
    "2020.0.0-M4": "Spring Boot >=2.4.0.M2 and <=2.4.0-M3",
    "2020.0.0": "Spring Boot >=2.4.0.M4 and <=2.4.0",
    "2020.0.3": "Spring Boot >=2.4.1 and <2.5.3-SNAPSHOT",
    "2020.0.4-SNAPSHOT": "Spring Boot >=2.5.3-SNAPSHOT"
    },

父工程

  • 在父的pom中添加Spring Cloud 依赖

    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
    <!--spring boot 父启动器依赖-->
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.8</version>
    <type>pom</type>
    </parent>

    <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <java.version>1.8</java.version>
    <spring-boot.version>2.4.8.RELEASE</spring-boot.version>
    <spring-cloud.version>2020.0.3</spring-cloud.version>
    <lombok.version>1.18.20</lombok.version>
    <jaxb.version>2.3.0</jaxb.version>
    </properties>

    <dependencyManagement>
    <dependencies>
    <!--spring cloud依赖管理,引入了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>
  • JDK9以上缺失的依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <!--因为Jdk9之后默认没有加载jaxb该模块,EurekaServer使用到,所以需要手动导入,否则EurekaServer服务无法启动-->
    <dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>${jaxb.version}</version>
    </dependency>
    <dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>${jaxb.version}</version>
    </dependency>
    <dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>${jaxb.version}</version>
    </dependency>
    <dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
    </dependency>
    <!--引入Jaxb,结束-->
  • 添加模块常用的依赖

    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
    <!--web依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--日志依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    <!--测试依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    <!--lombok工具-->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
    <scope>provided</scope>
    </dependency>
    <!-- Actuator可以帮助你监控和管理Spring Boot应用-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
    </dependency>
  • build工具

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <build>
    <plugins>
    <!--编译插件-->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>8</source>
    <target>8</target>
    <encoding>utf-8</encoding>
    </configuration>
    </plugin>
    <!--打包插件-->
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
  • 指定从aliyun上下载jar包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <repositories><!-- 代码库 -->
    <repository>
    <id>maven-ali</id>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <releases>
    <enabled>true</enabled>
    </releases>
    <snapshots>
    <enabled>true</enabled>
    <updatePolicy>always</updatePolicy>
    <checksumPolicy>fail</checksumPolicy>
    </snapshots>
    </repository>
    </repositories>
  • 完整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
    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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    <?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>org.example</groupId>
    <artifactId>springcloud-study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
    <module>eureka-server-8761</module>
    </modules>

    <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <java.version>1.8</java.version>
    <spring-boot.version>2.4.8</spring-boot.version>
    <spring-cloud.version>2020.0.3</spring-cloud.version>
    <lombok.version>1.18.20</lombok.version>
    <jaxb.version>2.3.0</jaxb.version>

    <swagger.version>2.6.1</swagger.version>
    <pagehelper.version>4.2.1</pagehelper.version>
    <druid.version>1.1.10</druid.version>
    <fastjson.version>1.2.62</fastjson.version>
    <mybatis.version>3.4.6</mybatis.version>
    </properties>

    <dependencies>
    <!--web依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--日志依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    <!--测试依赖-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    <!--lombok工具-->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
    <scope>provided</scope>
    </dependency>
    <!-- Actuator可以帮助你监控和管理Spring Boot应用-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    </dependencies>

    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>${spring-boot.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    <!--spring cloud依赖管理,引入了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>
    <!--热部署-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
    </dependency>
    </dependencies>
    </dependencyManagement>

    <repositories><!-- 代码库 -->
    <repository>
    <id>maven-ali</id>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <releases>
    <enabled>true</enabled>
    </releases>
    <snapshots>
    <enabled>true</enabled>
    <updatePolicy>always</updatePolicy>
    <checksumPolicy>fail</checksumPolicy>
    </snapshots>
    </repository>
    </repositories>

    <build>
    <plugins>
    <!--编译插件-->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>8</source>
    <target>8</target>
    <encoding>utf-8</encoding>
    </configuration>
    </plugin>
    <!--打包插件-->
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>${spring-boot.version}</version>
    </plugin>
    </plugins>
    </build>
    </project>

Eureka Server代码实现

添加依赖

  • 创建子项目eureka-server-8761

  • pom.xml中引入Eureka server依赖

    1
    2
    3
    4
    5
    <!--Eureka server依赖-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>

修改配置文件

  • application.yml添加 Eureka Server 相关配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#eureka server服务端口
server:
port: 8761
spring:
application:
name: eureka-server-8761 # 应用名称,应用名称会在Eureka中作为服务名称
# eureka 客户端配置(和Server交互),Eureka Server 其实也是一个Clien
eureka:
instance:
hostname: localhost # 当前eureka实例的主机名
client:
service-url:
# 配置客户端所交互的Eureka Server的地址
#(Eureka Server集群中每一个Server其实相对于其它Server来说都是Client)
# 集群模式下,defaultZone应该指向其它Eureka Server,如果有更多其它Server实例,逗号拼接即可
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
register-with-eureka: false # 单例是false,集群模式下改成true
fetch-registry: false # 集群模式下可以改成true
##单例模式下自己就是服务不需要从Eureka Server获取服务信息,默认为true,置为false
##集群模式下,就需要改为true,从集群上获取信息
dashboard:
enabled: true

创建启动类

  • 在SpringBoot启动类上面,使用@EnableEurekaServer声明当前项目为EurekaServer服务,提供服务注册和服务发现功能,即注册中心。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 声明本项目是一个Eureka服务
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaServerA {

    public static void main(String[] args) {
    SpringApplication.run(EurekaServerA.class, args);
    }
    }
  • 启动项目后,访问 http://localhsot:8761/,可以看到如下的信息面板,其中 Instances currently registered with Eureka 栏是空的,说明该注册中心中还没有注册任何服务

注册服务到Eureka Server

  • 建立子项目简历微服务,名称是service-resume-8080

  • 简历服务部署两个实例,分别占用8080、8081端⼝

  • 父工程中引入spring-cloud-commons依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-commons</artifactId>
    </dependency>
  • 子项目中引入eureka client的相关坐标

    1
    2
    3
    4
    5
    <!--eureka client 客户端依赖引入-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
  • 配置application.yml文件

    • 在application.yml 中添加Eureka Server高可用集群的地址及相关配置

    • 自定义实例(instance-id)的显示格式,加上版本号,便于多版本管理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      server:
      port: 8080
      spring:
      application:
      name: service-resume-8080 # 应用名称,应用名称会在Eureka中作为服务名称
      # eureka 客户端配置(和Server交互),Eureka Server 其实也是一个Clien
      eureka:
      instance:
      hostname: localhost # 当前eureka实例的主机名
      prefer-ip-address: true #服务实例中显示ip,而不是显示主机名(兼容老的eureka版本)
      # 可以自定义实例名称: 192.168.1.103: service-resume-8080:8080,我们
      instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
      client:
      service-url:
      # 配置客户端所交互的Eureka Server的访问地址
      defaultZone: http://localhost:8761/eureka/
      dashboard:
      enabled: true
  • 启动SpringBoot项目,再次访问Eureka的信息面板, 可以看到我们注册的实例

    1
    2
    3
    4
    5
    6
    7
    @SpringBootApplication
    public class ResumeApplication {

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

高可用注册中心搭建

  • 在微服务架构的分布式环境下,需要充分考虑到发生故障的情况,在生产环境中必须对给个组件进行高可用部署,注册中心也是一样,在生产环境中使用单节点显得并不是很合适,Eureka Serverde设计的时候就考虑了高可用的问题,在Eurek的服务治理中,所有的节点既是服务提供者,也是服务消费者。

  • Eureka的高可用实际上就是将自己作为服务向其他的注册中心注册自己,形成一组互相注册的服务注册中心,实现服务清单的互相同步。

  • 接下来在单节点的基础上搭建双节点的服务注册中心集群。

修改hosts文件

  • linux是 /etc/hosts ,windows在C:\Windows\System32\drivers\etc\hosts

    1
    2
    127.0.0.1 EurekaServerA
    127.0.0.1 EurekaServerB
  • 可以ping命令测试 EurekaServerA,EurekaServerB的连通性

配置文件

  • 复制二份eureka-server-8761的配置文件application-A|B.yml

  • application-A.yml 作为EurekaServerA注册中心的配置文件,application-B.yml 作为EurekaServerB注册中心的配置文件,

  • 修改A,B配置中的hostname,将service-url指向非自身的注册中心,register-with-eureka和fetch-registry改为true

    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
    #eureka serverA服务端口
    server:
    port: 8761
    spring:
    application:
    name: eureka-server # 应用名称,应用名称会在Eureka中作为服务名称
    # eureka 客户端配置(和Server交互),Eureka Server 其实也是一个Clien
    eureka:
    instance:
    hostname: EurekaServerA # 当前eureka实例的主机名
    client:
    service-url:
    # 配置客户端所交互的Eureka Server的地址
    #(Eureka Server集群中每一个Server其实相对于其它Server来说都是Client)
    # 集群模式下,defaultZone应该指向其它Eureka Server,如果有更多其它Server实例,逗号拼接即可
    defaultZone: http://EurekaServerB:8762/eureka/
    register-with-eureka: true # 单例是false,集群模式下改成 true
    fetch-registry: true # 集群模式下可以改成true
    ##单例模式下自己就是服务不需要从Eureka Server获取服务信息,默认为true,置为false
    ##集群模式下,就需要改为true,从集群上获取信息
    dashboard:
    enabled: true

    #eureka serverB服务端口
    server:
    port: 8762
    spring:
    application:
    name: eureka-server # 应用名称,应用名称会在Eureka中作为服务名称
    # eureka 客户端配置(和Server交互),Eureka Server 其实也是一个Clien
    eureka:
    instance:
    hostname: EurekaServerB # 当前eureka实例的主机名
    client:
    service-url:
    # 配置客户端所交互的Eureka Server的地址
    #(Eureka Server集群中每一个Server其实相对于其它Server来说都是Client)
    # 集群模式下,defaultZone应该指向其它Eureka Server,如果有更多其它Server实例,逗号拼接即可
    defaultZone: http://EurekaServerA:8761/eureka/
    register-with-eureka: true # 单例是false,集群模式下改成true
    fetch-registry: true # 集群模式下可以改成true
    ##单例模式下自己就是服务不需要从Eureka Server获取服务信息,默认为true,置为false
    ##集群模式下,就需要改为true,从集群上获取信息
    dashboard:
    enabled: true

启动二个服务

  • application.yml 配置文件中指定环境

    1
    2
    3
    spring:
    profiles:
    active: A
  • 我想使用双节点的注册中心,每次执行修改配置文件不太好,可以复制EurekaServerA的配置,然后可以通过命令行指定VM参数实现切换 -Dspring.profiles.active

  • 也可以在父工程中配置profiles,避免添加环境变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <profiles>
    <profile>
    <!-- 开发环境A -->
    <id>A</id>
    <activation>
    <!-- 默认开发环境 -->
    <activeByDefault>true</activeByDefault>
    </activation>
    <properties>
    <activatedProperties>A</activatedProperties>
    </properties>
    </profile>
    <profile>
    <!-- 开发环境B -->
    <id>B</id>
    <properties>
    <activatedProperties>B</activatedProperties>
    </properties>
    </profile>
    </profiles>
  • 在子项目中使用profiles的属性

    1
    2
    3
    spring:
    profiles:
    active: @activatedProperties@
  • 子项目的pom文件中添加参数保证profiles的属性能成功读取

    1
    2
    3
    4
    5
    6
    7
    8
    <build>
    <resources>
    <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
    </resource>
    </resources>
    </build>
  • 那么切换配置的时候,选择我门想要的环境,直接运行就可以运行了

观察信息

自我保护

  • 手动结束掉EurekB的服务后,查看面板信息,发现触发了自我保护机制,如果renews(last min) < renews threshold 就会激活自我保护模式

关闭自我保护

  • 修改注册中心的配置文件

    1
    2
    3
    4
    5
    6
    eureka:
    server:
    # 关闭自我保护机制(生产环境推荐打开)
    enable-self-preservation: false
    # 如过2秒类没有收到微服务心跳,就剔除该服务,单位毫秒
    eviction-interval-timer-in-ms: 2000
  • 客户端的配置

    1
    2
    3
    4
    5
    6
    eureka:
    instance:
    # eureka客户端发送心跳的间隔时间设置为1s,单位秒,默认30s
    lease-renewal-interval-in-seconds: 1
    # eureka客户端接收到心跳后等待时间上限是2s,默认90秒,超时3次就会剔除该服务
    lease-expiration-duration-in-seconds: 2
  • 手动停止ServerB后

注册服务到集群

  • 在上面的项目上进行修改

  • service-url指定多个Eurekaserver地址

1
2
3
4
5
6
7
8
eureka:
client:
service-url:
# 配置客户端所交互的Eureka Server的地址
# 注册到集群,就把多个Eurekaserver地址使用逗号连接起来即可;注册到单实例(非集群模式),那就写一个就ok
defaultZone:
- http://EurekaServerA:8761/eureka/
- http://EurekaServerB:8762/eureka/
  • 启动类添加注解 EnableDiscoveryClient

    • @EnableDiscoveryClient@EnableEurekaClient⼆者的功能是一样的
    • @EnableEurekaClient 开启Eureka Client(Eureka独有)
    • @EnableDiscoveryClient: 开启注册中心客户端 (通用型注解,比如注册到Eureka、Nacos等)
    • 说明:从SpringCloud的Edgware版本开始,不加注解也ok,但是建议大家加上
    1
    2
    3
    4
    5
    6
    7
    8
    @SpringBootApplication
    @EnableDiscoveryClient // 开启服务发现
    public class ResumeApplication {

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

提供服务接口

  • 编写服务API

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @RestController
    @SuppressWarnings(value = {"deprecation"})
    public class HelloController {

    @GetMapping({"/hello/{name}","/hello"})
    public Map<String, Object> hello(@PathVariable(value = "name", required = false) String name) {
    Map<String, Object> map = new HashMap<>();
    if (StringUtils.isEmpty(name)) {
    map.put("message", "hello, I am resume server.");
    } else {
    map.put("message", name);
    }

    return map;
    }
    }

通过Eureka调用服务提供者

  • 建立子项目作为微服务消费者 service-autodeliver-8090

  • 引入eureka client 客户端依赖

    1
    2
    3
    4
    5
    <!--eureka client 客户端依赖引入-->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

配置application.yml文件

  • 指定Eureka的地址,应用名称

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    server:
    port: 8090
    spring:
    application:
    name: service-autodeliver # 应用名称,应用名称会在Eureka中作为服务名称
    #注册到Eureka服务中心
    eureka:
    client:
    service-url:
    # 注册到集群,就把多个Eurekaserver地址使用逗号连接起来即可;注册到单实例(非集群模式),那就写一个就ok
    defaultZone:
    - http://EurekaServerA:8761/eureka/
    - http://EurekaServerB:8762/eureka/ instance:
    prefer-ip-address: true #服务实例中显示ip,而不是显示主机名(兼容老的eureka版本)
    # 实例名称: 192.168.1.103:service-autodeliver:8080,我们可以自定义它
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

启动类添加注解

  • 启动类如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @SpringBootApplication
    @EnableDiscoveryClient // 开启服务发现
    public class AutodeliverApplication8090 {

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

调用服务提供者

  • 配置restTemplate

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Configuration
    public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
    /** 读超时单位为ms */
    factory.setReadTimeout(10000);
    /** 连接超时单位为ms */
    factory.setConnectTimeout(10000);
    return factory;
    }
    }
  • 编写controller层

    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
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    @RestController
    public class AutodeliverController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    /**
    * 服务注册到Eureka之后的改造
    *
    * @param userId
    * @return
    */
    @GetMapping("/checkState/{userId}")
    public Map findResumeOpenState(@PathVariable Long userId) {
    // TODO 从Eureka Server中获取我们关注的那个服务的实例信息以及接口信息
    // 1、从 Eureka Server中获取lagou-service-resume服务的实例信息(使用客户端对象做这件事)
    List<ServiceInstance> instances = discoveryClient.getInstances("service-resume-8080");
    // 2、如果有多个实例,选择一个使用(负载均衡的过程)
    ServiceInstance serviceInstance = instances.get(0);
    // 3、从元数据信息获取host port 拼接请求地址
    String host = serviceInstance.getHost();
    int port = serviceInstance.getPort();
    String url = "http://" + host + ":" + port + "/hello/" + userId;
    System.out.println("===============>>>从EurekaServer集群获取服务实例拼接的url:" + url);
    // 4、消费者之间调用服务提供者
    // 调用远程服务—> 简历微服务接口 RestTemplate -> JdbcTempate
    // httpclient封装好多内容进行远程调用
    Map forObject = restTemplate.getForObject(url, Map.class);
    return forObject;
    }
    }

测试

参考