在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

單機(jī)如何優(yōu)化高性能秒殺系統(tǒng)

馬哥Linux運(yùn)維 ? 來(lái)源:馬哥Linux運(yùn)維 ? 作者:馬哥Linux運(yùn)維 ? 2022-08-26 10:43 ? 次閱讀

每到節(jié)假日期間,一二線城市返鄉(xiāng)、外出游玩的人們幾乎都面臨著一個(gè)問(wèn)題:搶火車(chē)票!雖然現(xiàn)在大多數(shù)情況下都能訂到票,但是放票瞬間即無(wú)票的場(chǎng)景,相信大家都深有體會(huì)。

尤其是春節(jié)期間,大家不僅使用12306,還會(huì)考慮“智行”和其他的搶票軟件,全國(guó)上下幾億人在這段時(shí)間都在搶票。

“12306服務(wù)”承受著這個(gè)世界上任何秒殺系統(tǒng)都無(wú)法超越的QPS,上百萬(wàn)的并發(fā)再正常不過(guò)了!筆者專門(mén)研究了一下“12306”的服務(wù)端架構(gòu),學(xué)習(xí)到了其系統(tǒng)設(shè)計(jì)上很多亮點(diǎn),在這里和大家分享一下并模擬一個(gè)例子:如何在100萬(wàn)人同時(shí)搶1萬(wàn)張火車(chē)票時(shí),系統(tǒng)提供正常、穩(wěn)定的服務(wù)。

1. 大型高并發(fā)系統(tǒng)架構(gòu)

高并發(fā)的系統(tǒng)架構(gòu)都會(huì)采用分布式集群部署,服務(wù)上層有著層層負(fù)載均衡,并提供各種容災(zāi)手段(雙火機(jī)房、節(jié)點(diǎn)容錯(cuò)、服務(wù)器災(zāi)備等)保證系統(tǒng)的高可用,流量也會(huì)根據(jù)不同的負(fù)載能力和配置策略均衡到不同的服務(wù)器上。下邊是一個(gè)簡(jiǎn)單的示意圖:

49a8f91c-247b-11ed-ba43-dac502259ad0.jpg

1.1 負(fù)載均衡簡(jiǎn)介

上圖中描述了用戶請(qǐng)求到服務(wù)器經(jīng)歷了三層的負(fù)載均衡,下邊分別簡(jiǎn)單介紹一下這三種負(fù)載均衡:

OSPF(開(kāi)放式最短鏈路優(yōu)先)是一個(gè)內(nèi)部網(wǎng)關(guān)協(xié)議(Interior Gateway Protocol,簡(jiǎn)稱IGP)。OSPF通過(guò)路由器之間通告網(wǎng)絡(luò)接口的狀態(tài)來(lái)建立鏈路狀態(tài)數(shù)據(jù)庫(kù),生成最短路徑樹(shù),OSPF會(huì)自動(dòng)計(jì)算路由接口上的Cost值,但也可以通過(guò)手工指定該接口的Cost值,手工指定的優(yōu)先于自動(dòng)計(jì)算的值。OSPF計(jì)算的Cost,同樣是和接口帶寬成反比,帶寬越高,Cost值越小。到達(dá)目標(biāo)相同Cost值的路徑,可以執(zhí)行負(fù)載均衡,最多6條鏈路同時(shí)執(zhí)行負(fù)載均衡。

LVS (Linux VirtualServer),它是一種集群(Cluster)技術(shù),采用IP負(fù)載均衡技術(shù)和基于內(nèi)容請(qǐng)求分發(fā)技術(shù)。調(diào)度器具有很好的吞吐率,將請(qǐng)求均衡地轉(zhuǎn)移到不同的服務(wù)器上執(zhí)行,且調(diào)度器自動(dòng)屏蔽掉服務(wù)器的故障,從而將一組服務(wù)器構(gòu)成一個(gè)高性能的、高可用的虛擬服務(wù)器。

Nginx想必大家都很熟悉了,是一款非常高性能的http代理/反向代理服務(wù)器,服務(wù)開(kāi)發(fā)中也經(jīng)常使用它來(lái)做負(fù)載均衡。Nginx實(shí)現(xiàn)負(fù)載均衡的方式主要有三種:輪詢、加權(quán)輪詢、ip hash輪詢,下面我們就針對(duì)Nginx的加權(quán)輪詢做專門(mén)的配置和測(cè)試

1.2 Nginx加權(quán)輪詢的演示

Nginx實(shí)現(xiàn)負(fù)載均衡通過(guò)upstream模塊實(shí)現(xiàn),其中加權(quán)輪詢的配置是可以給相關(guān)的服務(wù)加上一個(gè)權(quán)重值,配置的時(shí)候可能根據(jù)服務(wù)器的性能、負(fù)載能力設(shè)置相應(yīng)的負(fù)載。

下面是一個(gè)加權(quán)輪詢負(fù)載的配置,我將在本地的監(jiān)聽(tīng)3001-3004端口,分別配置1,2,3,4的權(quán)重:


