秒殺系統個人總結
對於高併發的專案,處理原則是儘量在上游處理,優先順序:客戶端-》後端記憶體-》redis-》資料庫;如果能在客戶端處理的,儘量不交給tomcat,能在tomcat處理的,儘量不要交給redis,畢竟tomcat-redis也存在網路互動,能在redis處理的,儘量不交給資料庫,一般高併發的瓶頸都是在資料庫。
另外可以使用nignx做負載均衡,橫向擴充套件處理伺服器,這時session一般就要儲存在redis,這個也是分散式session;可以使用rabbitMQ作為訂單的的處理,rabbitMQ是分散式訊息佇列系統,需要安裝,個人感覺什麼分散式開源系統,原理都是統一處理多個來源資訊,支援叢集;
在高併發環境下,可以限制使用者某時間段內的訪問次數,加驗證碼等手段減輕伺服器壓力;另外在高併發下也很容易出現數據錯誤的情況,例如這裡的多個使用者同時獲得剩餘商品都是1,然後就出現訂單多於總商品的情況,對於這種情況,可以:1,在資料庫表中合適的新增唯一值索引,2,利用rabbitMQ來延遲處理訂單,rabbitMQ是通過佇列的處理訊息,成功進入佇列的訂單給使用者返回排隊中的提示資訊,後面使用者方面可以通過輪詢的方式去查詢資料庫,如果資料庫存在該使用者的訂單資訊,即提示秒殺成功,如果資料庫的商品為0且訂單表不存在該使用者資訊,提示秒殺失敗,否則提示排隊中
1、頁面靜態化,頁面快取:
將頁面的寫成html,所有的資料都通過js非同步獲取,並且在頁面中加入快取功能,設定快取時間,springboot提供快取統一設定:
#static spring.resources.add-mappings=true spring.resources.cache-period= 3600 spring.resources.chain.cache=true spring.resources.chain.enabled=true spring.resources.chain.gzipped=true spring.resources.chain.html-application-cache=true spring.resources.static-locations=classpath:/static/
2、nignx負載均衡
upstream server_pool_miaosha{
server 127.0.0.1:8080 weight=1;
server 127.0.0.1:8081 weight=1;
}
server {
listen 81;
server_name localhost;
location / {
proxy_pass http://server_pool_miaosha;
}
3、分散式session
,隨機生成一個token作為key,當前使用者user作為value儲存在redis中,並且將該token儲存在客戶端的cookie中,對於客戶端每次請求都會自動的將cookie資訊傳遞到伺服器,這時我們就可以通過CooKi_NAME-TOKEN獲取token,進而去redis獲取使用者資訊
redisService.set(MiaoshaUserKey.token, token, user); Cookie cookie = new Cookie(COOKI_NAME_TOKEN, token); cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds()); cookie.setPath("/"); response.addCookie(cookie);
@Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class); HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class); String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN); String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN); if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) { return null; } String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken; return userService.getByToken(response, token); } private String getCookieValue(HttpServletRequest request, String cookiName) { Cookie[] cookies = request.getCookies(); if(cookies == null || cookies.length <=0){ return null; } for(Cookie cookie : cookies) { if(cookie.getName().equals(cookiName)) { return cookie.getValue(); } } return null; }4、隱藏秒殺地址
主要是通過客戶端在呼叫秒殺介面前要先到伺服器獲取一個隨機數,這個隨機數會儲存在redis中,然後在訪問地址中帶上這個隨機數
@RequestMapping(value="/{path}/do_miaosha", method= RequestMethod.POST) @ResponseBody public Result<Integer> miaosha(Model model, MiaoshaUser user, @RequestParam("goodsId")long goodsId, @PathVariable("path") String path) {
客戶端呼叫秒殺介面前:
$.ajax({ url:"/miaosha/path", type:"GET", data:{ goodsId:goodsId, verifyCode:$("#verifyCode").val() }, success:function(data){ if(data.code == 0){ var path = data.data; console.info("path:"+path); doMiaosha(path); }else{ layer.msg(data.msg); } },
客戶端呼叫秒殺介面:
$.ajax({ url:"/miaosha/"+path+"/do_miaosha", type:"POST", data:{ goodsId:$("#goodsId").val(), }, success:function(data){ if(data.code == 0){ getMiaoshaResult($("#goodsId").val()); //window.location.href="/order_detail.htm?orderId="+data.data.id; }else{ layer.msg(data.msg); } },
這個即能達到隱藏介面的功能
5、介面限流:
public class AccessInterceptor extends HandlerInterceptorAdapter{ @Autowired MiaoshaUserService userService; @Autowired RedisService redisService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if(handler instanceof HandlerMethod) { MiaoshaUser user = getUser(request, response); UserContext.setUser(user); HandlerMethod hm = (HandlerMethod)handler; AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if(accessLimit == null) { return true; } int seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); boolean needLogin = accessLimit.needLogin(); String key = request.getRequestURI();
通過自定義註解,新增攔截器,redis記錄通過設定超時時間來限制該使用者的某時間段內的訪問次數
6、jmeter工具壓測
一個完整的jmeter壓測包括執行緒組,HTTP請求預設值,HTTP請求,CSV Data Set Config ,聚合報告