高并发处理-锁
def test_demo():
if concurrnt_control('etc_user_id_add_score:%s' % user_id, timeout=30) == 1:
do_something()
def concurrnt_control(key, timeout=10):
'''
:param timeout: 超时时间
:param key: redis key值
:return:
这个在应对, 一个接口,可能在多个入口进行调用, 多个入口在多个服务器上进行处理, 所以要进行锁定,默认锁定10秒
'''
redis_conn = get_redis_connection()
with redis_conn.pipeline() as pipe:
try:
# 事务开始
# 将这个key对应的value值增加1,若value不存在,就设为0
pipe.incr(key, 1)
res = pipe.execute()
# 事务结束
# 把命令推送过去
if res[0] == 1:
# 设置到期时间
redis_conn.expire(key, timeout)
return res[0]
except Exception as ex:
print('=======================redis锁key的异常信息', ex)
return -1
def concurrnt_control_gift(yet_gift_total_amount_key, yet_person_total_count_key, yet_person_count, timeout=30):
'''
:param timeout: 超时时间
:param key: redis key值
:return:
'''
redis_conn = get_redis_connection()
with redis_conn.pipeline() as pipe:
try:
# 事务开始
pipe.set(yet_person_total_count_key, yet_person_count)
pipe.incr(yet_gift_total_amount_key, 1)
pipe.incr(yet_person_total_count_key, 1)
res = pipe.execute()
# 事务结束
# 把命令推送过去
if res[0] == 1:
# redis_conn.expire(lockkey, timeout)
redis_conn.expire(yet_person_total_count_key, timeout)
print("res====", res)
return res[0], res[1], res[2]
except Exception as ex:
print(ex)
return False, -1, -1
def receive_test():
...
"""yet_total_count,total_max_count, person_max_count 都是从redis缓存里面获取, yet_total_count是访问数量
yet_person_count 先从redis获取,如果不存在就从mysql查询,设定30秒的声明周期,为的是应对用户的连续领取更高速
为的是不超领
"""
if int(yet_total_count) < int(total_max_count) and int(yet_person_count) < int(person_max_count):
res = concurrnt_control_gift(yet_total_count_key, yet_person_total_count_key, yet_person_count, timeout=30)
if int(res[1]) <= int(total_max_count) and int(res[2]) != -1 and int(res[2]) <= int(person_max_count):
do something...
else:
return APIResponse(error={'msg': u'已领完!', 'code': 1002})
else:
return APIResponse(error={'msg': u'已领完!', 'code': 1002})
redis 高并发之商品秒杀系统 - 輪滑少年 - 博客园 (cnblogs.com)
Pipeline详解 - 月染霜华 - 博客园 (cnblogs.com)
单点问题
单点故障(英语:single point of failure,缩写SPOF)是指系统中一点失效,就会让整个系统无法运作的部件,换句话说,单点故障即会整体故障。
解决单点问题: 单点问题 - 简书 (jianshu.com)
解决高并发情况下用户优惠券超领问题
解决方案:
- 消峰,领取优惠券这种活动,给用户来个验证码啥的输入,限定短时间内的多次刷新等等来避免每个线程同时执行领取的操作
- 后端实现,锁+幂等性; 使用redis的decr (对key对应的数字做减1操作。如果key不存在,那么在操作之前,这个key对应的值会被置为0。如果key有一个错误类型的value或者是一个不能表示成数字的字符串,就返回错误。这个操作最大支持在64位有符号的整型数字。)可以实现原子性的递增递减操作控制优惠码不超送,然后给每个用户维护一个userid+优惠码活动的key保证幂等性,只要redis存在这种key,那就代表已经领取了,具体的优惠码分发可以异步执行。为了避免竞争(同一个用户,多个设备同时领取)
在分布式高并发场景下,领取百万的优惠码,怎样保证每个用户只能领取一个而且不超送? - 简书 (jianshu.com)
什么是 幂等性?
**幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。**举个最简单的例子,那就是支付,用户购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额发现多扣钱了,流水记录也变成了两条。在以前的单应用系统中,我们只需要把数据操作放入事务中即可,发生错误立即回滚,但是再响应客户端的时候也有可能出现网络中断或者异常等等。
那么如何设计接口才能做到幂等呢?
方法一:单次支付请求,也就是直接支付了,不需要额外的数据库操作了,这个时候发起异步请求创建一个唯一的ticketId,就是门票,这张门票只能使用一次就作废,具体步骤如下:
1、异步请求获取门票
2、调用支付,传入门票
3、根据门票ID查询此次操作是否存在,如果存在则表示该操作已经执行过,直接返回结果;如果不存在,支付扣款,保存结果
4、返回结果到客户端
如果步骤4通信失败,用户再次发起请求,那么最终结果还是一样的.
方法二:分布式环境下各个服务相互调用
这边就要举例我们的系统了,我们支付的时候先要扣款,然后更新订单,这个地方就涉及到了订单服务以及支付服务了。用户调用支付,扣款成功后,更新对应订单状态,然后再保存流水。而在这个地方就没必要使用门票ticketId了,因为会比较闲的麻烦(支付状态:未支付,已支付)
步骤:
1、查询订单支付状态
2、如果已经支付,直接返回结果
3、如果未支付,则支付扣款并且保存流水
4、返回支付结果
如果步骤4通信失败,用户再次发起请求,那么最终结果还是一样的
对于做过支付的朋友,幂等也可以称之为冲正,保证客户端与服务端的交易一致性,避免多次扣款。
以下为接口文档对于幂等的描述,通过请求id号来判断是否为同一个请求。