(高併發)防止重複點選,遮蔽多次無效請求的解決方案(優惠劵被重複領取,惡意擼羊毛)
一、問題描述:
單應用切換至分散式,優惠劵被同一時間同一優惠劵領取多張,比如使用模擬器1s內請求1000次,可能被領取100張。
以前插入前先查詢是否存在,無法有效解決,還是會被擼羊毛。
二、解決方法:
1.app前端增加控制,(比如按鈕點選後失效);
2.利用資料庫層的事務處理,比如:插入的同時查詢是否存在,利用資料庫自身的事務管理,同一sql實現
insert into buyer_collect(buyer_id,goods_id,create_time) select #{userId},#{goodsId},now() from dual where not exists(select 1 from buyer_collect where buyer_id = #{userId} and goods_id = #{goodsId})
3.redis互斥鎖(個人推薦,複用性好,穩定性好)
如果框架還未整合redis請自行查閱處理,這邊直接上解決方案:引入jar包,本人使用2.9
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
利用jedis類的set方法特性
nxxx的值只能取NX或者XX,如果取NX,則只有當key不存在是才進行set,如果取XX,則只有當key已經存在時才進行set expx的值只能取EX或者PX,代表資料過期時間的單位,EX代表秒,PX代表毫秒。 time 過期時間,單位是expx所代表的單位。
public String set(String key, String value, String nxxx, String expx, int time) { this.checkIsInMultiOrPipeline(); this.client.set(key, value, nxxx, expx, time); return this.client.getStatusCodeReply(); }
編寫公共方法,可根據自己的實際情況自行改造,本用例來源於網路,較為成熟穩定(親測可行)
1private static final String LOCK_SUCCESS = "OK"; 2private static final String SET_IF_NOT_EXIST = "NX"; 3private static final String SET_WITH_EXPIRE_TIME = "EX"; 4 5/** 6* 獲取分散式鎖 7* @param lockKey 鎖 8* @param requestId 請求標識 9* @param expireTime 超期時間 10* @return 是否獲取成功 11*/ 12publicboolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) { 13Jedis jedis = null; 14try { 15jedis = getJedis(); 16String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 17if (LOCK_SUCCESS.equals(result)) { 18return true; 19} 20return false; 21} finally { 22returnResource(jedis); 23} 24}
//Redis快取 5S內重複請求無效 Boolean result = null; String key = "專案名稱+功能模組" + userId + couponId; String requestId = UUID.randomUUID().toString(); try { //獲取分散式鎖 result = redisCacheService.tryGetDistributedLock(key, requestId, 5); }catch (Exception e){ return new JsonResult(JsonResultCode.FAILURE,"連線redis取數異常",""); } if(!result){ return new JsonResult(JsonResultCode.FAILURE,"請勿頻繁點選!",""); }
附上使用的一小段程式碼,希望能秒懂解決實際問題
userId :使用者id
couponId :優惠劵id
專案名稱+功能模組:根據自己專案命名 結合自己專案框架自行修改,一次編寫,複用性好。有效解決專案中型別的問題