#配置負(fù)載均衡    upstream load_rule {       server 127.0.0.1:3001 weight=1;       server 127.0.0.1:3002 weight=2;       server 127.0.0.1:3003 weight=3;       server 127.0.0.1:3004 weight=4;    }    ...    server {    listen       80;    server_name  load_balance.com www.load_balance.com;    location / {       proxy_pass http://load_rule;    }}
我在本地/etc/hosts目錄下配置了 www.load_balance.com的虛擬域名地址,接下來(lái)使用Go語(yǔ)言開(kāi)啟四個(gè)http端口監(jiān)聽(tīng)服務(wù),下面是監(jiān)聽(tīng)在3001端口的Go程序,其他幾個(gè)只需要修改端口即可:

package mainimport (  "net/http"  "os"  "strings")func main() {  http.HandleFunc("/buy/ticket", handleReq)  http.ListenAndServe(":3001", nil)}//處理請(qǐng)求函數(shù),根據(jù)請(qǐng)求將響應(yīng)結(jié)果信息寫(xiě)入日志func handleReq(w http.ResponseWriter, r *http.Request) {  failedMsg :=  "handle in port:"  writeLog(failedMsg, "./stat.log")}//寫(xiě)入日志func writeLog(msg string, logPath string) {  fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)  defer fd.Close()  content := strings.Join([]string{msg, "
"}, "3001")  buf := []byte(content)  fd.Write(buf)}
我將請(qǐng)求的端口日志信息寫(xiě)到了./stat.log文件當(dāng)中,然后使用ab壓測(cè)工具做壓測(cè):

ab -n 1000 -c 100 http://www.load_balance.com/buy/ticket
統(tǒng)計(jì)日志中的結(jié)果,3001-3004端口分別得到了100、200、300、400的請(qǐng)求量,這和我在nginx中配置的權(quán)重占比很好的吻合在了一起,并且負(fù)載后的流量非常的均勻、隨機(jī)。具體的實(shí)現(xiàn)大家可以參考nginx的upsteam模塊實(shí)現(xiàn)源碼,這里推薦一篇文章:Nginx 中 upstream 機(jī)制的負(fù)載均衡

https://www.kancloud.cn/digest/understandingnginx/202607

2.秒殺搶購(gòu)系統(tǒng)選型

回到我們最初提到的問(wèn)題中來(lái):火車(chē)票秒殺系統(tǒng)如何在高并發(fā)情況下提供正常、穩(wěn)定的服務(wù)呢?

從上面的介紹我們知道用戶秒殺流量通過(guò)層層的負(fù)載均衡,均勻到了不同的服務(wù)器上,即使如此,集群中的單機(jī)所承受的QPS也是非常高的。如何將單機(jī)性能優(yōu)化到極致呢?

要解決這個(gè)問(wèn)題,我們就要想明白一件事:通常訂票系統(tǒng)要處理生成訂單、減扣庫(kù)存、用戶支付這三個(gè)基本的階段,我們系統(tǒng)要做的事情是要保證火車(chē)票訂單不超賣(mài)、不少賣(mài),每張售賣(mài)的車(chē)票都必須支付才有效,還要保證系統(tǒng)承受極高的并發(fā)。

這三個(gè)階段的先后順序改怎么分配才更加合理呢?我們來(lái)分析一下:

2.1 下單減庫(kù)存

當(dāng)用戶并發(fā)請(qǐng)求到達(dá)服務(wù)端時(shí),首先創(chuàng)建訂單,然后扣除庫(kù)存,等待用戶支付。

這種順序是我們一般人首先會(huì)想到的解決方案,這種情況下也能保證訂單不會(huì)超賣(mài),因?yàn)閯?chuàng)建訂單之后就會(huì)減庫(kù)存,這是一個(gè)原子操作。

但是這樣也會(huì)產(chǎn)生一些問(wèn)題

第一就是在極限并發(fā)情況下,任何一個(gè)內(nèi)存操作的細(xì)節(jié)都至關(guān)影響性能,尤其像創(chuàng)建訂單這種邏輯,一般都需要存儲(chǔ)到磁盤(pán)數(shù)據(jù)庫(kù)的,對(duì)數(shù)據(jù)庫(kù)的壓力是可想而知的;

第二是如果用戶存在惡意下單的情況,只下單不支付這樣庫(kù)存就會(huì)變少,會(huì)少賣(mài)很多訂單,雖然服務(wù)端可以限制IP和用戶的購(gòu)買(mǎi)訂單數(shù)量,這也不算是一個(gè)好方法。

49bb0e2c-247b-11ed-ba43-dac502259ad0.jpg

2.2 支付減庫(kù)存

如果等待用戶支付了訂單在減庫(kù)存,第一感覺(jué)就是不會(huì)少賣(mài)。但是這是并發(fā)架構(gòu)的大忌,因?yàn)樵跇O限并發(fā)情況下,用戶可能會(huì)創(chuàng)建很多訂單,當(dāng)庫(kù)存減為零的時(shí)候很多用戶發(fā)現(xiàn)搶到的訂單支付不了了,這也就是所謂的“超賣(mài)”。也不能避免并發(fā)操作數(shù)據(jù)庫(kù)磁盤(pán)IO

