Hystrix
系统容错工具
1.快速失败-避免后台服务阻塞,故障向前传播,引起雪崩效应
2.限流
降级:
调用后台服务失败时(异常,服务崩溃,超时),执行当前服务中的一段代码,来向客户端发回相应结果。
1.添加hystrix依赖
2.启动类添加注解:@EnableCircuitBreaker
3.添加降级代码
//在远程调用方法上,添加注解,指定降级方法
//调用远程服务失败时,会自动跳转到指定的方法,执行降级代码
@HystrixCommand(fallbackMethod=“方法名”)
public …a(){
restTemplate.getForObject(…);
}
4.超时时间默认是1秒
1.在06项目下添加依赖
ALT+Insert
然后在sp06主启动类添加注解:@EnableCircuitBreaker
然后去controller包下添加降级方法
在方法上添加注解@HystrixCommand(fallbackMethod=“方法名”)
然后两个方法都添加完之后,将两个方法都复制,删除注解和方法体代码
测试:
重启,06,02项目
超时
Hystrix默认超时时间1秒
Hystrix超时时间设置,应该和ribbon最大超时时间配合设置,否则ribbon重试无效
熔断:
自动熔断条件:
10秒内,20次请求(前置条件,必须首先满足)
50%失败,执行了降级代码
熔断后,不向后台服务调用,而是直接执行降级代码
熔断后几秒钟,进入半开状态,在半开状态下,会向后台服务尝试发送一次用户调用。
- 如果调用失败,继续保持打开状态几秒钟
- 如果调用成功,会自动关闭断路器,恢复正常
测试:访问http://localhost:3001/item-service/35,然后F5快速刷新多次,会发现没有1秒的延迟了,直接降级
Hystrix dashboard
对hystrix容错处理情况进行监控
1.在06项目产生Hystrix监控日志
2.仪表盘从06项目抓取日志,进行分析处理
actuator
springBoot提供的监控工具
- 健康状态
- spring容器中所有的对象
- springMVC映射的所有路径
- 系统环境变量
- JVM内存镜像
- …
添加actuator:
1.添加actuator依赖
2.暴露监控数据
- m.e.w.e.i="*" 暴露所有监控数据
- m.e.w.e.i=health,mappings,beans暴露多个指定的监控数据
- m.e.w.e.i=hystrix.stream 暴露一个监控数据
hystrix利用actuator工具,暴露自己的监控日志:hystrix.stream
1.添加依赖
然后在06项目下的application.yml文件添加m.e.w.e.i:
#通过actuator暴露项目的监控日志
management:
endpoints:
web:
exposure:
include: hystrix.stream
测试actuator项目监控日志:
访问http://localhost:3001/actuator
搭建 Hystrix dashboard
1.新建项目:sp08-hystrixdashboard
2.添加依赖:hystrixdashboard
3.启动类添加注解:@EnableHystrixDashboard
4.yml配置允许抓取日志的服务器列表
5.http://localhost:4001/hystrix
仪表盘项目可以是一个完全独立的项目
新建一个项目sp08
添加依赖
修改08项目配置文件application.properties 为application.yml
server:
port: 4001
hystrix:
dashboard:
proxy-stream-allow-list: localhost
在启动类上添加一个注解:@EnableHystrixDashboard
测试:启动项目08,访问子路径http://localhost:4001/hystrix
在输入框中输入你要抓取数据的网址
http://localhost:3001/actuator/hystrix.stream
压力测试,去我们的亿发课前资料springcloud文件夹下
在该目录下cmd
用 ab 工具,以并发50次,来发送20000个请求
ab -n 20000 -c 50 http://localhost:3001/item-service/35
Feign
集成工具
- 远程调用
- ribbon
- hystrix
Feign远程调用
声明式客户端
只需要声明抽象的接口,抽象方法 ,就可以通过接口调用远程服务
具体代码不用写,通过动态代理对象生成代码
//1.调用哪个服务?
//2.调用这个服务的哪个路径?
//3.想这个路径提交什么参数?
@FeignClient(name=“item-service”)
public interface ItemClient{
@GetMapping("/{orderId}")
JsonResult<List> getItems(@PathVariable String orderId);
}
因为06项目属于演示项目,所以我们暂时不需要,将06项目从列表中移除
1.新建sp09-feign
2.添加依赖
- web
- eureka client
- feign
- ribbon
- hystrix
- actuator
3.添加sp01-commons依赖
4.yml
5.启动类添加@EnableFeignClients
6.声明式客户端接口
ItemClient
UserClient
7.在FeignController中调用这两个接口
然后生成项目,在sp09项目的pom.xml下添加sp01的依赖
将application.properties配置文件修改为application.yml
在启动类上添加注解@EnableFeignClients
创建一个包feign,在里面创建一个声明式客户端接口ItemClient
package cn.huac.sp09.feign;
import cn.huac.sp01.pojo.Item;
import cn.huac.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("orderId") String orderId);
@PostMapping("/decreaseNumber")
JsonResult decreaseNumber(@RequestBody List<Item> items);
}
再创建另一个声明式客户端接口UserClient
package cn.huac.sp09.feign;
import cn.huac.sp01.pojo.User;
import cn.huac.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("userId") Integer userId);
@PostMapping("/{userId}/score")
JsonResult addScore(@PathVariable("userId") Integer userId,@RequestParam("score") Integer score);
}
新建一个包controller,创建一个FeignController类
package cn.huac.sp09.controller;
import cn.huac.sp01.pojo.Item;
import cn.huac.sp01.pojo.User;
import cn.huac.sp09.feign.ItemClient;
import cn.huac.sp09.feign.UserClient;
import cn.huac.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@Slf4j
public class FeignController {
@Autowired
private ItemClient itemClient;
@Autowired
private UserClient userClient;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId){
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(@PathVariable Integer userId,Integer score){
return userClient.addScore(userId,score);
}
}
测试:
http://eureka1:2001
http://localhost:3001/item-service/35
http://localhost:3001/item-service/decreaseNumber
使用postman,POST发送以下格式数据:
[{“id”:1, “name”:“abc”, “number”:23},{“id”:2, “name”:“def”, “number”:11}]
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/
Feign集成ribbon
0配置,默认已经启用ribbon的负载均衡和重试
重试的默认配置参数:
ConnectTimeout=1000
ReadTimeout=1000
MaxAutoRetries=0
MaxAutoRetriesNextServer=1
可以对重试参数进行调整:
#对所有服务都有效
ribbon:
MaxAutoRetries: 1
#只对item-service 有效
item-service:
ribbon:
MaxAutoRetriesNextServer:0
Feign集成Hystrix
feign默认不启用hystrix
添加基础配置,启用hystrix
1.添加hystrix依赖,actuator依赖
2.yml配置:feign.hystrix.enable=true
3.注解:@EnableCircuitBreaker
4.暴露监控数据:m.e.w.e.i=hystrix.stream
之前我们在pom.xml中已经将依赖添加完了,所以这里我们不需要添加依赖了
然后在yml中添加feign.hystrix.enable=true
在主启动类添加注解@EnableCircuitBreaker
暴露监控数据:m.e.w.e.i=hystrix.stream
management:
endpoints:
web:
exposure:
include: hystrix.stream
降级代码
1.声明式客户端接口上,指定降级类
@FeignClient(name="item-service",fallback=降级类.class)
public interface ItemClient{
@GetMapping("/{orderId}")
JsonResult<List<Item>> getItems(@PathVariable String orderId);
}
2.实现降级类,这个类要实现声明式客户端接口
3.降级类添加@Component注解
降级代码:
@Component
public class ItemClientFB implements ItemClient{
@Override
public JsonResult<List<Item>> getItems(String orderId) {
return JsonResult.err().msg("调用商品服务失败");
}
@Override
public JsonResult decreaseNumber(List<Item> items) {
return JsonResult.err().msg("调用商品服务失败");
}
}
@Component
public class UserClientFB implements UserClient{
@Override
public JsonResult<User> getUser(Integer userId) {
return JsonResult.err().msg("调用用户服务失败");
}
@Override
public JsonResult addScore(Integer userId, Integer score) {
return JsonResult.err().msg("调用用户服务失败");
}
}
测试:重启sp09项目
http://localhost:3001/item-service/35