自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

從構(gòu)建分布式秒殺系統(tǒng)聊聊限流特技

存儲 存儲軟件 分布式
俗話說的好,冰凍三尺非一日之寒,滴水穿石非一日之功,羅馬也不是一天就建成的。兩周前秒殺案例初步成型,分享到了中國最大的同×××友網(wǎng)站-碼云。同時也收到了不少小伙伴的建議和投訴。我從不認(rèn)為分布式、集群、秒殺這些就應(yīng)該是大廠的專利,在互聯(lián)網(wǎng)的今天無論什么時候都要時刻武裝自己,只有這樣,也許你的春天就在明天。

前言

俗話說的好,冰凍三尺非一日之寒,滴水穿石非一日之功,羅馬也不是一天就建成的。兩周前秒殺案例初步成型,分享到了中國***的同×××友網(wǎng)站-碼云。同時也收到了不少小伙伴的建議和投訴。我從不認(rèn)為分布式、集群、秒殺這些就應(yīng)該是大廠的專利,在互聯(lián)網(wǎng)的今天無論什么時候都要時刻武裝自己,只有這樣,也許你的春天就在明天。

在開發(fā)秒殺系統(tǒng)案例的過程中,前面主要分享了隊(duì)列、緩存、鎖和分布式鎖以及靜態(tài)化等等。緩存的目的是為了提升系統(tǒng)訪問速度和增強(qiáng)系統(tǒng)的處理能力;分布式鎖解決了集群下數(shù)據(jù)的安全一致性問題;靜態(tài)化無疑是減輕了緩存以及DB層的壓力。

[[233265]]

限流

然而再牛逼的機(jī)器,再優(yōu)化的設(shè)計(jì),對于特殊場景我們也是要特殊處理的。就拿秒殺來說,可能會有***別的用戶進(jìn)行搶購,而商品數(shù)量遠(yuǎn)遠(yuǎn)小于用戶數(shù)量。如果這些請求都進(jìn)入隊(duì)列或者查詢緩存,對于最終結(jié)果沒有任何意義,徒增后臺華麗的數(shù)據(jù)。對此,為了減少資源浪費(fèi),減輕后端壓力,我們還需要對秒殺進(jìn)行限流,只需保障部分用戶服務(wù)正常即可。

就秒殺接口來說,當(dāng)訪問頻率或者并發(fā)請求超過其承受范圍的時候,這時候我們就要考慮限流來保證接口的可用性,以防止非預(yù)期的請求對系統(tǒng)壓力過大而引起的系統(tǒng)癱瘓。通常的策略就是拒絕多余的訪問,或者讓多余的訪問排隊(duì)等待服務(wù)。

限流算法

任何限流都不是漫無目的的,也不是一個開關(guān)就可以解決的問題,常用的限流算法有:令牌桶,漏桶。

令牌桶

