從0到1構(gòu)建分布式秒殺系統(tǒng)

image

前言

?最近,被推送了不少秒殺架構(gòu)的文章,忙里偷閑自己也總結(jié)了一下互聯(lián)網(wǎng)平臺(tái)秒殺架構(gòu)設(shè)計(jì),當(dāng)然也借鑒了不少同學(xué)的思路。俗話說(shuō),脫離案例講架構(gòu)都是耍流氓,最終使用SpringBoot模擬實(shí)現(xiàn)了部分秒殺場(chǎng)景,同時(shí)跟大家分享交流一下。

秒殺場(chǎng)景

秒殺場(chǎng)景無(wú)非就是多個(gè)用戶在同時(shí)搶購(gòu)一件或者多件商品,專用詞匯就是所謂的高并發(fā)?,F(xiàn)實(shí)中經(jīng)常被大家喜聞樂(lè)見(jiàn)的場(chǎng)景,一群大媽搶購(gòu)打折雞蛋的畫(huà)面一定不會(huì)陌生,如此場(chǎng)面讓服務(wù)員大姐很無(wú)奈,趕上不要錢(qián)了。

image

業(yè)務(wù)特點(diǎn)

  • 瞬間高并發(fā)、電腦旁邊的小哥哥、小姐姐們?nèi)绯泻鍝尩拇髬屢话?,瘋狂的點(diǎn)著鼠標(biāo)
  • 庫(kù)存少、便宜、稀缺限量,值得大家去搶購(gòu),如蘋(píng)果腎,小米粉,錘子粉(理解萬(wàn)歲)

用戶規(guī)模

用戶規(guī)??纱罂尚?,幾百或者上千人的活動(dòng)單體架構(gòu)足以可以應(yīng)付,簡(jiǎn)單的加鎖、進(jìn)程內(nèi)隊(duì)列就可以輕松搞定。一旦上升到百萬(wàn)、千萬(wàn)級(jí)別的規(guī)模就要考慮分布式集群來(lái)應(yīng)對(duì)瞬時(shí)高并發(fā)。

秒殺架構(gòu)

image

架構(gòu)層級(jí)

  • 一般商家在做活動(dòng)的時(shí)候,經(jīng)常會(huì)遇到各種不懷好意的DDOS攻擊(利用無(wú)辜的吃瓜群眾奪取資源),導(dǎo)致真正的我們無(wú)法獲得服務(wù)!所以說(shuō)高防IP還是很有必要的。

  • 搞活動(dòng)就意味著人多,接入SLB,對(duì)多臺(tái)云服務(wù)器進(jìn)行流量分發(fā),可以通過(guò)流量分發(fā)擴(kuò)展應(yīng)用系統(tǒng)對(duì)外的服務(wù)能力,通過(guò)消除單點(diǎn)故障提升應(yīng)用系統(tǒng)的可用性。

  • 基于SLB價(jià)格以及靈活性考慮后面我們接入Nginx做限流分發(fā),來(lái)保障后端服務(wù)的正常運(yùn)行。

  • 后端秒殺業(yè)務(wù)邏輯,基于Redis 或者 Zookeeper 分布式鎖,Kafka 或者 Redis 做消息隊(duì)列,DRDS數(shù)據(jù)庫(kù)中間件實(shí)現(xiàn)數(shù)據(jù)的讀寫(xiě)分離。

優(yōu)化思路

  • 分流、分流、分流,重要的事情說(shuō)三遍,再牛逼的機(jī)器也抵擋不住高級(jí)別的并發(fā)。

  • 限流、限流、限流,畢竟秒殺商品有限,防刷的前提下沒(méi)有絕對(duì)的公平,根據(jù)每個(gè)服務(wù)的負(fù)載能力,設(shè)定流量極限。

  • 緩存、緩存、緩存、盡量不要讓大量請(qǐng)求穿透到DB層,活動(dòng)開(kāi)始前商品信息可以推送至分布式緩存。

  • 異步、異步、異步,分析并識(shí)別出可以異步處理的邏輯,比如日志,縮短系統(tǒng)響應(yīng)時(shí)間。

  • 主備、主備、主備,如果有條件做好主備容災(zāi)方案也是非常有必要的(參考某年錘子的活動(dòng)被攻擊)。

  • 最后,為了支撐更高的并發(fā),追求更好的性能,可以對(duì)服務(wù)器的部署模型進(jìn)行優(yōu)化,部分請(qǐng)求走正常的秒殺流程,部分請(qǐng)求直接返回秒殺失敗,缺點(diǎn)是開(kāi)發(fā)部署時(shí)需要維護(hù)兩套邏輯。

