Eureka:注册中心
:::info
专门用来管理各个微服务之间的通讯地址、调用、处理故障、通过服务名可以实现服务的调用,以及做服务监控
:::
1. EurekaServer & EurekaClient
这一部分可以类比着RocketMQ的原理看
:::info
EurekaServer(服务端)(注册中心):
-
注册中心也是一个需要启动的服务,他是依托于我们的服务进行启动的,也就意味着你需要准备一个SpringBoot项目整合EurekaServer,跑起来变成一个Eureka的服务端,此时注册中心就有了
:::
:::info
EurekaClient(客户端)(其他服务): -
在微服务中只要是一个服务都必须注册进入到Eureka中,所有注册进入到EurekaServer中的服务都被称为Eureka的客户端也就是EurekaClient
:::
2. Eureka提供的四大功能
- **服务注册:**在服务启动的时候把自己的服务名称、IP、Port注册到注册中心,那么也就意味着服务中需要指定Eureka的注册地址
- **服务续约:**服务与注册中心之间保持一个30S的心跳机制,这是为了保证让注册中心知道自己还活着
- **服务发现:**每隔30S去注册中心中定时拉取所有的服务的注册地址清单保存到本地,这是为了各个服务能够知道其他服务的地址进行调用
- **服务下线:**注册中心检测到服务已经死了,并且满足剔除条件那么就会将服务的所有信息进行清除,Eureka有自我保护机制,防止网络波动导致的大范围下线,默认85%阈值
3. Eureka自我保护机制
当Eureka Server节点在短时间内丢失过多的客户端实例时:
Eureka会进入自我保护模式,以防止整个服务注册中心失效。
:::info
自我保护模式的工作原理如下:
- Eureka Server会定时统计每分钟内客户端实例的心跳信息,包括注册数量、续约数量等。
- 如果最近15分钟内的心跳失败率超过阈值(默认为85%),Eureka Server就会触发自我保护模式。
- 进入自我保护模式后,Eureka Server会暂停剔除失效实例的操作,同时将注册表中的实例保护起来,不会过期。
- 客户端继续发送心跳续约请求,尝试恢复正常状态。
- 当心跳失败率下降到阈值以下时,Eureka Server会自动退出自我保护模式,并恢复正常的实例剔除操作。
:::
4. Eureka简单使用
4.1 创建springboot项目
这里使用Maven父子项目举例
项目结构:
4.2 项目相关依赖
:::info
这里我使用的是:
SpringCloud-H版本-SR3
SpringBoot:2.2.X
:::
需要注意对应SpringCloud和SpringBoot的版本
<?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.noting</groupId>
<artifactId>SpringCloud-Netflix</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>eureka-server</module>
<module>pojo</module>
<module>server</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- springboot项目依赖,表明是springboot父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<!-- 父项目管理微服务的所有依赖,但是不引用,服务之间自己引用需要的依赖
统一管理依赖项版本
简化子模块的配置
避免重复定义
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 项目需要的的公共依赖 -->
<dependencies>
<!--小辣椒lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
4.3 注册中心服务端
- 依赖
<?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>SpringCloud-Netflix</artifactId>
<groupId>com.noting</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- springweb依赖,-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 服务端依赖,-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
- 服务端配置
server:
port: 8761 # 标识当前Eureka服务端的端口号
eureka:
instance:
hostname: localhost # 当前Eurek的实例名称
client:
registerWithEureka: false # 在客户端依赖中默认为true,所以服务端需要关闭
fetchRegistry: false # 在客户端依赖中默认为true,所以服务端需要关闭
serviceUrl: # http://localhost:8761/eureka/
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 启动类配置
package com.noting;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer // 标识当前服务是一个Eureka的服务端
public class EurekaServerApp {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApp.class, args);
}
}
- 访问效果
4.4 跨服务使用类
:::info
将被使用的服务打包,再在使用者的pom.xml中导入依赖
注意!需要注意防止出现循环依赖
:::
<!-- 导入的user-pojo的依赖 -->
<dependency>
<groupId>com.noting</groupId>
<artifactId>user-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
4.5 客户端
- 依赖
<?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>server</artifactId>
<groupId>com.noting</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 导入的user-pojo的依赖 -->
<dependency>
<groupId>com.noting</groupId>
<artifactId>user-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--springweb依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 表示注册到eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
- 客户端配置
server:
port: 10011
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/ # 表示注册中心的地址
instance: # 打开IP注册
instance-id: ${spring.application.name}:${server.port} # 设置实例名称
prefer-ip-address: true # 开启IP注册
# 指定服务名称,此服务下的集群所有服务都叫此服务名
spring:
application:
name: user-server
- 启动类配置
package com.noting;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient // 这个注解可以省略,因为导入了client依赖
public class userServerApp {
public static void main(String[] args) {
SpringApplication.run(userServerApp.class, args);
}
}
- 接口:可以浏览器访问
package com.noting.controller;
import com.noting.domain.User;
import org.springframework.beans.factory.annotation.Value;
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;
@RestController
@RequestMapping("/user")
public class userController {
@Value("${server.port}")
private String port;
@GetMapping("/{id}")
public User getUser(@PathVariable("id") Long id){
User user = new User(id, "三无鸢", "学习微服务-----" + port);
return user;
}
}
4.6 客户端简单设置集群
- 启动客户端之后修改配置文件端口号
- 最后启动刚刚复制修改启动项
4.7 服务A调用另外的服务B(集群)的接口
注意调用方式和负载均衡
A接口
package com.noting.controller;
import com.noting.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@RequestMapping("/order")
public class orderController {
@Autowired
// RestTemplate 用于发送 HTTP 请求的类,通过调用其 getForObject() 方法来发起 GET 请求。
private RestTemplate restTemplate;
@GetMapping("/{id}")
public User getUser(@PathVariable("id") Long id){
// 这里由于使用user使用了集群,并且restTemplate设置了负载均衡,这里调用的地址写为eureka的客户端的服务名
User user = restTemplate.getForObject("http://user-server/user/" + id, User.class);
return user;
}
}
A启动类
package com.noting;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class orderServerApp {
public static void main(String[] args) {
SpringApplication.run(orderServerApp.class, args);
}
// 此注解的意思是将方法中响应的对象交给spring容器管理,此注解必须在@Configuration注解的类中使用
// 方法名称就是容器中Bean的名称
@Bean
// 复杂均衡的的注解,让RestTemplate具有负载均衡的能力
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
// 配置负载均衡为随机算法
@Bean
public RandomRule randomRule(){
return new RandomRule();
}
}
B接口
package com.noting.controller;
import com.noting.domain.User;
import org.springframework.beans.factory.annotation.Value;
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;
@RestController
@RequestMapping("/user")
public class userController {
@Value("${server.port}")
private String port;
@GetMapping("/{id}")
public User getUser(@PathVariable("id") Long id){
User user = new User(id, "三无鸢", "学习微服务-----" + port);
return user;
}
}