49c69184-247b-11ed-ba43-dac502259ad0.jpg

2.3 預(yù)扣庫(kù)存

從上邊兩種方案的考慮,我們可以得出結(jié)論:只要?jiǎng)?chuàng)建訂單,就要頻繁操作數(shù)據(jù)庫(kù)IO。那么有沒(méi)有一種不需要直接操作數(shù)據(jù)庫(kù)IO的方案呢,這就是預(yù)扣庫(kù)存。先扣除了庫(kù)存,保證不超賣(mài),然后異步生成用戶訂單,這樣響應(yīng)給用戶的速度就會(huì)快很多;

那么怎么保證不少賣(mài)呢?用戶拿到了訂單,不支付怎么辦?我們都知道現(xiàn)在訂單都有有效期,比如說(shuō)用戶五分鐘內(nèi)不支付,訂單就失效了,訂單一旦失效,就會(huì)加入新的庫(kù)存,這也是現(xiàn)在很多網(wǎng)上零售企業(yè)保證商品不少賣(mài)采用的方案。

訂單的生成是異步的,一般都會(huì)放到MQ、kafka這樣的即時(shí)消費(fèi)隊(duì)列中處理,訂單量比較少的情況下,生成訂單非常快,用戶幾乎不用排隊(duì)。

49cfd0be-247b-11ed-ba43-dac502259ad0.jpg

3. 扣庫(kù)存的藝術(shù)

從上面的分析可知,顯然預(yù)扣庫(kù)存的方案最合理。我們進(jìn)一步分析扣庫(kù)存的細(xì)節(jié),這里還有很大的優(yōu)化空間,庫(kù)存存在哪里?怎樣保證高并發(fā)下,正確的扣庫(kù)存,還能快速的響應(yīng)用戶請(qǐng)求?

在單機(jī)低并發(fā)情況下,我們實(shí)現(xiàn)扣庫(kù)存通常是這樣的: 49db31b6-247b-11ed-ba43-dac502259ad0.jpg ? 為了保證扣庫(kù)存和生成訂單的原子性,需要采用事務(wù)處理,然后取庫(kù)存判斷、減庫(kù)存,最后提交事務(wù),整個(gè)流程有很多IO,對(duì)數(shù)據(jù)庫(kù)的操作又是阻塞的。這種方式根本不適合高并發(fā)的秒殺系統(tǒng)。 ? 接下來(lái)我們對(duì)單機(jī)扣庫(kù)存的方案做優(yōu)化:本地扣庫(kù)存。我們把一定的庫(kù)存量分配到本地機(jī)器,直接在內(nèi)存中減庫(kù)存,然后按照之前的邏輯異步創(chuàng)建訂單。改進(jìn)過(guò)之后的單機(jī)系統(tǒng)是這樣的: ? 49e6b464-247b-11ed-ba43-dac502259ad0.jpg ? 這樣就避免了對(duì)數(shù)據(jù)庫(kù)頻繁的IO操作,只在內(nèi)存中做運(yùn)算,極大的提高了單機(jī)抗并發(fā)的能力。但是百萬(wàn)的用戶請(qǐng)求量單機(jī)是無(wú)論如何也抗不住的,雖然nginx處理網(wǎng)絡(luò)請(qǐng)求使用epoll模型,c10k的問(wèn)題在業(yè)界早已得到了解決。 ? 但是linux系統(tǒng)下,一切資源皆文件,網(wǎng)絡(luò)請(qǐng)求也是這樣,大量的文件描述符會(huì)使操作系統(tǒng)瞬間失去響應(yīng)。上面我們提到了nginx的加權(quán)均衡策略,我們不妨假設(shè)將100W的用戶請(qǐng)求量平均均衡到100臺(tái)服務(wù)器上,這樣單機(jī)所承受的并發(fā)量就小了很多。

