SpringBoot開發案例從0到1構建分佈式秒殺系統

SpringBoot開發案例從0到1構建分佈式秒殺系統

前言

最近,被推送了很多秒殺架構的文章,忙裏偷閒本身也總結了一下互聯網平臺秒殺架構設計,固然也借鑑了很多同窗的思路。俗話說,脫離案例講架構都是耍流氓,最終使用SpringBoot模擬實現了部分秒殺場景,同時跟你們分享交流一下。前端

秒殺場景

秒殺場景無非就是多個用戶在同時搶購一件或者多件商品,專用詞彙就是所謂的高併發。現實中常常被你們喜聞樂見的場景,一羣大媽搶購打折雞蛋的畫面必定不會陌生,如此場面讓服務員大姐很無奈,遇上不要錢了。java

SpringBoot開發案例從0到1構建分佈式秒殺系統

業務特色

  • 瞬間高併發、電腦旁邊的小哥哥、×××姐們如超市哄搶的大媽通常,瘋狂的點着鼠標
  • 庫存少、便宜、稀缺限量,值得你們去搶購,如蘋果腎,小米粉,錘子粉(理解萬歲)

用戶規模

用戶規模可大可小,幾百或者上千人的活動單體架構足以能夠應付,簡單的加鎖、進程內隊列就能夠輕鬆搞定。一旦上升到百萬、千萬級別的規模就要考慮分佈式集羣來應對瞬時高併發。git

秒殺架構

SpringBoot開發案例從0到1構建分佈式秒殺系統

架構層級

  • 通常商家在作活動的時候,常常會遇到各類不懷好意的DDOS***(利用無辜的吃瓜羣衆奪取資源),致使真正的咱們沒法得到服務!因此說高防IP仍是頗有必要的。web

  • 搞活動就意味着人多,接入SLB,對多臺雲服務器進行流量分發,能夠經過流量分發擴展應用系統對外的服務能力,經過消除單點故障提高應用系統的可用性。redis

  • 基於SLB價格以及靈活性考慮後面咱們接入Nginx作限流分發,來保障後端服務的正常運行。spring

  • 後端秒殺業務邏輯,基於Redis 或者 Zookeeper 分佈式鎖,Kafka 或者 Redis 作消息隊列,DRDS數據庫中間件實現數據的讀寫分離。

優化思路

  • 分流、分流、分流,重要的事情說三遍,再牛逼的機器也抵擋不住高級別的併發。sql

  • 限流、限流、限流,畢竟秒殺商品有限,防刷的前提下沒有絕對的公平,根據每一個服務的負載能力,設定流量極限。數據庫

  • 緩存、緩存、緩存、儘可能不要讓大量請求穿透到DB層,活動開始前商品信息能夠推送至分佈式緩存。後端

  • 異步、異步、異步,分析並識別出能夠異步處理的邏輯,好比日誌,縮短系統響應時間。api

  • 主備、主備、主備,若是有條件作好主備容災方案也是很是有必要的(參考某年錘子的活動被***)。

  • 最後,爲了支撐更高的併發,追求更好的性能,能夠對服務器的部署模型進行優化,部分請求走正常的秒殺流程,部分請求直接返回秒殺失敗,缺點是開發部署時須要維護兩套邏輯。

分層優化

  • 前端優化:活動開始前生成靜態商品頁面推送緩存和CDN,靜態文件(JS/CSS)請求推送至文件服務器和CDN。
  • 網絡優化:若是是全國用戶,最好是BGP多線機房,減小網絡延遲。
  • 應用服務優化:Nginx最佳配置、Tomcat鏈接池優化、數據庫配置優化、數據庫鏈接池優化。

全鏈路壓測

  • 分析需壓測業務場景涉及系統
  • 協調各個壓測系統資源並搭建壓測環境
  • 壓測數據隔離以及監控(響應時間、吞吐量、錯誤率等數據以圖表形式實時顯示)
  • 壓測結果統計(平均響應時間、平均吞吐量等數據以圖表形式在測試結束後顯示)
  • 優化單個系統性能、關聯流程以及整個業務流程

整個壓測優化過程就是一個不斷優化不斷改進的過程,事先經過測試不斷髮現問題,優化系統,避免問題,指定應急方案,才能讓系統的穩定性和性能都獲得質的提高。

代碼案例

可能秒殺架構原理你們都懂,網上也有很多實現方式,但大多都是文字的描述,告訴你如何如何,什麼加鎖、緩存、隊列之類。但不多全面有的案例告訴你如何去作,既然是從0到1,但願如下代碼案例能夠幫助到你。固然最終落實到生產,還有很長的路要走,要根據本身的業務進行編碼,實施並部署。

