暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JAVA秒杀系统的简单实现(redis+rabbitmq)

IT大咖说 2020-12-18
2321

1.分析

  • 秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。

  • 秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。

  • 秒杀业务流程比较简单,一般就是下订单减库存。

上述三点的主要问题就是在高并发的情况下保证数据的一致性。

2.使用的技术和架构

2.1秒杀架构图

2.2流程

  • 使用 redis 缓存秒杀的商品信息,秒杀成功后使用消息队列发送订单信息,然后将更新后数据重新写入redis。

  • RabbitMQ监听器在接受到消息后,将订单信息写入数据库。

  • 在秒杀时使用redisson对商品信息上锁

2.3流程图

3.准备工作

3.1安装redis cluster

csdn上教程一大堆,这里我就不多赘述了。需要注意的点是,如果使用的是阿里云服务器(centos 7),在安装完后一定要去阿里云服务器控制台添加安全规则,去开放你使用的对应端口号。https://blog.csdn.net/CFrieman/article/details/83583085

3.2安装RabbitMQ和erlang

还是直接附上链接。需要说明的一点是,在安装erlang时,电脑名称不可以是中文,erlang的版本和rabbitmq的版本一定要对应,负责会安装失败。https://blog.csdn.net/qq_36505948/article/details/82734133

4.具体实现

4.1SeckillService

public class SeckillService {	@Autowired	 private RedisClusterClient rt;	@Autowired	private SeckillMapper sm;	@Autowired	private RedissonClient redissonClient; // 加锁	@Autowired	private RabbitmqSendMessage rsm;	@Autowired	private SecorderMapper om;    	/**	 * 初始化 ,将mysql中的商品信息缓存到redis中	 * @return	 */	public List<Seckill> querySeckill() {		List<Seckill> list =   (List<Seckill>) rt.get("secgoods");		if(list==null) {			list = sm.selectByExample(null);			rt.set("secgoods", list, 60*30);		}		return list;	}	public boolean queryStartTime(Seckill sec) {		Date date = new Date();// 比较时间,是否到秒杀时间		Date startTime = sec.getStarttime();		// 秒杀活动还未开始		if (startTime.getTime() > date.getTime()) {			return false;		}		return true;	}	// 减库存redis	public void decreaseStock(String id) {        int goodsid = Integer.parseInt(id);        List<Seckill> list =   (List<Seckill>) rt.get("secgoods");		if (list!=null)		{			for (Seckill sec : list)			{				if (goodsid==sec.getId())				{					sec.setCount(sec.getCount()-1);					//写回redis					rt.set("secgoods", list, 60*30);										return ;				}			}		}	}	//	public Seckill findSec(String secid) {		 List<Seckill> list =   (List<Seckill>) rt.get("secgoods");		int id = Integer.parseInt(secid);		for(Seckill sec:list) {			if(sec.getId()==id) {				return sec;			}		}		return null;	}	// 开始秒杀	public String goSeckill(String goodsid, String username) {		String key = username + ":" + goodsid;		String secid = goodsid;		Long value = (Long) rt.get(key);		if (value != null) {			return "exist";		}		Seckill sec = findSec(secid);		boolean flag = queryStartTime(sec);		if (!flag) {			return "notTime";		}		RLock rLock = redissonClient.getLock("miaosha");		rLock.lock();		if (sec.getCount() > 0) {			decreaseStock(goodsid); // 减少库存			rt.set(key, System.currentTimeMillis(), 60*30);			Secorder newOrder = new Secorder();			newOrder.setCreatetime(new Date());			newOrder.setGoodsid(Integer.parseInt(goodsid));			newOrder.setStatus("未付款");			newOrder.setUsername(username);			String json = JSONObject.toJSONString(newOrder);			rsm.send(json); // 异步下单			rLock.unlock(); // 解锁			return "success";		} else {			rLock.unlock();			return "failed";		}	}	// 写入mysql	public void saveOrder(String json) {		Secorder order = JSON.parseObject(json, Secorder.class);		int n = sm.updateCount(order.getGoodsid());		int m = om.insert(order);	}	}

4.2 RabbitmqListenner

@Servicepublic class RabbitmqListenner implements MessageListener {		@Autowired	private SeckillService ss;	    @Override    public void onMessage(Message msg) {    	byte[] data = msg.getBody();		try {			String 	json = new String(data,"utf-8");			System.out.println(json);			ss.saveOrder(json);   //将监听到的订单写入MySQL		} catch (UnsupportedEncodingException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}		    }}

4.3 RabbitmqSendMessage

public class RabbitmqSendMessage {    @Autowired    private RabbitTemplate   rt;    private final String QUEEN_NAME = "MIAOSHA";    /**     * 发送消息     * @param msg     */    public void send(String msg)    {        rt.convertAndSend(QUEEN_NAME,msg);    }}

4.4
以上就是整个业务流程的核心代码,使用redisson保证数据一致性,用rabbitmq异步下单将下单及写数据库这个长操作变成两个短操作。GitHub源码地址,关于数据库建表什么的,大家直接去源码里看吧。

5.优化

  • 限流:使用验证码,请求秒杀接口需要验证图形验证码的正确性,这样也很好的防止脚本的不断访问;

  • 防刷:一个用户对一个路径的访问次数在一定时间内有限制,使用redis可以解决

  • 接口地址隐藏:接口地址传参,保证秒杀接口不是一个固定路径,防止接口被刷,同时也可以有效隐藏秒杀地址。

来源:

https://www.toutiao.com/i6906372359805092356/

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com

来都来了,走啥走,留个言呗~

 IT大咖说  |  关于版权 

由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!

感谢您对IT大咖说的热心支持!

相关推荐

推荐文章

文章转载自IT大咖说,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论