Redis 集群故障探測
在生產環境中,如果 Redis 集群崩潰了,那么會導致大量的請求打到數據庫中,會導致整個系統都崩潰,所以系統需要可以識別緩存故障,限流保護數據庫,并且啟動接口的降級機制
降級方案設計
我們在系統中操作 Redis 一般都是通過工具類來進行操作的,假設工具類有兩個 RedisCache
和 RedisLock
,那么通過 AOP 對這兩個工具類的所有方法做一個切面,如果在這兩個類中執行 Redis 操作時,Redis 掛掉了,就會拋出異常(Redis 連接失敗),那么我們在切面的處理方法上捕捉異常,再記錄下來,判斷是 Redis 集群掛了還是展示網絡波動
判斷是集群掛掉還是網絡波動的話,我們可以配置規則,比如 30 秒內出現了 3 次 Redis 連接失敗,就認為 Redis 掛掉了(可以使用 Hotkey 配置規則),那么如何自動恢復呢?可以設置 hotkey 中的緩存過期時間,設置為 60 秒,那么緩存過期之后,會再次嘗試去操作 Redis,如果 Redis 恢復了就可以正常使用了,如果還沒有恢復,會繼續向 hotkey 中 set 數據,切面中記錄 Redis 故障代碼如下:
@Around("redisCachePointcut() || redisLockPointcut()")
public Object around(ProceedingJoinPoint point) {// 簽名信息Signature signature = point.getSignature();// 強轉為方法信息MethodSignature methodSignature = (MethodSignature) signature;// 參數名稱String[] parameterNames = methodSignature.getParameterNames();//執行的對象Object target = point.getTarget();log.debug("處理方法:{}.{}", target.getClass().getName() , methodSignature.getMethod().getName());Object[] parameterValues = point.getArgs();//查看入參log.debug("參數名:{},參數值:{}", JSONObject.toJSONString(parameterNames), JSONObject.toJSONString(parameterValues));Class returnType = methodSignature.getReturnType();// 返回類型是否布爾類型boolean booleanType = boolean.class.equals(returnType) || Boolean.class.equals(returnType);try {if (Objects.nonNull(JdHotKeyStore.get("redis_connection_failed"))) {// 值不為空表示redis連接失敗,這里就不再繼續請求redis了,直接返回false或者nulllog.error("獲取緩存失敗,redis連接失敗,直接返回 false 或者 null");if (booleanType) {return false;}return null;}return point.proceed();} catch (Throwable throwable) {log.error("執行方法:{}失敗,異常信息:{}", methodSignature.getMethod().getName(), throwable);/** redis連接失敗,不拋異常,返回空值,* 繼續用數據庫提供服務,避免整個服務異常* 一分鐘之內或者30秒之內出現了幾次redis連接失敗* 此時可以設置一個key,告訴hotkey,redis連接不上了,指定1分鐘左右的過期時間* 下次獲取緩存的時候,先根據hotkey來判斷,redis是否異常了* hotkey在1分鐘之后,會刪除key,下次再有redis請求過來,重新去看redis能否連接* 這樣可以簡單的實現redis掛掉之后直接走數據庫的降級*/if (JdHotKeyStore.isHotKey("redis_connection_failed")) {JdHotKeyStore.smartSet("redis_connection_failed", "{}");}// 讓后續操作繼續,判斷返回類型是Boolean則返回false,其他類型返回nulllog.error("緩存操作失敗,直接返回 false 或者 null");if (booleanType) {return false;}return null;}
}
如果 Redis 故障的話,通過 key=redis_connection_failed
就已經記錄下來了,那么降級操作的話,就從本地緩存 caffeine
中取數據,如果取不到,再查詢數據庫,降級流程如下:
這里如果本地緩存中沒有數據的話,需要查詢數據庫之后,再將數據庫中的數據放入本地緩存中,這里還是需要加鎖的,那么我們就加本地鎖即可 ReentrantLock