SpringBoot開發案例從0到1構建分散式秒殺系統
05-23
SpringBoot開發案例從0到1構建分散式秒殺系統
業務特點
推薦閱讀:
前言
最近,被推送了不少秒殺架構的文章,忙裡偷閒自己也總結了一下互聯網平台秒殺架構設計,當然也借鑒了不少同學的思路。俗話說,脫離案例講架構都是耍流氓,最終使用SpringBoot模擬實現了部分秒殺場景,同時跟大家分享交流一下。秒殺場景秒殺場景無非就是多個用戶在同時搶購一件或者多件商品,專用辭彙就是所謂的高並發。現實中經常被大家喜聞樂見的場景,一群大媽搶購打折雞蛋的畫面一定不會陌生,如此場面讓服務員大姐很無奈,趕上不要錢了。- 瞬間高並發、電腦旁邊的小哥哥、小姐姐們如超市哄搶的大媽一般,瘋狂的點著滑鼠
- 庫存少、便宜、稀缺限量,值得大家去搶購,如蘋果腎,小米粉,鎚子粉(理解萬歲)
架構層級
- 一般商家在做活動的時候,經常會遇到各種不懷好意的DDOS攻擊(利用無辜的吃瓜群眾奪取資源),導致真正的我們無法獲得服務!所以說高防IP還是很有必要的。
- 搞活動就意味著人多,接入SLB,對多台雲伺服器進行流量分發,可以通過流量分發擴展應用系統對外的服務能力,通過消除單點故障提升應用系統的可用性。
- 基於SLB價格以及靈活性考慮後面我們接入Nginx做限流分發,來保障後端服務的正常運行。
- 後端秒殺業務邏輯,基於Redis 或者 Zookeeper 分散式鎖,Kafka 或者 Redis 做消息隊列,DRDS資料庫中間件實現數據的讀寫分離。
- 分流、分流、分流,重要的事情說三遍,再牛逼的機器也抵擋不住高級別的並發。
- 限流、限流、限流,畢竟秒殺商品有限,防刷的前提下沒有絕對的公平,根據每個服務的負載能力,設定流量極限。
- 緩存、緩存、緩存、盡量不要讓大量請求穿透到DB層,活動開始前商品信息可以推送至分散式緩存。
- 非同步、非同步、非同步,分析並識別出可以非同步處理的邏輯,比如日誌,縮短系統響應時間。
- 主備、主備、主備,如果有條件做好主備容災方案也是非常有必要的(參考某年鎚子的活動被攻擊)。
- 最後,為了支撐更高的並發,追求更好的性能,可以對伺服器的部署模型進行優化,部分請求走正常的秒殺流程,部分請求直接返回秒殺失敗,缺點是開發部署時需要維護兩套邏輯。
- 前端優化:活動開始前生成靜態商品頁面推送緩存和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 服務的正常運行(高可用)?
- 高並發下秒殺業務如何做到不影響其他業務(隔離性)?
推薦閱讀:
※yaraft 的開發近況〔2017.11〕
※Snowflake演算法
※接受「不完美」:分散式事務學習總結
※從零開始開發一個單機存儲引擎