服务注册Eureka基础
1 微服务的注册中心
1.1 概述
- 注册中心可以说是微服务架构中的“通讯录”,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其他服务的时候,就从这里找到服务的地址,进行调用。
1.2 注册中心的主要作用
- 服务注册中心(简称注册中心)是微服务架构非常重要的一个组件,在微服务架构里面起到了协调者的作用。注册中心一般包含如下几个功能:
- 1️⃣服务发现:
- 服务注册/反注册:保存服务提供者和服务调用者的信息。
- 服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功能。
- 服务路由(可选):具有筛选整合服务提供者的能力。
- 2️⃣服务配置:
- 配置订阅:服务提供者和服务调用者订阅服务相关的配置。
- 配置下发:主动将配置推送给服务提供者和服务调用者。
- 3️⃣服务健康检测:
- 检测服务提供者的健康状况。
1.3 常见的注册中心
1.3.1 zookeeper
- zookeeper是一个分布式服务框架,是Apache Hadoop的一个子项目,它主要是用来解决分布式应用经常遇到的一些数据管理问题,如统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等等。
- 简单的说,zookeeper=文件系统+监听通知机制。
1.3.2 Eureka
- Eureka是在Java语言上,基于RESTful API开发的服务注册和发现组件,SpringCloud Netflix中的重要组件。
1.3.3 Consul
- Consul是HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发现和注册软件,采用Raft算法保证服务的一致性,且支持健康检查。
1.3.4 Nacos
- Nacos是一个更基于构建云原生应用的动态服务发现、配置管理和服务管理平台。简单的说,Nacos就是注册中心+配置中心的组合,提供简单易用的特性集,帮助我们解决微服务开发必会涉及到的服务注册和发现、服务配置、服务管理等问题。Nacos还是SpringCloud Alibaba组件之一,负责服务注册和发现。
1.3.5 总结
组件名 | 语言 | CAP | 一致性算法 | 服务健康检查 | 对外暴露接口 |
---|---|---|---|---|---|
Eureka | Java | AP | 无 | 可以配置支持 | HTTP |
Consul | Go | CP | Raft | 支持 | HTTP/DNS |
Zookeeper | Java | CP | Paxos | 支持 | 客户端 |
Nacos | Java | AP | Raft | 支持 | HTTP |
2 Eureka的概述
2.1 Eureka的基础知识
- Eureka是Netflix开发的服务发现框架,SpringCloud将它集成到自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。
- 上图简要描述了Eureka的基本架构,由3个角色组成:
- 1️⃣Eureka Server:提供服务注册和发现。
- 2️⃣Service Provider:服务提供方(将自身服务注册到Eureka,从而使得服务消费方能够找到)。
- 3️⃣Service Consumer:服务消费方(从Eureka获取注册服务列表,从而能够消费服务)。
2.2 Eureka的交互流程和原理
- 由上图可知,Eureka包含两个组件:Eureka Server和Eureka Client,它们的作用如下:
- 1️⃣Eureka Client是一个Java客户端,用于简化和Eureka Server的交互。
- 2️⃣Eureka Server提供服务发现能力,各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息。
- 3️⃣微服务启动后,会周期性的向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内(默认为90秒)没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点。
- 4️⃣每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册表的同步。
- 5️⃣Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕机,服务消费者依然可以使用缓存中的信息找到服务提供者。
- 综上,Eureka通过心跳检测、健康检查和客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。
3 Eureka的使用步骤
- 1️⃣搭建Eureka Server。
- 创建工程(eureka_server)。
- 导入Eureka对应的坐标。
- 配置application.yml。
- 配置启动类。
- 2️⃣将服务提供者注册到Eureka Server上。
- 3️⃣服务消费者通过注册中心获取服务列表,并调用。
4 搭建Eureka Server(注册中心)
4.1 搭建Eureka Server(注册中心)
4.1.1 在pom.xml中导入相关jar包的坐标
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.sunxiaping</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka_service</artifactId>
<dependencies>
<!-- 导入Eureka Server对应的坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
4.1.2 配置yml
- application.yml
server:
port: 9000 #端口
#配置Eureka Server
eureka:
instance:
# 主机地址名称
hostname: localhost
client:
register-with-eureka: false # 是否将自己注册到注册中心
fetch-registry: false # 是否从Eureka中获取服务列表
service-url: # 配置暴露给Eureka Client的请求地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4.1.3 配置启动类
- EurekaApplication.java
package com.sunxiaping.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author 许威威
* @version 1.0
*/
@SpringBootApplication
@EnableEurekaServer //开启Eureka Server
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
4.2 服务中心管理平台
- 打开浏览器访问http://localhost:9000/,即可进入Eureka Server内置的管理控制台,显示效果如下:
5 服务注册到Eureka注册中心
5.1 在商品微服务中引入Eureka Client的坐标
- 修改部分:
<!-- 导入Eureka Client对应的坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 完整部分:
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.sunxiaping</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product_service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 导入Eureka Client对应的坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.2.RELEASE</version>
<configuration>
<fork>true</fork>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
5.2 修改yml配置文件
- application.yml
server:
port: 9001 # 微服务的端口号
spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
# 配置 eureka
eureka:
client:
service-url: # Eureka Server的地址
defaultZone: http://localhost:9000/eureka/
5.3 配置启动类
- ProductApplication.java
package com.sunxiaping.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient //开启Eureka Client
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
5.4 测试
5.5 actuator与注册微服务信息完善
5.5.1 主机名:服务名称修改
- 当前问题:
- 修改服务提供者的application.yml文件。
- 修改部分:
# 配置 eureka
eureka:
instance:
# 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
instance-id: service-product:9001
client:
service-url: # Eureka Server的地址
defaultZone: http://localhost:9000/eureka/
- 完整部分:
server:
port: 9001 # 微服务的端口号
spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
# 配置 eureka
eureka:
instance:
# 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
instance-id: service-product:9001
client:
service-url: # Eureka Server的地址
defaultZone: http://localhost:9000/eureka/
- 修改之后:
5.5.2 显示IP信息
- 当前问题:
- 修改服务提供者的application.yml文件。
- 修改部分:
# 配置 eureka
eureka:
instance:
# 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
instance-id: service-product:9001
# 显示IP信息
prefer-ip-address: true
client:
service-url: # Eureka Server的地址
defaultZone: http://localhost:9000/eureka/
- 完整部分:
server:
port: 9001 # 微服务的端口号
spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
# 配置 eureka
eureka:
instance:
# 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
instance-id: service-product:9001
# 显示IP信息
prefer-ip-address: true
client:
service-url: # Eureka Server的地址
defaultZone: http://localhost:9000/eureka/
- 修改之后:
5.5.3 微服务info内容详细信息
- 当前问题:点击超链接报告ErrorPage
- 修改服务提供者的pom.xml。
- 修改部分:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 完整部分:
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.sunxiaping</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product_service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 导入Eureka Client对应的坐标 -->
<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-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.2.RELEASE</version>
<configuration>
<fork>true</fork>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
-
在总工程添加build信息。
-
修改部分:
<build>
<finalName>spring_cloud_demo</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<delimit>$</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
- 完整部分:
<?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>
<packaging>pom</packaging>
<modules>
<module>product_service</module>
<module>spring_cloud_common</module>
<module>order_service</module>
<module>eureka_service</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>org.sunxiaping</groupId>
<artifactId>spring_cloud_demo</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/libs-release-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<finalName>spring_cloud_demo</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<delimit>$</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 修改服务提供者的application.yml文件。
- 修改部分:
# 微服务info内容详细信息
info:
app.name: xxx
company.name: xxx
build.artifactId: $project.artifactId$
build.version: $project.version$
- 完整部分:
server:
port: 9001 # 微服务的端口号
spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
# 配置 eureka
eureka:
instance:
# 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
instance-id: service-product:9001
# 显示IP信息
prefer-ip-address: true
client:
service-url: # Eureka Server的地址
defaultZone: http://localhost:9000/eureka/
# 微服务info内容详细信息
info:
app.name: xxx
company.name: xxx
build.artifactId: $project.artifactId$
build.version: $project.version$
- 修改之后:
6 Eureka的元数据
6.1 概述
- Eureka的元数据有两种:标准元数据和自定义元数据。
- 1️⃣标注元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会发布在服务注册表中,用于服务之间的调用。
- 2️⃣自定义元数据:可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这些元数据可以在远程客户端中访问。
6.2 服务消费者通过注册中心获取服务列表,并调用
6.2.1 在订单微服务中引入Eureka Client的坐标
- 修改部分:
<!-- 导入Eureka Client对应的坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 完整部分:
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.sunxiaping</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order_service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 导入Eureka Client对应的坐标 -->
<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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
6.2.2 修改yml配置文件
- application.yml
server:
port: 9002 # 微服务的端口号
spring:
application:
name: service-order # 微服务的名称
datasource:
url: jdbc:mysql://192.168.1.57:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
# 配置Eureka
eureka:
instance:
# 实例的名称
instance-id: service-order:9002
# 显示IP信息
prefer-ip-address: true
client:
service-url: # Eureka Server的地址
defaultZone: http://localhost:9000/eureka/
# 微服务info内容详细信息
info:
app.name: xxx
company.name: xxx
build.artifactId: $project.artifactId$
build.version: $project.version$
6.2.3 修改Controller
- OrderController.java
package com.sunxiaping.order.controller;
import com.sunxiaping.order.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.List;
@RestController
@RequestMapping(value = "/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
/**
* SpringCloud提供的获取元数据的工具类
* 调用方法获取服务的元数据
*/
@Autowired
private DiscoveryClient discoveryClient;
/**
* 通过订单系统,调用商品微服务根据id查询商品信息
*
* @param id
* @return
*/
@GetMapping(value = "/buy/{id}")
public Product buy(@PathVariable(value = "id") Long id) {
List<ServiceInstance> instanceList = discoveryClient.getInstances("service-product");
ServiceInstance serviceInstance = instanceList.get(0);
URI uri = serviceInstance.getUri();
if (null != uri) {
return restTemplate.getForObject(uri.toString() + "/product/findById/" + id, Product.class);
}
return null;
}
}
7 Eureka中的自我保护
7.1 概述
- 微服务第一次注册成功后,每30秒会发送一次心跳将服务的实例信息注册到注册中心。通知Eureka Server该实例依然存在。如果超过90秒没有发送心跳,则服务器将从注册中心将此服务移除。
- Eureka Server在运行期间,会统计心跳失败的比例在15分钟内是否低于85%,如果出现低于85%的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致),那么Eureka就会认为客户端和注册中心出现了网络故障,此时会做如下的处理:
- 1️⃣Eureka不再从注册列表中删除因为长时间没有收到心跳而应该过期的服务。
- 2️⃣Eureka仍然能够接受新服务的注册和查询请求,但是不会同步到其他节点上(保证当前节点依然可用)。
- 3️⃣当网络稳定的时候,当前实例新的注册信息会被同步到其他节点中。
- 验证自我保护机制开启,并不会马上呈现在web后台上,而是默认需要等待5分钟(可以通过eureka.server.wait-time-in-ms-when-sync-empty配置),即5分钟后你就会看到如下所示的提示信息:
7.2 如何关闭自我保护机制
- 通过设置
eureka.enableSelfPreservation=false
来关闭自我保护机制。