java秒杀实现、干货

1.秒杀场景-电商平台促销活动

在这里插入图片描述

2.秒杀特点

  • 高并发
  • 抢购有时间限制
  • 数量有限,先到先得

3.java实践

秒杀在高并发情况下,性能问题、 数据不一致的问题(只有10件库存, 被12个人买到问题)需要解决,我们借助redis实现秒杀, redis作为内存数据库,支持高并发,支持原子操作,适合分布式扩容特点

  1. 秒杀活动表模型MYSQL(商品详情表,商品参数表 自己构建)

在这里插入图片描述

status:活动状态,0 待开始,1 进行中 2 已结束

2.活动开始
每各10秒轮询待开始秒杀活动表,符合开始条件,则初始化一个redis队列,存放指定数量的活动商品到队列中(商品id相同),更改活动状态为开始种, 代码片段如下:

@Scheduled(cron = "0/10 * * * * ?")
	public void startSecKill() {
		List<SecKill> list = secKillDao.findWaitingSecKillList();
		boolean lock=redisTemplate.opsForValue().setIfAbsent("seckill:lock",
				"1", 5L, TimeUnit.SECONDS);
		if (lock) {
			for (SecKill sk : list) {
				redisTemplate.delete(Constant.SEK_KILL_QUEUE + sk.getId());
				for (int i = 0; i < sk.getCount(); i++) {
					redisTemplate.opsForList().rightPush(
							Constant.SEK_KILL_QUEUE+ sk.getId(), sk.getProductId());
				}
				sk.setStatus(1);
				secKillDao.updateStatus(sk);
			}
			
		}
	}
  1. 用户下单
    用户在登录的状态下购买,并发用户从队列中取商品id,如果用户已经购买,将获取的商品id重新还回队列。否则消费掉,直到队列中数据为0,抢购完毕,代码片段如下:
//controller
@RequestMapping("/seckill")
@ResponseBody
public ResponseData order(Long id, HttpServletRequest request)
 {
	ResponseData result = new ResponseData();
	try {
		Integer userId = this.getUserId(request);
		if (userId == null) {
			result.setStatus(ResponseData.STATUS_ERROR);
			result.setMessage("请先登录");
			return result;
	    }
			secKillService.handle(id, userId);
		} catch (Exception e) {
			result.setStatus(ResponseData.STATUS_ERROR);
			result.put("message", e.getMessage());
		}
		return result;
	}

	private Integer getUserId(HttpServletRequest request) {
		UserInfo userInfo = (UserInfo) request.getSession().getAttribute(
				"login_user");
		if (userInfo == null) {
			return null;
		}
		return userInfo.getUserId();
	}

//constant
package com.study.seckill;

public class Constant {

	public static final String SEK_KILL_QUEUE="seckill:items:";
	
	public static final String SEK_KILL_BUY="seckill:users:";
}

//service
package com.study.seckill.service;
import com.study.seckill.dao.SecKillDao;
import com.study.seckill.model.SecKill;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class SecKillService {
    @Resource
    private SecKillDao secKillDao;
    @Resource
    private RedisTemplate redisTemplate;
    private void check(SecKill sk) throws Exception{
    	if (sk == null) {
            throw new Exception("秒杀活动不存在");
        }
        if (sk.getStatus() == 0) {
            throw new Exception("秒杀活动还未开始");
        } else if (sk.getStatus() == 2) {
            throw new Exception("秒杀活动已经结束");
        }
    }
    /**
     * 
     * <B>方法名称:</B><BR>
     * <B>概要说明:</B><BR>
     * @param id 活动id
     * @param userid
     * @param num
     * @throws Exception
     */
    public void handle(Long id, Integer userid) throws Exception {
        SecKill sk = secKillDao.findById(id);
        check(sk);
        Integer productId = (Integer) redisTemplate.opsForList().leftPop(Constant.SEK_KILL_QUEUE + sk.getId());
 if (productId != null) {
     boolean isExisted = redisTemplate.opsForSet().isMember(Constant.SEK_KILL_BUY + sk.getId(), userid);
  if (!isExisted) { redisTemplate.opsForSet().add(Constant.SEK_KILL_BUY + sk.getId(), userid);
            }else{
                redisTemplate.opsForList().rightPush("seckill:items:" + sk.getId(), sk.getProductId());
                throw new Exception("您已经参加过此活动");
            }
        } else {
            throw new Exception("该商品已被抢光!");
        }


    }

    
}

https://web.sendcloud.net

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、付费专栏及课程。

余额充值