首頁>技術>

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可以解決介面地址隱藏:介面地址傳參,保證秒殺介面不是一個固定路徑,防止介面被刷,同時也可以有效隱藏秒殺地址。

17
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 面試官不講碼德,一上來就問我Chrome瀏覽器的渲染原理