需求實現的幾個考慮重點:
- 發生服務降級後向管理員發出警報
- 考慮集羣環境下需要報警哪臺服務器哪個服務發生了降級
- 同一個服務降級一個時間段內只發送一次警報,超過這個時間段任然存在再次警報
- 服務降級發生的觸發與發送短信任務應該是異步的
- 基於集羣通信和報警時效考慮,使用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);
});
}