秒杀设计实现

针对于高并发的设计,对于服务的不同阶段,都有不同的设计实现方案,下面是针对部分场景的一些实现



一些概念性知识如下

秒杀的方式
限价、限量限价、限量限时限价


秒杀的业务特点
瞬时高并发、库存少、流程短、预热



购物流程
选择商品=>加入购物车=>确认订单=>提交订单=>支付成功

秒杀的时候一般会减少流程,没有加入购物车和确认订单


关键性思想(redis的卓越性能,一分钟百万请求)

秒杀实现的必备性思想,能够进行异步处理的一律异步处理,能够减少的步骤绝不冗余,最好针对洪峰流量都采用类似redis的内存数据库进行临时数据存储,最后在慢慢更新到关系型数据库,







秒杀开始前准备项

将一些秒杀商品实现存入redis内存数据库,比如存库数量,商品必要信息等


秒杀实现流程

消费者提交订单==>订单服务器接口利用redis进行库存加减操作,当库存不够了之后,所有的请求就直接进行返回,告诉消费者商品已售完===>针对已经跳过计数器获取到库存的===>将订单信息写入到redis订单队列里面去,依旧不进行mysql数据库操作====>写完订单队列之后跳转到订单支付界面====>到redis中查询订单状态信息====>然后进行支付,支付完成后=== 将支付信息保存到更新订单状态队列(后续异步处理)===保存后直接返回界面,抢购成功




详情实现图

 

 

 


单项目部署,可以直接采用redis字符串的计数器,每次请求,存库都减少一个,判断库存已经卖完,后续请求全部返回

set num 1
incr key: 将key中储存的数字值增1,并返回值
decr key: 将key中储存的数字值减1,并返回值





但在分布式部署的情况下,可能会出现一些极端的情况,比如A机器在减数的时候,B机器判断还有库存,超卖了

超卖解决:
利用redis的事务监听机制,解决超卖问题
大致解决是,在事务开始之前将秒杀商品设置到到redis数据库 比如(set order_ clothes  10000)
每次前端请求到后端时,判断redis还有没有库存,没有直接返回已卖完或请重试等内容
一旦redis还有库存,就采用redis的watch机制进行处理,防止在分布式环境下出现的问题
下面是redis官方的一段代码,以供参考
import redis
def key_for(user_id):
    return "account_{}".format(user_id)
def double_account(client, user_id):
    key = key_for(user_id)
    while True:
        client.watch(key)
        value = int(client.get(key))
        value *= 2      # 加倍
        pipe = client.pipeline(transaction=True)
        pipe.multi()
        pipe.set(key, value)
        try:
            pipe.execute()
            break           # 总算成功了
        except redis.WatchError:
            continue            # 事务被打断了,重试 
        return int(client.get(key)) # 重新获取余额

user_id = "abc"
client.setnx(key_for(user_id), 5) # setnx 做初始化 
print double_account(client, user_id)




利用监听机制更高级的问题
假如A客户端在master拿到了锁,但是加锁的key还没有来得及同步到slave,master发生故障,此时B客户端获得了锁,造成问题
Redis更加高级分布式实现redlock或者redisson实现


问题汇总
如何防止程序员自己写代码去进行秒杀抢购    URL动态化
恶意请求(黄牛)........   针对这一项,一般会在nginx层面就做一些限制,比如某一请求量太过夸张,直接拉黑




如何区分普通商品与秒杀商品,可能某一普通商品一下变得非常热门   


秒杀架构越大,所要涉及的东西也就越多
kafka消息队列
canal实时数据同步
Apache Druid实时数据分析



并发那些事:https://blog.csdn.net/xiao_xia_ming/article/details/106202458

redis必备知识点:https://blog.csdn.net/xiao_xia_ming/article/details/106040376

java实现秒杀系统@Controller @RequestMapping("seckill")//url:/模块/资源/{id}/细分 /seckill/list public class SeckillController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private SeckillService seckillService; @RequestMapping(value="/list",method = RequestMethod.GET) public String list(Model model){ //获取列表页 List list=seckillService.getSeckillList(); model.addAttribute("list",list); //list.jsp+model = ModelAndView return "list";//WEB-INF/jsp/"list".jsp } @RequestMapping(value = "/{seckillId}/detail",method = RequestMethod.GET) public String detail(@PathVariable("seckillId") Long seckillId, Model model){ if (seckillId == null){ return "redirect:/seckill/list"; } Seckill seckill = seckillService.getById(seckillId); if (seckill == null){ return "forward:/seckill/list"; } model.addAttribute("seckill",seckill); return "detail"; } //ajax json @RequestMapping(value = "/{seckillId}/exposer", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) @ResponseBody public SeckillResult exposer(@PathVariable("seckillId") Long seckillId){ SeckillResult result; try { Exposer exposer =seckillService.exportSeckillUrl(seckillId); result = new SeckillResult(true,exposer); } catch (Exception e) { logger.error(e.getMessage(),e); result = new SeckillResult(false,e.getMessage()); } return result; } @RequestMapping(value = "/{seckillId}/{md5}/execution", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"} ) @ResponseBody public SeckillResult execute(@PathVariable("seckillId")Long seckillId,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值