令牌桶算法是網(wǎng)絡(luò)流量×××(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一種算法。典型情況下,令牌桶算法用來控制發(fā)送到網(wǎng)絡(luò)上的數(shù)據(jù)的數(shù)目,并允許突發(fā)數(shù)據(jù)的發(fā)送(百科)。

在秒殺活動中,用戶的請求速率是不固定的,這里我們假定為10r/s,令牌按照5個每秒的速率放入令牌桶,桶中最多存放20個令牌。仔細(xì)想想,是不是總有那么一部分請求被丟棄。

漏桶

漏桶算法的主要目的是控制數(shù)據(jù)注入到網(wǎng)絡(luò)的速率,平滑網(wǎng)絡(luò)上的突發(fā)流量。漏桶算法提供了一種機(jī)制,通過它,突發(fā)流量可以被×××以便為網(wǎng)絡(luò)提供一個穩(wěn)定的流量(百科)。

令牌桶是無論你流入速率多大,我都按照既定的速率去處理,如果桶滿則拒絕服務(wù)。

應(yīng)用限流

Tomcat

在Tomcat容器中,我們可以通過自定義線程池,配置***連接數(shù),請求處理隊(duì)列等參數(shù)來達(dá)到限流的目的(圖片源自網(wǎng)絡(luò))。

Tomcat默認(rèn)使用自帶的連接池,這里我們也可以自定義實(shí)現(xiàn),打開/conf/server.xml文件,在Connector之前配置一個線程池:

  1. <Executor name="tomcatThreadPool" 
  2.         namePrefix="tomcatThreadPool-" 
  3.         maxThreads="1000" 
  4.         maxIdleTime="300000" 
  5.         minSpareThreads="200"/> 
  • name:共享線程池的名字。這是Connector為了共享線程池要引用的名字,該名字必須唯一。默認(rèn)值:None;
  • namePrefix:在JVM上,每個運(yùn)行線程都可以有一個name 字符串。這一屬性為線程池中每個線程的name字符串設(shè)置了一個前綴,Tomcat將把線程號追加到這一前綴的后面。默認(rèn)值:tomcat-exec-;
  • maxThreads:該線程池可以容納的***線程數(shù)。默認(rèn)值:200;
  • maxIdleTime:在Tomcat關(guān)閉一個空閑線程之前,允許空閑線程持續(xù)的時間(以毫秒為單位)。只有當(dāng)前活躍的線程數(shù)大于minSpareThread的值,才會關(guān)閉空閑線程。默認(rèn)值:60000(一分鐘)。
  • minSpareThreads:Tomcat應(yīng)該始終打開的最小不活躍線程數(shù)。默認(rèn)值:25。

配置Connector

  1. <Connector executor="tomcatThreadPool" 
  2.            port="8080" protocol="HTTP/1.1" 
  3.            connectionTimeout="20000" 
  4.            redirectPort="8443" 
  5.            minProcessors="5" 
  6.            maxProcessors="75" 
  7.            acceptCount="1000"/> 
  • executor:表示使用該參數(shù)值對應(yīng)的線程池;
  • minProcessors:服務(wù)器啟動時創(chuàng)建的處理請求的線程數(shù);
  • maxProcessors:***可以創(chuàng)建的處理請求的線程數(shù);
  • acceptCount:指定當(dāng)所有可以使用的處理請求的線程數(shù)都被使用時,可以放到處理隊(duì)列中的請求數(shù),超過這個數(shù)的請求將不予處理。

API限流

秒殺活動中,接口的請求量會是平時的數(shù)百倍甚至數(shù)千倍,從而有可能導(dǎo)致接口不可用,并引發(fā)連鎖反應(yīng)導(dǎo)致整個系統(tǒng)崩潰,甚至有可能會影響到其它服務(wù)。

那么如何應(yīng)對這種突然事件呢?這里我們采用開源工具包guava提供的限流工具類RateLimiter進(jìn)行API限流,該類基于"令牌桶算法",開箱即用。

  1. /** 
  2.  * 自定義注解  限流 
  3.  */ 
  4. @Target({ElementType.PARAMETER, ElementType.METHOD}) 
  5. @Retention(RetentionPolicy.RUNTIME) 
  6. @Documented 
  7. public  @interface ServiceLimit { 
  8.      String description()  default ""

自定義切面

  1. /** 
  2.  * 限流 AOP 
  3.  */ 
  4. @Component 
  5. @Scope 
  6. @Aspect 
  7. public class LimitAspect { 
  8.     //每秒只發(fā)出100個令牌,此處是單進(jìn)程服務(wù)的限流,內(nèi)部采用令牌捅算法實(shí)現(xiàn) 
  9.     private static   RateLimiter rateLimiter = RateLimiter.create(100.0); 
  10.  
  11.     //Service層切點(diǎn)  限流 
  12.     @Pointcut("@annotation(com.itstyle.seckill.common.aop.ServiceLimit)")   
  13.     public void ServiceAspect() { 
  14.  
  15.     } 
  16.  
  17.     @Around("ServiceAspect()"
  18.     public  Object around(ProceedingJoinPoint joinPoint) {  
  19.         Boolean flag = rateLimiter.tryAcquire(); 
  20.         Object obj = null
  21.         try { 
  22.             if(flag){ 
  23.                 obj = joinPoint.proceed(); 
  24.             } 
  25.         } catch (Throwable e) { 
  26.             e.printStackTrace(); 
  27.         } 
  28.         return obj; 
  29.     } 

業(yè)務(wù)實(shí)現(xiàn):

  1. @Override 
  2. @ServiceLimit 
  3. @Transactional 
  4. public Result startSeckil(long seckillId, long userId) { 
  5.     //省略部分業(yè)務(wù)代碼,詳見秒殺源碼 

分布式限流

Nginx

  1. #統(tǒng)一在http域中進(jìn)行配置 
  2. #限制請求 
  3. limit_req_zone $binary_remote_addr $uri zone=api_read:20m rate=50r/s; 
  4. #按ip配置一個連接 zone 
  5. limit_conn_zone $binary_remote_addr zone=perip_conn:10m; 
  6. #按server配置一個連接 zone 
  7. limit_conn_zone $server_name zone=perserver_conn:100m; 
  8. server { 
  9.         listen       80; 
  10.         server_name  seckill.52itstyle.com; 
  11.         index index.jsp; 
  12.         location / { 
  13.               #請求限流排隊(duì)通過 burst默認(rèn)是0 
  14.               limit_req zone=api_read burst=5; 
  15.               #連接數(shù)限制,每個IP并發(fā)請求為2 
  16.               limit_conn perip_conn 2; 
  17.               #服務(wù)所限制的連接數(shù)(即限制了該server并發(fā)連接數(shù)量) 
  18.               limit_conn perserver_conn 1000; 
  19.               #連接限速 
  20.               limit_rate 100k; 
  21.               proxy_pass      http://seckill; 
  22.         } 
  23. upstream seckill { 
  24.         fair; 
  25.         server  172.16.1.120:8080 weight=1  max_fails=2 fail_timeout=30s; 
  26.         server  172.16.1.130:8080 weight=1  max_fails=2 fail_timeout=30s; 

配置說明

  1. imit_conn_zone 

是針對每個IP定義一個存儲session狀態(tài)的容器。這個示例中定義了一個100m的容器,按照32bytes/session,可以處理3200000個session。

  1. limit_rate 300k; 

對每個連接限速300k. 注意,這里是對連接限速,而不是對IP限速。如果一個IP允許兩個并發(fā)連接,那么這個IP就是限速limit_rate×2。

  1. burst=5; 

這相當(dāng)于桶的大小,如果某個請求超過了系統(tǒng)處理速度,會被放入桶中,等待被處理。如果桶滿了,那么抱歉,請求直接返回503,客戶端得到一個服務(wù)器忙的響應(yīng)。如果系統(tǒng)處理請求的速度比較慢,桶里的請求也不能一直待在里面,如果超過一定時間,也是會被直接退回,返回服務(wù)器忙的響應(yīng)。

OpenResty

背影有沒有很熟悉,對這就是那個直呼理解萬歲老羅,2015年老羅在錘子科技T2發(fā)布會上將門票收入捐贈給了 OpenResty,也相信老羅是個有情懷的胖子。

這里我們使用 OpenResty 開源的限流方案,測試案例使用OpenResty1.13.6.1***版本,自帶lua-resty-limit-traffic模塊以及案例 ,實(shí)現(xiàn)起來更為方便。

限制接口總并發(fā)數(shù)/請求數(shù)

秒殺活動中,由于突發(fā)流量暴增,有可能會影響整個系統(tǒng)的穩(wěn)定性從而造成崩潰,這時候我們就要限制秒殺接口的總并發(fā)數(shù)/請求數(shù)。

這里我們采用 lua-resty-limit-traffic中的resty.limit.count模塊實(shí)現(xiàn),由于文章篇幅具體代碼參見源碼openresty/lua/limit_count.lua。

限制接口時間窗請求數(shù)

秒殺場景下,有時候并都是人肉鼠標(biāo),比如12306的搶票軟件,軟件刷票可比人肉鼠標(biāo)快多了。此時我們就要對客戶端單位時間內(nèi)的請求數(shù)進(jìn)行限制,以至于刷票不是那么猖獗。當(dāng)然了道高一尺魔高一丈,搶票軟件總是會有辦法繞開你的防線,從另一方面講也促進(jìn)了技術(shù)的進(jìn)步。

這里我們采用 lua-resty-limit-traffic中的resty.limit.conn模塊實(shí)現(xiàn),具體代碼參見源碼openresty/lua/limit_conn.lua。

平滑限制接口請求數(shù)

之前的限流方式允許突發(fā)流量,也就是說瞬時流量都會被允許。突然流量如果不加以限制會影響整個系統(tǒng)的穩(wěn)定性,因此在秒殺場景中需要對請求×××為平均速率處理,即20r/s。

這里我們采用 lua-resty-limit-traffic 中的resty.limit.req 模塊實(shí)現(xiàn)漏桶限流和令牌桶限流。

其實(shí)漏桶和令牌桶根本的區(qū)別就是,如何處理超過請求速率的請求。漏桶會把請求放入隊(duì)列中去等待均速處理,隊(duì)列滿則拒絕服務(wù);令牌桶在桶容量允許的情況下直接處理這些突發(fā)請求。

漏桶

桶容量大于零,并且是延遲模式。如果桶沒滿,則進(jìn)入請求隊(duì)列以固定速率等待處理,否則請求被拒絕。

令牌桶

桶容量大于零,并且是非延遲模式。如果桶中存在令牌,則允許突發(fā)流量,否則請求被拒絕。

壓測

為了測試以上配置效果,我們采用AB壓測,Linux下執(zhí)行以下命令即可:

  1. # 安裝 
  2. yum -y install httpd-tools 
  3. # 查看ab版本 
  4. ab -v 
  5. # 查看幫助 
  6. ab --help 

測試命令:

  1. ab -n 1000 -c 100 http://127.0.0.1/ 

測試結(jié)果:

  1. Server Software:        openresty/1.13.6.1  #服務(wù)器軟件 
  2. Server Hostname:        127.0.0.1     #IP 
  3. Server Port:            80            #請求端口號 
  4.  
  5. Document Path:          /             #文件路徑 
  6. Document Length:        12 bytes      #頁面字節(jié)數(shù) 
  7.  
  8. Concurrency Level:      100           #請求的并發(fā)數(shù) 
  9. Time taken for tests:   4.999 seconds #總訪問時間 
  10. Complete requests:      1000          #總請求樹 
  11. Failed requests:        0             #請求失敗數(shù)量 
  12. Write errors:           0 
  13. Total transferred:      140000 bytes  #請求總數(shù)據(jù)大小 
  14. HTML transferred:       12000 bytes   #html頁面實(shí)際總字節(jié)數(shù) 
  15. Requests per second:    200.06 [#/sec] (mean) #每秒多少請求,這個是非常重要的參數(shù)數(shù)值,服務(wù)器的吞吐量 
  16. Time per request:       499.857 [ms] (mean) #用戶平均請求等待時間  
  17. Time per request:       4.999 [ms] (mean, across all concurrent requests)  # 服務(wù)器平均處理時間,也就是服務(wù)器吞吐量的倒數(shù)  
  18. Transfer rate:          27.35 [Kbytes/sec] received #每秒獲取的數(shù)據(jù)長度 
  19.  
  20. Connection Times (ms) 
  21.               min  mean[+/-sd] median   max 
  22. Connect:        0    0   0.8      0       4 
  23. Processing:     5  474  89.1    500     501 
  24. Waiting:        2  474  89.2    500     501 
  25. Total:          9  475  88.4    500     501 
  26.  
  27. Percentage of the requests served within a certain time (ms) 
  28.   50%    500 
  29.   66%    500 
  30.   75%    500 
  31.   80%    500 
  32.   90%    501 
  33.   95%    501 
  34.   98%    501 
  35.   99%    501 
  36.  100%    501 (longest request) 

總結(jié)

以上限流方案,只是針對此次秒殺案例做一個簡單的小結(jié),大家也不要刻意區(qū)分那種方案的好壞,只要適合業(yè)務(wù)場景就是***的。

參考

https://github.com/openresty/lua-resty-limit-traffic

https://blog.52itstyle.com/archives/1764/

https://blog.52itstyle.com/archives/775/

分享是快樂的,也見證了個人成長歷程,文章大多都是工作經(jīng)驗(yàn)總結(jié)以及平時學(xué)習(xí)積累,基于自身認(rèn)知不足之處在所難免,也請大家指正,共同進(jìn)步。

本文版權(quán)歸作者所有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出, 如有問題, 可郵件(345849402@qq.com)咨詢。

責(zé)任編輯:武曉燕 來源: 51CTO博客
相關(guān)推薦

2018-06-11 11:12:09

秒殺限流分布式

2018-07-19 14:53:23

秒殺websocket異步

2016-11-28 08:58:43

系統(tǒng)限流算法

2016-11-28 08:58:43

系統(tǒng)限流

2023-02-10 00:04:53

2017-12-20 16:15:30

分布式系統(tǒng)架構(gòu)

2024-07-05 08:26:54

2018-01-23 15:55:23

分布式系統(tǒng)架構(gòu)

2017-09-01 05:35:58

分布式計(jì)算存儲

2022-06-13 10:01:36

Apollo攜程框架

2025-03-06 11:30:15

2023-04-06 08:52:54

Sentinel分布式系統(tǒng)

2022-05-11 13:55:18

高可用性分布式彈性

2023-01-06 16:42:28

2023-05-29 14:07:00

Zuul網(wǎng)關(guān)系統(tǒng)

2023-05-12 08:23:03

分布式系統(tǒng)網(wǎng)絡(luò)

2022-01-17 09:18:28

JMeter分布式壓測

2022-03-07 08:14:27

并發(fā)分布式

2023-05-12 11:52:21

緩存場景性能

2021-02-01 09:35:53

關(guān)系型數(shù)據(jù)庫模型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號