商城拍卖活动设计方案 瞬时并发高可用

2024-02-14 09:38

本文主要是介绍商城拍卖活动设计方案 瞬时并发高可用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

后端源码传送门:https://download.csdn.net/download/sun5769675/87323033

最近参加了公司内部的一个24小时编程比赛,组了个四人的小团队,设计了一个拍卖的功能,功能需求如下:

1. 登陆

2. 支付保证金

3. 商品信息展示阶段

4. 拍卖阶段叫价(最后一秒有人出价会延时5分钟)

5. 尾款支付

针对功能需求我们计划创建三个项目,一个后台拍卖活动数据维护,一个小程序做C端,一个api项目对前面两个项目做接口层的支持,业务流程走向如下:

接口层部分做了如下8个接口,当然还有8个数据维护的接口这里就不列举了,就是针对表数据的一个增删改查,后面看DB设计即可知晓,流程没有画的特别详细但是主要流程都写了

下面看下DB设计一共5张表

下面看下我们后端维护数据的页面设计:

这边设计的后台数据维护功能是一个拍卖活动下,可以配置多个单位级的拍卖商品,是一对多的关系。

然后是看下我们的小程序页面其实很简单的,就登陆成功后到列表页开始倒计时,然后点击详情,倒计时结束,进入拍卖中的倒计时,需要交保证金,交完保证金,去竞价即可,活动时间结束,谁拍到了就去付尾款。

竞拍部分代码预览:
 