你將會在代碼案例中學到如下知識(不按期補充):

  • 如何你們SpringBoot微服務
  • ThreadPoolExecutor線程池的使用
  • ReentrantLock和Synchronized的使用場景
  • 數據庫鎖機制(悲觀鎖、樂觀鎖)
  • 分佈式鎖(RedissLock、Zookeeper)
  • 進程內消息隊列(LinkedBlockingQueue、ArrayBlockingQueue、ConcurrentLinkedQueue)
  • 分佈式消息隊列(Redis、Kafka)

代碼結構:

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─itstyle
│  │  │          └─seckill
│  │  │              │  Application.java
│  │  │              │  
│  │  │              ├─common
│  │  │              │  ├─api
│  │  │              │  │      SwaggerConfig.java 
│  │  │              │  │      
│  │  │              │  ├─config
│  │  │              │  │      IndexController.java  
│  │  │              │  │      
│  │  │              │  ├─dynamicquery   
│  │  │              │  │      DynamicQuery.java
│  │  │              │  │      DynamicQueryImpl.java
│  │  │              │  │      NativeQueryResultEntity.java
│  │  │              │  │      
│  │  │              │  ├─entity   
│  │  │              │  │      Result.java
│  │  │              │  │      Seckill.java
│  │  │              │  │      SuccessKilled.java
│  │  │              │  │      
│  │  │              │  ├─enums
│  │  │              │  │      SeckillStatEnum.java
│  │  │              │  │      
│  │  │              │  ├─interceptor
│  │  │              │  │      MyAdapter.java
│  │  │              │  │      
│  │  │              │  └─redis
│  │  │              │          RedisConfig.java
│  │  │              │          RedisUtil.java
│  │  │              │          
│  │  │              ├─distributedlock
│  │  │              │  ├─redis
│  │  │              │  │      RedissLockDemo.java
│  │  │              │  │      RedissLockUtil.java
│  │  │              │  │      RedissonAutoConfiguration.java
│  │  │              │  │      RedissonProperties.java
│  │  │              │  │      
│  │  │              │  └─zookeeper
│  │  │              │          ZkLockUtil.java
│  │  │              │          
│  │  │              ├─queue
│  │  │              │  ├─jvm
│  │  │              │  │      SeckillQueue.java
│  │  │              │  │      TaskRunner.java
│  │  │              │  │      
│  │  │              │  ├─kafka
│  │  │              │  │      KafkaConsumer.java
│  │  │              │  │      KafkaSender.java
│  │  │              │  │      
│  │  │              │  └─redis
│  │  │              │          RedisConsumer.java
│  │  │              │          RedisSender.java
│  │  │              │          RedisSubListenerConfig.java
│  │  │              │          
│  │  │              ├─repository
│  │  │              │      SeckillRepository.java
│  │  │              │      
│  │  │              ├─service
│  │  │              │  │  ISeckillDistributedService.java
│  │  │              │  │  ISeckillService.java
│  │  │              │  │  
│  │  │              │  └─impl
│  │  │              │          SeckillDistributedServiceImpl.java
│  │  │              │          SeckillServiceImpl.java
│  │  │              │          
│  │  │              └─web
│  │  │                      SeckillController.java
│  │  │                      SeckillDistributedController.java
│  │  │                      
│  │  ├─resources
│  │  │  │  application.properties
│  │  │  │  logback-spring.xml
│  │  │  │  
│  │  │  ├─sql
│  │  │  │      seckill.sql
│  │  │  │      
│  │  │  ├─static
│  │  │  └─templates
│  │  └─webapp

思考改進

  • 如何防止單個用戶重複秒殺下單?
  • 如何防止惡意調用秒殺接口?
  • 若是用戶秒殺成功,一直不支付該怎麼辦?
  • 消息隊列處理完成後,若是異步通知給用戶秒殺成功?
  • 如何保障 Redis、Zookeeper 、Kafka 服務的正常運行(高可用)?
  • 高併發下秒殺業務如何作到不影響其餘業務(隔離性)?

碼雲下載:從0到1構建分佈式秒殺系統

可供參考

企業雲解析DNS

Nginx學習之負載均衡

Nginx學習之緩存配置

Nginx學習之HTTP/2.0配置

Nginx學習之如何防止流量***

SpringBoot開發案例之整合Kafka實現消息隊列

相關文章
相關標籤/搜索