然后我們每臺(tái)機(jī)器本地庫(kù)存100張火車(chē)票,100臺(tái)服務(wù)器上的總庫(kù)存還是1萬(wàn),這樣保證了庫(kù)存訂單不超賣(mài),下面是我們描述的集群架構(gòu): 49f300a2-247b-11ed-ba43-dac502259ad0.jpg ? 問(wèn)題接踵而至,在高并發(fā)情況下,現(xiàn)在我們還無(wú)法保證系統(tǒng)的高可用,假如這100臺(tái)服務(wù)器上有兩三臺(tái)機(jī)器因?yàn)榭覆蛔〔l(fā)的流量或者其他的原因宕機(jī)了。那么這些服務(wù)器上的訂單就賣(mài)不出去了,這就造成了訂單的少賣(mài)。 ? 要解決這個(gè)問(wèn)題,我們需要對(duì)總訂單量做統(tǒng)一的管理,這就是接下來(lái)的容錯(cuò)方案。服務(wù)器不僅要在本地減庫(kù)存,另外要遠(yuǎn)程統(tǒng)一減庫(kù)存。有了遠(yuǎn)程統(tǒng)一減庫(kù)存的操作,我們就可以根據(jù)機(jī)器負(fù)載情況,為每臺(tái)機(jī)器分配一些多余的“buffer庫(kù)存”用來(lái)防止機(jī)器中有機(jī)器宕機(jī)的情況。我們結(jié)合下面架構(gòu)圖具體分析一下: ? 49ff1388-247b-11ed-ba43-dac502259ad0.jpg ? 我們采用Redis存儲(chǔ)統(tǒng)一庫(kù)存,因?yàn)镽edis的性能非常高,號(hào)稱單機(jī)QPS能抗10W的并發(fā)。在本地減庫(kù)存以后,如果本地有訂單,我們?cè)偃フ?qǐng)求redis遠(yuǎn)程減庫(kù)存,本地減庫(kù)存和遠(yuǎn)程減庫(kù)存都成功了,才返回給用戶搶票成功的提示,這樣也能有效的保證訂單不會(huì)超賣(mài)。 ? 當(dāng)機(jī)器中有機(jī)器宕機(jī)時(shí),因?yàn)槊總€(gè)機(jī)器上有預(yù)留的buffer余票,所以宕機(jī)機(jī)器上的余票依然能夠在其他機(jī)器上得到彌補(bǔ),保證了不少賣(mài)。 ? buffer余票設(shè)置多少合適呢,理論上buffer設(shè)置的越多,系統(tǒng)容忍宕機(jī)的機(jī)器數(shù)量就越多,但是buffer設(shè)置的太大也會(huì)對(duì)redis造成一定的影響。 ? 雖然redis內(nèi)存數(shù)據(jù)庫(kù)抗并發(fā)能力非常高,請(qǐng)求依然會(huì)走一次網(wǎng)絡(luò)IO,其實(shí)搶票過(guò)程中對(duì)redis的請(qǐng)求次數(shù)是本地庫(kù)存和buffer庫(kù)存的總量,因?yàn)楫?dāng)本地庫(kù)存不足時(shí),系統(tǒng)直接返回用戶“已售罄”的信息提示,就不會(huì)再走統(tǒng)一扣庫(kù)存的邏輯,這在一定程度上也避免了巨大的網(wǎng)絡(luò)請(qǐng)求量把redis壓跨,所以buffer值設(shè)置多少,需要架構(gòu)師對(duì)系統(tǒng)的負(fù)載能力做認(rèn)真的考量。 ?

4. 代碼演示

Go語(yǔ)言原生為并發(fā)設(shè)計(jì),我采用go語(yǔ)言給大家演示一下單機(jī)搶票的具體流程。

4.1 初始化工作

go包中的init函數(shù)先于main函數(shù)執(zhí)行,在這個(gè)階段主要做一些準(zhǔn)備性工作。我們系統(tǒng)需要做的準(zhǔn)備工作有:初始化本地庫(kù)存、初始化遠(yuǎn)程redis存儲(chǔ)統(tǒng)一庫(kù)存的hash鍵值、初始化redis連接池;

另外還需要初始化一個(gè)大小為1的int類(lèi)型chan,目的是實(shí)現(xiàn)分布式鎖的功能,也可以直接使用讀寫(xiě)鎖或者使用redis等其他的方式避免資源競(jìng)爭(zhēng),但使用channel更加高效,這就是go語(yǔ)言的哲學(xué):不要通過(guò)共享內(nèi)存來(lái)通信,而要通過(guò)通信來(lái)共享內(nèi)存。

redis庫(kù)使用的是redigo,下面是代碼實(shí)現(xiàn):


...//localSpike包結(jié)構(gòu)體定義package localSpiketype LocalSpike struct {  LocalInStock     int64  LocalSalesVolume int64}...//remoteSpike對(duì)hash結(jié)構(gòu)的定義和redis連接池package remoteSpike//遠(yuǎn)程訂單存儲(chǔ)健值type RemoteSpikeKeys struct {  SpikeOrderHashKey string  //redis中秒殺訂單hash結(jié)構(gòu)key  TotalInventoryKey string  //hash結(jié)構(gòu)中總訂單庫(kù)存key  QuantityOfOrderKey string  //hash結(jié)構(gòu)中已有訂單數(shù)量key}//初始化redis連接池func NewPool() *redis.Pool {  return &redis.Pool{    MaxIdle:   10000,    MaxActive: 12000, // max number of connections    Dial: func() (redis.Conn, error) {      c, err := redis.Dial("tcp", ":6379")      if err != nil {        panic(err.Error())      }      return c, err    },  }}...func init() {  localSpike = localSpike2.LocalSpike{    LocalInStock:     150,    LocalSalesVolume: 0,  }  remoteSpike = remoteSpike2.RemoteSpikeKeys{    SpikeOrderHashKey:  "ticket_hash_key",    TotalInventoryKey:  "ticket_total_nums",    QuantityOfOrderKey: "ticket_sold_nums",  }  redisPool = remoteSpike2.NewPool()  done = make(chan int, 1)  done <- 1}

