需求实现的几个考虑重点:
- 发生服务降级后向管理员发出警报
- 考虑集群环境下需要报警哪台服务器哪个服务发生了降级
- 同一个服务降级一个时间段内只发送一次警报,超过这个时间段任然存在再次警报
- 服务降级发生的触发与发送短信任务应该是异步的
- 基于集群通信和报警时效考虑,使用redis实现,如果将一个降级报警设置了过期时间,没过期不重发报警,需要考虑热点缓存问题
实现代码如下:
1,添加redis依赖
pom文件
<!--Spring Boot与Redis整合依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
yml文件
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
2,修改处理器
@Autowired
private StringRedisTemplate template;
//短信任务放入线程处理
private ForkJoinPool pool = new ForkJoinPool(5);
//原有的请求添加HttpServletRequest获取ip
@HystrixCommand(fallbackMethod = "getHystrixHandle")
@GetMapping("/get/{id}")
public Depart getHandle(@PathVariable("id") int id, HttpServletRequest request) {
return service.getDepartById(id);
}
//服务降级方法发送报警短信
public Depart getHystrixHandle(@PathVariable("id") int id,
HttpServletRequest request) {
//指定存放到Redis中的key为 ip_发生降级的方法名
String ip = request.getLocalAddr();
String key = ip + "_getDepartById";
alarm(key);
Depart depart = new Depart();
depart.setId(id);
depart.setName("no this depart");
return depart;
}
// 降级发生后的报警
private void alarm(String key) {
//获取Redis操作对象
BoundValueOperations<String, String> ops = template.boundValueOps(key);
String value = ops.get();
//redis中不存在降级服务key时才发送短信,每次发送短信设置过期时间,时间到了再次发送
//redis设置过期时间时必须考虑热点缓存问题,发送短信但还没有设置值时会重复进入方法发送短信,这里使用双重检测锁解决
if(value == null) {
synchronized (this) {
value = ops.get();
if(value == null) {
// 发送短信
sendFallbackMsg(key);
value = "已发生服务降级";
ops.set(value, 10, TimeUnit.SECONDS);
}
}
}
}
// 使用线程池实现异步短信发送
private void sendFallbackMsg(String key) {
pool.submit(() -> {
System.out.println("发送服务异常报警短信:" + key);
});
}