目录
一、简介
二、Eureka两大组件
三、Eureka服务注册与发现
示例1
四、Eureka Server集群
示例2
五、Eureka 自我保护机制
SpringCloud将Eureka与Netflix中的其他开源服务组件一起整合进Spring Cloud Netflix模块中,Eureka是其中的子组件之一。主要负责Spring Cloud的服务注册与发现功能。
Spring Cloud使用Spring Boot思想为Eureka增加了自动化配置,开发人员只需要引入相关依赖和注解,就能将Spring Boot构建的微服务轻松与Eureka整合。
Eureka采用CS(Client/Server,客户端/服务器)架构。包括以下两大组件:
Eureka实现服务注册于发现的原理:
Eureka实现服务注册与发现的流程如下:
服务注册中心(Eureka Server)所扮演的角色十分重要,它是服务提供者和服务消费者之间的桥梁。服务提供者只有将自己的服务注册到服务注册中心才可能被服务消费者调用,而服务消费者也只有通过服务注册中心获取服务注册中心获取可用服务列表后,才能调用所需的服务。
下面,我们通过一个案例来展示一下Eureka是如何实现服务注册与发现的。
1、创建主工程(Maven Project)
由于本案例中,会涉及到多个由SpringBoot创建的微服务,为了方便管理,这里我们采用Maven的多Module结构(即一个Project包含多个Module)来构建工程。
创建一个名为spring-cloud-demo2的Maven主工程,然后在该工程的pom.xml中使用dependencyManagement来管理SpringCloud的版本,内容如下。
4.0.0
pom
micro-service-cloud-api
org.springframework.boot
spring-boot-starter-parent
2.3.6.RELEASE
net.biancheng.c
spring-cloud-demo2
0.0.1-SNAPSHOT
8
8
UTF-8
1.8
1.8
4.12
1.2.17
1.16.18
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR12
pom
import
microservicecloud
src/main/resources
true
org.apache.maven.plugins
maven-resources-plugin
$
2、创建公共子模块(Maven Module)
1.在主工程下,创建一个名为micro-service-cloud-api的Maven Module: micro-service-cloud-api,其pom.xml配置如下:
spring-cloud-demo2
net.biancheng.c
0.0.1-SNAPSHOT
4.0.0
micro-service-cloud-api
8
8
org.projectlombok
lombok
注:micro-service-cloud-api是整个工程的公共子模块,它包含了一些其他子模块共有的内容,例如实体类,公共工具类,公共依赖类等。当其他子模块需要使用公共子模块中的内容时,只需要在其pom.xml引入公共子模块的依赖即可。
2.在micro-service-cloud-api的net.biancheng.c.entity包下,创建一个名为Dept的实体类,代码如下。
package net.biancheng.c.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@NoArgsConstructor //无参构造函数
@Data // 提供类的get、set、equals、hashCode、canEqual、toString 方法
@Accessors(chain = true)
public class Dept implements Serializable {
private Integer deptNo;
private String deptName;
private String dbSource;
}
3.搭建服务注册中心
1.在主工程下创建一个名为micro-service-cloud-eureka-7001的SpringBoot Module作为服务注册中心,并在其pom.xml中引入以下依赖。
4.0.0
spring-cloud-demo2
net.biancheng.c
0.0.1-SNAPSHOT
net.biancheng.c
micro-service-cloud-eureka-7001
0.0.1-SNAPSHOT
micro-service-cloud-eureka-7001
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
2.在micro-service-cloud-eureka-7001的类路径(/resouces目录)下,添加一个配置文件application.yml,配置内容如下。
server:
port: 7001 #该 Module 的端口号
eureka:
instance:
hostname: localhost #eureka服务端的实例名称,
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机版服务注册中心
3.在micro-service-cloud-eurka-7001的主启动类上使用@EnableEurekaServer注解开启服务注册中心功能,接受其他服务的注册,代码如下。
package net.biancheng.c;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer //开启 Eureka server,接受其他微服务的注册
public class MicroServiceCloudEureka7001Application {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudEureka7001Application.class, args);
}
}
4.启动micro-service-cloud-eurka-7001,使用浏览器访问Eureka服务注册中心主页,地址为“http://localhost:7001/”,结果如下图。
4.搭建服务提供者
1.在主工程下创建一个名为micro-service-cloud-provider-dept-8001的Spring Boot Module,并在其pom.xml中引入以下依赖。
4.0.0
spring-cloud-demo2
net.biancheng.c
0.0.1-SNAPSHOT
net.biancheng.c
micro-service-cloud-provider-dept-8001
0.0.1-SNAPSHOT
micro-service-cloud-provider-dept-8001
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
true
org.springframework.boot
spring-boot-starter-test
test
net.biancheng.c
micro-service-cloud-api
${project.version}
junit
junit
4.12
mysql
mysql-connector-java
5.1.49
ch.qos.logback
logback-core
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.0
org.springframework
springloaded
1.2.8.RELEASE
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-actuator
org.mybatis.generator
mybatis-generator-maven-plugin
1.4.0
src/main/resources/mybatis-generator/generatorConfig.xml
true
true
mysql
mysql-connector-java
5.1.49
org.mybatis.generator
mybatis-generator-core
1.4.0
org.springframework.boot
spring-boot-maven-plugin
2.在micro-service-cloud-provider-dept-8001类路径(/resources目录)下,添加配置文件application.yml,配置内容如下。
server:
port: 8001 #服务端口号
spring:
application:
name: microServiceCloudProviderDept #微服务名称,对外暴漏的微服务名称,十分重要
################################################## JDBC 通用配置 ##########################################
datasource:
username: root #数据库登陆用户名
password: root #数据库登陆密码
url: jdbc:mysql://127.0.0.1:3306/bianchengbang_jdbc #数据库url
driver-class-name: com.mysql.jdbc.Driver #数据库驱动
############################### 不检查 spring.config.import=configserver:##################
# cloud:
# config:
# enabled: false
###################################### MyBatis 配置 ######################################
mybatis:
# 指定 mapper.xml 的位置
mapper-locations: classpath:mybatis/mapper/*.xml
#扫描实体类的位置,在此处指明扫描实体类的包,在 mapper.xml 中就可以不写实体类的全路径名
type-aliases-package: net.biancheng.c.entity
configuration:
#默认开启驼峰命名法,可以不用设置该属性
map-underscore-to-camel-case: true
########################################### Spring cloud 自定义服务名称和 ip 地址###############################################
eureka:
client: #将客户端注册到 eureka 服务列表内
service-url:
defaultZone: http://eureka7001.com:7001/eureka #这个地址是 7001注册中心在 application.yml 中暴露出来额注册地址 (单机版)
instance:
instance-id: spring-cloud-provider-8001 #自定义服务名称信息
prefer-ip-address: true #显示访问路径的 ip 地址
########################################## spring cloud 使用 Spring Boot actuator 监控完善信息###################################
# Spring Boot 2.50对 actuator 监控屏蔽了大多数的节点,只暴露了 heath 节点,本段配置(*)就是为了开启所有的节点
management:
endpoints:
web:
exposure:
include: "*" # * 在yaml 文件属于关键字,所以需要加引号
info:
app.name: micro-service-cloud-provider-dept
company.name: c.biancheng.net
build.aetifactId: @project.artifactId@
build.version: @project.version@
3.在net.biancheng,c,mapper包下创建一个名为DeptMapper的接口,代码如下。
package net.biancheng.c.mapper;
import net.biancheng.c.entity.Dept;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DeptMapper {
//根据主键获取数据
Dept selectByPrimaryKey(Integer deptNo);
//获取表中的全部数据
List GetAll();
}
4.在resources/mybatis/mapper/目录下,创建一个名为DeptMapper.xml的MyBatis映射文件,配置内容如下。
dept_no
, dept_name, db_source
5.在net.biancheng.c.service包下创建一个名为DeptService的接口,代码如下。
package net.biancheng.c.service;
import net.biancheng.c.entity.Dept;
import java.util.List;
public interface DeptService {
Dept get(Integer deptNo);
List selectAll();
}
6.在net.biancheng.c.service.impl包下创建DeptService接口的实现类DeptServiceImpl,代码如下。
package net.biancheng.c.service.impl;
import net.biancheng.c.entity.Dept;
import net.biancheng.c.mapper.DeptMapper;
import net.biancheng.c.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("deptService")
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public Dept get(Integer deptNo) {
return deptMapper.selectByPrimaryKey(deptNo);
}
@Override
public List selectAll() {
return deptMapper.GetAll();
}
}
7.在net.biancheng.c.controller包下创建一个名为DeptController的Controller类,代码如下。
package net.biancheng.c.controller;
import lombok.extern.slf4j.Slf4j;
import net.biancheng.c.entity.Dept;
import net.biancheng.c.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 服务提供者的控制层
* author:c语言中文网 c.biancheng.net
*/
@RestController
@Slf4j
public class DeptController {
@Autowired
private DeptService deptService;
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") int id) {
return deptService.get(id);
}
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List list() {
return deptService.selectAll();
}
}
8.在micro-service-cloud-provider-dept-8001的主启动类上,使用@EnableEurekaClient注解开启Eureka客户端功能,将服务注册到服务注册中心(Eureka Server),代码如下。
package net.biancheng.c;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient // Spring cloud Eureka 客户端,自动将本服务注册到 Eureka Server 注册中心中
public class MicroServiceCloudProviderDept8001Application {
public static void main(String[] args) {
SpringApplication.run(MicroServiceCloudProviderDept8001Application.class, args);
}
}
9.依次启动micro-service-cloud-eurka-7001和micro-service-cloud-provider-dept-8001,使用浏览器再次访问Eureka服务注册中心主页(http://localhost:7001/),如下图。
从图中可以看到,Instances currently registered with Eureka(注册到Eureka Server的实例)选择中已经包含了一条服务信息,即已经有服务注册到Eureka Server上了。
Instances currently registered with Eureka选择中包含以下内容:
10.在MySQL的bianchengbang_jdbc数据库中执行以下SQL,准备测试数据。
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`dept_no` int NOT NULL AUTO_INCREMENT,
`dept_name` varchar(255) DEFAULT NULL,
`db_source` varchar(255) DEFAULT NULL,
PRIMARY KEY (`dept_no`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `dept` VALUES ('1', '开发部', 'bianchengbang_jdbc');
INSERT INTO `dept` VALUES ('2', '人事部', 'bianchengbang_jdbc');
INSERT INTO `dept` VALUES ('3', '财务部', 'bianchengbang_jdbc');
INSERT INTO `dept` VALUES ('4', '市场部', 'bianchengbang_jdbc');
INSERT INTO `dept` VALUES ('5', '运维部', 'bianchengbang_jdbc');
11.使用浏览器访问“http://licalhost:8001/dept/list”,结果如下图。
在微服务框架中,一个系统往往由十几个甚至几十个服务组成,若将这些服务全部注册到同一个Eureka Server中,就极有可能导致Eureka Server因不堪重负而崩溃,最终导致整个系统瘫痪,解决这个问题最直接的办法就是部署Eureka Server集群。
我们知道,在Eureka实现服务注册与发现时一共涉及了3个角色:服务注册中心、服务提供者以及服务消费者,这三个角色分工明确,各司其职。但是其实在Eureka中,所有服务都既是服务消费者也是服务提供者,服务注册中心也不例外。
我们在搭建服务注册中心时,在application.yml中涉及了这样的配置:
eureka:
client:
register-with-eureka: false #false 表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
这样设置的原因是micro-service-cloud-eureka-7001本身自己就是服务注册中心,服务注册中心是不能将自己注册到自己身上的,但服务注册中心是可以将自己作为服务向其他的服务注册中心注册自己的。
举个例子,有两个 Eureka Server 分别为 A 和 B,虽然 A 不能将自己注册到 A 上,B 也不能将自己注册到 B 上,但 A 是可以作为一个服务把自己注册到 B 上的,同理 B 也可以将自己注册到 A 上。
这样就可以形成一组互相注册的 Eureka Server 集群,当服务提供者发送注册请求到 Eureka Server 时,Eureka Server 会将请求转发给集群中所有与之相连的 Eureka Server 上,以实现 Eureka Server 之间的服务同步。
通过服务同步,服务消费者可以在集群中的任意一台 Eureka Server 上获取服务提供者提供的服务。这样,即使集群中的某个服务注册中心发生故障,服务消费者仍然可以从集群中的其他 Eureka Server 中获取服务信息并调用,而不会导致系统的整体瘫痪,这就是 Eureka Server 集群的高可用性。
下面我们在示例 1 的基础上进行扩展,构建一个拥有 3 个 Eureka Server 实例的集群。
1. 参照 micro-service-cloud-eureka-7001 的搭建过程,在主工程下另外再创建两个 Eureka Server:micro-service-cloud-eureka-7002 和 micro-service-cloud-eureka-7003,此时这 3 个 Eureka Server 无论是 Maven 依赖、代码还是配置都是一模一样的。
2. 修改 micro-service-cloud-eureka-7001、micro-service-cloud-eureka-7002、micro-service-cloud-eureka-7003 中 application.yml 的配置,具体配置如下 。
micro-service-cloud-eureka-7001 中 application.yml 的配置如下。
server:
port: 7001 #端口号
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false 表示不向注册中心注册自己。
fetch-registry: false #false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #单机版
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #集群版 将当前的 Eureka Server 注册到 7003 和 7003 上,形成一组互相注册的 Eureka Server 集群
micro-service-cloud-eureka-7002 中 application.yml 的配置如下。
server:
port: 7002 #端口号
eureka:
instance:
hostname: eureka7002.com #Eureka Server 实例名称
client:
register-with-eureka: false #false 表示不向注册中心注册自己。
fetch-registry: false #false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ #将这个 Eureka Server 注册到 7001 和 7003 上
micro-service-cloud-eureka-7003 中 application.yml 的配置如下。
server:
port: 7003 #端口号
eureka:
instance:
hostname: eureka7003.com #Eureka Server 实例名称
client:
register-with-eureka: false #false 表示不向注册中心注册自己。
fetch-registry: false #false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #将这个 Eureka Server 注册到 7001 和 7002 上
3. 由于我们是在本地搭建的 Eureka Server 集群,因此我们需要修改本地的 host 文件,Windows 操作系统的电脑在 C:/Windows/System/drivers/etc/hosts 中修改,Mac 系统的电脑则需要在 vim/etc/hosts 中修改,修改内容如下。
#Spring Cloud eureka 集群
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
4. 修改 micro-service-cloud-provider-dept-8001(服务提供者)配置文件 application.yml 中 eureka.client.service-url.defaultZone 的取值,将服务注册到 Eureka Server 集群上,具体配置如下。
eureka:
client: #将客户端注册到 eureka 服务列表内
service-url:
#defaultZone: http://eureka7001.com:7001/eureka #这个地址是 7001 注册中心在 application.yml 中暴露出来的注册地址 (单机版)
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #将服务注册到 Eureka Server 集群
5. 启动 micro-service-cloud-eureka-7001,使用浏览器访问“http://eureka7001.com:7001/”,结果如下图。
从上图可以看到, 服务提供者(micro-service-cloud-provider-dept-8001)的服务已经注册到了 Eureka Server 7001,并且在 DS Replicas 选项中也显示了集群中的另外两个 Eureka Server:Eureka Server 7002 和 Eureka Server 7003。
6. 启动 micro-service-cloud-eureka-7002,使用浏览器访问“http://eureka7002.com:7002/”,结果如下图。
7. 启动 micro-service-cloud-eureka-7003,使用浏览器访问“http://eureka7003.com:7003/”,结果如下图。
从上图可以看到, 服务提供者(micro-service-cloud-provider-dept-8001)所提供的服务已经注册到了 Eureka Server 7003,并且在 DS Replicas 选项中也显示了集群中的另外两个 Eureka Server:Eureka Server 7001 和 Eureka Server 7002。
自此我们就完成了 Eureka Server 集群的搭建和使用。
当我们在本地调试基于 Eureka 的程序时,Eureka 服务注册中心很有可能会出现如下图所示的红色警告。
实际上,这个警告是触发了 Eureka 的自我保护机制而出现的。默认情况下,如果 Eureka Server 在一段时间内(默认为 90 秒)没有接收到某个服务提供者(Eureka Client)的心跳,就会将这个服务提供者提供的服务从服务注册表中移除。 这样服务消费者就再也无法从服务注册中心中获取到这个服务了,更无法调用该服务。
但在实际的分布式微服务系统中,健康的服务(Eureka Client)也有可能会由于网络故障(例如网络延迟、卡顿、拥挤等原因)而无法与 Eureka Server 正常通讯。若此时 Eureka Server 因为没有接收心跳而误将健康的服务从服务列表中移除,这显然是不合理的。而 Eureka 的自我保护机制就是来解决此问题的。
所谓 “Eureka 的自我保护机制”,其中心思想就是“好死不如赖活着”。如果 Eureka Server 在一段时间内没有接收到 Eureka Client 的心跳,那么 Eureka Server 就会开启自我保护模式,将所有的 Eureka Client 的注册信息保护起来,而不是直接从服务注册表中移除。一旦网络恢复,这些 Eureka Client 提供的服务还可以继续被服务消费者消费。
综上,Eureka 的自我保护机制是一种应对网络异常的安全保护措施。它的架构哲学是:宁可同时保留所有微服务(健康的服务和不健康的服务都会保留)也不盲目移除任何健康的服务。通过 Eureka 的自我保护机制,可以让 Eureka Server 集群更加的健壮、稳定。
Eureka 的自我保护机制也存在弊端。如果在 Eureka 自我保护机制触发期间,服务提供者提供的服务出现问题,那么服务消费者就很容易获取到已经不存在的服务进而出现调用失败的情况,此时,我们可以通过客户端的容错机制来解决此问题,详情请参考 Spring Cloud Netflix Ribbon 和 Spring Cloud Netflix Hystrix。
默认情况下,Eureka 的自我保护机制是开启的,如果想要关闭,则需要在配置文件中添加以下配置。
eureka:
server:
enable-self-preservation: false # false 关闭 Eureka 的自我保护机制,默认是开启,一般不建议大家修改