4.2 本地扣庫(kù)存和統(tǒng)一扣庫(kù)存

本地扣庫(kù)存邏輯非常簡(jiǎn)單,用戶請(qǐng)求過(guò)來(lái),添加銷(xiāo)量,然后對(duì)比銷(xiāo)量是否大于本地庫(kù)存,返回bool值:


package localSpike//本地扣庫(kù)存,返回bool值func (spike *LocalSpike) LocalDeductionStock() bool{  spike.LocalSalesVolume = spike.LocalSalesVolume + 1  return spike.LocalSalesVolume < spike.LocalInStock}
注意這里對(duì)共享數(shù)據(jù)LocalSalesVolume的操作是要使用鎖來(lái)實(shí)現(xiàn)的,但是因?yàn)楸镜乜蹘?kù)存和統(tǒng)一扣庫(kù)存是一個(gè)原子性操作,所以在最上層使用channel來(lái)實(shí)現(xiàn),這塊后邊會(huì)講。 統(tǒng)一扣庫(kù)存操作redis,因?yàn)閞edis是單線程的,而我們要實(shí)現(xiàn)從中取數(shù)據(jù),寫(xiě)數(shù)據(jù)并計(jì)算一些列步驟,我們要配合lua腳本打包命令,保證操作的原子性:

package remoteSpike......const LuaScript = `        local ticket_key = KEYS[1]        local ticket_total_key = ARGV[1]        local ticket_sold_key = ARGV[2]        local ticket_total_nums = tonumber(redis.call('HGET', ticket_key, ticket_total_key))        local ticket_sold_nums = tonumber(redis.call('HGET', ticket_key, ticket_sold_key))    -- 查看是否還有余票,增加訂單數(shù)量,返回結(jié)果值       if(ticket_total_nums >= ticket_sold_nums) then            return redis.call('HINCRBY', ticket_key, ticket_sold_key, 1)        end        return 0`//遠(yuǎn)端統(tǒng)一扣庫(kù)存func (RemoteSpikeKeys *RemoteSpikeKeys) RemoteDeductionStock(conn redis.Conn) bool {  lua := redis.NewScript(1, LuaScript)  result, err := redis.Int(lua.Do(conn, RemoteSpikeKeys.SpikeOrderHashKey, RemoteSpikeKeys.TotalInventoryKey, RemoteSpikeKeys.QuantityOfOrderKey))  if err != nil {    return false  }  return result != 0}
我們使用hash結(jié)構(gòu)存儲(chǔ)總庫(kù)存和總銷(xiāo)量的信息,用戶請(qǐng)求過(guò)來(lái)時(shí),判斷總銷(xiāo)量是否大于庫(kù)存,然后返回相關(guān)的bool值。在啟動(dòng)服務(wù)之前,我們需要初始化redis的初始庫(kù)存信息:

 hmset ticket_hash_key "ticket_total_nums" 10000 "ticket_sold_nums" 0

4.3 響應(yīng)用戶信息

我們開(kāi)啟一個(gè)http服務(wù),監(jiān)聽(tīng)在一個(gè)端口上:


package main...func main() {  http.HandleFunc("/buy/ticket", handleReq)  http.ListenAndServe(":3005", nil)}
上面我們做完了所有的初始化工作,接下來(lái)handleReq的邏輯非常清晰,判斷是否搶票成功,返回給用戶信息就可以了。

package main//處理請(qǐng)求函數(shù),根據(jù)請(qǐng)求將響應(yīng)結(jié)果信息寫(xiě)入日志func handleReq(w http.ResponseWriter, r *http.Request) {  redisConn := redisPool.Get()  LogMsg := ""  <-done  //全局讀寫(xiě)鎖  if localSpike.LocalDeductionStock() && remoteSpike.RemoteDeductionStock(redisConn) {    util.RespJson(w, 1,  "搶票成功", nil)    LogMsg = LogMsg + "result:1,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)  } else {    util.RespJson(w, -1, "已售罄", nil)    LogMsg = LogMsg + "result:0,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)  }  done <- 1
  //將搶票狀態(tài)寫(xiě)入到log中  writeLog(LogMsg, "./stat.log")}func writeLog(msg string, logPath string) {  fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)  defer fd.Close()  content := strings.Join([]string{msg, "
"}, "")  buf := []byte(content)  fd.Write(buf)}
前邊提到我們扣庫(kù)存時(shí)要考慮競(jìng)態(tài)條件,我們這里是使用channel避免并發(fā)的讀寫(xiě),保證了請(qǐng)求的高效順序執(zhí)行。我們將接口的返回信息寫(xiě)入到了./stat.log文件方便做壓測(cè)統(tǒng)計(jì)。

4.4 單機(jī)服務(wù)壓測(cè)

