1.首先咱們新建一個Controller用於秒殺:java
package com.imooc.Controller; import com.imooc.service.impl.SeckillServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Created by zhongliahi on 2018/6/11. * 秒殺測試 */ @RestController @RequestMapping(value = "/skill") @Slf4j public class SeckillController { @Autowired private SeckillServiceImpl seckillService; //@PathVariable 能夠將 URL 中佔位符參數綁定到控制器處理方法的入參中 // URL 中的 {xxx} 佔位符能夠經過@PathVariable(「xxx「) 綁定到操做方法的入參中。 @GetMapping(value = "/query/{productId}") public String query(@PathVariable String productId) throws Exception{ return seckillService.querySeckillProductInfo(productId); } @GetMapping("/order/{productId}") public String skill(@PathVariable String productId) throws Exception{ log.info("秒殺----productId:"+productId); seckillService.orderProductMockDiffUser(productId); return seckillService.querySeckillProductInfo(productId); } }
2.創建一個Serviceweb
package com.imooc.service; /** * Created by zhongliahi on 2018/6/11. */ public interface SeckillService { String queryMap(String productId); String querySeckillProductInfo(String productId); void orderProductMockDiffUser(String productId); }
3.實現Servicespring
package com.imooc.service.impl; import com.imooc.Exception.SellException; import com.imooc.enums.ExceptionEnum; import com.imooc.service.SeckillService; import com.imooc.util.KeyUtils; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; /** * Created by zhonglihai on 2018/6/11. * 秒殺Serviceimpl * 演示 */ @Service public class SeckillServiceImpl implements SeckillService { /** * 秒殺特價 1000000份 * @param productId * @return */ static Map<String,Integer> products; static Map<String,Integer> stock; static Map<String,String> orders; static{ /** * ,模擬多個表,商品信息表,庫存表,秒殺成功訂單表 */ products =new HashMap<>(); stock=new HashMap<>(); orders=new HashMap<>(); products.put("123",1000000); stock.put("123",1000000); } @Override public String queryMap(String productId) { return "活動特價,限量:"+products.get(productId)+",還剩:"+stock.get(productId) +"份"+",成功下單用戶數:"+orders.size()+"人。"; } @Override public String querySeckillProductInfo(String productId) { return this.queryMap(productId); } /** * 主要秒殺的邏輯 * @param productId */ @Override public synchronized void orderProductMockDiffUser(String productId) { //查詢該商品庫存,爲0則活動結束 int stockNum=stock.get(productId); if(stockNum==0){ throw new SellException(ExceptionEnum.SECKILL_OVER); }else{ //2.下單(模擬不一樣用戶opendid不一樣) orders.put(KeyUtils.getUniqueKey(),productId); //3.減庫存 stockNum=stockNum-1; try{ Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } stock.put(productId,stockNum); } } }
關於壓測:apache
壓力測試是一種基本的質量保證行爲,在秒殺活動中更爲重要,能有效測量超賣(賣出去的比庫存的多)、少賣(有買了可是沒賣)等現象,目前主流的壓測工具備Jmeter、LoadRunner等,老一點的有apache ab,正好本人機器裝有Apace服務,所以使用apache ab作壓測。瀏覽器
項目中,咱們只添加了一件商品,productId爲123,啓動項目,在瀏覽器中查詢,URL:http://127.0.0.1:8080/sell/skill/query/123,結果以下:服務器
能夠看到,項目能正常訪問,查詢的庫存爲1000000份。如今咱們咋瀏覽器上進行秒殺,URL:http://127.0.0.1:8080/sell/skill/order/123併發
咱們已經秒殺了一件商品,那麼如何實現高併發秒殺呢?這就須要使用上面介紹的Apache ab進行壓測app
使用方法:安裝Apache Http Services 後,配置相關環境變量,保證能在命令行直接調用。負載均衡
測試命令:ab -n 1000 -c 10 http://127.0.0.1:8080/sell/skill/order/123分佈式
其中ab表示在命令行調取apache ab壓測工具,-n表示發起1000條請求,-c 表示10個併發
http://127.0.0.1:8080/sell/skill/order/123 :表示測試的URL.
(注意:壓測會佔用大量電腦資源,特別是併發大的時候)
結果:
能夠發現,秒殺仍是比較快的,僅用了19秒。如今咱們來查看庫存;
-----------------------------------------
重點:查看庫存發現雖然秒殺都成功了,可是庫存量與下單成功量之和與總量不對應:999878+1000>1000000,出現了超賣現象。
下面咱們在秒殺方法上加上synchronized關鍵字,修SeckillServiceImpl,對秒殺方法上鎖
而後重啓項目,在此從新查詢庫存:
沒問題!
繼續併發秒殺:
測試完成,明顯能夠感受到,訪問慢了不少,用了100多秒,由於咱們使用了資源鎖,保證每次只有一個線程去調用它。
如今咱們在來查看庫存。
重點:能夠發現,庫存與下單都是正確的,使用synchronized是一種資源控制的解決辦法
那麼,秒殺中直接使用synchronized進行鎖控制有什麼很差的地方呢?
1.沒法作到細粒度的控制,在測試中,咱們只有一個商品,若是有多個商品呢?
多個商品參與秒殺活動,有的人秒殺商品A、有的秒殺商品B,都要走秒殺方法,使用synchronized
會同樣的慢。
2.只支持單點(單機、服務器環境),沒法作到水平擴展,若是項目使用負載均衡,會出現混亂。
那麼,又有什麼好的辦法能夠解決上面提到的問題?
答案固然是有,那就是分佈式鎖。