1. Hystrix DashBoard
hystrix 对请求的降级和熔断,可以产生监控信息,hystrix dashboard可以实时的进行监控,底层利用actuator来暴露hystrix错误处理的监控日志
1.1 actuator是什么
springboot提供的项目监控工具,们可以暴露项目中的多种监控数据
1.2 相关功能
- 健康状态
- sping容器中所有的对象
- springmvc映射的所有路径
- 环境变量
- …
1.3 如何添加actuator
1.3.1 添加actuator依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1.3.2 配置yml
management:
endpoints:
web:
exposure:
//暴露所有监控信息
include: "*"
1.3.3 查看监控数据
- http://localhost:3001/actuator
1.4 Hystrix DashBoard 仪表盘
1.4.1 创建sp08-hystrixdashboard项目
1.4.2 配置yml
- hystrix 仪表盘可以为完全独立项目
- 也不需要连接eureka
# hystrix 仪表盘可以为完全独立项目
# 也不需要连接eureka
server:
port: 4001
# hystrix dashboard
hystrix:
dashboard:
proxy-stream-allow-list: localhost
# 注册eureka,可以不注册
1.4.3 启动类添加@EnableHystrixDashboard注解
package cn.tedu.sp08;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class Sp08HystrixdashboardApplication {
public static void main(String[] args) {
SpringApplication.run(Sp08HystrixdashboardApplication.class, args);
}
}
1.4.4 开启监控
- http://localhost:4001/hystrix
- http://localhost:3001/actuator/hystrix.stream
1.4.5 访问网址测试
开启所有启动类
访问下面网址进行测试
- http://localhost:3001/item-service/35
- http://localhost:3001/user-service/7
- http://localhost:3001/user-service/7/score?score=100
- http://localhost:3001/order-service/123abc
- http://localhost:3001/order-service/
1.4.6 图标分析
1.4.7 压力测试
- hystrix 熔断
整个链路达到一定的阈值,默认情况下,10秒内产生超过20次请求,则符合第一个条件。满足第一个条件的情况下,如果请求的错误百分比大于阈值,则会打开断路器,默认为50%。Hystrix的逻辑,先判断是否满足第一个条件,再判断第二个条件,如果两个条件都满足,则会开启断路器.断路器打开 5 秒后,会处于半开状态,会尝试转发请求,如果仍然失败,保持打开状态,如果成功,则关闭断路器
1.4.7.1 使用 apache 的并发访问测试工具 ab
http://httpd.apache.org/docs/current/platform/windows.html#down
在工具文件bin目录下,开启cmd,输入下面命令测试,次数根据配置自定
ab -n 20000 -c 10 http://localhost:3001/item-service/35
ab -n 20000 -c 10 http://localhost:3001/user-service/35
ab -n 20000 -c 10 http://localhost:3001/order-service/35
1.4.7.2 测试效果
2. feign整合ribbon/hystrix
- 微服务应用中,ribbon 和 hystrix 总是同时出现,feign 整合了两者,并提供了声明式消费者客户端
- feign 使用声明式客户端接口做远程调用
2.1 创建sp09-feign项目
2.1.1 pom.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 https://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.3.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>sp09-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sp09-feign</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencies>
<!--actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--Openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<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>
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>sp01-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.1.2 application.yml
spring:
application:
name: feign
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
2.1.3 配置启动类
添加注解:
- @EnableFeignClients
- @EnableDiscoveryClient
package cn.tedu.com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class Sp09FeignApplication {
public static void main(String[] args) {
SpringApplication.run(Sp09FeignApplication.class, args);
}
}
2.2 实现远程调用
2.2.1 feign 声明式客户端
feign 利用了我们熟悉的 spring mvc 注解来对接口方法进行设置,降低了我们的学习成本。
通过这些设置,feign可以拼接后台服务的访问路径和提交的参数
例如:
@GetMapping("/{userId}/score")
JsonResult addScore(@PathVariable Integer userId, @RequestParam Integer score);
当这样调用该方法:
service.addScore(7, 100);
那么 feign 会向服务器发送请求:
http://用户微服务/7/score?score=100
注意:如果 score 参数名与变量名不同,需要添加参数名设置:
@GetMapping("/{userId}/score")
JsonResult addScore(@PathVariable Integer userId, @RequestParam("score") Integer s);
2.2.2 编辑ItemClient
package cn.tedu.com.service;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(name="item-service")
public interface ItemClient {
//获取商品列表
@GetMapping("/{orderId}")
JsonResult<List<Item>> getItems(@PathVariable String orderId);
//减少商品库存
@PostMapping("/decreaseNumber")
JsonResult<?> decreaseNumber(@RequestBody List<Item> items);
}
2.2.3 编辑UserClient
package cn.tedu.com.service;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "user-service")
public interface UserClient {
//获取用户信息
@GetMapping("/{userId}")
JsonResult<User> getUser(@PathVariable Integer userId);
// 拼接路径 /{userId}/score?score=新增积分
@GetMapping("/{userId}/score")
JsonResult addScore(@PathVariable Integer userId,@RequestParam Integer score);
}
2.2.4 编辑OrderClient
package cn.tedu.com.service;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "order-service")
public interface OrderClient {
//获取订单信息
@GetMapping("/{orderId}")
JsonResult<Order> getOrder(@PathVariable String orderId);
//添加订单
@GetMapping("/")
JsonResult addOrder();
}
2.2.5 编辑FeignController
package cn.tedu.com.controller;
import cn.tedu.com.service.ItemClient;
import cn.tedu.com.service.OrderClient;
import cn.tedu.com.service.UserClient;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class FeignController {
@Autowired
private ItemClient itemClient;
@Autowired
private OrderClient orderClient;
@Autowired
private UserClient userClient;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId){
//通过ItemClient 声明式客户端接口,调用远程服务
return itemClient.getItems(orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult<?> decreaseNumber(@RequestBody List<Item> items){
return itemClient.decreaseNumber(items);
}
@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId){
return userClient.getUser(userId);
}
@GetMapping("/user-service/{userId}/score")
public JsonResult<?> addScore(Integer userId,Integer score){
return userClient.addScore(userId, score);
}
@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId){
return orderClient.getOrder(orderId);
}
@GetMapping("/order-service/")
public JsonResult addOrder(){
return orderClient.addOrder();
}
}
2.2.6 测试
启动所有启动类
使用postman,GET发送以下格式数据:
- http://eureka1:2001
- http://localhost:3001/item-service/35
- http://localhost:3001/item-service/decreaseNumber
- http://localhost:3001/user-service/7
- http://localhost:3001/user-service/7/score?score=100
- http://localhost:3001/order-service/123abc
- http://localhost:3001/order-service/
使用postman,POST发送以下格式数据:
- [{“id”:1, “name”:“abc”, “number”:23},{“id”:2, “name”:“def”, “number”:11}]
2.3 feign实现负载均衡和重试功能
2.3.1 启用ribbon
无需额外配置,feign 默认已启用了 ribbon 负载均衡和重试机制。可以通过配置对参数进行调整
重试默认配置参数:
ConnectTimeout=1000
ReadTimeout=1000
MaxAutoRetries=0
MaxAutoRetriesNextServer=1
2.3.2 配置application.yml
- ribbon.xxx 全局配置,对所有服务都有效
- item-service.ribbon.xxx 对特定服务实例的配置
spring:
application:
name: feign
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
ConnectTimeout: 1000
ReadTimeout: 1000
item-service:
ribbon:
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 2
ConnectTimeout: 1000
ReadTimeout: 500
2.3.3 测试
访问测试
- http://localhost:3001/item-service/35
默认1秒会快速失败,没有降级方法时,会显示白板页
添加配置,暂时减小降级超时时间,以便对降级进行测试
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
2.4 Feign实现降级
Feign默认关闭,Feign不推荐启动Hystrix
2.4.1 启动hystrix
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 配置applicaiton.yml
feign:
hystrix:
enabled: true
- 启动类添加注解: @EnableCircuitBreaker
2.4.2 暴露hystrix监控
- 添加actuator依赖(已添加)
- 配置yml暴露监控端点:
management:
endpoints:
web:
exposure:
include: hystrix.stream
2.4.3 实现降级类
2.4.3.1 远程调用接口指定降级类
2.4.3.1.1 ItemClient
2.4.3.1.2 UserClient
2.4.3.1.3 OrderClient
2.4.3.2 ItemClientFB
package cn.tedu.com.service;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ItemClientFB implements ItemClient{
@Override
public JsonResult<List<Item>> getItems(String orderId) {
return JsonResult.err("无法获取订单商品列表");
}
@Override
public JsonResult<?> decreaseNumber(List<Item> items) {
return JsonResult.err("无法修改商品库存");
}
}
2.4.3.3 UserClientFB
package cn.tedu.com.service;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;
@Component
public class UserClientFB implements UserClient{
@Override
public JsonResult<User> getUser(Integer userId) {
return JsonResult.err("无法获取用户信息");
}
@Override
public JsonResult addScore(Integer userId, Integer score) {
return JsonResult.err("无法增加用户积分");
}
}
2.4.3.4 OrderClientFB
package cn.tedu.com.service;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.web.util.JsonResult;
import org.springframework.stereotype.Component;
@Component
public class OrderClientFB implements OrderClient{
@Override
public JsonResult<Order> getOrder(String orderId) {
return JsonResult.err("无法获取订单信息");
}
@Override
public JsonResult addOrder() {
return JsonResult.err("无法添加订单信息");
}
}
2.4.4 测试
2.5 监控和熔断测试
2.5.1 查看监控端点
http:localhost:3001/actuator
2.5.2 启动Hystrix Dashboard
访问 http://localhost:4001/hystrix
- 填入 feign 监控路径:
http://localhost:3001/actuator/hystrix.stream - 访问微服务,以产生监控数据
http://localhost:3001/item-service/35
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/
2.5.3 使用ab工具测试熔断
ab -n 20000 -c 10 http://localhost:3001/item-service/35
断路器状态为 Open,所有请求会被短路,直接降级执行 fallback 方法