開(kāi)啟服務(wù),我們使用ab壓測(cè)工具進(jìn)行測(cè)試:


ab -n 10000 -c 100 http://127.0.0.1:3005/buy/ticket
下面是我本地低配mac的壓測(cè)信息

This is ApacheBench, Version 2.3 <$Revision: 1826891 $>Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/Licensed to The Apache Software Foundation, http://www.apache.org/Benchmarking 127.0.0.1 (be patient)Completed 1000 requestsCompleted 2000 requestsCompleted 3000 requestsCompleted 4000 requestsCompleted 5000 requestsCompleted 6000 requestsCompleted 7000 requestsCompleted 8000 requestsCompleted 9000 requestsCompleted 10000 requestsFinished 10000 requestsServer Software:Server Hostname:        127.0.0.1Server Port:            3005Document Path:          /buy/ticketDocument Length:        29 bytesConcurrency Level:      100Time taken for tests:   2.339 secondsComplete requests:      10000Failed requests:        0Total transferred:      1370000 bytesHTML transferred:       290000 bytesRequests per second:    4275.96 [#/sec] (mean)Time per request:       23.387 [ms] (mean)Time per request:       0.234 [ms] (mean, across all concurrent requests)Transfer rate:          572.08 [Kbytes/sec] receivedConnection Times (ms)              min  mean[+/-sd] median   maxConnect:        0    8  14.7      6     223Processing:     2   15  17.6     11     232Waiting:        1   11  13.5      8     225Total:          7   23  22.8     18     239Percentage of the requests served within a certain time (ms)  50%     18  66%     24  75%     26  80%     28  90%     33  95%     39  98%     45  99%     54 100%    239 (longest request)
根據(jù)指標(biāo)顯示,我單機(jī)每秒就能處理4000+的請(qǐng)求,正常服務(wù)器都是多核配置,處理1W+的請(qǐng)求根本沒(méi)有問(wèn)題。而且查看日志發(fā)現(xiàn)整個(gè)服務(wù)過(guò)程中,請(qǐng)求都很正常,流量均勻,redis也很正常:


//stat.log...result:1,localSales:145result:1,localSales:146result:1,localSales:147result:1,localSales:148result:1,localSales:149result:1,localSales:150result:0,localSales:151result:0,localSales:152result:0,localSales:153result:0,localSales:154result:0,localSales:156...

5.總結(jié)回顧

總體來(lái)說(shuō),秒殺系統(tǒng)是非常復(fù)雜的。我們這里只是簡(jiǎn)單介紹模擬了一下單機(jī)如何優(yōu)化到高性能,集群如何避免單點(diǎn)故障,保證訂單不超賣(mài)、不少賣(mài)的一些策略,完整的訂單系統(tǒng)還有訂單進(jìn)度的查看,每臺(tái)服務(wù)器上都有一個(gè)任務(wù),定時(shí)的從總庫(kù)存同步余票和庫(kù)存信息展示給用戶,還有用戶在訂單有效期內(nèi)不支付,釋放訂單,補(bǔ)充到庫(kù)存等等。 我們實(shí)現(xiàn)了高并發(fā)搶票的核心邏輯,可以說(shuō)系統(tǒng)設(shè)計(jì)的非常的巧妙,巧妙的避開(kāi)了對(duì)DB數(shù)據(jù)庫(kù)IO的操作,對(duì)Redis網(wǎng)絡(luò)IO的高并發(fā)請(qǐng)求,幾乎所有的計(jì)算都是在內(nèi)存中完成的,而且有效的保證了不超賣(mài)、不少賣(mài),還能夠容忍部分機(jī)器的宕機(jī)。我覺(jué)得其中有兩點(diǎn)特別值得學(xué)習(xí)總結(jié):

負(fù)載均衡,分而治之。通過(guò)負(fù)載均衡,將不同的流量劃分到不同的機(jī)器上,每臺(tái)機(jī)器處理好自己的請(qǐng)求,將自己的性能發(fā)揮到極致,這樣系統(tǒng)的整體也就能承受極高的并發(fā)了,就像工作的的一個(gè)團(tuán)隊(duì),每個(gè)人都將自己的價(jià)值發(fā)揮到了極致,團(tuán)隊(duì)成長(zhǎng)自然是很大的。

