增加README.md文章导航
This commit is contained in:
parent
b43a723910
commit
1bf52fa739
@ -12,12 +12,14 @@
|
||||
<a href="#14-查看监控状态">1.4 查看监控状态</a><br/>
|
||||
<a href="#三自定义健康检查指标">三、自定义健康检查指标</a><br/>
|
||||
<a href="#四自定义健康状态聚合规则">四、自定义健康状态聚合规则</a><br/>
|
||||
<a href="#五@Endpoint自定义端点">五、@Endpoint自定义端点</a><br/>
|
||||
<a href="#五Endpoint自定义端点">五、@Endpoint自定义端点</a><br/>
|
||||
<a href="#51-自定义端点">5.1 自定义端点</a><br/>
|
||||
<a href="#52-访问自定义端点http//1270018080/actuator/customEndPoint">5.2 访问自定义端点http://127.0.0.1:8080/actuator/customEndPoint</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、用例涉及到的概念综述
|
||||
|
||||
### 1.1 端点
|
||||
|
@ -12,6 +12,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构说明
|
||||
|
@ -10,6 +10,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构
|
||||
|
@ -11,6 +11,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构
|
||||
|
@ -5,15 +5,17 @@
|
||||
<a href="#三公共模块boot-dubbo-common">三、公共模块(boot-dubbo-common)</a><br/>
|
||||
<a href="#四-服务提供者boot-dubbo-provider">四、 服务提供者(boot-dubbo-provider)</a><br/>
|
||||
<a href="#41-提供方配置">4.1 提供方配置</a><br/>
|
||||
<a href="#42--使用注解@Service暴露服务">4.2 使用注解@Service暴露服务</a><br/>
|
||||
<a href="#42--使用注解Service暴露服务">4.2 使用注解@Service暴露服务</a><br/>
|
||||
<a href="#五服务消费者boot-dubbo-consumer">五、服务消费者(boot-dubbo-consumer)</a><br/>
|
||||
<a href="#1消费方的配置">1.消费方的配置</a><br/>
|
||||
<a href="#2使用注解@Reference引用远程服务">2.使用注解@Reference引用远程服务</a><br/>
|
||||
<a href="#2使用注解Reference引用远程服务">2.使用注解@Reference引用远程服务</a><br/>
|
||||
<a href="#六项目构建的说明">六、项目构建的说明</a><br/>
|
||||
<a href="#七关于dubbo新版本管理控制台的安装说明">七、关于dubbo新版本管理控制台的安装说明</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、 项目结构说明
|
||||
|
||||
1.1 按照dubbo 文档推荐的服务最佳实践,建议将服务接口、服务模型、服务异常等均放在 API 包中,所以项目采用maven多模块的构建方式,在spring-boot-dubbo下构建三个子模块:
|
||||
|
@ -10,6 +10,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构
|
||||
|
@ -12,7 +12,7 @@
|
||||
<a href="#二-整合-kafka">二、 整合 kafka</a><br/>
|
||||
<a href="#21-kafka基本配置">2.1 kafka基本配置</a><br/>
|
||||
<a href="#22-KafkaTemplate实现消息发送">2.2 KafkaTemplate实现消息发送</a><br/>
|
||||
<a href="#23--@KafkaListener注解实现消息的监听">2.3 @KafkaListener注解实现消息的监听</a><br/>
|
||||
<a href="#23--KafkaListener注解实现消息的监听">2.3 @KafkaListener注解实现消息的监听</a><br/>
|
||||
<a href="#24-测试整合结果">2.4 测试整合结果</a><br/>
|
||||
<a href="#三关于多消费者组的测试">三、关于多消费者组的测试</a><br/>
|
||||
<a href="#31--创建多分区主题">3.1 创建多分区主题</a><br/>
|
||||
@ -23,6 +23,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、kafka的相关概念:
|
||||
|
||||
### 1.主题和分区
|
||||
|
@ -13,6 +13,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
### 1.1 XMemcached客户端说明
|
||||
|
@ -10,6 +10,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 用例结构
|
||||
|
@ -10,6 +10,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构
|
||||
|
@ -5,7 +5,7 @@
|
||||
<a href="#三公共模块rabbitmq-common">三、公共模块(rabbitmq-common)</a><br/>
|
||||
<a href="#四服务消费者rabbitmq-consumer">四、服务消费者(rabbitmq-consumer)</a><br/>
|
||||
<a href="#41-消息消费者配置">4.1 消息消费者配置</a><br/>
|
||||
<a href="#42-使用注解@RabbitListener和@RabbitHandler创建消息监听者">4.2 使用注解@RabbitListener和@RabbitHandler创建消息监听者</a><br/>
|
||||
<a href="#42-使用注解RabbitListener和RabbitHandler创建消息监听者">4.2 使用注解@RabbitListener和@RabbitHandler创建消息监听者</a><br/>
|
||||
<a href="#五-消息生产者rabbitmq-producer">五、 消息生产者(rabbitmq-producer)</a><br/>
|
||||
<a href="#51-消息生产者配置">5.1 消息生产者配置</a><br/>
|
||||
<a href="#52--创建消息生产者">5.2 创建消息生产者</a><br/>
|
||||
@ -14,6 +14,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、 项目结构说明
|
||||
|
||||
1.1 之前关于spring 整合 rabbitmq 我们采用的是单项目的方式,为了使得用例更具有实际意义,这里采用maven多模块的构建方式,在spring-boot-rabbitmq下构建三个子模块:
|
||||
|
@ -32,6 +32,8 @@
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构
|
||||
|
@ -7,11 +7,13 @@
|
||||
<a href="#21-新建过滤器监听器和servlet">2.1 新建过滤器、监听器和servlet</a><br/>
|
||||
<a href="#22-注册过滤器监听器和servlet">2.2 注册过滤器、监听器和servlet</a><br/>
|
||||
<a href="#三采用注解方式整合-servlet">三、采用注解方式整合 servlet</a><br/>
|
||||
<a href="#31-新建过滤器监听器和servlet分别使用@WebFilter@WebListener@WebServlet注解标注">3.1 新建过滤器、监听器和servlet,分别使用@WebFilter、@WebListener、@WebServlet注解标注</a><br/>
|
||||
<a href="#31-新建过滤器监听器和servlet分别使用WebFilterWebListenerWebServlet注解标注">3.1 新建过滤器、监听器和servlet,分别使用@WebFilter、@WebListener、@WebServlet注解标注</a><br/>
|
||||
<a href="#32-使注解生效">3.2 使注解生效</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构说明
|
||||
|
@ -1,2 +0,0 @@
|
||||
@Profile({"dev","test"})
|
||||
@ConditionalOnProperty(name = "swagger.enable", havingValue = "true")
|
@ -9,6 +9,8 @@
|
||||
<a href="#23-新建controller和showjsp-测试整合是否成功">2.3 新建controller和show.jsp 测试整合是否成功</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
#### 1.1 项目结构说明
|
||||
|
@ -4,12 +4,14 @@
|
||||
<a href="#11-项目结构说明">1.1 项目结构说明</a><br/>
|
||||
<a href="#12-主要依赖">1.2 主要依赖</a><br/>
|
||||
<a href="#二spring-boot-websocket">二、spring boot websocket</a><br/>
|
||||
<a href="#21-创建消息处理类ChatSocket使用@ServerEndpoint声明websocket服务">2.1 创建消息处理类ChatSocket,使用@ServerEndpoint声明websocket服务</a><br/>
|
||||
<a href="#22-配置ServerEndpointExporterServerEndpointExporter会在运行时候自动注册我们用@ServerEndpoint声明的websocket服务。">2.2 配置ServerEndpointExporter,ServerEndpointExporter会在运行时候自动注册我们用@ServerEndpoint声明的websocket服务。</a><br/>
|
||||
<a href="#21-创建消息处理类ChatSocket使用ServerEndpoint声明websocket服务">2.1 创建消息处理类ChatSocket,使用@ServerEndpoint声明websocket服务</a><br/>
|
||||
<a href="#22-配置ServerEndpointExporterServerEndpointExporter会在运行时候自动注册我们用ServerEndpoint声明的websocket服务。">2.2 配置ServerEndpointExporter,ServerEndpointExporter会在运行时候自动注册我们用@ServerEndpoint声明的websocket服务。</a><br/>
|
||||
<a href="#23-前端websocket的实现">2.3 前端websocket的实现</a><br/>
|
||||
<a href="#24-简单登录的实现">2.4 简单登录的实现</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
## 一、说明
|
||||
|
||||
### 1.1 项目结构说明
|
||||
|
@ -1,455 +1,443 @@
|
||||
# spring-cloud-config
|
||||
## 目录<br/>
|
||||
# spring-cloud-config
|
||||
|
||||
## 目录<br/>
|
||||
<a href="#一config-简介">一、config 简介</a><br/>
|
||||
<a href="#二项目结构">二、项目结构</a><br/>
|
||||
<a href="#三config-server-配置中心的实现">三、config-server 配置中心的实现</a><br/>
|
||||
<a href="#31-导入依赖">3.1 导入依赖 </a><br/>
|
||||
<a href="#32-在启动类上添加@EnableDiscoveryClient和@EnableConfigServer-注解">3.2 在启动类上添加@EnableDiscoveryClient和@EnableConfigServer 注解</a><br/>
|
||||
<a href="#33--指定注册中心地址并配置git仓库地址的配置文件路径">3.3 指定注册中心地址,并配置git仓库地址的配置文件路径</a><br/>
|
||||
<a href="#34--启动eureka和config-server服务访问-http//localhost8020/application-devyml">3.4 启动eureka和config-server服务,访问 http://localhost:8020/application-dev.yml</a><br/>
|
||||
<a href="#35-http请求地址和资源文件映射">3.5 http请求地址和资源文件映射</a><br/>
|
||||
<a href="#四config-client-搭建">四、config-client 搭建</a><br/>
|
||||
<a href="#41-导入依赖">4.1 导入依赖</a><br/>
|
||||
<a href="#42-新建-`bootstrapyml`配置文件指定注册中心地址和配置中心服务名并在启动类上开启自动注册@EnableDiscoveryClient">4.2 新建 `bootstrap.yml`配置文件,指定注册中心地址和配置中心服务名,并在启动类上开启自动注册@EnableDiscoveryClient</a><br/>
|
||||
<a href="#43-创建配置映射类用于测试">4.3 创建配置映射类用于测试</a><br/>
|
||||
<a href="#44-依次启动eurekaconfig-serverconfig-client-访问-http//localhost8030/programmer">4.4 依次启动eureka,config-server,config-client ,访问 http://localhost:8030/programmer</a><br/>
|
||||
<a href="#五集成-spring-cloud-bus-实现配置热更新">五、集成 spring-cloud-bus 实现配置热更新</a><br/>
|
||||
<a href="#51-消息总线简介">5.1 消息总线简介</a><br/>
|
||||
<a href="#51-导入bus依赖">5.1 导入bus依赖</a><br/>
|
||||
<a href="#52-修改bootstrapyml-配置开启总线配置配置rabbitmq--和-开启热刷新[端点]https//githubcom/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator">5.2 修改bootstrap.yml 配置,开启总线配置,配置rabbitmq 和 开启热刷新[端点](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator)</a><br/>
|
||||
<a href="#53-用@RefreshScope指定需要热刷新的配置">5.3 用@RefreshScope指定需要热刷新的配置</a><br/>
|
||||
<a href="#54-依次启动eurekaconfig-server-config-client-服务">5.4 依次启动eureka,config-server, config-client 服务</a><br/>
|
||||
<a href="#56--直接在-git-上修改配置文件然后用-`post`-触发热刷新端点-http//localhost8030/actuator/bus-refresh-即可看到配置已经热刷新">5.6 直接在 git 上修改配置文件,然后用 `post` 触发热刷新端点 http://localhost:8030/actuator/bus-refresh ,即可看到配置已经热刷新</a><br/>
|
||||
|
||||
## 正文<br/>
|
||||
|
||||
## 一、config 简介
|
||||
|
||||
spring cloud config 分为服务端和客户端,服务端称为分布式配置中心,集中管理配置文件,客户端为各个业务单元,它们从配置中心获取相关配置,同时config 还实现了配置热更新,在服务不停机的情况下刷新配置。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
+ config-server: 配置中心;
|
||||
+ config-client: 服务单元,可以从配置中心获取相关配置;
|
||||
+ eureka: 注册中心。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-config.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 三、config-server 配置中心的实现
|
||||
|
||||
#### 3.1 导入依赖
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.heibaiying.config</groupId>
|
||||
<artifactId>spring-cloud-config</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-server</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
</dependency>
|
||||
<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>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.2 在启动类上添加@EnableDiscoveryClient和@EnableConfigServer 注解
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableConfigServer
|
||||
public class ConfigServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConfigServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.3 指定注册中心地址,并配置git仓库地址的配置文件路径
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8020
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: config-server
|
||||
cloud:
|
||||
config:
|
||||
server:
|
||||
git:
|
||||
uri: https://github.com/heibaiying/spring-samples-for-all/
|
||||
search-paths: spring-cloud/spring-cloud-test-config/
|
||||
# 如果代码仓库是公开的 则 不需要设置用户名和密码
|
||||
username:
|
||||
password:
|
||||
# 指定拉取的配置文件的存放位置,配置文件最后存储的目录为 basedir + search-paths
|
||||
# 这个地方还需要注意的是,配置文件的仓库最好只放配置文件
|
||||
# 因为配置中心不仅会拉取search-paths下的文件,还会把uri指定仓库中的全部文件拉取到basedir下
|
||||
basedir: D:\git-config
|
||||
# 指定分支
|
||||
label: master
|
||||
```
|
||||
|
||||
这里的git 仓库就是本用例的仓库,是公开的仓库,所以不用配置用户名和密码,配置文件如下
|
||||
|
||||
- application.yml 为主配置;
|
||||
- application-dev.yml 为开发环境配置。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-git.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 3.4 启动eureka和config-server服务,访问 http://localhost:8020/application-dev.yml
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-application-dev.png"/> </div>
|
||||
|
||||
这里需要注意的拉取配置的时候,我们此时指定拉取的是dev配置,application.yml实际 配置如下:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-dev.png"/> </div>
|
||||
|
||||
这说明在用配置中心拉取配置的时候,和我们在本地开发的时候是一致的,配置是互补的,即dev中的实际配置应该是主配置和dev配置的结合,且遵循同名属性精确优先的原则。
|
||||
|
||||
|
||||
|
||||
#### 3.5 http请求地址和资源文件映射
|
||||
|
||||
在本用例中如果我们想要直接访问主配置,用以下路径 http://localhost:8020/application.yml 是不行的,会得到错误页面。如果想要访问主配置,,可以用http://localhost:8020/application-X.yml,其中可以是任意字符,原因是:
|
||||
|
||||
请求地址和实际的配置文件应该遵循以下规则,application为配置文件名,profile 为环境,label为分支(如果不指定默认就是master分支)。
|
||||
|
||||
- /{application}/{profile}[/{label}]
|
||||
- /{application}-{profile}.yml
|
||||
- /{label}/{application}-{profile}.yml
|
||||
- /{application}-{profile}.properties
|
||||
- /{label}/{application}-{profile}.properties
|
||||
|
||||
访问主配置:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-a.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 四、config-client 搭建
|
||||
|
||||
#### 4.1 导入依赖
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.heibaiying.config</groupId>
|
||||
<artifactId>spring-cloud-config</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-client</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!--config client-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-config</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>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
```
|
||||
|
||||
#### 4.2 新建 `bootstrap.yml`配置文件,指定注册中心地址和配置中心服务名,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
这里需要特别说明的是,在之前的所有项目中我们采用的配置文件都是application.yml,但是这里**一定要采用bootstrap.yml**。
|
||||
|
||||
假设我们的数据库配置是放在远程配置中心的,那么我们应该先去远程配置中心拉取配置,然后再去进行数据库的自动化配置,反之如果我们先进行了数据库的自动化配置,那么就会因为找不到url或驱动而抛出异常。
|
||||
|
||||
- bootstrap.yml(bootstrap.properties)用来程序引导时执行,应用于更加早期配置信息读取,bootstrap.yml 先于 application.yml 加载。
|
||||
|
||||
- application.yml(application.properties) 应用程序各个模块的配置信息。
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8030
|
||||
spring:
|
||||
application:
|
||||
name: config-client
|
||||
cloud:
|
||||
config:
|
||||
discovery:
|
||||
enabled: true
|
||||
# 这里我们指定的是服务名 如果配置中心有多个,且用同一个服务名,我们的客户端拉取配置的时候是负载均衡的,配置中心也就是高可用的
|
||||
serviceId: config-server
|
||||
# 指定分支
|
||||
label: master
|
||||
# 指定环境
|
||||
profile: dev
|
||||
|
||||
|
||||
# 注意指定注册中心的配置不要从公共配置中拉取,要在本地的配置文件中指定
|
||||
# 因为我们必须要先从注册中心去获取可用的配置中心, 从配置中心去拉取配置
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ConfigClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConfigClientApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### 4.3 创建配置映射类用于测试
|
||||
|
||||
```java
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "programmer")
|
||||
@Data
|
||||
@ToString
|
||||
public class Programmer{
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private boolean married;
|
||||
private Date hireDate;
|
||||
private float salary;
|
||||
private int random;
|
||||
private Map<String, String> skill;
|
||||
private List company;
|
||||
private School school;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class ConfigController {
|
||||
|
||||
@Autowired
|
||||
private Programmer programmer;
|
||||
|
||||
@RequestMapping("programmer")
|
||||
public String getProgrammer() {
|
||||
return programmer.toString();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.4 依次启动eureka,config-server,config-client ,访问 http://localhost:8030/programmer
|
||||
|
||||
这里需要注意是在启动eureka和config-server,要稍等一会在启动config-client,这里是为了确保config-server已经将服务注册到eureka,然后我们的config-client才能从eureka中获取配置中心的服务。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-client-programmer.png"/> </div>
|
||||
|
||||
启动的时候可以从控制台看到如下拉取服务的信息:
|
||||
|
||||
```shell
|
||||
Fetching config from server at : http://localhost:8020/
|
||||
Located environment: name=config-client, profiles=[dev], label=master, version=50dcfb85cd751e4f28761cd6bad84c1f73034002, state=null
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、集成 spring-cloud-bus 实现配置热更新
|
||||
|
||||
#### 5.1 消息总线简介
|
||||
|
||||
在微服务的架构中,我们通常想要构建一个共同的消息主题被所有微服务实例所监听,以便对所有微服务实例的管理和通知,这就是消息总线,spring cloud bus 就是消息总线的一种实现。
|
||||
|
||||
目前spring cloud bus 支持的消息中间件有 RabbitMQ和kafka, 我们下面的整合采用的是RrabbitMQ。
|
||||
|
||||
关于热更新只需要对配置客户端(config-client)做更改,不需要对(config-server)做改动。
|
||||
|
||||
#### 5.1 导入bus依赖
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
|
||||
</dependency>
|
||||
<!--因为要用到端点功能(主要是刷新端点),所以需要导入actuator-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 5.2 修改bootstrap.yml 配置,开启总线配置,配置rabbitmq 和 开启热刷新[端点](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator)
|
||||
|
||||
```yml
|
||||
server:
|
||||
port: 8030
|
||||
spring:
|
||||
application:
|
||||
name: config-client
|
||||
cloud:
|
||||
config:
|
||||
discovery:
|
||||
enabled: true
|
||||
# 这里我们指定的是服务名 如果配置中心有多个,且用同一个服务名,我们的客户端拉取配置的时候是负载均衡的,配置中心也就是高可用
|
||||
serviceId: config-server
|
||||
# 指定分支
|
||||
label: master
|
||||
# 指定环境
|
||||
profile: dev
|
||||
bus:
|
||||
#开启总线
|
||||
enabled: true
|
||||
# 打开ack跟踪的标志(默认关闭)
|
||||
trace:
|
||||
enabled: true
|
||||
# 使用bus实现热更新
|
||||
rabbitmq:
|
||||
host: 127.0.0.1
|
||||
port: 5672
|
||||
username: guest
|
||||
password: guest
|
||||
|
||||
|
||||
# 注意指定注册中心的配置不要从公共配置中拉取,要在本地的配置文件中指定
|
||||
# 因为我们必须要先从注册中心去获取可用的配置中心, 然后从配置中心去拉取配置
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
|
||||
# 暴露热刷新的端点
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: bus-refresh
|
||||
|
||||
```
|
||||
|
||||
#### 5.3 用@RefreshScope指定需要热刷新的配置
|
||||
|
||||
```java
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "programmer")
|
||||
@Data
|
||||
@ToString
|
||||
@RefreshScope // 定义下面配置热刷新范围
|
||||
public class Programmer{
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private boolean married;
|
||||
private Date hireDate;
|
||||
private float salary;
|
||||
private int random;
|
||||
private Map<String, String> skill;
|
||||
private List company;
|
||||
private School school;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.4 依次启动eureka,config-server, config-client 服务
|
||||
|
||||
在client服务端启动时候,可以在控制台 看到bus 自动创建了交换机、队列等
|
||||
|
||||
```
|
||||
Created new connection: rabbitConnectionFactory#496c6d94:22/SimpleConnection@185d85d2 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 63713]
|
||||
o.s.amqp.rabbit.core.RabbitAdmin : Auto-declaring a non-durable, auto-delete, or exclusive Queue (springCloudBus.anonymous.iY4TIIi9TSe0bL-TWAMhWg) durable:false, auto-delete:true, exclusive:true. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.
|
||||
```
|
||||
|
||||
也可以在 rabbitmq 管控台查看
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-bus-exchange.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-bus-queue.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 5.6 直接在 git 上修改配置文件,然后用 `post` 触发热刷新端点 http://localhost:8030/actuator/bus-refresh ,即可看到配置已经热刷新
|
||||
|
||||
注意: 这里的只能用 post 方式请求 ,可以用 postman 等测试软件
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/bus-refresh.png"/> </div>
|
||||
|
||||
热刷新的过程在控制台有详细的打印,部分日志如下:
|
||||
|
||||
```shell
|
||||
# 消息传播
|
||||
Attempting to connect to: [127.0.0.1:5672]
|
||||
Created new connection: rabbitConnectionFactory.publisher#b00f2d6:0/SimpleConnection@403c0406 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 62748]
|
||||
# 从配置中心拉取配置文件
|
||||
Fetching config from server at : http://DESKTOP-8JGSFLJ:8020/
|
||||
# 刷新应用上下文 AnnotationConfigApplicationContext
|
||||
Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@62e12f66
|
||||
```
|
||||
|
||||
|
||||
## 一、config 简介
|
||||
|
||||
spring cloud config 分为服务端和客户端,服务端称为分布式配置中心,集中管理配置文件,客户端为各个业务单元,它们从配置中心获取相关配置,同时config 还实现了配置热更新,在服务不停机的情况下刷新配置。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
+ config-server: 配置中心;
|
||||
+ config-client: 服务单元,可以从配置中心获取相关配置;
|
||||
+ eureka: 注册中心。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-config.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 三、config-server 配置中心的实现
|
||||
|
||||
#### 3.1 导入依赖
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.heibaiying.config</groupId>
|
||||
<artifactId>spring-cloud-config</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-server</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
</dependency>
|
||||
<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>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.2 在启动类上添加@EnableDiscoveryClient和@EnableConfigServer 注解
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableConfigServer
|
||||
public class ConfigServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConfigServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.3 指定注册中心地址,并配置git仓库地址的配置文件路径
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8020
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: config-server
|
||||
cloud:
|
||||
config:
|
||||
server:
|
||||
git:
|
||||
uri: https://github.com/heibaiying/spring-samples-for-all/
|
||||
search-paths: spring-cloud/spring-cloud-test-config/
|
||||
# 如果代码仓库是公开的 则 不需要设置用户名和密码
|
||||
username:
|
||||
password:
|
||||
# 指定拉取的配置文件的存放位置,配置文件最后存储的目录为 basedir + search-paths
|
||||
# 这个地方还需要注意的是,配置文件的仓库最好只放配置文件
|
||||
# 因为配置中心不仅会拉取search-paths下的文件,还会把uri指定仓库中的全部文件拉取到basedir下
|
||||
basedir: D:\git-config
|
||||
# 指定分支
|
||||
label: master
|
||||
```
|
||||
|
||||
这里的git 仓库就是本用例的仓库,是公开的仓库,所以不用配置用户名和密码,配置文件如下
|
||||
|
||||
- application.yml 为主配置;
|
||||
- application-dev.yml 为开发环境配置。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-git.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 3.4 启动eureka和config-server服务,访问 http://localhost:8020/application-dev.yml
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-application-dev.png"/> </div>
|
||||
|
||||
这里需要注意的拉取配置的时候,我们此时指定拉取的是dev配置,application.yml实际 配置如下:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-dev.png"/> </div>
|
||||
|
||||
这说明在用配置中心拉取配置的时候,和我们在本地开发的时候是一致的,配置是互补的,即dev中的实际配置应该是主配置和dev配置的结合,且遵循同名属性精确优先的原则。
|
||||
|
||||
|
||||
|
||||
#### 3.5 http请求地址和资源文件映射
|
||||
|
||||
在本用例中如果我们想要直接访问主配置,用以下路径 http://localhost:8020/application.yml 是不行的,会得到错误页面。如果想要访问主配置,,可以用http://localhost:8020/application-X.yml,其中可以是任意字符,原因是:
|
||||
|
||||
请求地址和实际的配置文件应该遵循以下规则,application为配置文件名,profile 为环境,label为分支(如果不指定默认就是master分支)。
|
||||
|
||||
- /{application}/{profile}[/{label}]
|
||||
- /{application}-{profile}.yml
|
||||
- /{label}/{application}-{profile}.yml
|
||||
- /{application}-{profile}.properties
|
||||
- /{label}/{application}-{profile}.properties
|
||||
|
||||
访问主配置:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-a.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 四、config-client 搭建
|
||||
|
||||
#### 4.1 导入依赖
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.heibaiying.config</groupId>
|
||||
<artifactId>spring-cloud-config</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>config-client</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!--config client-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-config</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>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
```
|
||||
|
||||
#### 4.2 新建 `bootstrap.yml`配置文件,指定注册中心地址和配置中心服务名,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
这里需要特别说明的是,在之前的所有项目中我们采用的配置文件都是application.yml,但是这里**一定要采用bootstrap.yml**。
|
||||
|
||||
假设我们的数据库配置是放在远程配置中心的,那么我们应该先去远程配置中心拉取配置,然后再去进行数据库的自动化配置,反之如果我们先进行了数据库的自动化配置,那么就会因为找不到url或驱动而抛出异常。
|
||||
|
||||
- bootstrap.yml(bootstrap.properties)用来程序引导时执行,应用于更加早期配置信息读取,bootstrap.yml 先于 application.yml 加载。
|
||||
|
||||
- application.yml(application.properties) 应用程序各个模块的配置信息。
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8030
|
||||
spring:
|
||||
application:
|
||||
name: config-client
|
||||
cloud:
|
||||
config:
|
||||
discovery:
|
||||
enabled: true
|
||||
# 这里我们指定的是服务名 如果配置中心有多个,且用同一个服务名,我们的客户端拉取配置的时候是负载均衡的,配置中心也就是高可用的
|
||||
serviceId: config-server
|
||||
# 指定分支
|
||||
label: master
|
||||
# 指定环境
|
||||
profile: dev
|
||||
|
||||
|
||||
# 注意指定注册中心的配置不要从公共配置中拉取,要在本地的配置文件中指定
|
||||
# 因为我们必须要先从注册中心去获取可用的配置中心, 从配置中心去拉取配置
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ConfigClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConfigClientApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### 4.3 创建配置映射类用于测试
|
||||
|
||||
```java
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "programmer")
|
||||
@Data
|
||||
@ToString
|
||||
public class Programmer{
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private boolean married;
|
||||
private Date hireDate;
|
||||
private float salary;
|
||||
private int random;
|
||||
private Map<String, String> skill;
|
||||
private List company;
|
||||
private School school;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class ConfigController {
|
||||
|
||||
@Autowired
|
||||
private Programmer programmer;
|
||||
|
||||
@RequestMapping("programmer")
|
||||
public String getProgrammer() {
|
||||
return programmer.toString();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.4 依次启动eureka,config-server,config-client ,访问 http://localhost:8030/programmer
|
||||
|
||||
这里需要注意是在启动eureka和config-server,要稍等一会在启动config-client,这里是为了确保config-server已经将服务注册到eureka,然后我们的config-client才能从eureka中获取配置中心的服务。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/config-client-programmer.png"/> </div>
|
||||
|
||||
启动的时候可以从控制台看到如下拉取服务的信息:
|
||||
|
||||
```shell
|
||||
Fetching config from server at : http://localhost:8020/
|
||||
Located environment: name=config-client, profiles=[dev], label=master, version=50dcfb85cd751e4f28761cd6bad84c1f73034002, state=null
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、集成 spring-cloud-bus 实现配置热更新
|
||||
|
||||
#### 5.1 消息总线简介
|
||||
|
||||
在微服务的架构中,我们通常想要构建一个共同的消息主题被所有微服务实例所监听,以便对所有微服务实例的管理和通知,这就是消息总线,spring cloud bus 就是消息总线的一种实现。
|
||||
|
||||
目前spring cloud bus 支持的消息中间件有 RabbitMQ和kafka, 我们下面的整合采用的是RrabbitMQ。
|
||||
|
||||
关于热更新只需要对配置客户端(config-client)做更改,不需要对(config-server)做改动。
|
||||
|
||||
#### 5.1 导入bus依赖
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
|
||||
</dependency>
|
||||
<!--因为要用到端点功能(主要是刷新端点),所以需要导入actuator-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 5.2 修改bootstrap.yml 配置,开启总线配置,配置rabbitmq 和 开启热刷新[端点](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator)
|
||||
|
||||
```yml
|
||||
server:
|
||||
port: 8030
|
||||
spring:
|
||||
application:
|
||||
name: config-client
|
||||
cloud:
|
||||
config:
|
||||
discovery:
|
||||
enabled: true
|
||||
# 这里我们指定的是服务名 如果配置中心有多个,且用同一个服务名,我们的客户端拉取配置的时候是负载均衡的,配置中心也就是高可用
|
||||
serviceId: config-server
|
||||
# 指定分支
|
||||
label: master
|
||||
# 指定环境
|
||||
profile: dev
|
||||
bus:
|
||||
#开启总线
|
||||
enabled: true
|
||||
# 打开ack跟踪的标志(默认关闭)
|
||||
trace:
|
||||
enabled: true
|
||||
# 使用bus实现热更新
|
||||
rabbitmq:
|
||||
host: 127.0.0.1
|
||||
port: 5672
|
||||
username: guest
|
||||
password: guest
|
||||
|
||||
|
||||
# 注意指定注册中心的配置不要从公共配置中拉取,要在本地的配置文件中指定
|
||||
# 因为我们必须要先从注册中心去获取可用的配置中心, 然后从配置中心去拉取配置
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
|
||||
# 暴露热刷新的端点
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: bus-refresh
|
||||
|
||||
```
|
||||
|
||||
#### 5.3 用@RefreshScope指定需要热刷新的配置
|
||||
|
||||
```java
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "programmer")
|
||||
@Data
|
||||
@ToString
|
||||
@RefreshScope // 定义下面配置热刷新范围
|
||||
public class Programmer{
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private boolean married;
|
||||
private Date hireDate;
|
||||
private float salary;
|
||||
private int random;
|
||||
private Map<String, String> skill;
|
||||
private List company;
|
||||
private School school;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.4 依次启动eureka,config-server, config-client 服务
|
||||
|
||||
在client服务端启动时候,可以在控制台 看到bus 自动创建了交换机、队列等
|
||||
|
||||
```
|
||||
Created new connection: rabbitConnectionFactory#496c6d94:22/SimpleConnection@185d85d2 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 63713]
|
||||
o.s.amqp.rabbit.core.RabbitAdmin : Auto-declaring a non-durable, auto-delete, or exclusive Queue (springCloudBus.anonymous.iY4TIIi9TSe0bL-TWAMhWg) durable:false, auto-delete:true, exclusive:true. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.
|
||||
```
|
||||
|
||||
也可以在 rabbitmq 管控台查看
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-bus-exchange.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-bus-queue.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 5.6 直接在 git 上修改配置文件,然后用 `post` 触发热刷新端点 http://localhost:8030/actuator/bus-refresh ,即可看到配置已经热刷新
|
||||
|
||||
注意: 这里的只能用 post 方式请求 ,可以用 postman 等测试软件
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/bus-refresh.png"/> </div>
|
||||
|
||||
热刷新的过程在控制台有详细的打印,部分日志如下:
|
||||
|
||||
```shell
|
||||
# 消息传播
|
||||
Attempting to connect to: [127.0.0.1:5672]
|
||||
Created new connection: rabbitConnectionFactory.publisher#b00f2d6:0/SimpleConnection@403c0406 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 62748]
|
||||
# 从配置中心拉取配置文件
|
||||
Fetching config from server at : http://DESKTOP-8JGSFLJ:8020/
|
||||
# 刷新应用上下文 AnnotationConfigApplicationContext
|
||||
Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@62e12f66
|
||||
```
|
||||
|
||||
|
@ -5,18 +5,18 @@
|
||||
<a href="#二三步搭建eureka-高可用注册中心">二、三步搭建eureka 高可用注册中心</a><br/>
|
||||
<a href="#21-引入eureka服务端依赖">2.1 引入eureka服务端依赖</a><br/>
|
||||
<a href="#22--创建三份配置文件分别代表不同注册中心的配置">2.2 创建三份配置文件,分别代表不同注册中心的配置</a><br/>
|
||||
<a href="#23-启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置">2.3 启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置</a><br/>
|
||||
<a href="#23-启动类上增加注解EnableEurekaServer激活eureka服务端自动配置">2.3 启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置</a><br/>
|
||||
<a href="#三三步搭建eureka-客户端">三、三步搭建eureka 客户端</a><br/>
|
||||
<a href="#31-引入eureka客户端依赖">3.1 引入eureka客户端依赖</a><br/>
|
||||
<a href="#32-eureka-客户端配置指定注册中心地址">3.2 eureka 客户端配置,指定注册中心地址</a><br/>
|
||||
<a href="#33-启动类上增加注解@EnableDiscoveryClient激活eureka客户端自动配置">3.3 启动类上增加注解@EnableDiscoveryClient激活eureka客户端自动配置</a><br/>
|
||||
<a href="#33-启动类上增加注解EnableDiscoveryClient激活eureka客户端自动配置">3.3 启动类上增加注解@EnableDiscoveryClient激活eureka客户端自动配置</a><br/>
|
||||
<a href="#4启动项目">4.启动项目 </a><br/>
|
||||
<a href="#41-这里我们可以采用命令行方式指定配置分别启动三个注册中心">4.1 这里我们可以采用命令行方式指定配置,分别启动三个注册中心</a><br/>
|
||||
<a href="#42--高可用集群搭建成功的判定">4.2 高可用集群搭建成功的判定</a><br/>
|
||||
<a href="#**421--点击下面注册中心的可用实例列表中的地址访问链接分以下几个情况**">4.2.1 点击下面注册中心的可用实例列表中的地址,访问链接分以下几个情况:</a><br/>
|
||||
<a href="#43--prefer-ip-address-参数说明">4.3 prefer-ip-address 参数说明</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
## 一、项目结构
|
||||
|
||||
eureka-server为服务注册中心,负责服务的管理;
|
||||
|
@ -5,15 +5,16 @@
|
||||
<a href="#三三步搭建eureka-服务注册中心">三、三步搭建eureka 服务注册中心</a><br/>
|
||||
<a href="#31-引入eureka服务端依赖">3.1 引入eureka服务端依赖</a><br/>
|
||||
<a href="#32-eureka-服务端配置">3.2 eureka 服务端配置</a><br/>
|
||||
<a href="#33-启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置">3.3 启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置</a><br/>
|
||||
<a href="#33-启动类上增加注解EnableEurekaServer激活eureka服务端自动配置">3.3 启动类上增加注解@EnableEurekaServer激活eureka服务端自动配置</a><br/>
|
||||
<a href="#四三步搭建eureka-客户端">四、三步搭建eureka 客户端</a><br/>
|
||||
<a href="#41-引入eureka客户端依赖">4.1 引入eureka客户端依赖</a><br/>
|
||||
<a href="#42-eureka-客户端配置">4.2 eureka 客户端配置</a><br/>
|
||||
<a href="#43-启动类上增加注解@EnableDiscoveryClient激活eureka客户端自动配置">4.3 启动类上增加注解@EnableDiscoveryClient激活eureka客户端自动配置</a><br/>
|
||||
<a href="#43-启动类上增加注解EnableDiscoveryClient激活eureka客户端自动配置">4.3 启动类上增加注解@EnableDiscoveryClient激活eureka客户端自动配置</a><br/>
|
||||
<a href="#五启动项目">五、启动项目 </a><br/>
|
||||
<a href="#51-进入注册中心控制台查看服务注册情况">5.1 进入注册中心控制台,查看服务注册情况</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
## 一、eureka 简介
|
||||
|
||||
Spring Cloud Eureka使用Netflix Eureka来实现服务注册与发现,它既包含了服务端组件,也包含了客户端组件。
|
||||
|
@ -1,400 +1,388 @@
|
||||
# spring-cloud-feign
|
||||
## 目录<br/>
|
||||
# spring-cloud-feign
|
||||
|
||||
## 目录<br/>
|
||||
<a href="#一feign-简介">一、feign 简介</a><br/>
|
||||
<a href="#二项目结构">二、项目结构</a><br/>
|
||||
<a href="#三服务提供者的实现">三、服务提供者的实现</a><br/>
|
||||
<a href="#31-产品服务由`ProductService`提供并通过`ProducerController`将服务暴露给外部调用。">3.1 产品服务由`ProductService`提供,并通过`ProducerController`将服务暴露给外部调用。</a><br/>
|
||||
<a href="#32-指定注册中心地址并在启动类上开启自动注册@EnableDiscoveryClient">3.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient</a><br/>
|
||||
<a href="#四服务消费者的实现">四、服务消费者的实现</a><br/>
|
||||
<a href="#41-导入openfeign依赖">4.1 导入openfeign依赖</a><br/>
|
||||
<a href="#42-指定注册中心地址并在启动类上添加注解@EnableDiscoveryClient和@EnableFeignClients">4.2 指定注册中心地址,并在启动类上添加注解@EnableDiscoveryClient和@EnableFeignClients</a><br/>
|
||||
<a href="#43-创建服务调用公共接口">4.3 创建服务调用公共接口</a><br/>
|
||||
<a href="#44-继承公共接口创建CProductFeign-用@FeignClient声明为feign客户端">4.4 继承公共接口,创建CProductFeign, 用@FeignClient声明为feign客户端</a><br/>
|
||||
<a href="#45--注入使用-feign-服务调用接口">4.5 注入使用 feign 服务调用接口</a><br/>
|
||||
<a href="#五启动测试">五、启动测试</a><br/>
|
||||
<a href="#51-启动一个Eureka服务三个producer服务注意区分端口和一个消费者服务">5.1 启动一个Eureka服务、三个producer服务(注意区分端口)、和一个消费者服务</a><br/>
|
||||
<a href="#52--访问http//localhost8080/sell/products-查看负载均衡的调用结果">5.2 访问http://localhost:8080/sell/products 查看负载均衡的调用结果</a><br/>
|
||||
<a href="#六-feign-的服务容错">六、 feign 的服务容错</a><br/>
|
||||
<a href="#61-feign-的依赖中默认导入了hystrix-的相关依赖我们不需要额外导入只需要开启相关配置即可">6.1 feign 的依赖中默认导入了hystrix 的相关依赖,我们不需要额外导入,只需要开启相关配置即可</a><br/>
|
||||
<a href="#62-在applicationyml-中开启hystrix">6.2 在application.yml 中开启hystrix</a><br/>
|
||||
<a href="#63-创建`CProductFeignImpl`继承feign接口CProductFeign定义熔断时候的回退处理">6.3 创建`CProductFeignImpl`,继承feign接口(CProductFeign),定义熔断时候的回退处理</a><br/>
|
||||
<a href="#64-在-@FeignClient-注解中用fallback参数指定熔断时候的回退处理">6.4 在 @FeignClient 注解中,用fallback参数指定熔断时候的回退处理</a><br/>
|
||||
<a href="#65-测试熔断处理">6.5 测试熔断处理</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
## 一、feign 简介
|
||||
|
||||
在上一个用例中,我们使用ribbon+restTemplate 实现服务之间的远程调用,实际上每一个调用都是模板化的内容,所以spring cloud Feign 在此基础上进行了进一步的封装。我们只需要定义一个接口并使用feign注解的方式来进行配置,同时采用springMvc 注解进行参数绑定就可以完成服务的调用。feign同时还内置实现了负载均衡、服务容错等功能。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用feign调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-feign.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 三、服务提供者的实现
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-producer.png"/> </div>
|
||||
|
||||
#### 3.1 产品服务由`ProductService`提供,并通过`ProducerController`将服务暴露给外部调用。
|
||||
|
||||
ProductService.java:
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 产品提供接口实现类
|
||||
*/
|
||||
@Service
|
||||
public class ProductService implements IProductService, ApplicationListener<WebServerInitializedEvent> {
|
||||
|
||||
private static List<Product> productList = new ArrayList<>();
|
||||
|
||||
public Product queryProductById(int id) {
|
||||
return productList.stream().filter(p->p.getId()==id).collect(Collectors.toList()).get(0);
|
||||
}
|
||||
|
||||
|
||||
public List<Product> queryAllProducts() {
|
||||
return productList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveProduct(Product product) {
|
||||
productList.add(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(WebServerInitializedEvent event) {
|
||||
int port = event.getWebServer().getPort();
|
||||
for (long i = 0; i < 20; i++) {
|
||||
productList.add(new Product(i, port + "产品" + i, i / 2 == 0, new Date(), 66.66f * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ProducerController.java:
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class ProducerController implements ProductFeign {
|
||||
|
||||
@Autowired
|
||||
private IProductService productService;
|
||||
|
||||
@GetMapping("products")
|
||||
public List<Product> productList() {
|
||||
return productService.queryAllProducts();
|
||||
}
|
||||
|
||||
@GetMapping("product/{id}")
|
||||
public Product productDetail(@PathVariable int id) {
|
||||
return productService.queryProductById(id);
|
||||
}
|
||||
|
||||
@PostMapping("product")
|
||||
public void save(@RequestBody Product product) {
|
||||
productService.saveProduct(product);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8020
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: producer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ProducerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProducerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 四、服务消费者的实现
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-consumer.png"/> </div>
|
||||
|
||||
#### 4.1 导入openfeign依赖
|
||||
|
||||
```xml
|
||||
<!-- feign 依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 4.2 指定注册中心地址,并在启动类上添加注解@EnableDiscoveryClient和@EnableFeignClients
|
||||
|
||||
@EnableFeignClients 会去扫描工程中所有用 @FeignClient 声明的 feign 客户端。
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8080
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: consumer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 创建服务调用公共接口
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 声明式服务调用
|
||||
*/
|
||||
public interface ProductFeign {
|
||||
|
||||
@GetMapping("products")
|
||||
List<Product> productList();
|
||||
|
||||
/**
|
||||
* 这是需要强调的是使用feign时候@PathVariable一定要用value指明参数,
|
||||
* 不然会抛出.IllegalStateException: PathVariable annotation was empty on param 异常
|
||||
*/
|
||||
@GetMapping("product/{id}")
|
||||
Product productDetail(@PathVariable(value = "id") int id);
|
||||
|
||||
|
||||
@PostMapping("product")
|
||||
void save(@RequestBody Product product);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
按照官方对于服务最佳化的推荐,这里我们的服务调用接口放在公共模块中,因为在实际的开发中,同一个服务调用接口可能被多个模块所使用。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/common-feign.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 4.4 继承公共接口,创建CProductFeign, 用@FeignClient声明为feign客户端
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 声明式接口调用
|
||||
*/
|
||||
@FeignClient(value = "producer",configuration = FeignConfig.class)
|
||||
public interface CProductFeign extends ProductFeign {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.5 注入使用 feign 服务调用接口
|
||||
|
||||
```java
|
||||
@Controller
|
||||
@RequestMapping("sell")
|
||||
public class SellController {
|
||||
|
||||
@Autowired
|
||||
private CProductFeign cproductFeign;
|
||||
|
||||
@GetMapping("products")
|
||||
public String productList(Model model) {
|
||||
List<Product> products = cproductFeign.productList();
|
||||
model.addAttribute("products", products);
|
||||
return "products";
|
||||
}
|
||||
|
||||
@GetMapping("product/{id}")
|
||||
public String productDetail(@PathVariable int id, Model model) {
|
||||
Product product = cproductFeign.productDetail(id);
|
||||
model.addAttribute("product", product);
|
||||
return "product";
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("product")
|
||||
public String save(@RequestParam String productName) {
|
||||
long id = Math.round(Math.random() * 100);
|
||||
Product product = new Product(id, productName, false, new Date(), 88);
|
||||
cproductFeign.save(product);
|
||||
return "redirect:products";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、启动测试
|
||||
|
||||
#### 5.1 启动一个Eureka服务、三个producer服务(注意区分端口)、和一个消费者服务
|
||||
|
||||
feign 的依赖中导入了spring-cloud-starter-netflix-ribbon依赖,并且在内部实现了基于ribbon的客户端负载均衡,所以我们这里启动三个producer实例来观察负载均衡的情况。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-app.png"/> </div>
|
||||
|
||||
**服务注册中心:**
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-eureka.png"/> </div>
|
||||
|
||||
#### 5.2 访问http://localhost:8080/sell/products 查看负载均衡的调用结果
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8020.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8030.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-8040.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 六、 feign 的服务容错
|
||||
|
||||
#### 6.1 feign 的依赖中默认导入了hystrix 的相关依赖,我们不需要额外导入,只需要开启相关配置即可
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-hystrix-maven.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 6.2 在application.yml 中开启hystrix
|
||||
|
||||
```yml
|
||||
feign:
|
||||
hystrix:
|
||||
# 如果为true,则OpenFign客户端将使用Hystrix断路器进行封装 默认为false
|
||||
enabled: true
|
||||
```
|
||||
|
||||
#### 6.3 创建`CProductFeignImpl`,继承feign接口(CProductFeign),定义熔断时候的回退处理
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 定义发生熔断时候的回退处理。除了继承自CProductFeign,还需要用@Component声明为spring的组件
|
||||
*/
|
||||
@Component
|
||||
public class CProductFeignImpl implements CProductFeign {
|
||||
|
||||
// 发生熔断时候,返回空集合,前端页面会做容错显示
|
||||
@Override
|
||||
public List<Product> productList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Product productDetail(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Product product) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
页面的简单容错处理:
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>产品列表</title>
|
||||
</head>
|
||||
<body>
|
||||
<h3>产品列表:点击查看详情</h3>
|
||||
<form action="/sell/product" method="post">
|
||||
<input type="text" name="productName">
|
||||
<input type="submit" value="新增产品">
|
||||
</form>
|
||||
<ul>
|
||||
<#if (products?size>0) >
|
||||
<#list products as product>
|
||||
<li>
|
||||
<a href="/sell/product/${product.id}">${product.name}</a>
|
||||
</li>
|
||||
</#list>
|
||||
<#else>
|
||||
<h4 style="color: red">当前排队人数过多,请之后再购买!</h4>
|
||||
</#if>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### 6.4 在 @FeignClient 注解中,用fallback参数指定熔断时候的回退处理
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 声明式接口调用
|
||||
*/
|
||||
@FeignClient(value = "producer",configuration = FeignConfig.class,fallback = CProductFeignImpl.class)
|
||||
public interface CProductFeign extends ProductFeign {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.5 测试熔断处理
|
||||
|
||||
hystrix 默认调用超时时间为2s ,这里我们使用线程休眠的方式来模拟超时熔断。
|
||||
|
||||
```java
|
||||
public List<Product> queryAllProducts() {
|
||||
/*用于测试 hystrix 超时熔断
|
||||
try {
|
||||
int i = new Random().nextInt(2500);
|
||||
Thread.sleep(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
return productList;
|
||||
}
|
||||
```
|
||||
|
||||
测试结果:
|
||||
|
||||
|
||||
## 一、feign 简介
|
||||
|
||||
在上一个用例中,我们使用ribbon+restTemplate 实现服务之间的远程调用,实际上每一个调用都是模板化的内容,所以spring cloud Feign 在此基础上进行了进一步的封装。我们只需要定义一个接口并使用feign注解的方式来进行配置,同时采用springMvc 注解进行参数绑定就可以完成服务的调用。feign同时还内置实现了负载均衡、服务容错等功能。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用feign调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-feign.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 三、服务提供者的实现
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-producer.png"/> </div>
|
||||
|
||||
#### 3.1 产品服务由`ProductService`提供,并通过`ProducerController`将服务暴露给外部调用。
|
||||
|
||||
ProductService.java:
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 产品提供接口实现类
|
||||
*/
|
||||
@Service
|
||||
public class ProductService implements IProductService, ApplicationListener<WebServerInitializedEvent> {
|
||||
|
||||
private static List<Product> productList = new ArrayList<>();
|
||||
|
||||
public Product queryProductById(int id) {
|
||||
return productList.stream().filter(p->p.getId()==id).collect(Collectors.toList()).get(0);
|
||||
}
|
||||
|
||||
|
||||
public List<Product> queryAllProducts() {
|
||||
return productList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveProduct(Product product) {
|
||||
productList.add(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(WebServerInitializedEvent event) {
|
||||
int port = event.getWebServer().getPort();
|
||||
for (long i = 0; i < 20; i++) {
|
||||
productList.add(new Product(i, port + "产品" + i, i / 2 == 0, new Date(), 66.66f * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ProducerController.java:
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class ProducerController implements ProductFeign {
|
||||
|
||||
@Autowired
|
||||
private IProductService productService;
|
||||
|
||||
@GetMapping("products")
|
||||
public List<Product> productList() {
|
||||
return productService.queryAllProducts();
|
||||
}
|
||||
|
||||
@GetMapping("product/{id}")
|
||||
public Product productDetail(@PathVariable int id) {
|
||||
return productService.queryProductById(id);
|
||||
}
|
||||
|
||||
@PostMapping("product")
|
||||
public void save(@RequestBody Product product) {
|
||||
productService.saveProduct(product);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8020
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: producer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ProducerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProducerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 四、服务消费者的实现
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-consumer.png"/> </div>
|
||||
|
||||
#### 4.1 导入openfeign依赖
|
||||
|
||||
```xml
|
||||
<!-- feign 依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 4.2 指定注册中心地址,并在启动类上添加注解@EnableDiscoveryClient和@EnableFeignClients
|
||||
|
||||
@EnableFeignClients 会去扫描工程中所有用 @FeignClient 声明的 feign 客户端。
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8080
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: consumer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 创建服务调用公共接口
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 声明式服务调用
|
||||
*/
|
||||
public interface ProductFeign {
|
||||
|
||||
@GetMapping("products")
|
||||
List<Product> productList();
|
||||
|
||||
/**
|
||||
* 这是需要强调的是使用feign时候@PathVariable一定要用value指明参数,
|
||||
* 不然会抛出.IllegalStateException: PathVariable annotation was empty on param 异常
|
||||
*/
|
||||
@GetMapping("product/{id}")
|
||||
Product productDetail(@PathVariable(value = "id") int id);
|
||||
|
||||
|
||||
@PostMapping("product")
|
||||
void save(@RequestBody Product product);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
按照官方对于服务最佳化的推荐,这里我们的服务调用接口放在公共模块中,因为在实际的开发中,同一个服务调用接口可能被多个模块所使用。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/common-feign.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 4.4 继承公共接口,创建CProductFeign, 用@FeignClient声明为feign客户端
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 声明式接口调用
|
||||
*/
|
||||
@FeignClient(value = "producer",configuration = FeignConfig.class)
|
||||
public interface CProductFeign extends ProductFeign {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.5 注入使用 feign 服务调用接口
|
||||
|
||||
```java
|
||||
@Controller
|
||||
@RequestMapping("sell")
|
||||
public class SellController {
|
||||
|
||||
@Autowired
|
||||
private CProductFeign cproductFeign;
|
||||
|
||||
@GetMapping("products")
|
||||
public String productList(Model model) {
|
||||
List<Product> products = cproductFeign.productList();
|
||||
model.addAttribute("products", products);
|
||||
return "products";
|
||||
}
|
||||
|
||||
@GetMapping("product/{id}")
|
||||
public String productDetail(@PathVariable int id, Model model) {
|
||||
Product product = cproductFeign.productDetail(id);
|
||||
model.addAttribute("product", product);
|
||||
return "product";
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("product")
|
||||
public String save(@RequestParam String productName) {
|
||||
long id = Math.round(Math.random() * 100);
|
||||
Product product = new Product(id, productName, false, new Date(), 88);
|
||||
cproductFeign.save(product);
|
||||
return "redirect:products";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、启动测试
|
||||
|
||||
#### 5.1 启动一个Eureka服务、三个producer服务(注意区分端口)、和一个消费者服务
|
||||
|
||||
feign 的依赖中导入了spring-cloud-starter-netflix-ribbon依赖,并且在内部实现了基于ribbon的客户端负载均衡,所以我们这里启动三个producer实例来观察负载均衡的情况。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-app.png"/> </div>
|
||||
|
||||
**服务注册中心:**
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-eureka.png"/> </div>
|
||||
|
||||
#### 5.2 访问http://localhost:8080/sell/products 查看负载均衡的调用结果
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8020.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8030.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-8040.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 六、 feign 的服务容错
|
||||
|
||||
#### 6.1 feign 的依赖中默认导入了hystrix 的相关依赖,我们不需要额外导入,只需要开启相关配置即可
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-hystrix-maven.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 6.2 在application.yml 中开启hystrix
|
||||
|
||||
```yml
|
||||
feign:
|
||||
hystrix:
|
||||
# 如果为true,则OpenFign客户端将使用Hystrix断路器进行封装 默认为false
|
||||
enabled: true
|
||||
```
|
||||
|
||||
#### 6.3 创建`CProductFeignImpl`,继承feign接口(CProductFeign),定义熔断时候的回退处理
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 定义发生熔断时候的回退处理。除了继承自CProductFeign,还需要用@Component声明为spring的组件
|
||||
*/
|
||||
@Component
|
||||
public class CProductFeignImpl implements CProductFeign {
|
||||
|
||||
// 发生熔断时候,返回空集合,前端页面会做容错显示
|
||||
@Override
|
||||
public List<Product> productList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Product productDetail(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Product product) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
页面的简单容错处理:
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>产品列表</title>
|
||||
</head>
|
||||
<body>
|
||||
<h3>产品列表:点击查看详情</h3>
|
||||
<form action="/sell/product" method="post">
|
||||
<input type="text" name="productName">
|
||||
<input type="submit" value="新增产品">
|
||||
</form>
|
||||
<ul>
|
||||
<#if (products?size>0) >
|
||||
<#list products as product>
|
||||
<li>
|
||||
<a href="/sell/product/${product.id}">${product.name}</a>
|
||||
</li>
|
||||
</#list>
|
||||
<#else>
|
||||
<h4 style="color: red">当前排队人数过多,请之后再购买!</h4>
|
||||
</#if>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### 6.4 在 @FeignClient 注解中,用fallback参数指定熔断时候的回退处理
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 声明式接口调用
|
||||
*/
|
||||
@FeignClient(value = "producer",configuration = FeignConfig.class,fallback = CProductFeignImpl.class)
|
||||
public interface CProductFeign extends ProductFeign {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.5 测试熔断处理
|
||||
|
||||
hystrix 默认调用超时时间为2s ,这里我们使用线程休眠的方式来模拟超时熔断。
|
||||
|
||||
```java
|
||||
public List<Product> queryAllProducts() {
|
||||
/*用于测试 hystrix 超时熔断
|
||||
try {
|
||||
int i = new Random().nextInt(2500);
|
||||
Thread.sleep(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
return productList;
|
||||
}
|
||||
```
|
||||
|
||||
测试结果:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/feign-hystrix.png"/> </div>
|
@ -1,346 +1,338 @@
|
||||
# spring-cloud-hystrix-turbine
|
||||
## 目录<br/>
|
||||
# spring-cloud-hystrix-turbine
|
||||
|
||||
## 目录<br/>
|
||||
<a href="#一hystrix-简介">一、hystrix 简介</a><br/>
|
||||
<a href="#11-熔断器">1.1 熔断器</a><br/>
|
||||
<a href="#12-hystrix-工作机制">1.2 hystrix 工作机制</a><br/>
|
||||
<a href="#二项目结构">二、项目结构</a><br/>
|
||||
<a href="#三整合-hystrix-以consumer模块为例">三、整合 hystrix (以consumer模块为例)</a><br/>
|
||||
<a href="#31-引入依赖">3.1 引入依赖</a><br/>
|
||||
<a href="#32-暴露端点">3.2 暴露端点</a><br/>
|
||||
<a href="#33-在启动类上添加注解@EnableHystrix和@EnableHystrixDashboard">3.3 在启动类上添加注解@EnableHystrix和@EnableHystrixDashboard</a><br/>
|
||||
<a href="#34--使用-@HystrixCommand-定义失败回退的方法">3.4 使用 @HystrixCommand 定义失败回退的方法</a><br/>
|
||||
<a href="#35-模拟熔断">3.5 模拟熔断</a><br/>
|
||||
<a href="#35-启动服务访问-localhost8030/hystrix">3.5 启动服务,访问 localhost:8030/hystrix</a><br/>
|
||||
<a href="#四使用turbine-实现聚合监控">四、使用turbine 实现聚合监控</a><br/>
|
||||
<a href="#41-创建turbine模块导入依赖">4.1 创建turbine模块,导入依赖</a><br/>
|
||||
<a href="#42-指定注册中心地址和聚合的项目这里我们监控-consumerproducer-两个项目">4.2 指定注册中心地址和聚合的项目,这里我们监控 consumer,producer 两个项目</a><br/>
|
||||
<a href="#43-在启动类上添加注解">4.3 在启动类上添加注解</a><br/>
|
||||
<a href="#44-依次启动eurekaproducerconsumerturbine四个项目">4.4 依次启动eureka、producer、consumer、turbine四个项目</a><br/>
|
||||
<a href="#五整合过程中可能出现的问题">五、整合过程中可能出现的问题</a><br/>
|
||||
<a href="#51-无法访问监控页面">5.1 无法访问监控页面</a><br/>
|
||||
<a href="#52-页面一直loading-或者访问端点页面一直出现ping">5.2 页面一直loading 或者访问端点页面一直出现ping</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
## 一、hystrix 简介
|
||||
|
||||
#### 1.1 熔断器
|
||||
|
||||
在分布式系统中,由于服务之间相互的依赖调用,如果一个服务单元发生了故障就有可能导致故障蔓延至整个系统,从而衍生出一系列的保护机制,断路器就是其中之一。
|
||||
|
||||
断路器可以在服务单元发生故障的时候,及时切断与服务单元的连接,避免资源被长时间占用。spring cloud hystrix组件实现了断路器、线程隔离等一系列基本功能,并具有服务降级、服务熔断、请求缓存、请求合并以及服务监控等配套功能。
|
||||
|
||||
|
||||
|
||||
#### 1.2 hystrix 工作机制
|
||||
|
||||
- 当一个服务的处理请求的失败次数低于阈值时候,熔断器处于关闭状态,服务正常;
|
||||
- 当一个服务的处理请求的失败次数大于阈值时候,熔断器开启,这时候所有的请求都会执行快速失败,是不会去调用实际的服务的;
|
||||
- 当熔断器处于打开状态的一段时间后,熔断器处于半打开状态,这时候一定数量的请求回去调用实际的服务,如果调用成功,则代表服务可用了,熔断器关闭;如果还是失败,则代表服务还是不可用,熔断器继续关闭。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/circuitbreaker.png"/> </div>
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
[spring-cloud-ribbon](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-ribbon)用例已经实现通过ribbon+restTemplate实现服务间的调用,本用例在其基础上进行hystrix 的整合。
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用RestTemplate调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心;
|
||||
+ turbine:多个熔断器的聚合监控。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-hystrix.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 三、整合 hystrix (以consumer模块为例)
|
||||
|
||||
#### 3.1 引入依赖
|
||||
|
||||
hystrix的仪表盘功能实际上是从[端点](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator)获取数据,所以需要actuator starter开启端点的相关功能。
|
||||
|
||||
```xml
|
||||
<!--hystrix 依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<!--hystrix 监控仪表盘依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
</dependency>
|
||||
<!--健康检查依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 3.2 暴露端点
|
||||
|
||||
```java
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
# 需要开启hystrix.stream端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点
|
||||
include: "*"
|
||||
```
|
||||
|
||||
#### 3.3 在启动类上添加注解@EnableHystrix和@EnableHystrixDashboard
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableHystrix
|
||||
@EnableHystrixDashboard
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.4 使用 @HystrixCommand 定义失败回退的方法
|
||||
|
||||
```java
|
||||
@HystrixCommand(fallbackMethod = "queryProductsFail")
|
||||
public List<Product> queryAllProducts() {
|
||||
ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://producer/products", List.class);
|
||||
List<Product> productList = responseEntity.getBody();
|
||||
return productList;
|
||||
}
|
||||
|
||||
// 如果发送熔断返回空集合,在前端判断处理
|
||||
public List<Product> queryProductsFail() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>产品列表</title>
|
||||
</head>
|
||||
<body>
|
||||
<h3>产品列表:点击查看详情</h3>
|
||||
<form action="/sell/product" method="post">
|
||||
<input type="text" name="productName">
|
||||
<input type="submit" value="新增产品">
|
||||
</form>
|
||||
<ul>
|
||||
<#if (products?size>0) >
|
||||
<#list products as product>
|
||||
<li>
|
||||
<a href="/sell/product/${product.id}">${product.name}</a>
|
||||
</li>
|
||||
</#list>
|
||||
<#else>
|
||||
<h4 style="color: red">当前排队人数过多,请之后再购买!</h4>
|
||||
</#if>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### 3.5 模拟熔断
|
||||
|
||||
这里被调用方采用线程休眠的方式模拟服务超时,Hystrix默认超时时间为2s,调用远程服务时候超过这个时间,会触发熔断。
|
||||
|
||||
```java
|
||||
public List<Product> queryAllProducts() {
|
||||
// hystrix 默认超时是2秒
|
||||
int i = new Random().nextInt(2500);
|
||||
try {
|
||||
Thread.sleep(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return productList;
|
||||
}
|
||||
```
|
||||
|
||||
3.5 启动服务,访问http://localhost:8030/sell/products ,多次刷新查看熔断情况
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030.png"/> </div>
|
||||
|
||||
#### 3.5 启动服务,访问 localhost:8030/hystrix
|
||||
|
||||
依次输出http://localhost:8030/actuator/hystrix.stream(监控地址) ,2000(延迟时间),title可以任意填写,进入监控台。
|
||||
|
||||
需要注意的是在spring cloud Finchley.SR2,监控地址中都是有/actuator的,因为在spring boot 2.x 的所有端点(包括自定义端点)都是暴露在这个路径下,在启动时候的控制台输出的日志可以查看到所有暴露端点的映射。
|
||||
|
||||
**登录页面**:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-single-login.png"/> </div>
|
||||
|
||||
**监控页面**:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030-login.png"/> </div>
|
||||
|
||||
**关于各个参数的说明参见[官方wiki](https://github.com/Netflix-Skunkworks/hystrix-dashboard/wiki)提供的图**:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dashboard.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 四、使用turbine 实现聚合监控
|
||||
|
||||
单体监控和聚合监控:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dashboard-direct-vs-turbine-640.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 4.1 创建turbine模块,导入依赖
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.heibaiying.hystrix</groupId>
|
||||
<artifactId>spring-cloud-hystrx</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>turbine</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<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>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.2 指定注册中心地址和聚合的项目,这里我们监控 consumer,producer 两个项目
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8040
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: turbine
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
# 指定聚合的项目
|
||||
turbine:
|
||||
aggregator:
|
||||
cluster-config: default
|
||||
combine-host-port: true
|
||||
app-config: consumer,producer
|
||||
clusterNameExpression: "'default'"
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.3 在启动类上添加注解
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableHystrix
|
||||
@EnableHystrixDashboard
|
||||
@EnableTurbine
|
||||
public class TurbineApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TurbineApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.4 依次启动eureka、producer、consumer、turbine四个项目
|
||||
|
||||
在 localhost:8030/hystrix或者localhost:8030/hystrix(consumer和producer都集成了hystrix) 页面输入http://localhost:8040/turbine.stream,查看断路器聚合信息
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-cluster-login.png"/> </div>
|
||||
|
||||
**显示了不同服务单元(consumer,producer)的多个断路器信息:**
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-cluster.png"/> </div>
|
||||
|
||||
## 五、整合过程中可能出现的问题
|
||||
|
||||
#### 5.1 无法访问监控页面
|
||||
|
||||
1. 一般是端点链接输入不对,在F版本的spring cloud 中,输入监控的端点链接是 http://localhost:8030/actuator/hystrix.stream ,中间是有/actuator/(之前版本的没有/actuator/)
|
||||
|
||||
2. 没有暴露端点链接,暴露端点链接有两种方式,一种是我们在上文中提到的基于配置的方式
|
||||
|
||||
```yaml
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
# 需要开启hystrix.stream端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点
|
||||
include: "*"
|
||||
```
|
||||
|
||||
第二种方式是基于代码的方式,如下:
|
||||
|
||||
```java
|
||||
@Bean
|
||||
public ServletRegistrationBean getServlet() {
|
||||
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
|
||||
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
|
||||
registrationBean.setLoadOnStartup(1);
|
||||
registrationBean.addUrlMappings("/actuator/hystrix.stream");
|
||||
registrationBean.setName("HystrixMetricsStreamServlet");
|
||||
return registrationBean;
|
||||
}
|
||||
```
|
||||
|
||||
这两种方式二选一即可,就算是采用代码的方式,还是建议将地址设置为/actuator/hystrix.stream,而不是原来的hystrix.stream,因为turbine默认也是从/actuator/hystrix.stream去获取信息。
|
||||
|
||||
#### 5.2 页面一直loading 或者访问端点页面一直出现ping
|
||||
|
||||
这种情况是熔断器所在的方法没有被调用,所以没有产生监控数据,不是整合问题,这时候调用一下熔断器所在方法即可。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-loading.png"/> </div>
|
||||
|
||||
## 一、hystrix 简介
|
||||
|
||||
#### 1.1 熔断器
|
||||
|
||||
在分布式系统中,由于服务之间相互的依赖调用,如果一个服务单元发生了故障就有可能导致故障蔓延至整个系统,从而衍生出一系列的保护机制,断路器就是其中之一。
|
||||
|
||||
断路器可以在服务单元发生故障的时候,及时切断与服务单元的连接,避免资源被长时间占用。spring cloud hystrix组件实现了断路器、线程隔离等一系列基本功能,并具有服务降级、服务熔断、请求缓存、请求合并以及服务监控等配套功能。
|
||||
|
||||
|
||||
|
||||
#### 1.2 hystrix 工作机制
|
||||
|
||||
- 当一个服务的处理请求的失败次数低于阈值时候,熔断器处于关闭状态,服务正常;
|
||||
- 当一个服务的处理请求的失败次数大于阈值时候,熔断器开启,这时候所有的请求都会执行快速失败,是不会去调用实际的服务的;
|
||||
- 当熔断器处于打开状态的一段时间后,熔断器处于半打开状态,这时候一定数量的请求回去调用实际的服务,如果调用成功,则代表服务可用了,熔断器关闭;如果还是失败,则代表服务还是不可用,熔断器继续关闭。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/circuitbreaker.png"/> </div>
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
[spring-cloud-ribbon](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-ribbon)用例已经实现通过ribbon+restTemplate实现服务间的调用,本用例在其基础上进行hystrix 的整合。
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用RestTemplate调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心;
|
||||
+ turbine:多个熔断器的聚合监控。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-hystrix.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 三、整合 hystrix (以consumer模块为例)
|
||||
|
||||
#### 3.1 引入依赖
|
||||
|
||||
hystrix的仪表盘功能实际上是从[端点](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-boot/spring-boot-actuator)获取数据,所以需要actuator starter开启端点的相关功能。
|
||||
|
||||
```xml
|
||||
<!--hystrix 依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<!--hystrix 监控仪表盘依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
</dependency>
|
||||
<!--健康检查依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 3.2 暴露端点
|
||||
|
||||
```java
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
# 需要开启hystrix.stream端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点
|
||||
include: "*"
|
||||
```
|
||||
|
||||
#### 3.3 在启动类上添加注解@EnableHystrix和@EnableHystrixDashboard
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableHystrix
|
||||
@EnableHystrixDashboard
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.4 使用 @HystrixCommand 定义失败回退的方法
|
||||
|
||||
```java
|
||||
@HystrixCommand(fallbackMethod = "queryProductsFail")
|
||||
public List<Product> queryAllProducts() {
|
||||
ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://producer/products", List.class);
|
||||
List<Product> productList = responseEntity.getBody();
|
||||
return productList;
|
||||
}
|
||||
|
||||
// 如果发送熔断返回空集合,在前端判断处理
|
||||
public List<Product> queryProductsFail() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>产品列表</title>
|
||||
</head>
|
||||
<body>
|
||||
<h3>产品列表:点击查看详情</h3>
|
||||
<form action="/sell/product" method="post">
|
||||
<input type="text" name="productName">
|
||||
<input type="submit" value="新增产品">
|
||||
</form>
|
||||
<ul>
|
||||
<#if (products?size>0) >
|
||||
<#list products as product>
|
||||
<li>
|
||||
<a href="/sell/product/${product.id}">${product.name}</a>
|
||||
</li>
|
||||
</#list>
|
||||
<#else>
|
||||
<h4 style="color: red">当前排队人数过多,请之后再购买!</h4>
|
||||
</#if>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### 3.5 模拟熔断
|
||||
|
||||
这里被调用方采用线程休眠的方式模拟服务超时,Hystrix默认超时时间为2s,调用远程服务时候超过这个时间,会触发熔断。
|
||||
|
||||
```java
|
||||
public List<Product> queryAllProducts() {
|
||||
// hystrix 默认超时是2秒
|
||||
int i = new Random().nextInt(2500);
|
||||
try {
|
||||
Thread.sleep(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return productList;
|
||||
}
|
||||
```
|
||||
|
||||
3.5 启动服务,访问http://localhost:8030/sell/products ,多次刷新查看熔断情况
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030.png"/> </div>
|
||||
|
||||
#### 3.5 启动服务,访问 localhost:8030/hystrix
|
||||
|
||||
依次输出http://localhost:8030/actuator/hystrix.stream(监控地址) ,2000(延迟时间),title可以任意填写,进入监控台。
|
||||
|
||||
需要注意的是在spring cloud Finchley.SR2,监控地址中都是有/actuator的,因为在spring boot 2.x 的所有端点(包括自定义端点)都是暴露在这个路径下,在启动时候的控制台输出的日志可以查看到所有暴露端点的映射。
|
||||
|
||||
**登录页面**:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-single-login.png"/> </div>
|
||||
|
||||
**监控页面**:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-8030-login.png"/> </div>
|
||||
|
||||
**关于各个参数的说明参见[官方wiki](https://github.com/Netflix-Skunkworks/hystrix-dashboard/wiki)提供的图**:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dashboard.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 四、使用turbine 实现聚合监控
|
||||
|
||||
单体监控和聚合监控:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/dashboard-direct-vs-turbine-640.png"/> </div>
|
||||
|
||||
|
||||
|
||||
#### 4.1 创建turbine模块,导入依赖
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
<parent>
|
||||
<groupId>com.heibaiying.hystrix</groupId>
|
||||
<artifactId>spring-cloud-hystrx</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>turbine</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<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>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.2 指定注册中心地址和聚合的项目,这里我们监控 consumer,producer 两个项目
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8040
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: turbine
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
# 指定聚合的项目
|
||||
turbine:
|
||||
aggregator:
|
||||
cluster-config: default
|
||||
combine-host-port: true
|
||||
app-config: consumer,producer
|
||||
clusterNameExpression: "'default'"
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.3 在启动类上添加注解
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableHystrix
|
||||
@EnableHystrixDashboard
|
||||
@EnableTurbine
|
||||
public class TurbineApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TurbineApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.4 依次启动eureka、producer、consumer、turbine四个项目
|
||||
|
||||
在 localhost:8030/hystrix或者localhost:8030/hystrix(consumer和producer都集成了hystrix) 页面输入http://localhost:8040/turbine.stream,查看断路器聚合信息
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-cluster-login.png"/> </div>
|
||||
|
||||
**显示了不同服务单元(consumer,producer)的多个断路器信息:**
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-cluster.png"/> </div>
|
||||
|
||||
## 五、整合过程中可能出现的问题
|
||||
|
||||
#### 5.1 无法访问监控页面
|
||||
|
||||
1. 一般是端点链接输入不对,在F版本的spring cloud 中,输入监控的端点链接是 http://localhost:8030/actuator/hystrix.stream ,中间是有/actuator/(之前版本的没有/actuator/)
|
||||
|
||||
2. 没有暴露端点链接,暴露端点链接有两种方式,一种是我们在上文中提到的基于配置的方式
|
||||
|
||||
```yaml
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
# 需要开启hystrix.stream端点的暴露 这样才能获取到监控信息 * 代表开启所有可监控端点
|
||||
include: "*"
|
||||
```
|
||||
|
||||
第二种方式是基于代码的方式,如下:
|
||||
|
||||
```java
|
||||
@Bean
|
||||
public ServletRegistrationBean getServlet() {
|
||||
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
|
||||
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
|
||||
registrationBean.setLoadOnStartup(1);
|
||||
registrationBean.addUrlMappings("/actuator/hystrix.stream");
|
||||
registrationBean.setName("HystrixMetricsStreamServlet");
|
||||
return registrationBean;
|
||||
}
|
||||
```
|
||||
|
||||
这两种方式二选一即可,就算是采用代码的方式,还是建议将地址设置为/actuator/hystrix.stream,而不是原来的hystrix.stream,因为turbine默认也是从/actuator/hystrix.stream去获取信息。
|
||||
|
||||
#### 5.2 页面一直loading 或者访问端点页面一直出现ping
|
||||
|
||||
这种情况是熔断器所在的方法没有被调用,所以没有产生监控数据,不是整合问题,这时候调用一下熔断器所在方法即可。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/hystrix-loading.png"/> </div>
|
||||
|
@ -1,331 +1,325 @@
|
||||
# spring-cloud-ribbon
|
||||
## 目录<br/>
|
||||
# spring-cloud-ribbon
|
||||
|
||||
## 目录<br/>
|
||||
<a href="#一ribbon-简介">一、ribbon 简介</a><br/>
|
||||
<a href="#二项目结构">二、项目结构</a><br/>
|
||||
<a href="#三服务提供者的实现">三、服务提供者的实现</a><br/>
|
||||
<a href="#31-产品服务由`ProductService`提供并通过`ProducerController`将服务暴露给外部调用。">3.1 产品服务由`ProductService`提供,并通过`ProducerController`将服务暴露给外部调用。</a><br/>
|
||||
<a href="#32-指定注册中心地址并在启动类上开启自动注册@EnableDiscoveryClient">3.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient</a><br/>
|
||||
<a href="#四服务消费者的实现">四、服务消费者的实现</a><br/>
|
||||
<a href="#41-导入负载均衡需要的依赖">4.1 导入负载均衡需要的依赖</a><br/>
|
||||
<a href="#42-指定注册中心地址并在启动类上开启自动注册@EnableDiscoveryClient">4.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient</a><br/>
|
||||
<a href="#43-使用@LoadBalanced配置RestTemplate即可实现客户端负载均衡">4.3 使用@LoadBalanced配置RestTemplate即可实现客户端负载均衡</a><br/>
|
||||
<a href="#44-使用RestTemplate调用远程服务">4.4 使用RestTemplate调用远程服务</a><br/>
|
||||
<a href="#五启动测试">五、启动测试</a><br/>
|
||||
<a href="#51-启动一个Eureka服务三个producer服务注意区分端口和一个消费者服务">5.1 启动一个Eureka服务、三个producer服务(注意区分端口)、和一个消费者服务</a><br/>
|
||||
<a href="#52--访问http//localhost8080/sell/products-查看负载均衡的调用结果">5.2 访问http://localhost:8080/sell/products 查看负载均衡的调用结果</a><br/>
|
||||
<a href="#六-附1-关于RestTemplate的说明">六、 附1: 关于RestTemplate的说明</a><br/>
|
||||
<a href="#61--restTemplate-规范">6.1 restTemplate 规范</a><br/>
|
||||
<a href="#62-ForEntity和ForObject的区别">6.2 ForEntity()和ForObject的区别</a><br/>
|
||||
<a href="#62-ForEntity和ForObject的区别">6.2 ForEntity()和ForObject()的区别</a><br/>
|
||||
<a href="#七-附2-关于ribbon更多负载均衡的策略">七、 附2: 关于ribbon更多负载均衡的策略</a><br/>
|
||||
<a href="#71-内置的负载均衡的策略如下图">7.1 内置的负载均衡的策略如下图</a><br/>
|
||||
<a href="#72-配置文件指定负载均衡的方式">7.2 配置文件指定负载均衡的方式</a><br/>
|
||||
<a href="#73-代码指定负载均衡的方式">7.3 代码指定负载均衡的方式</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
## 一、ribbon 简介
|
||||
|
||||
ribbon是Netfix公司开源的负载均衡组件,采用服务端负载均衡的方式,即消费者客户端维护可用的服务列表,并通过负载均衡的方式将请求按照指定的策略分摊给消费者,从而达到负载均衡的方式。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用RestTemplate调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心,ribbon 从注册中心获取可用的服务列表,是实现负载均衡的基础。这里使用我们在[服务的注册与发现](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-eureka)这个用例中搭建的简单注册中心作为测试即可。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 三、服务提供者的实现
|
||||
|
||||
#### 3.1 产品服务由`ProductService`提供,并通过`ProducerController`将服务暴露给外部调用。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/ribbon-producer.png"/> </div>
|
||||
|
||||
ProductService.java:
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 产品提供接口实现类
|
||||
* 这里为了之后直观的看到负载均衡的结果,我们继承了 ApplicationListener,从 WebServerInitializedEvent 获取服务的端口号,并拼接在产品名称上
|
||||
*/
|
||||
@Service
|
||||
public class ProductService implements IProductService, ApplicationListener<WebServerInitializedEvent> {
|
||||
|
||||
private static List<Product> productList = new ArrayList<>();
|
||||
|
||||
public Product queryProductById(int id) {
|
||||
for (Product product : productList) {
|
||||
if (product.getId() == id) {
|
||||
return product;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Product> queryAllProducts() {
|
||||
return productList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveProduct(Product product) {
|
||||
productList.add(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(WebServerInitializedEvent event) {
|
||||
int port = event.getWebServer().getPort();
|
||||
for (long i = 0; i < 20; i++) {
|
||||
productList.add(new Product(i, port + "产品" + i, i / 2 == 0, new Date(), 66.66f * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ProducerController.java:
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class ProducerController {
|
||||
|
||||
@Autowired
|
||||
private IProductService productService;
|
||||
|
||||
@GetMapping("products")
|
||||
public List<Product> productList() {
|
||||
return productService.queryAllProducts();
|
||||
}
|
||||
|
||||
@GetMapping("product/{id}")
|
||||
public Product productDetail(@PathVariable int id) {
|
||||
return productService.queryProductById(id);
|
||||
}
|
||||
|
||||
@PostMapping("product")
|
||||
public void save(@RequestBody Product product) {
|
||||
productService.saveProduct(product);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8020
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: producer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ProducerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProducerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 四、服务消费者的实现
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/ribbon-consumer.png"/> </div>
|
||||
|
||||
#### 4.1 导入负载均衡需要的依赖
|
||||
|
||||
```xml
|
||||
<!--ribbon 依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 4.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8080
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: consumer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 使用@LoadBalanced配置RestTemplate即可实现客户端负载均衡
|
||||
|
||||
```java
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RibbonConfig {
|
||||
|
||||
@LoadBalanced // 配置客户端负载均衡
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.4 使用RestTemplate调用远程服务
|
||||
|
||||
这里我们在调用远程服务的时候,url填写的是服务名+具体接口地址 ,由于我们的同一个服务会存在多个实例,在使用@LoadBalanced配置RestTemplate调用服务时,客户端就会从按照指定的负载均衡的方式将请求分摊到多个实例上。(默认的负载均衡采用的是RoundRobinRule(轮询)的策略,有特殊需求时候可以采用其他内置的策略规则,或者实现IRule来定义自己的负载均衡策略)。
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class ProductService implements IProductService {
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
public Product queryProductById(int id) {
|
||||
ResponseEntity<Product> responseEntity = restTemplate.getForEntity("http://producer/product/{1}", Product.class, id);
|
||||
return responseEntity.getBody();
|
||||
}
|
||||
|
||||
|
||||
public List<Product> queryAllProducts() {
|
||||
ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://producer/products", List.class);
|
||||
List<Product> productList = responseEntity.getBody();
|
||||
return productList;
|
||||
}
|
||||
|
||||
public void saveProduct(Product product) {
|
||||
restTemplate.postForObject("http://producer/product", product, Void.class);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、启动测试
|
||||
|
||||
#### 5.1 启动一个Eureka服务、三个producer服务(注意区分端口)、和一个消费者服务
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-app.png"/> </div>
|
||||
|
||||
**服务注册中心:**
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-eureka.png"/> </div>
|
||||
|
||||
#### 5.2 访问http://localhost:8080/sell/products 查看负载均衡的调用结果
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8020.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8030.png"/> </div>
|
||||
|
||||
## 六、 附1: 关于RestTemplate的说明
|
||||
|
||||
#### 6.1 restTemplate 规范
|
||||
|
||||
restTemplate 调用对应resultful接口时候,使用的方法应该与接口声明方式(@GetMapping、@PostMapping、@PutMapping、@DeleteMapping)保持一致。请求类型与对应的调用方法如下。
|
||||
|
||||
- GET请求(getForObject 、getForEntity)
|
||||
- POST请求(postForObject 、postForEntity)
|
||||
- PUT请求(put)
|
||||
- DELETE请求 (delete)
|
||||
|
||||
#### 6.2 ForEntity()和ForObject的区别
|
||||
|
||||
- `ForEntity()`发送一个请求,返回的ResponseEntity包含了响应体所映射成的对象
|
||||
|
||||
- `ForObject()`发送一个请求,返回的请求体将映射为一个对象
|
||||
|
||||
例如:
|
||||
|
||||
```java
|
||||
ResponseEntity<Product> responseEntity = restTemplate.getForEntity("http://producer/product/{1}", Product.class, id);
|
||||
Product product = restTemplate.getForObject("http://producer/product/{1}", Product.class, id);
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 七、 附2: 关于ribbon更多负载均衡的策略
|
||||
|
||||
Ribbon内置了多种负载均衡策略,如果有更复杂的需求,可以自己实现IRule。
|
||||
|
||||
#### 7.1 内置的负载均衡的策略如下图
|
||||
|
||||

|
||||
|
||||
图片来源于博客:[Ribbon负载均衡策略与自定义配置](https://blog.csdn.net/jrn1012/article/details/77837680)
|
||||
|
||||
|
||||
|
||||
#### 7.2 配置文件指定负载均衡的方式
|
||||
|
||||
要设置`IRule`名为的服务名称`users`,您可以设置以下属性:
|
||||
|
||||
```yaml
|
||||
users:
|
||||
ribbon:
|
||||
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 7.3 代码指定负载均衡的方式
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
|
||||
public class TestConfiguration {
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class CustomConfiguration {
|
||||
|
||||
@Bean
|
||||
public IRule ribbonRule() {
|
||||
return new BestAvailableRule();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在使用代码方式的时候需要注意 [官方文档](http://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html#_customizing_the_ribbon_client)中关于注解方式有以下强调:
|
||||
|
||||
|
||||
The CustomConfiguration clas must be a @Configuration class, but take care that it is not in a @ComponentScan for the main application context. Otherwise, it is shared by all the @RibbonClients. If you use @ComponentScan (or @SpringBootApplication), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the @ComponentScan).
|
||||
|
||||
|
||||
该`CustomConfiguration`类必须是`@Configuration`标注的,但需要注意的它不是在`@ComponentScan`主应用程序上下文。否则,它将由所有`@RibbonClients`共享。如果你使用`@ComponentScan`(或`@SpringBootApplication`),你需要采取一些措施来避免它被扫描到(例如,你可以把它放在一个独立的,非重叠的包,或用`@ComponentScan`时显示扫描指定的包)。
|
||||
|
||||
## 一、ribbon 简介
|
||||
|
||||
ribbon是Netfix公司开源的负载均衡组件,采用服务端负载均衡的方式,即消费者客户端维护可用的服务列表,并通过负载均衡的方式将请求按照指定的策略分摊给消费者,从而达到负载均衡的方式。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用RestTemplate调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心,ribbon 从注册中心获取可用的服务列表,是实现负载均衡的基础。这里使用我们在[服务的注册与发现](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-eureka)这个用例中搭建的简单注册中心作为测试即可。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 三、服务提供者的实现
|
||||
|
||||
#### 3.1 产品服务由`ProductService`提供,并通过`ProducerController`将服务暴露给外部调用。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/ribbon-producer.png"/> </div>
|
||||
|
||||
ProductService.java:
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 产品提供接口实现类
|
||||
* 这里为了之后直观的看到负载均衡的结果,我们继承了 ApplicationListener,从 WebServerInitializedEvent 获取服务的端口号,并拼接在产品名称上
|
||||
*/
|
||||
@Service
|
||||
public class ProductService implements IProductService, ApplicationListener<WebServerInitializedEvent> {
|
||||
|
||||
private static List<Product> productList = new ArrayList<>();
|
||||
|
||||
public Product queryProductById(int id) {
|
||||
for (Product product : productList) {
|
||||
if (product.getId() == id) {
|
||||
return product;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Product> queryAllProducts() {
|
||||
return productList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveProduct(Product product) {
|
||||
productList.add(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(WebServerInitializedEvent event) {
|
||||
int port = event.getWebServer().getPort();
|
||||
for (long i = 0; i < 20; i++) {
|
||||
productList.add(new Product(i, port + "产品" + i, i / 2 == 0, new Date(), 66.66f * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ProducerController.java:
|
||||
|
||||
```java
|
||||
@RestController
|
||||
public class ProducerController {
|
||||
|
||||
@Autowired
|
||||
private IProductService productService;
|
||||
|
||||
@GetMapping("products")
|
||||
public List<Product> productList() {
|
||||
return productService.queryAllProducts();
|
||||
}
|
||||
|
||||
@GetMapping("product/{id}")
|
||||
public Product productDetail(@PathVariable int id) {
|
||||
return productService.queryProductById(id);
|
||||
}
|
||||
|
||||
@PostMapping("product")
|
||||
public void save(@RequestBody Product product) {
|
||||
productService.saveProduct(product);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8020
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: producer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ProducerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProducerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 四、服务消费者的实现
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/ribbon-consumer.png"/> </div>
|
||||
|
||||
#### 4.1 导入负载均衡需要的依赖
|
||||
|
||||
```xml
|
||||
<!--ribbon 依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### 4.2 指定注册中心地址,并在启动类上开启自动注册@EnableDiscoveryClient
|
||||
|
||||
```java
|
||||
server:
|
||||
port: 8080
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: consumer
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
```
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 使用@LoadBalanced配置RestTemplate即可实现客户端负载均衡
|
||||
|
||||
```java
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class RibbonConfig {
|
||||
|
||||
@LoadBalanced // 配置客户端负载均衡
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.4 使用RestTemplate调用远程服务
|
||||
|
||||
这里我们在调用远程服务的时候,url填写的是服务名+具体接口地址 ,由于我们的同一个服务会存在多个实例,在使用@LoadBalanced配置RestTemplate调用服务时,客户端就会从按照指定的负载均衡的方式将请求分摊到多个实例上。(默认的负载均衡采用的是RoundRobinRule(轮询)的策略,有特殊需求时候可以采用其他内置的策略规则,或者实现IRule来定义自己的负载均衡策略)。
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class ProductService implements IProductService {
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
public Product queryProductById(int id) {
|
||||
ResponseEntity<Product> responseEntity = restTemplate.getForEntity("http://producer/product/{1}", Product.class, id);
|
||||
return responseEntity.getBody();
|
||||
}
|
||||
|
||||
|
||||
public List<Product> queryAllProducts() {
|
||||
ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://producer/products", List.class);
|
||||
List<Product> productList = responseEntity.getBody();
|
||||
return productList;
|
||||
}
|
||||
|
||||
public void saveProduct(Product product) {
|
||||
restTemplate.postForObject("http://producer/product", product, Void.class);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、启动测试
|
||||
|
||||
#### 5.1 启动一个Eureka服务、三个producer服务(注意区分端口)、和一个消费者服务
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-app.png"/> </div>
|
||||
|
||||
**服务注册中心:**
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-eureka.png"/> </div>
|
||||
|
||||
#### 5.2 访问http://localhost:8080/sell/products 查看负载均衡的调用结果
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8020.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-ribbon-products-8030.png"/> </div>
|
||||
|
||||
## 六、 附1: 关于RestTemplate的说明
|
||||
|
||||
#### 6.1 restTemplate 规范
|
||||
|
||||
restTemplate 调用对应resultful接口时候,使用的方法应该与接口声明方式(@GetMapping、@PostMapping、@PutMapping、@DeleteMapping)保持一致。请求类型与对应的调用方法如下。
|
||||
|
||||
- GET请求(getForObject 、getForEntity)
|
||||
- POST请求(postForObject 、postForEntity)
|
||||
- PUT请求(put)
|
||||
- DELETE请求 (delete)
|
||||
|
||||
#### 6.2 ForEntity()和ForObject()的区别
|
||||
|
||||
- `ForEntity()`发送一个请求,返回的ResponseEntity包含了响应体所映射成的对象
|
||||
|
||||
- `ForObject()`发送一个请求,返回的请求体将映射为一个对象
|
||||
|
||||
例如:
|
||||
|
||||
```java
|
||||
ResponseEntity<Product> responseEntity = restTemplate.getForEntity("http://producer/product/{1}", Product.class, id);
|
||||
Product product = restTemplate.getForObject("http://producer/product/{1}", Product.class, id);
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 七、 附2: 关于ribbon更多负载均衡的策略
|
||||
|
||||
Ribbon内置了多种负载均衡策略,如果有更复杂的需求,可以自己实现IRule。
|
||||
|
||||
#### 7.1 内置的负载均衡的策略如下图
|
||||
|
||||

|
||||
|
||||
图片来源于博客:[Ribbon负载均衡策略与自定义配置](https://blog.csdn.net/jrn1012/article/details/77837680)
|
||||
|
||||
|
||||
|
||||
#### 7.2 配置文件指定负载均衡的方式
|
||||
|
||||
要设置`IRule`名为的服务名称`users`,您可以设置以下属性:
|
||||
|
||||
```yaml
|
||||
users:
|
||||
ribbon:
|
||||
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 7.3 代码指定负载均衡的方式
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
|
||||
public class TestConfiguration {
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class CustomConfiguration {
|
||||
|
||||
@Bean
|
||||
public IRule ribbonRule() {
|
||||
return new BestAvailableRule();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在使用代码方式的时候需要注意 [官方文档](http://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html#_customizing_the_ribbon_client)中关于注解方式有以下强调:
|
||||
|
||||
|
||||
The CustomConfiguration clas must be a @Configuration class, but take care that it is not in a @ComponentScan for the main application context. Otherwise, it is shared by all the @RibbonClients. If you use @ComponentScan (or @SpringBootApplication), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the @ComponentScan).
|
||||
|
||||
|
||||
该`CustomConfiguration`类必须是`@Configuration`标注的,但需要注意的它不是在`@ComponentScan`主应用程序上下文。否则,它将由所有`@RibbonClients`共享。如果你使用`@ComponentScan`(或`@SpringBootApplication`),你需要采取一些措施来避免它被扫描到(例如,你可以把它放在一个独立的,非重叠的包,或用`@ComponentScan`时显示扫描指定的包)。
|
||||
|
@ -9,6 +9,7 @@
|
||||
<a href="#五启动项目">五、启动项目</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
## 一、简介
|
||||
|
||||
在微服务架构中,几乎每一个前端的请求都会经过多个服务单元协调来提供服务,形成复杂的服务调用链路。当服务发生问题时候,很难知道问题来源于链路的哪一个环节,这时候就需要进行链路追踪。
|
||||
|
@ -1,408 +1,401 @@
|
||||
# spring-cloud-zuul
|
||||
## 目录<br/>
|
||||
# spring-cloud-zuul
|
||||
|
||||
## 目录<br/>
|
||||
<a href="#一zuul简介">一、zuul简介</a><br/>
|
||||
<a href="#11-API-网关">1.1 API 网关</a><br/>
|
||||
<a href="#12-zuul">1.2 zuul</a><br/>
|
||||
<a href="#二项目结构">二、项目结构</a><br/>
|
||||
<a href="#三构建api-网关-zuul">三、构建api 网关 zuul</a><br/>
|
||||
<a href="#31-引入依赖">3.1 引入依赖</a><br/>
|
||||
<a href="#32-在启动类上添加注解@EnableZuulProxy和@EnableDiscoveryClient">3.2 在启动类上添加注解@EnableZuulProxy和@EnableDiscoveryClient</a><br/>
|
||||
<a href="#33--指定注册中心配置网关的路由规则">3.3 指定注册中心、配置网关的路由规则</a><br/>
|
||||
<a href="#34--启动eurekaproducerconsumerzuul服务访问-localhost8090/consumer/sell/product">3.4 启动eureka、producer、consumer、zuul服务,访问 localhost:8090/consumer/sell/product </a><br/>
|
||||
<a href="#四错误熔断">四、错误熔断</a><br/>
|
||||
<a href="#41--zuul-默认整合了-hystrix-不用导入其他额外依赖">4.1 zuul 默认整合了 hystrix ,不用导入其他额外依赖</a><br/>
|
||||
<a href="#42-创建-CustomZuulFallbackProvider并实现FallbackProvider-接口同时用@Component声明为spring-组件即可实现熔断时候的回退服务">4.2 创建 CustomZuulFallbackProvider并实现FallbackProvider 接口,同时用@Component声明为spring 组件,即可实现熔断时候的回退服务</a><br/>
|
||||
<a href="#五zuul--过滤器">五、zuul 过滤器</a><br/>
|
||||
<a href="#六负载均衡">六、负载均衡</a><br/>
|
||||
<a href="#zuul-默认集成了ribbon-实现了负载均衡。只要启动多个实例即可查看到负载均衡的效果。">zuul 默认集成了ribbon 实现了负载均衡。只要启动多个实例即可查看到负载均衡的效果。</a><br/>
|
||||
<a href="#这里我们直接在idea-中启动多个实例来测试">这里我们直接在idea 中启动多个实例来测试:</a><br/>
|
||||
<a href="#负载均衡测试结果">负载均衡测试结果:</a><br/>
|
||||
<a href="#七附关于版本问题可能导致的-zuul-启动失败">七、附:关于版本问题可能导致的 zuul 启动失败</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
## 一、zuul简介
|
||||
|
||||
### 1.1 API 网关
|
||||
|
||||
api 网关是整个微服务系统的门面,所有的外部访问需要通过网关进行调度和过滤。它实现了请求转发、负载均衡、校验过滤、错误熔断、服务聚合等功能。
|
||||
|
||||
下图是直观的显示api Gateway 在微服务网关中的作用(图片引用自spring boot 官网)。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/apiGateway.png"/> </div>
|
||||
|
||||
### 1.2 zuul
|
||||
|
||||
spring cloud 中提供了基础Net flix Zuul 实现的网关组件,这就是Zuul,它除了实现负载均衡、错误熔断、路由转发等功能,还能与spring 其他组件无缝配合使用。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
[spring-cloud-feign](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-feign)用例已经实现通过feign实现服务间的调用,且提供了两个业务服务单元(consumer、producer),可以方便直观的测试zuul的路由、负载均衡、和错误熔断等功能,所以本用例在其基础上进行zuul的整合。
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用feign调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心;
|
||||
+ zuul: api网关。
|
||||
|
||||
聚合项目目录如下:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-zuul.png"/> </div>
|
||||
|
||||
zuul 项目目录如下:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 三、构建api 网关 zuul
|
||||
|
||||
#### 3.1 引入依赖
|
||||
|
||||
主要的依赖是 spring-cloud-starter-netflix-zuul
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<artifactId>zuul</artifactId>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud.version>Finchley.SR2</spring-cloud.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-freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--eureka-client-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<!--zuul-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
</project>
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.2 在启动类上添加注解@EnableZuulProxy和@EnableDiscoveryClient
|
||||
|
||||
@EnableZuulProxy会自动设置Zuul服务器端点并在其中开启一些反向代理过滤器,以便将请求转发到后端服务器。
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableZuulProxy
|
||||
@EnableDiscoveryClient
|
||||
public class ZuulApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ZuulApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.3 指定注册中心、配置网关的路由规则
|
||||
|
||||
zuul 需要指定注册中心的地址,zuul 会从eureka获取其他微服务的实例信息,然后按照指定的路由规则进行请求转发。
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8090
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: zuul
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
# 网关的路由
|
||||
zuul:
|
||||
routes:
|
||||
xxxx: #这个地方的值是可以任意的字符串
|
||||
path: /producer/**
|
||||
serviceId: producer
|
||||
consumer:
|
||||
path: /consumer/**
|
||||
serviceId: consumer
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.4 启动eureka、producer、consumer、zuul服务,访问 localhost:8090/consumer/sell/product
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 四、错误熔断
|
||||
|
||||
#### 4.1 zuul 默认整合了 hystrix ,不用导入其他额外依赖
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-hystrix.png"/> </div>
|
||||
|
||||
#### 4.2 创建 CustomZuulFallbackProvider并实现FallbackProvider 接口,同时用@Component声明为spring 组件,即可实现熔断时候的回退服务
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : zuul 的熔断器
|
||||
*/
|
||||
@Component
|
||||
public class CustomZuulFallbackProvider implements FallbackProvider {
|
||||
|
||||
/*
|
||||
* 定义熔断将用于哪些路由的服务
|
||||
*/
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return "consumer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
|
||||
return new ClientHttpResponse() {
|
||||
|
||||
/**
|
||||
* 返回响应的HTTP状态代码
|
||||
*/
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return HttpStatus.SERVICE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回HTTP状态代码
|
||||
*/
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return HttpStatus.SERVICE_UNAVAILABLE.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回响应的HTTP状态文本
|
||||
*/
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将消息正文作为输入流返回
|
||||
*/
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return new ByteArrayInputStream("商城崩溃了,请稍后重试!".getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将消息正文作为输入流返回
|
||||
*/
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
|
||||
return httpHeaders;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
正确返回了内容、同时返回的http状态码也和我们设置的一样。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-broker.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 五、zuul 过滤器
|
||||
|
||||
创建自定义过滤器继承自CustomZuulFilter,当我们访问网关的时候,如果判断session 中没有对应的 code,则跳转到我们自定义的登录页面。
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 自定义filter过滤器
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class CustomZuulFilter extends ZuulFilter {
|
||||
|
||||
/**
|
||||
* 返回过滤器的类型
|
||||
*/
|
||||
@Override
|
||||
public String filterType() {
|
||||
return FilterConstants.PRE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回过滤器的优先级顺序
|
||||
*/
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从此方法返回“true”意味着应该调用下面的 run()方法
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZuulFilter的核心校验方法
|
||||
*/
|
||||
@Override
|
||||
public Object run() throws ZuulException {
|
||||
RequestContext currentContext = RequestContext.getCurrentContext();
|
||||
HttpServletRequest request = currentContext.getRequest();
|
||||
String code = (String)request.getSession().getAttribute("code");
|
||||
if (StringUtils.isEmpty(code)){
|
||||
// 设置值为false 不将请求转发到对应的服务上
|
||||
currentContext.setSendZuulResponse(false);
|
||||
// 设置返回的状态码
|
||||
currentContext.setResponseStatusCode(HttpStatus.NON_AUTHORITATIVE_INFORMATION.value());
|
||||
HttpServletResponse response = currentContext.getResponse();
|
||||
try {
|
||||
// 跳转到登录页面
|
||||
response.sendRedirect("/index");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
index.ftl:
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/login" method="post">
|
||||
<input name="username" type="text">
|
||||
<button id="btn">输入临时用户名后登录!</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 六、负载均衡
|
||||
|
||||
#### zuul 默认集成了ribbon 实现了负载均衡。只要启动多个实例即可查看到负载均衡的效果。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-ribbon.png"/> </div>
|
||||
|
||||
#### 这里我们直接在idea 中启动多个实例来测试:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-config.png"/> </div>
|
||||
|
||||
#### 负载均衡测试结果:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer-8040.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer-8030.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 七、附:关于版本问题可能导致的 zuul 启动失败
|
||||
|
||||
如果出现以下错误导致启动失败,是 spring boot 版本不兼容导致的错误,Finchley SR2版本 spring cloud 中的 zuul 和 spring boot 2.1.x 版本存在不兼容。如果出现这个问题,则将 spring boot 将至 2.0.x 的版本即可,用例中采用的是 2.0.8 版本。在实际的开发中应该严格遵循spring 官方的版本依赖说明。
|
||||
|
||||
```java
|
||||
APPLICATION FAILED TO START
|
||||
|
||||
---
|
||||
|
||||
Description:
|
||||
|
||||
The bean 'counterFactory', defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration$ZuulCounterFactoryConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration$ZuulMetricsConfiguration.class] and overriding is disabled.
|
||||
|
||||
Action:
|
||||
|
||||
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
|
||||
|
||||
```
|
||||
|
||||
**spring cloud 版本说明**:
|
||||
|
||||
| Release Train | Boot Version |
|
||||
| ------------- | ------------ |
|
||||
| Greenwich | 2.1.x |
|
||||
| Finchley | 2.0.x |
|
||||
| Edgware | 1.5.x |
|
||||
| Dalston | 1.5.x |
|
||||
|
||||
更多组件的版本说明可以在[spring cloud overview](https://spring.io/projects/spring-cloud#overview) 页面查看。
|
||||
|
||||
## 一、zuul简介
|
||||
|
||||
### 1.1 API 网关
|
||||
|
||||
api 网关是整个微服务系统的门面,所有的外部访问需要通过网关进行调度和过滤。它实现了请求转发、负载均衡、校验过滤、错误熔断、服务聚合等功能。
|
||||
|
||||
下图是直观的显示api Gateway 在微服务网关中的作用(图片引用自spring boot 官网)。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/apiGateway.png"/> </div>
|
||||
|
||||
### 1.2 zuul
|
||||
|
||||
spring cloud 中提供了基础Net flix Zuul 实现的网关组件,这就是Zuul,它除了实现负载均衡、错误熔断、路由转发等功能,还能与spring 其他组件无缝配合使用。
|
||||
|
||||
|
||||
|
||||
## 二、项目结构
|
||||
|
||||
[spring-cloud-feign](https://github.com/heibaiying/spring-samples-for-all/tree/master/spring-cloud/spring-cloud-feign)用例已经实现通过feign实现服务间的调用,且提供了两个业务服务单元(consumer、producer),可以方便直观的测试zuul的路由、负载均衡、和错误熔断等功能,所以本用例在其基础上进行zuul的整合。
|
||||
|
||||
+ common: 公共的接口和实体类;
|
||||
+ consumer: 服务的消费者,采用feign调用产品服务;
|
||||
+ producer:服务的提供者;
|
||||
+ eureka: 注册中心;
|
||||
+ zuul: api网关。
|
||||
|
||||
聚合项目目录如下:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/spring-cloud-zuul.png"/> </div>
|
||||
|
||||
zuul 项目目录如下:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 三、构建api 网关 zuul
|
||||
|
||||
#### 3.1 引入依赖
|
||||
|
||||
主要的依赖是 spring-cloud-starter-netflix-zuul
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.8.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<artifactId>zuul</artifactId>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud.version>Finchley.SR2</spring-cloud.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-freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--eureka-client-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<!--zuul-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
</project>
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.2 在启动类上添加注解@EnableZuulProxy和@EnableDiscoveryClient
|
||||
|
||||
@EnableZuulProxy会自动设置Zuul服务器端点并在其中开启一些反向代理过滤器,以便将请求转发到后端服务器。
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableZuulProxy
|
||||
@EnableDiscoveryClient
|
||||
public class ZuulApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ZuulApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.3 指定注册中心、配置网关的路由规则
|
||||
|
||||
zuul 需要指定注册中心的地址,zuul 会从eureka获取其他微服务的实例信息,然后按照指定的路由规则进行请求转发。
|
||||
|
||||
```yaml
|
||||
server:
|
||||
port: 8090
|
||||
# 指定服务命名
|
||||
spring:
|
||||
application:
|
||||
name: zuul
|
||||
# 指定注册中心地址
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://localhost:8010/eureka/
|
||||
# 网关的路由
|
||||
zuul:
|
||||
routes:
|
||||
xxxx: #这个地方的值是可以任意的字符串
|
||||
path: /producer/**
|
||||
serviceId: producer
|
||||
consumer:
|
||||
path: /consumer/**
|
||||
serviceId: consumer
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.4 启动eureka、producer、consumer、zuul服务,访问 localhost:8090/consumer/sell/product
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 四、错误熔断
|
||||
|
||||
#### 4.1 zuul 默认整合了 hystrix ,不用导入其他额外依赖
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-hystrix.png"/> </div>
|
||||
|
||||
#### 4.2 创建 CustomZuulFallbackProvider并实现FallbackProvider 接口,同时用@Component声明为spring 组件,即可实现熔断时候的回退服务
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : zuul 的熔断器
|
||||
*/
|
||||
@Component
|
||||
public class CustomZuulFallbackProvider implements FallbackProvider {
|
||||
|
||||
/*
|
||||
* 定义熔断将用于哪些路由的服务
|
||||
*/
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return "consumer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
|
||||
return new ClientHttpResponse() {
|
||||
|
||||
/**
|
||||
* 返回响应的HTTP状态代码
|
||||
*/
|
||||
@Override
|
||||
public HttpStatus getStatusCode() throws IOException {
|
||||
return HttpStatus.SERVICE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回HTTP状态代码
|
||||
*/
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return HttpStatus.SERVICE_UNAVAILABLE.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回响应的HTTP状态文本
|
||||
*/
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将消息正文作为输入流返回
|
||||
*/
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return new ByteArrayInputStream("商城崩溃了,请稍后重试!".getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将消息正文作为输入流返回
|
||||
*/
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
|
||||
return httpHeaders;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
正确返回了内容、同时返回的http状态码也和我们设置的一样。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-broker.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 五、zuul 过滤器
|
||||
|
||||
创建自定义过滤器继承自CustomZuulFilter,当我们访问网关的时候,如果判断session 中没有对应的 code,则跳转到我们自定义的登录页面。
|
||||
|
||||
```java
|
||||
/**
|
||||
* @author : heibaiying
|
||||
* @description : 自定义filter过滤器
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class CustomZuulFilter extends ZuulFilter {
|
||||
|
||||
/**
|
||||
* 返回过滤器的类型
|
||||
*/
|
||||
@Override
|
||||
public String filterType() {
|
||||
return FilterConstants.PRE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回过滤器的优先级顺序
|
||||
*/
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从此方法返回“true”意味着应该调用下面的 run()方法
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZuulFilter的核心校验方法
|
||||
*/
|
||||
@Override
|
||||
public Object run() throws ZuulException {
|
||||
RequestContext currentContext = RequestContext.getCurrentContext();
|
||||
HttpServletRequest request = currentContext.getRequest();
|
||||
String code = (String)request.getSession().getAttribute("code");
|
||||
if (StringUtils.isEmpty(code)){
|
||||
// 设置值为false 不将请求转发到对应的服务上
|
||||
currentContext.setSendZuulResponse(false);
|
||||
// 设置返回的状态码
|
||||
currentContext.setResponseStatusCode(HttpStatus.NON_AUTHORITATIVE_INFORMATION.value());
|
||||
HttpServletResponse response = currentContext.getResponse();
|
||||
try {
|
||||
// 跳转到登录页面
|
||||
response.sendRedirect("/index");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
index.ftl:
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/login" method="post">
|
||||
<input name="username" type="text">
|
||||
<button id="btn">输入临时用户名后登录!</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 六、负载均衡
|
||||
|
||||
#### zuul 默认集成了ribbon 实现了负载均衡。只要启动多个实例即可查看到负载均衡的效果。
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-ribbon.png"/> </div>
|
||||
|
||||
#### 这里我们直接在idea 中启动多个实例来测试:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-config.png"/> </div>
|
||||
|
||||
#### 负载均衡测试结果:
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer-8040.png"/> </div>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/spring-samples-for-all/blob/master/pictures/zuul-consumer-8030.png"/> </div>
|
||||
|
||||
|
||||
|
||||
## 七、附:关于版本问题可能导致的 zuul 启动失败
|
||||
|
||||
如果出现以下错误导致启动失败,是 spring boot 版本不兼容导致的错误,Finchley SR2版本 spring cloud 中的 zuul 和 spring boot 2.1.x 版本存在不兼容。如果出现这个问题,则将 spring boot 将至 2.0.x 的版本即可,用例中采用的是 2.0.8 版本。在实际的开发中应该严格遵循spring 官方的版本依赖说明。
|
||||
|
||||
```java
|
||||
APPLICATION FAILED TO START
|
||||
|
||||
---
|
||||
|
||||
Description:
|
||||
|
||||
The bean 'counterFactory', defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration$ZuulCounterFactoryConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/cloud/netflix/zuul/ZuulServerAutoConfiguration$ZuulMetricsConfiguration.class] and overriding is disabled.
|
||||
|
||||
Action:
|
||||
|
||||
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
|
||||
|
||||
```
|
||||
|
||||
**spring cloud 版本说明**:
|
||||
|
||||
| Release Train | Boot Version |
|
||||
| ------------- | ------------ |
|
||||
| Greenwich | 2.1.x |
|
||||
| Finchley | 2.0.x |
|
||||
| Edgware | 1.5.x |
|
||||
| Dalston | 1.5.x |
|
||||
|
||||
更多组件的版本说明可以在[spring cloud overview](https://spring.io/projects/spring-cloud#overview) 页面查看。
|
||||
|
Loading…
x
Reference in New Issue
Block a user