1.概念: 任意多次執行所產生的影響皆與一次執行的影響相同。
依照這個意義,最終的意義就是 對資料庫的影響只能是一次性的,不能重複處理。如何保證其冪等性,通常有以下手段:
1: 資料庫建立唯一性索引,可以保證最終插入資料庫的只有一條資料
2: token 機制,每次介面請求前先取得一個token,然後再下次請求的時候在請求的header 體中加上這個token,後台進行驗證,如果驗證通過刪除token,下次請求再次判斷token
3: 悲觀鎖或樂觀鎖,悲觀鎖可以保證每次for update 的時候其他sql 無法update 資料(在資料庫引擎是innodb 的時候, select 的條件必須是唯一索引, 防止鎖全表)
4: 先查詢後判斷,首先透過查詢資料庫是否存在數據,如果存在證明已經請求過了,直接拒絕該請求,如果沒有存在,就證明是第一次進來,直接放行。
redis 實作自動冪等的原理圖:
1: 首先是搭建redis 伺服器。
2: 引入springboot 中到的redis 的stater,或者Spring 封裝的jedis 也可以,後面主要用到的api 就是它的set 方法和exists 方法, 這裡我們使用springboot 的封裝好的redisTemplate
程式碼如下:
/* redis工具类 */ @Component public class RedisService { @Autowired private RedisTemplate redisTemplate; /** * 写入缓存 * @param key * @param value * @return */ public boolean set(final String key,Object value){ boolean result = false; try { ValueOperations<Serializable,Object> operations = redisTemplate.opsForValue(); operations.set(key,value); result = true; }catch (Exception e){ result = false; e.printStackTrace(); } return result; } /** * 写入缓存有效期 * @return */ public boolean setEx(final String key ,Object value,Long expireTime){ boolean result = false; try { ValueOperations<Serializable,Object> operations = redisTemplate.opsForValue(); operations.set(key,value); redisTemplate.expire(key,expireTime, TimeUnit.SECONDS);//有效期 result = true; }catch (Exception e){ result = false; e.printStackTrace(); } return result; } /** * 判断缓存中是否有对应的value * @param key * @return */ public boolean exists(final String key){ return redisTemplate.hasKey(key); } /** * 读取缓存 * @param key * @return */ public Object get(final String key){ Object obj = null; ValueOperations<Serializable,Object> operations= redisTemplate.opsForValue(); obj = operations.get(key); return obj; } /** * 删除对应的value * @param key * @return */ public boolean remvoe(final String key){ if(exists(key)){ Boolean delete = redisTemplate.delete(key); return delete; } return false; } }
自訂一個註解,定義此註解的主要目的是把它加在需要實作冪等的方法上,凡是某個方法註解了它,都會實現自動冪等。後台利用反射如果掃描到這個註解,就會處理這個方法實現自動冪等,使用元註解ElementType.METHOD 表示它只能放在方法上,etentionPolicy.RUNTIME 表示它在運行時
package com.yxkj.springboot_redis_interceptor.annotion; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoIdempotent { }
我們新建一個接口,創建token 服務,裡面主要是兩個方法,一個用來創建token,一個用來驗證token。創建 token 主要產生的是字串,檢驗 token 的話主要是傳達 request 對象,為什麼要傳 request 物件?主要作用就是取得header 裡面的token, 然後檢驗,透過拋出的Exception 來獲取具體的報錯資訊回傳給前端
public interface TokenService { /** * 创建token * @return */ String createToken(); /** * 检验token的合法性 * @param request * @return * @throws Exception */ boolean checkToken(HttpServletRequest request) throws Exception; }
token 引用了redis服務,創建token 採用隨機演算法工具類別產生隨機uuid 字串, 然後放入到redis 中(為了防止資料的冗餘保留, 這裡設定過期時間為10000 秒, 具體可視業務而定),如果放入成功,最後回傳這個token 值。 checkToken 方法就是從 header 中取得 token 到值 (如果 header 中拿不到,就從 paramter 中取得),如若不存在, 直接拋出異常。這個異常訊息可以被攔截器捕捉到,然後回到前端。
以上是springboot如何實現介面自動冪等的詳細內容。更多資訊請關注PHP中文網其他相關文章!