合理的使用并發(fā)和異步。自epoll網(wǎng)絡(luò)架構(gòu)模型解決了c10k問(wèn)題以來(lái),異步越來(lái)被服務(wù)端開(kāi)發(fā)人員所接受,能夠用異步來(lái)做的工作,就用異步來(lái)做,在功能拆解上能達(dá)到意想不到的效果,這點(diǎn)在nginx、node.js、redis上都能體現(xiàn),他們處理網(wǎng)絡(luò)請(qǐng)求使用的epoll模型,用實(shí)踐告訴了我們單線程依然可以發(fā)揮強(qiáng)大的威力。服務(wù)器已經(jīng)進(jìn)入了多核時(shí)代,go語(yǔ)言這種天生為并發(fā)而生的語(yǔ)言,完美的發(fā)揮了服務(wù)器多核優(yōu)勢(shì),很多可以并發(fā)處理的任務(wù)都可以使用并發(fā)來(lái)解決,比如go處理http請(qǐng)求時(shí)每個(gè)請(qǐng)求都會(huì)在一個(gè)goroutine中執(zhí)行,總之:怎樣合理的壓榨CPU,讓其發(fā)揮出應(yīng)有的價(jià)值,是我們一直需要探索學(xué)習(xí)的方向。

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    12

    文章

    9248

    瀏覽量

    85737
  • 軟件
    +關(guān)注

    關(guān)注

    69

    文章

    4981

    瀏覽量

    87798
  • 單機(jī)
    +關(guān)注

    關(guān)注

    0

    文章

    16

    瀏覽量

    6295

