使用redis设计幂等接口

来源:互联网 发布:115个java面试题和答案 编辑:程序博客网 时间:2024/05/16 12:58


幂等:

幂等性是系统的接口对外一种承诺(而不是实现), 承诺只要调用接口成功, 外部多次调用对系统的影响是一致的. 声明为幂等的接口会认为外部调用失败是常态, 并且失败之后必然会有重试

举个例子:
有一个订单系统,对外提供了一个处理接口,
如果有个订单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 timeimport redisdef check_validity(orderno, del_time):    return Truedef deal():    return Trueif __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的存储空间也影响了订单的状态保存多久。



0 0
原创粉丝点击