天天看點

使用redis設計幂等接口

幂等:

幂等性是系統的接口對外一種承諾(而不是實作), 承諾隻要調用接口成功, 外部多次調用對系統的影響是一緻的. 聲明為幂等的接口會認為外部調用失敗是常态, 并且失敗之後必然會有重試

舉個例子:

有一個訂單系統,對外提供了一個處理接口,

如果有個訂單001是要扣除使用者的100塊錢,那麼訂單001被多次調用,

也隻會處理成功一次,也就是隻會扣除使用者100塊。

也可以了解為去除重複調用

訂單的狀态:

a) 無記錄

b) 正在被處理

c) 處理成功

我們需要考慮多種情況:

情況1:

a) a線程開始處理訂單001

b) a線程處理訂單001成功

c) a線程記錄訂單001處理成功

情況2:

a) a線程開始處理訂單001

b) a線程處理訂單001失敗,復原

c) a線程删除訂單001的記錄

情況3:

a) a線程已成功處理訂單001

b) b線程開始處理訂單001,發現訂單001已經被成功處理,放棄此次處理

情況4:

a) a線程開始處理訂單001,正在進行中

b) b線程開始處理訂單001,發現訂單001正在被處理且未逾時,放棄此次處理

情況5:

a) a線程開始處理訂單001,且逾時了釋放了訂單001的狀态

b) b線程開始處理訂單001,發現訂單001無人處理,那麼鎖定訂單001開始處理

我們用redis來設計處理的幂等性,但是需要注意的是,這裡幂等性的有效期依賴redis的key的生命周期:

a) 正在處理,假設5分鐘則認為處理逾時(這個值需要根據程式的處理邏輯的逾時時間設定)

SET 訂單号                         時間戳                過期時間

SET 1893505609317740 1466849127 EX 300 NX

b) 成功處理,利用SET的時間戳控制設定權

SET 訂單号                       成功  過期時間

SET 1893505609317740 0 EX 86400 XX

c) 删除訂單,隻允許建立者删除,利用SET的時間戳控制删除權

DEL 1893505609317740

上python示範代碼:

# -*- coding: utf-8 -*-

import time
import redis

def check_validity(orderno, del_time):
    return True

def deal():
    return True

if __name__ == '__main__':

    # 連接配接redis
    r = redis.StrictRedis(host='localhost', port=6379, db=0)

    # 訂單号
    orderno = "1893505609317740"

    # 目前時間戳
    sec = int(time.time())

    # 處理逾時時間
    deal_timeout = 300

    # 訂單狀态儲存多久,比如24小時不支付就失效了
    del_time = 86400

    # 是否成功處理
    status = False

    # 檢查訂單的有效期
    if check_validity(orderno, del_time):
        print "orderno is ok"
    else:
        print "orderno is overdue"
        exit()

    # 試圖鎖住訂單
    res = r.set(orderno, sec, ex=deal_timeout, nx=True)
    if res == True:
        print "set key succeed"
    else:
        print "set key failed, key maybe exist, return"
        exit()

    # 成功處理了/失敗了
    status = deal()

    # 更改訂單狀态
    redis_sec = r.get(orderno)
    if sec != int(redis_sec):
        print "check timestamp failed"
        exit()
    else:
        print "check timestamp success"

    if status == True:
        # 設定訂單處理成功.設定為0
        res = r.set(orderno, 0, ex=del_time, xx=True)
        print "deal succeed: %s" % res
    else:
        # 删除訂單
        res = r.delete(orderno)
        print "deal failed: %s" % res
           

小結:

這種設計依然有缺陷,但是能保證大部分的訂單是幂等性的,

首先要保證redis是高可用,

其次訂單的處理過期時間很重要,不能随意設定,必須根據程式處理流程推理出來,

其次redis的存儲空間也影響了訂單的狀态儲存多久。

繼續閱讀