分層優(yōu)化

  • 前端優(yōu)化:活動(dòng)開(kāi)始前生成靜態(tài)商品頁(yè)面推送緩存和CDN,靜態(tài)文件(JS/CSS)請(qǐng)求推送至文件服務(wù)器和CDN。
  • 網(wǎng)絡(luò)優(yōu)化:如果是全國(guó)用戶,最好是BGP多線機(jī)房,減少網(wǎng)絡(luò)延遲。
  • 應(yīng)用服務(wù)優(yōu)化:Nginx最佳配置、Tomcat連接池優(yōu)化、數(shù)據(jù)庫(kù)配置優(yōu)化、數(shù)據(jù)庫(kù)連接池優(yōu)化。

全鏈路壓測(cè)

  • 分析需壓測(cè)業(yè)務(wù)場(chǎng)景涉及系統(tǒng)
  • 協(xié)調(diào)各個(gè)壓測(cè)系統(tǒng)資源并搭建壓測(cè)環(huán)境
  • 壓測(cè)數(shù)據(jù)隔離以及監(jiān)控(響應(yīng)時(shí)間、吞吐量、錯(cuò)誤率等數(shù)據(jù)以圖表形式實(shí)時(shí)顯示)
  • 壓測(cè)結(jié)果統(tǒng)計(jì)(平均響應(yīng)時(shí)間、平均吞吐量等數(shù)據(jù)以圖表形式在測(cè)試結(jié)束后顯示)
  • 優(yōu)化單個(gè)系統(tǒng)性能、關(guān)聯(lián)流程以及整個(gè)業(yè)務(wù)流程

整個(gè)壓測(cè)優(yōu)化過(guò)程就是一個(gè)不斷優(yōu)化不斷改進(jìn)的過(guò)程,事先通過(guò)測(cè)試不斷發(fā)現(xiàn)問(wèn)題,優(yōu)化系統(tǒng),避免問(wèn)題,指定應(yīng)急方案,才能讓系統(tǒng)的穩(wěn)定性和性能都得到質(zhì)的提升。

代碼案例

可能秒殺架構(gòu)原理大家都懂,網(wǎng)上也有不少實(shí)現(xiàn)方式,但大多都是文字的描述,告訴你如何如何,什么加鎖、緩存、隊(duì)列之類。但很少全面有的案例告訴你如何去做,既然是從0到1,希望以下代碼案例可以幫助到你。當(dāng)然最終落實(shí)到生產(chǎn),還有很長(zhǎng)的路要走,要根據(jù)自己的業(yè)務(wù)進(jìn)行編碼,實(shí)施并部署。

你將會(huì)在代碼案例中學(xué)到以下知識(shí)(不定期補(bǔ)充):

  • 如何大家SpringBoot微服務(wù)
  • ThreadPoolExecutor線程池的使用
  • ReentrantLock和Synchronized的使用場(chǎng)景
  • 數(shù)據(jù)庫(kù)鎖機(jī)制(悲觀鎖、樂(lè)觀鎖)
  • 分布式鎖(RedissLock、Zookeeper)
  • 進(jìn)程內(nèi)消息隊(duì)列(LinkedBlockingQueue、ArrayBlockingQueue、ConcurrentLinkedQueue)
  • 分布式消息隊(duì)列(Redis、Kafka)

代碼結(jié)構(gòu):

├─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

思考改進(jìn)

  • 如何防止單個(gè)用戶重復(fù)秒殺下單?
  • 如何防止惡意調(diào)用秒殺接口?
  • 如果用戶秒殺成功,一直不支付該怎么辦?
  • 消息隊(duì)列處理完成后,如果異步通知給用戶秒殺成功?
  • 如何保障 Redis、Zookeeper 、Kafka 服務(wù)的正常運(yùn)行(高可用)?
  • 高并發(fā)下秒殺業(yè)務(wù)如何做到不影響其他業(yè)務(wù)(隔離性)?

碼云下載:從0到1構(gòu)建分布式秒殺系統(tǒng),脫離案例講架構(gòu)都是耍流氓

可供參考

企業(yè)云解析DNS

Nginx學(xué)習(xí)之負(fù)載均衡

Nginx學(xué)習(xí)之緩存配置

Nginx學(xué)習(xí)之HTTP/2.0配置

Nginx學(xué)習(xí)之如何防止流量攻擊

SpringBoot開(kāi)發(fā)案例之整合Kafka實(shí)現(xiàn)消息隊(duì)列

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容