原文標(biāo)題:秒殺系統(tǒng)的架構(gòu)(Golang 實(shí)現(xiàn))

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    FPGA_ASIC高性能數(shù)字系統(tǒng)設(shè)計(jì)

    FPGA/ASIC高性能數(shù)字系統(tǒng)設(shè)計(jì) 狀態(tài)機(jī)與數(shù)據(jù)路徑 1 有限狀態(tài)機(jī) 1.1 基本概念 1.2 狀態(tài)機(jī)分類(lèi) 1.3 狀態(tài)機(jī)描述方法 1.4 狀態(tài)機(jī)的編碼風(fēng)格 1.5 可綜合的fsm編碼 1.6
    發(fā)表于 03-02 09:35

    HBase性能優(yōu)化方法總結(jié)

    對(duì)于寫(xiě)密集型提高性能需盡量減少刷寫(xiě)、合并和拆分的次數(shù),以減少I(mǎi)O壓力,提高系統(tǒng)性能。除了以上方法可以提高HBase性能之外,還可以采用以下方法:1. JVM垃圾回收優(yōu)化;2. 本地me
    發(fā)表于 04-20 17:16

    優(yōu)化了功率級(jí)的20位1MSPS高性能數(shù)據(jù)采集系統(tǒng)

    描述該適用于高性能數(shù)據(jù)采集 (DAQ) 系統(tǒng)的參考設(shè)計(jì)優(yōu)化了功率級(jí),以降低功耗并最大程度地減小開(kāi)關(guān)穩(wěn)壓器的 EMI 影響(通過(guò)使用 LMS3635-Q1 降壓轉(zhuǎn)換器)。與 LM53635 降壓轉(zhuǎn)換器
    發(fā)表于 12-05 13:56

    高性能DAQ系統(tǒng)的ADC電壓基準(zhǔn)兩級(jí)緩沖器優(yōu)化設(shè)計(jì)

    描述適用于高性能 DAQ 系統(tǒng)的 TIDA-01055 參考設(shè)計(jì)優(yōu)化了 ADC 基準(zhǔn)緩沖器,以提高 SNR 性能并降低功耗(使用 TI OPA837 高速運(yùn)算放大器)。該器件用于復(fù)合緩
    發(fā)表于 12-07 11:51

    C波段超高性能微波天線的饋源系統(tǒng)的設(shè)計(jì)方法介紹

      本文介紹了用于微波接力天線饋源中的C波段超高性能饋源系統(tǒng)的設(shè)計(jì)方法,利用高頻結(jié)構(gòu)仿真軟件對(duì)其進(jìn)行了優(yōu)化設(shè)計(jì)。對(duì)一些重要的和不易調(diào)整的尺寸用加偏差的方法來(lái)確定加工精度。計(jì)算結(jié)果與實(shí)測(cè)結(jié)果吻合的較好
    發(fā)表于 06-11 07:14

    高性能ADC助力ATE系統(tǒng)提升

    高性能 ADC 使 ATE 系統(tǒng)準(zhǔn)確達(dá)到全新水平
    發(fā)表于 07-31 14:22

    如何在電源轉(zhuǎn)換應(yīng)用中實(shí)現(xiàn)高性能、成本優(yōu)化型實(shí)時(shí)控制設(shè)計(jì)?

    如何在電源轉(zhuǎn)換應(yīng)用中實(shí)現(xiàn)高性能、成本優(yōu)化型實(shí)時(shí)控制設(shè)計(jì)
    發(fā)表于 03-16 07:56

    求大佬分享一種優(yōu)化高性能高可靠性的嵌入式大屏幕LED顯示系統(tǒng)

    本文提出一種優(yōu)化高性能高可靠性的嵌入式大屏幕LED顯示系統(tǒng),只需要用1片F(xiàn)PGA和2片SRAM就可以實(shí)現(xiàn)大屏幕LED顯示的驅(qū)動(dòng)和內(nèi)容更換,可以說(shuō)其性能已經(jīng)大有改善。本設(shè)計(jì)可以應(yīng)對(duì)多種
    發(fā)表于 06-04 06:02

    電源系統(tǒng)優(yōu)化系列——如何分析高性能信號(hào)鏈中電源紋波

    在電源系統(tǒng)優(yōu)化"系列文章的 第1部分 ,我們介紹了如何量化電源噪聲靈敏度,以及如何將這些量值與信號(hào)鏈中產(chǎn)生的實(shí)際影響聯(lián)系起來(lái)。有人問(wèn)到:高性能模擬信號(hào)處理器件要實(shí)現(xiàn)出色性能,真正的噪聲
    發(fā)表于 07-03 07:00

    AutoKernel高性能算子自動(dòng)優(yōu)化工具

    主要由資深HPC工程師(高性能計(jì)算優(yōu)化工程師)進(jìn)行開(kāi)發(fā),為了加快開(kāi)發(fā)進(jìn)程,縮短深度學(xué)習(xí)應(yīng)用落地周期,自動(dòng)化算子優(yōu)化是一個(gè)趨勢(shì)。AutoKernel是由OPEN AI LAB提出的高性能
    發(fā)表于 12-14 06:18

    ARM性能庫(kù)入門(mén)(單機(jī)版)

    ARM性能庫(kù)為ARM處理器上的高性能計(jì)算應(yīng)用程序提供優(yōu)化的標(biāo)準(zhǔn)核心數(shù)學(xué)庫(kù)。 可通過(guò)Fortran和C接口訪問(wèn)的庫(kù)例程包括: ·BLAS-基本線性代數(shù)子程序(包括XBLAS、擴(kuò)展精度BLAS
    發(fā)表于 08-25 06:36

    高性能紙幣處理系統(tǒng)設(shè)計(jì)的仿真優(yōu)化資料說(shuō)明

    為了幫助全世界的現(xiàn)金中心安全地清分與處理紙幣,德國(guó)捷德貨幣技術(shù)公司的工程師與物理學(xué)家使用多物理場(chǎng)仿真開(kāi)發(fā)了磁性、光學(xué)與超聲傳感器,從而對(duì)采用模塊化設(shè)計(jì)的高性能紙幣處理系統(tǒng)進(jìn)行了優(yōu)化
    的頭像 發(fā)表于 02-23 10:57 ?3438次閱讀

    阿里的秒殺系統(tǒng)是如何設(shè)計(jì)的?

    阿里的秒殺系統(tǒng)是怎么設(shè)計(jì)的?,服務(wù)器,redis,調(diào)用,后端
    的頭像 發(fā)表于 02-20 11:23 ?1969次閱讀
    阿里的<b class='flag-5'>秒殺</b><b class='flag-5'>系統(tǒng)</b>是如何設(shè)計(jì)的?

    如何優(yōu)化DCS系統(tǒng)性能

    工作狀態(tài)。選擇高性能的處理器、大容量?jī)?nèi)存、高速硬盤(pán)以及可靠的通訊模塊,以提高系統(tǒng)的運(yùn)行速度和響應(yīng)能力。對(duì)于老化或故障的設(shè)備,及時(shí)更換或修理。 硬件參數(shù)設(shè)置 :通過(guò)合理設(shè)置硬件參數(shù)和優(yōu)化硬件配置,進(jìn)一步提高
    的頭像 發(fā)表于 11-13 09:19 ?575次閱讀

    如何優(yōu)化MEMS設(shè)計(jì)以提高性能

    優(yōu)化MEMS(微機(jī)電系統(tǒng))設(shè)計(jì)以提高性能是一個(gè)復(fù)雜且多維的任務(wù),涉及多個(gè)學(xué)科和技術(shù)的綜合應(yīng)用。以下是一些關(guān)鍵的優(yōu)化策略和方法: 一、系統(tǒng)級(jí)設(shè)
    的頭像 發(fā)表于 11-20 10:21 ?486次閱讀
    主站蜘蛛池模板: 国产三级日本三级日产三 | 在线天堂中文新版www| 黄色片 720p| 四虎影院免费视频| 美女视频一区二区| 2021国产精品久久| 美女牲交视频一级毛片| 99久久无色码中文字幕| 久久综合久久久| 啪啪激情综合| 日韩一级免费毛片| 网www天堂资源在线| 在线日韩一区| 天天怕夜夜怕狠狠怕| 一级毛片aaaaaa视频免费看| 深夜动态福利gif动态进| 欧美精品激情| 97精品伊人久久大香线蕉| 久久观看| 久久天天综合| 欧美天天爽| 久久免费看视频| 欧美一区色| 美女扒开尿口给男人桶爽视频| 色网站观看| 18性夜影院午夜寂寞影院免费| h视频在线观看网站| 日本人69xxⅹ69| 亚洲专区一| 美女隐私黄www视频| 香蕉视频在线观看黄| 涩涩涩丁香色婷五月网视色| 四虎影院在线免费| 免费的色网站| 精品免费久久久久久成人影院| 女生张开腿让男人桶| 末成年一级在线看片| 成 人 色综合| 午夜性刺激免费视频观看不卡专区| 亚洲第七页| 日本加勒比高清一本大道|