使用分布式鎖解決多次回調(diào)問(wèn)題

問(wèn)題

系統(tǒng)有一個(gè)支付回調(diào)接口,偶爾出現(xiàn)了秒級(jí)內(nèi)回調(diào)2次的情況。我們?cè)诨卣{(diào)接口中寫(xiě)了推送任務(wù)。結(jié)果就是2次重復(fù)推送寓娩。偽代碼如下:

1. 獲得訂單
2. 更改信息和狀態(tài)(已支付)
3. 通知,推送呼渣,定時(shí)任務(wù)

在接口加入狀態(tài)判斷仍然有2次推送的情況出現(xiàn)棘伴。也就是說(shuō)2個(gè)線程出現(xiàn)了并發(fā)的情況。這個(gè)時(shí)候就要使用到分布式鎖來(lái)限制程序的并發(fā)執(zhí)行屁置。

1. 獲得訂單
x. 如果訂單狀態(tài)為已支付焊夸,則拋異常或直接return
2. 更改信息和狀態(tài)(已支付)
3. 通知蓝角,推送阱穗,定時(shí)任務(wù)

分布式鎖

分布式鎖是控制分布式系統(tǒng)之間同步訪問(wèn)共享資源的一種方式饭冬。在分布式系統(tǒng)中,常常需要協(xié)調(diào)他們的動(dòng)作揪阶。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源昌抠,那么訪問(wèn)這些資源的時(shí)候,往往需要互斥來(lái)防止彼此干擾來(lái)保證一致性遣钳,在這種情況下扰魂,便需要使用到分布式鎖。

分布式系統(tǒng)中蕴茴,常常需要協(xié)調(diào)他們的動(dòng)作劝评。如果不同的系統(tǒng)或是同一個(gè)系統(tǒng)的不同主機(jī)之間共享了一個(gè)或一組資源,那么訪問(wèn)這些資源的時(shí)候倦淀,往往需要互斥來(lái)防止彼此干擾來(lái)保證一致性蒋畜,這個(gè)時(shí)候,便需要使用到分布式鎖撞叽。

首先姻成,為了確保分布式鎖可用,我們至少要確保鎖的實(shí)現(xiàn)同時(shí)滿足以下四個(gè)條件:

  • 互斥性愿棋。在任意時(shí)刻科展,只有一個(gè)客戶端能持有鎖。
  • 不會(huì)發(fā)生死鎖糠雨。即使有一個(gè)客戶端在持有鎖的期間崩潰而沒(méi)有主動(dòng)解鎖才睹,也能保證后續(xù)其他客戶端能加鎖。
  • 具有容錯(cuò)性甘邀。只要大部分的Redis節(jié)點(diǎn)正常運(yùn)行琅攘,客戶端就可以加鎖和解鎖。
  • 解鈴還須系鈴人松邪。加鎖和解鎖必須是同一個(gè)客戶端坞琴,客戶端自己不能把別人加的鎖給解了。

Redis實(shí)現(xiàn)分布式鎖

@Slf4j
public class RedisLock {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long RELEASE_SUCCESS = 1L;


    /**
     * 嘗試獲取分布式鎖
     * @param jedis Redis客戶端
     * @param lockKey 鎖
     * @param requestId 請(qǐng)求標(biāo)識(shí)
     * @param expireTime 超期時(shí)間
     * @return 是否獲取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
//        log.info("lock -> {} lockKey -> {} request -> {}" ,result, lockKey, requestId);
        return LOCK_SUCCESS.equals(result);

    }


    /**
     * 釋放分布式鎖
     * @param jedis Redis客戶端
     * @param lockKey 鎖
     * @param requestId 請(qǐng)求標(biāo)識(shí)
     * @return 是否釋放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        return RELEASE_SUCCESS.equals(result);

    }
}

簡(jiǎn)單的測(cè)試逗抑,controller省略

public void updateStatus(String id) throws Exception {
        Jedis jedis = jedisPool.getResource();
        String requestId = SerialGenerator.randomUUID();
        boolean isGetLock = RedisLock.tryGetDistributedLock(jedis, "onPaySuccess" + id, requestId , 5);
        if (isGetLock) {
            Optional<MerchantOrder> byId = merchantOrderRepository.findById(id);
            MerchantOrder order = byId.get();
            if (order.getStatus() == OrderStatus.PAID) {
                throw new  BaseException("重復(fù)消費(fèi)");
            }
            order.setStatus(OrderStatus.PAID);
            merchantOrderRepository.save(order);
        }
        RedisLock.releaseDistributedLock(jedis, "onPaySuccess" + id,requestId);
    }

JMeter測(cè)試

分別 啟用10個(gè)和100個(gè)線程對(duì)接口進(jìn)行測(cè)試剧辐。從log可以看出只有一個(gè)線程的log打印出了sql語(yǔ)句,后面的都顯示重復(fù)消費(fèi)锋八。

額外的單機(jī)非集群思路

  • 只要在update t set state =1 where state=0時(shí)接受下row
  • 如果row=0浙于,再查一次冪等返回
  • 畢竟前置已經(jīng)查過(guò)了,更新時(shí)碰撞概率太低

碰到問(wèn)題挟纱,解決問(wèn)題,僅做記錄腐宋。如有問(wèn)題紊服,歡迎指教檀轨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市欺嗤,隨后出現(xiàn)的幾起案子参萄,更是在濱河造成了極大的恐慌,老刑警劉巖煎饼,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讹挎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吆玖,警方通過(guò)查閱死者的電腦和手機(jī)筒溃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沾乘,“玉大人怜奖,你說(shuō)我怎么就攤上這事〕嵴螅” “怎么了歪玲?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)掷匠。 經(jīng)常有香客問(wèn)我滥崩,道長(zhǎng),這世上最難降的妖魔是什么讹语? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任钙皮,我火速辦了婚禮,結(jié)果婚禮上募强,老公的妹妹穿的比我還像新娘株灸。我一直安慰自己,他們只是感情好擎值,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布慌烧。 她就那樣靜靜地躺著,像睡著了一般鸠儿。 火紅的嫁衣襯著肌膚如雪屹蚊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天进每,我揣著相機(jī)與錄音汹粤,去河邊找鬼。 笑死田晚,一個(gè)胖子當(dāng)著我的面吹牛嘱兼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贤徒,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼芹壕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼汇四!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起踢涌,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤通孽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后睁壁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體背苦,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年潘明,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了行剂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钉疫,死狀恐怖硼讽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牲阁,我是刑警寧澤固阁,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站城菊,受9級(jí)特大地震影響备燃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凌唬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一并齐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧客税,春花似錦况褪、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至秧均,卻和暖如春食侮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背目胡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工锯七, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人誉己。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓眉尸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子效五,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 引題 比如在同一個(gè)節(jié)點(diǎn)上地消,兩個(gè)線程并發(fā)的操作A的賬戶炉峰,都是取錢(qián)畏妖,如果不加鎖,A的賬戶可能會(huì)出現(xiàn)負(fù)數(shù)疼阔,正確的方式是對(duì)...
    阿康8182閱讀 4,793評(píng)論 0 75
  • 鎖 分布式鎖 distributed locks 資源有限戒劫,爭(zhēng)搶難免,最簡(jiǎn)單粗暴的辦法是誰(shuí)的拳頭大誰(shuí)就可以搶到最好...
    曲水流觴TechRill閱讀 11,469評(píng)論 9 43
  • 伴隨著一聲突如其來(lái)的開(kāi)門(mén)聲。 女子揚(yáng)起臉驚喜的問(wèn)到“你回來(lái)了嗎淘邻?”茵典,滿臉的欣喜,眸子里全是甜蜜宾舅。 ...
    林小瀾閱讀 158評(píng)論 0 0
  • 新鮮的東西太誘惑人统阿,當(dāng)時(shí)有多沉醉,后來(lái)就可能有多殘忍筹我。 高楊是互聯(lián)網(wǎng)時(shí)代下典型的富二代和創(chuàng)二代扶平,所以他可以在大浪淘...
    回聲echo閱讀 318評(píng)論 0 0
  • 其實(shí)结澄,在寫(xiě)這篇文章之前,我特意去看了一下去年今天寫(xiě)的文章岸夯,每年的這一天麻献,大家都會(huì)跟這一年揮手說(shuō)告別,迎接嶄新的一年...
    筱靜的幸福生活閱讀 315評(píng)論 0 0