/*** * 竞拍,单次加价竞拍或一口价竞拍<br>* 1、校验单次加价,要大于默认加价<br>* 2、currentPrice与redis中价格比较<br>* 3、判断锁是否存在,存在返回,不存在加锁<br>* 4、加锁失败返回<br>* 5、currentPrice与redis中价格比较,小于解锁后返回<br>* 6、执行拍卖<br>* 7、有异常解锁<br>* * @param auctionRequestParamCommand * @return */
@Overridepublic ResultCommand singleOrOnceAuctionProcess(AuctionRequestParamCommand auctionRequestParamCommand) {String code = auctionRequestParamCommand.getCode();Integer memberId = auctionRequestParamCommand.getMemberId();Integer skuId =  auctionRequestParamCommand.getSkuId();BigDecimal addPrice = auctionRequestParamCommand.getAddPrice();BigDecimal currentPrice = auctionRequestParamCommand.getCurrentPrice();//查询定金记录AuctionRequestParamCommand depositQuery = new AuctionRequestParamCommand();depositQuery.setCode(code);depositQuery.setMemberId(memberId);depositQuery.setSkuId(skuId);ResultCommand depositLog = auctionActivityDepositManager.findMemberDeposit(depositQuery);if (null == depositLog || depositLog.getData() ==null) {LOGGER.info("该用户没有交拍卖定金:memberId:{},活动code{},拍卖商品ID{}", memberId, code, skuId);return new ResultCommand(ErrorCode.ERROR_CODE_FIND_DEPOSIT_ERROR);}Date now = new Date();// 当前拍卖商品commandStoAuctionProductCommand auctionProductCommand = null;// 当前拍卖价格String redisPrice = null;// 参数校验if(StringUtils.isEmpty(code) || memberId == null || skuId == null || currentPrice == null) {LOGGER.info("竞拍参数校验为空:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_PARAM_EMPTY);}// 获取活动信息ResultCommand resultCommand = auctionActivityDetail(code);if (resultCommand == null|| !ErrorCode.ERROR_CODE_OK.getCode().equals(resultCommand.getCode())|| null == resultCommand.getData()) {LOGGER.info("竞拍获取活动信息为空!参数:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_DATA_EMPTY);}AuctionActivityCommand auctionActivityCommand = (AuctionActivityCommand) resultCommand.getData();// 判断有效期// 判断竞拍商品的有效时间List<StoAuctionProductCommand> auctionProducts = auctionActivityCommand.getAuctionProducts();for (StoAuctionProductCommand stoAuctionProductCommand : auctionProducts) {if(!skuId.equals(stoAuctionProductCommand.getId())) {continue;}// 商品的有效期是否在有效范围内if (stoAuctionProductCommand.getStartTime() != null && stoAuctionProductCommand.getEndTime() != null&& now.after(stoAuctionProductCommand.getStartTime()) && now.before(stoAuctionProductCommand.getEndTime())) {// 校验通过auctionProductCommand = stoAuctionProductCommand;break;}LOGGER.info("竞拍活动商品不在有效时间范围内!参数:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_OVER_EXPIRE);}// 当前竞拍价格缓存key:plugin_auction_活动编码_sku编码_拍卖商品表idString redisPriceKey = CacheConstants.PLUGIN_AUCTION_ACCOUNT_PRICE_KEY.concat(code).concat(JOIN_SYMBOL).concat(auctionProductCommand.getSkuCode().concat(JOIN_SYMBOL).concat(String.valueOf(auctionProductCommand.getId())));// 单次叫价拍卖用户加的价不能比默认的小if(addPrice != null && addPrice.compareTo(auctionProductCommand.getSinglePrice()) == -1) {return new ResultCommand(ErrorCode.ERROR_CODE_ADDPIRCE_LESS);}if(addPrice == null){// 自定义加价为空,使用默认单次加价addPrice = auctionProductCommand.getSinglePrice();}// 这个try/catch块中的内容是 在加锁成功之前的一些校验,这个通过说明加锁成功了try {redisPrice = redisUtils.getString(redisPriceKey);/*** 当前竞拍价格的设置* @see CacheConstant.PLUGIN_AUCTION_ACCOUNT_PRICE_KEY*/if(StringUtils.isEmpty(redisPrice)) {redisUtils.setString(redisPriceKey,String.valueOf(auctionProductCommand.getBasePrice()));redisPrice = String.valueOf(auctionProductCommand.getBasePrice());}// 比较用户看到的竞拍价与缓存中的价格if(currentPrice.compareTo(new BigDecimal(redisPrice)) == -1) {// 价格比缓存中的小return new ResultCommand(ErrorCode.ERROR_CODE_CURRENTPRICE_EXPIRED);}// 为当前竞拍设置锁if not exits,锁期间其他进入的竞拍会被拦截// 锁设置5秒Long setNXResult = redisUtils.setnx(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY,CacheConstants.LOCK_FOR_CURRENT_PRICE_VALUE,5,TimeUnit.SECONDS);if(!Status.REDIS_SETNX_SUCCESS.getCode().equals(Integer.valueOf(String.valueOf(setNXResult)))) {// 设置锁失败LOGGER.info("竞拍设置锁失败!参数:{}", auctionRequestParamCommand.toString());return new ResultCommand(ErrorCode.ERROR_CODE_CURRENTPRICE_EXPIRED);}} catch (Exception e) {LOGGER.error("在加锁成功之前的校验出错", e);return new ResultCommand(ErrorCode.ERROR_CODE_SYSTEM_ERROR);}StoAuctionRecord auctionRecord = new StoAuctionRecord();try {// 锁设置成功redisPrice = redisUtils.getString(redisPriceKey);// 再次比较用户看到的竞拍价与缓存中的价格if(currentPrice.compareTo(new BigDecimal(redisPrice)) == -1) {// 价格比缓存中的小,解锁redisUtils.deleteKey(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY);return new ResultCommand(ErrorCode.ERROR_CODE_CURRENTPRICE_EXPIRED);}// 这个是会员竞拍到的价格BigDecimal auctionPrice = null;// 下面开始允许会员竞拍 1、保存竞拍记录;2、刷缓存// 单次叫价的竞拍价为 当前竞拍价加上addPriceauctionPrice = new BigDecimal(redisPrice).add(addPrice);auctionRecord.setAuctionActivityId(auctionActivityCommand.getId());auctionRecord.setAuctionPrice(auctionPrice);auctionRecord.setAuctionProductId(skuId);auctionRecord.setCreateBy(String.valueOf(memberId));auctionRecord.setCreateTime(new Date());auctionRecord.setLifecycle(Status.ACTIVE.getCode());auctionRecord.setMemberId(memberId);auctionRecord.setModifyBy(String.valueOf(memberId));auctionRecord.setModifyTime(new Date());// 保存拍卖纪录auctionRecord.setId(baseMysqlCRUDManager.save(auctionRecord));// 刷新当前竞拍价格缓存redisUtils.setString(redisPriceKey, String.valueOf(auctionPrice));// 拍卖顺延auctionExtendDate(code,skuId);// 竞拍成功解锁redisUtils.deleteKey(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY);return new ResultCommand(ErrorCode.ERROR_CODE_OK, auctionRecord);} catch (Exception e) {// 这里抓取到的异常要手动回滚、执行解锁操作if(auctionRecord != null && auctionRecord.getId() != null) {LOGGER.error("锁期间竞拍异常!回滚拍卖纪录");if(auctionRecord.getId() != null){baseMysqlCRUDManager.delete(auctionRecord);}}String newRedisPrice = redisUtils.getString(redisPriceKey);if(!redisPrice.equals(newRedisPrice)) {LOGGER.error("锁期间竞拍异常!商品当前拍卖价格缓存回滚");redisUtils.setString(redisPriceKey, redisPrice);}LOGGER.error("锁期间竞拍异常!解锁");redisUtils.deleteKey(CacheConstants.LOCK_FOR_CURRENT_PRICE_KEY);return new ResultCommand(ErrorCode.ERROR_CODE_SYSTEM_ERROR);}}

这篇关于商城拍卖活动设计方案 瞬时并发高可用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/708135

相关文章

centos7基于keepalived+nginx部署k8s1.26.0高可用集群

《centos7基于keepalived+nginx部署k8s1.26.0高可用集群》Kubernetes是一个开源的容器编排平台,用于自动化地部署、扩展和管理容器化应用程序,在生产环境中,为了确保集... 目录一、初始化(所有节点都执行)二、安装containerd(所有节点都执行)三、安装docker-

高并发环境中保持幂等性

在高并发环境中保持幂等性是一项重要的挑战。幂等性指的是无论操作执行多少次,其效果都是相同的。确保操作的幂等性可以避免重复执行带来的副作用。以下是一些保持幂等性的常用方法: 唯一标识符: 请求唯一标识:在每次请求中引入唯一标识符(如 UUID 或者生成的唯一 ID),在处理请求时,系统可以检查这个标识符是否已经处理过,如果是,则忽略重复请求。幂等键(Idempotency Key):客户端在每次

Java并发编程之——BlockingQueue(队列)

一、什么是BlockingQueue BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种: 1. 当队列满了的时候进行入队列操作2. 当队列空了的时候进行出队列操作123 因此,当一个线程试图对一个已经满了的队列进行入队列操作时,它将会被阻塞,除非有另一个线程做了出队列操作;同样,当一个线程试图对一个空

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

JAVA用最简单的方法来构建一个高可用的服务端,提升系统可用性

一、什么是提升系统的高可用性 JAVA服务端,顾名思义就是23体验网为用户提供服务的。停工时间,就是不能向用户提供服务的时间。高可用,就是系统具有高度可用性,尽量减少停工时间。如何用最简单的方法来搭建一个高效率可用的服务端JAVA呢? 停工的原因一般有: 服务器故障。例如服务器宕机,服务器网络出现问题,机房或者机架出现问题等;访问量急剧上升,导致服务器压力过大导致访问量急剧上升的原因;时间和

java线程深度解析(五)——并发模型(生产者-消费者)

http://blog.csdn.net/Daybreak1209/article/details/51378055 三、生产者-消费者模式     在经典的多线程模式中,生产者-消费者为多线程间协作提供了良好的解决方案。基本原理是两类线程,即若干个生产者和若干个消费者,生产者负责提交用户请求任务(到内存缓冲区),消费者线程负责处理任务(从内存缓冲区中取任务进行处理),两类线程之

java线程深度解析(四)——并发模型(Master-Worker)

http://blog.csdn.net/daybreak1209/article/details/51372929 二、Master-worker ——分而治之      Master-worker常用的并行模式之一,核心思想是由两个进程协作工作,master负责接收和分配任务,worker负责处理任务,并把处理结果返回给Master进程,由Master进行汇总,返回给客

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理 秒杀系统是应对高并发、高压力下的典型业务场景,涉及到并发控制、库存管理、事务管理等多个关键技术点。本文将深入剖析秒杀商品业务中常见的几个核心问题,包括 AOP 事务管理、同步锁机制、乐观锁、CAS 操作,以及用户限购策略。通过这些技术的结合,确保秒杀系统在高并发场景下的稳定性和一致性。 1. AOP 代理对象与事务管理 在秒杀商品

PostgreSQL中的多版本并发控制(MVCC)深入解析

引言 PostgreSQL作为一款强大的开源关系数据库管理系统,以其高性能、高可靠性和丰富的功能特性而广受欢迎。在并发控制方面,PostgreSQL采用了多版本并发控制(MVCC)机制,该机制为数据库提供了高效的数据访问和更新能力,同时保证了数据的一致性和隔离性。本文将深入解析PostgreSQL中的MVCC功能,探讨其工作原理、使用场景,并通过具体SQL示例来展示其在实际应用中的表现。 一、

使用协程实现高并发的I/O处理

文章目录 1. 协程简介1.1 什么是协程?1.2 协程的特点1.3 Python 中的协程 2. 协程的基本概念2.1 事件循环2.2 协程函数2.3 Future 对象 3. 使用协程实现高并发的 I/O 处理3.1 网络请求3.2 文件读写 4. 实际应用场景4.1 网络爬虫4.2 文件处理 5. 性能分析5.1 上下文切换开销5.2 I/O 等待时间 6. 最佳实践6.1 使用 as