《計算機-如何解決高併發》

一個小型的網站,可以使用最簡單的html靜態頁面就實現了,配合一些圖片達到美化效果,所有的頁面均存放在一個目錄下,這樣的網站對系統架構、性能的要求都很簡單。隨着互聯網業務的不斷豐富,網站相關的技術經過這些年的發展,已經細分到很細的方方面面,尤其對於大型網站來說,所採用的技術更是涉及面非常廣,從硬件到軟件、編程語言、數據庫、WebServer、防火牆等各個領域都有了很高的要求,已經不是原來簡單的html靜態網站所能比擬的。

大型網站,比如門戶網站,在面對大量用戶訪問、高併發請求方面,基本的解決方案集中在這樣幾個環節:使用高性能的服務器、高性能的數據庫、高效率的編程語言、還有高性能的Web容器。這幾個解決思路在一定程度上意味着更大的投入。

1、HTML靜態化

其實大家都知道,效率最高、消耗最小的就是純靜態化的html頁面,所以我們儘可能使我們的網站上的頁面採用靜態頁面來實現,這個最簡單的方法其實也是最有效的方法。但是對於大量內容並且頻繁更新的網站,我們無法全部手動去挨個實現,於是出現了我們常見的信息發佈系統CMS,像我們常訪問的各個門戶站點的新聞頻道,甚至他們的其他頻道,都是通過信息發佈系統來管理和實現的,信息發佈系統可以實現最簡單的信息錄入自動生成靜態頁面,還能具備頻道管理、權限管理、自動抓取等功能,對於一個大型網站來說,擁有一套高效、可管理的CMS是必不可少的。

除了門戶和信息發佈類型的網站,對於交互性要求很高的社區類型網站來說,儘可能的靜態化也是提高性能的必要手段,將社區內的帖子、文章進行實時的靜態化、有更新的時候再重新靜態化也是大量使用的策略,像Mop的大雜燴就是使用了這樣的策略,網易社區等也是如此。

同時,html靜態化也是某些緩存策略使用的手段,對於系統中頻繁使用數據庫查詢但是內容更新很小的應用,可以考慮使用html靜態化來實現。比如論壇中論壇的公用設置信息,這些信息目前的主流論壇都可以進行後臺管理並且存儲在數據庫中,這些信息其實大量被前臺程序調用,但是更新頻率很小,可以考慮將這部分內容進行後臺更新的時候進行靜態化,這樣避免了大量的數據庫訪問請求。

2、圖片服務器分離

大家知道,對於Web服務器來說,不管是Apache、IIS還是其他容器,圖片是最消耗資源的,於是我們有必要將圖片與頁面進行分離,這是基本上大型網站都會採用的策略,他們都有獨立的、甚至很多臺的圖片服務器。這樣的架構可以降低提供頁面訪問請求的服務器系統壓力,並且可以保證系統不會因爲圖片問題而崩潰。

在應用服務器和圖片服務器上,可以進行不同的配置優化,比如apache在配置ContentType的時候可以儘量少支持、儘可能少的LoadModule,保證更高的系統消耗和執行效率。

3、數據庫集羣、庫表散列

大型網站都有複雜的應用,這些應用必須使用數據庫,那麼在面對大量訪問的時候,數據庫的瓶頸很快就能顯現出來,這時一臺數據庫將很快無法滿足應用,於是我們需要使用數據庫集羣或者庫表散列。

在數據庫集羣方面,很多數據庫都有自己的解決方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是類似的方案,您使用了什麼樣的DB,就參考相應的解決方案來實施即可。

上面提到的數據庫集羣由於在架構、成本、擴張性方面都會受到所採用DB類型的限制,於是我們需要從應用程序的角度來考慮改善系統架構,庫表散列是常用並且最有效的解決方案。

我們在應用程序中安裝業務和應用或者功能模塊將數據庫進行分離,不同的模塊對應不同的數據庫或者表,再按照一定的策略對某個頁面或者功能進行更小的數據庫散列,比如用戶表,按照用戶ID進行表散列,這樣就能夠低成本的提升系統的性能並且有很好的擴展性。

sohu的論壇就是採用了這樣的架構,將論壇的用戶、設置、帖子等信息進行數據庫分離,然後對帖子、用戶按照板塊和ID進行散列數據庫和表,最終可以在配置文件中進行簡單的配置便能讓系統隨時增加一臺低成本的數據庫進來補充系統性能。

4、緩存

緩存一詞搞技術的都接觸過,很多地方用到緩存。網站架構和網站開發中的緩存也是非常重要。這裏先講述最基本的兩種緩存。高級和分佈式的緩存在後面講述。

架構方面的緩存,對Apache比較熟悉的人都能知道Apache提供了自己的緩存模塊,也可以使用外加的Squid模塊進行緩存,這兩種方式均可以有效的提高Apache的訪問響應能力。

網站程序開發方面的緩存,Linux上提供的Memory Cache是常用的緩存接口,可以在web開發中使用,比如用Java開發的時候就可以調用MemoryCache對一些數據進行緩存和通訊共享,一些大型社區使用了這樣的架構。另外,在使用web語言開發的時候,各種語言基本都有自己的緩存模塊和方法,PHP有Pear的Cache模塊,Java就更多了,.net不是很熟悉,相信也肯定有。

5、鏡像

鏡像是大型網站常採用的提高性能和數據安全性的方式,鏡像的技術可以解決不同網絡接入商和地域帶來的用戶訪問速度差異,比如ChinaNet和EduNet之間的差異就促使了很多網站在教育網內搭建鏡像站點,數據進行定時更新或者實時更新。在鏡像的細節技術方面,這裏不闡述太深,有很多專業的現成的解決架構和產品可選。也有廉價的通過軟件實現的思路,比如Linux上的rsync等工具。

6、負載均衡

負載均衡將是大型網站解決高負荷訪問和大量併發請求採用的高端解決辦法。
  負載均衡技術發展了多年,有很多專業的服務提供商和產品可以選擇,我個人接觸過一些解決方法,其中有兩個架構可以給大家做參考。

(1)、硬件四層交換

第四層交換使用第三層和第四層信息包的報頭信息,根據應用區間識別業務流,將整個區間段的業務流分配到合適的應用服務器進行處理。

第四層交換功能就像是虛IP,指向物理服務器。它傳輸的業務服從的協議多種多樣,有HTTP、FTP、NFS、Telnet或其他協議。這些業務在物理服務器基礎上,需要複雜的載量平衡算法。在IP世界,業務類型由終端TCP或UDP端口地址來決定,在第四層交換中的應用區間則由源端和終端IP地址、TCP和UDP端口共同決定。

在硬件四層交換產品領域,有一些知名的產品可以選擇,比如Alteon、F5等,這些產品很昂貴,但是物有所值,能夠提供非常優秀的性能和很靈活的管理能力。“Yahoo中國”當初接近2000臺服務器,只使用了三、四臺Alteon就搞定了。

(2)、軟件四層交換

大家知道了硬件四層交換機的原理後,基於OSI模型來實現的軟件四層交換也就應運而生,這樣的解決方案實現的原理一致,不過性能稍差。但是滿足一定量的壓力還是遊刃有餘的,有人說軟件實現方式其實更靈活,處理能力完全看你配置的熟悉能力。

軟件四層交換我們可以使用Linux上常用的LVS來解決,LVS就是Linux Virtual Server,他提供了基於心跳線heartbeat的實時災難應對解決方案,提高系統的強壯性,同時可供了靈活的虛擬VIP配置和管理功能,可以同時滿足多種應用需求,這對於分佈式的系統來說必不可少。

一個典型的使用負載均衡的策略就是,在軟件或者硬件四層交換的基礎上搭建squid集羣,這種思路在很多大型網站包括搜索引擎上被採用,這樣的架構低成本、高性能還有很強的擴張性,隨時往架構裏面增減節點都非常容易。

對於大型網站來說,前面提到的每個方法可能都會被同時使用到,這裏介紹得比較淺顯,具體實現過程中很多細節還需要大家慢慢熟悉和體會。有時一個很小的squid參數或者apache參數設置,對於系統性能的影響就會很大。

7、最新:CDN加速技術

什麼是CDN?

CDN的全稱是內容分發網絡。其目的是通過在現有的Internet中增加一層新的網絡架構,將網站的內容發佈到最接近用戶的網絡“邊緣”,使用戶可以就近取得所需的內容,提高用戶訪問網站的響應速度。

CDN有別於鏡像,因爲它比鏡像更智能,或者可以做這樣一個比喻:CDN=更智能的鏡像+緩存+流量導流。因而,CDN可以明顯提高Internet網絡中信息流動的效率。從技術上全面解決由於網絡帶寬小、用戶訪問量大、網點分佈不均等問題,提高用戶訪問網站的響應速度。

CDN的類型特點

CDN的實現分爲三類:鏡像、高速緩存、專線。

鏡像站點(Mirror Site),是最常見的,它讓內容直接發佈,適用於靜態和準動態的數據同步。但是購買和維護新服務器的費用較高,還必須在各個地區設置鏡像服務器,配備專業技術人員進行管理與維護。對於大型網站來說,更新所用的帶寬成本也大大提高了。

高速緩存,成本較低,適用於靜態內容。Internet的統計表明,超過80%的用戶經常訪問的是20%的網站的內容,在這個規律下,緩存服務器可以處理大部分客戶的靜態請求,而原始的服務器只需處理約20%左右的非緩存請求和動態請求,於是大大加快了客戶請求的響應時間,並降低了原始服務器的負載。

CDN服務一般會在全國範圍內的關鍵節點上放置緩存服務器。

專線,讓用戶直接訪問數據源,可以實現數據的動態同步。

CDN的實例

舉個例子來說,當某用戶訪問網站時,網站會利用全球負載均衡技術,將用戶的訪問指向到距離用戶最近的正常工作的緩存服務器上,直接響應用戶的請求。

當用戶訪問已經使用了CDN服務的網站時,其解析過程與傳統解析方式的最大區別就在於網站的授權域名服務器不是以傳統的輪詢方式來響應本地DNS的解析請求,而是充分考慮用戶發起請求的地點和當時網絡的情況,來決定把用戶的請求定向到離用戶最近同時負載相對較輕的節點緩存服務器上。

通過用戶定位算法和服務器健康檢測算法綜合後的數據,可以將用戶的請求就近定向到分佈在網絡“邊緣”的緩存服務器上,保證用戶的訪問能得到更及時可靠的響應。

由於大量的用戶訪問都由分佈在網絡邊緣的CDN節點緩存服務器直接響應了,這就不僅提高了用戶的訪問質量,同時有效地降低了源服務器的負載壓力。

附:某CDN服務商的服務說明

採用GCDN加速方式

採用了GCDN加速方式以後,系統會在瀏覽用戶和您的服務器之間增加一臺GCDN服務器。瀏覽用戶訪問您的服務器時,一般靜態數據,如圖片、多媒體資料等數據將直接從GCDN服務器讀取,使得從主服務器上讀取靜態數據的交換量大大減少。

爲VIP型虛擬主機而特加的VPN高速壓縮通道,使用高速壓縮的電信<>網通、電信<>國際(HK)、網通<==>國際(HK)等跨網專線通道,智能多線,自動獲取最快路徑,極速的動態實時併發響應速度,實現了網站的動態腳本實時同步,對動態網站有一個更加明顯的加速效果。

每個網絡運營商(電信、網通、鐵通、教育網)均有您服務器的GCDN服務器,無論瀏覽用戶是來自何處,GCDN都能讓您的服務器展現最快的速度!另外,我們將對您的數據進行實時備份,讓您的數據更安全!

轉自:https://blog.csdn.net/y_h_t/article/details/6322823

如何解決高併發,秒殺問題
2018年06月27日 22:46:54 my_qq_990814268 閱讀數:2709
相信不少人會被這個問題困擾,分享大家一篇這樣的文章,希望能夠幫到你!
一、秒殺業務爲什麼難做?
1)im系統,例如qq或者微博,每個人都讀自己的數據(好友列表、羣列表、個人信息);
2)微博系統,每個人讀你關注的人的數據,一個人讀多個人的數據;
3)秒殺系統,庫存只有一份,所有人會在集中的時間讀和寫這些數據,多個人讀一個數據。

例如:小米手機每週二的秒殺,可能手機只有1萬部,但瞬時進入的流量可能是幾百幾千萬。
又例如:12306搶票,票是有限的,庫存一份,瞬時流量非常多,都讀相同的庫存。讀寫衝突,鎖非常嚴重,這是秒殺業務難的地方。那我們怎麼優化秒殺業務的架構呢?

二、優化方向
優化方向有兩個(今天就講這兩個點):
(1)將請求儘量攔截在系統上游(不要讓鎖衝突落到數據庫上去)。傳統秒殺系統之所以掛,請求都壓倒了後端數據層,數據讀寫鎖衝突嚴重,併發高響應慢,幾乎所有請求都超時,流量雖大,下單成功的有效流量甚小。以12306爲例,一趟火車其實只有2000張票,200w個人來買,基本沒有人能買成功,請求有效率爲0。

(2)充分利用緩存,秒殺買票,這是一個典型的讀多寫少的應用場景,大部分請求是車次查詢,票查詢,下單和支付纔是寫請求。一趟火車其實只有2000張票,200w個人來買,最多2000個人下單成功,其他人都是查詢庫存,寫比例只有0.1%,讀比例佔99.9%,非常適合使用緩存來優化。好,後續講講怎麼個“將請求儘量攔截在系統上游”法,以及怎麼個“緩存”法,講講細節。

三、常見秒殺架構
常見的站點架構基本是這樣的(絕對不畫忽悠類的架構圖)

秒殺架構

(1)瀏覽器端,最上層,會執行到一些JS代碼
(2)站點層,這一層會訪問後端數據,拼html頁面返回給瀏覽器
(3)服務層,向上遊屏蔽底層數據細節,提供數據訪問
(4)數據層,最終的庫存是存在這裏的,mysql是一個典型(當然還有會緩存)
這個圖雖然簡單,但能形象的說明大流量高併發的秒殺業務架構,大家要記得這一張圖。後面細細解析各個層級怎麼優化。

四、各層次優化細節
第一層,客戶端怎麼優化(瀏覽器層,APP層)

問大家一個問題,大家都玩過微信的搖一搖搶紅包對吧,每次搖一搖,就會往後端發送請求麼?回顧我們下單搶票的場景,點擊了“查詢”按鈕之後,系統那個卡呀,進度條漲的慢呀,作爲用戶,我會不自覺的再去點擊“查詢”,對麼?繼續點,繼續點,點點點。。。有用麼?平白無故的增加了系統負載,一個用戶點5次,80%的請求是這麼多出來的,怎麼整?

(a)產品層面,用戶點擊“查詢”或者“購票”後,按鈕置灰,禁止用戶重複提交請求;
(b)JS層面,限制用戶在x秒之內只能提交一次請求;

APP層面,可以做類似的事情,雖然你瘋狂的在搖微信,其實x秒才向後端發起一次請求。這就是所謂的“將請求儘量攔截在系統上游”,越上游越好,瀏覽器層,APP層就給攔住,這樣就能擋住80%+的請求,這種辦法只能攔住普通用戶(但99%的用戶是普通用戶)對於羣內的高端程序員是攔不住的。firebug一抓包,http長啥樣都知道,js是萬萬攔不住程序員寫for循環,調用http接口的,這部分請求怎麼處理?

第二層,站點層面的請求攔截
怎麼攔截?怎麼防止程序員寫for循環調用,有去重依據麼?ip?cookie-id?…想複雜了,這類業務都需要登錄,用uid即可。在站點層面,對uid進行請求計數和去重,甚至不需要統一存儲計數,直接站點層內存存儲(這樣計數會不準,但最簡單)。一個uid,5秒只准透過1個請求,這樣又能攔住99%的for循環請求。

5s只透過一個請求,其餘的請求怎麼辦?緩存,頁面緩存,同一個uid,限制訪問頻度,做頁面緩存,x秒內到達站點層的請求,均返回同一頁面。同一個item的查詢,例如車次,做頁面緩存,x秒內到達站點層的請求,均返回同一頁面。如此限流,既能保證用戶有良好的用戶體驗(沒有返回404)又能保證系統的健壯性(利用頁面緩存,把請求攔截在站點層了)。

頁面緩存不一定要保證所有站點返回一致的頁面,直接放在每個站點的內存也是可以的。優點是簡單,壞處是http請求落到不同的站點,返回的車票數據可能不一樣,這是站點層的請求攔截與緩存優化。

好,這個方式攔住了寫for循環發http請求的程序員,有些高端程序員(黑客)控制了10w個肉雞,手裏有10w個uid,同時發請求(先不考慮實名制的問題,小米搶手機不需要實名制),這下怎麼辦,站點層按照uid限流攔不住了。

第三層 服務層來攔截(反正就是不要讓請求落到數據庫上去)

服務層怎麼攔截?大哥,我是服務層,我清楚的知道小米只有1萬部手機,我清楚的知道一列火車只有2000張車票,我透10w個請求去數據庫有什麼意義呢?沒錯,請求隊列!

對於寫請求,做請求隊列,每次只透有限的寫請求去數據層(下訂單,支付這樣的寫業務)

1w部手機,只透1w個下單請求去db

3k張火車票,只透3k個下單請求去db

如果均成功再放下一批,如果庫存不夠則隊列裏的寫請求全部返回“已售完”。

對於讀請求,怎麼優化?cache抗,不管是memcached還是redis,單機抗個每秒10w應該都是沒什麼問題的。如此限流,只有非常少的寫請求,和非常少的讀緩存mis的請求會透到數據層去,又有99.9%的請求被攔住了。

當然,還有業務規則上的一些優化。回想12306所做的,分時分段售票,原來統一10點賣票,現在8點,8點半,9點,…每隔半個小時放出一批:將流量攤勻。

其次,數據粒度的優化:你去購票,對於餘票查詢這個業務,票剩了58張,還是26張,你真的關注麼,其實我們只關心有票和無票?流量大的時候,做一個粗粒度的“有票”“無票”緩存即可。

第三,一些業務邏輯的異步:例如下單業務與 支付業務的分離。這些優化都是結合 業務 來的,我之前分享過一個觀點“一切脫離業務的架構設計都是耍流氓”架構的優化也要針對業務。

第四層 最後是數據庫層

瀏覽器攔截了80%,站點層攔截了99.9%並做了頁面緩存,服務層又做了寫請求隊列與數據緩存,每次透到數據庫層的請求都是可控的。db基本就沒什麼壓力了,閒庭信步,單機也能扛得住,還是那句話,庫存是有限的,小米的產能有限,透這麼多請求來數據庫沒有意義。

全部透到數據庫,100w個下單,0個成功,請求有效率0%。透3k個到數據,全部成功,請求有效率100%。

五、總結
上文應該描述的非常清楚了,沒什麼總結了,對於秒殺系統,再次重複下我個人經驗的兩個架構優化思路:
(1)儘量將請求攔截在系統上游(越上游越好);
(2)讀多寫少的常用多使用緩存(緩存抗讀壓力);

瀏覽器和APP:做限速

站點層:按照uid做限速,做頁面緩存

服務層:按照業務做寫請求隊列控制流量,做數據緩存

數據層:閒庭信步

並且:結合業務做優化

六、Q&A
問題1、按你的架構,其實壓力最大的反而是站點層,假設真實有效的請求數有1000萬,不太可能限制請求連接數吧,那麼這部分的壓力怎麼處理?

答:每秒鐘的併發可能沒有1kw,假設有1kw,解決方案2個:

(1)站點層是可以通過加機器擴容的,最不濟1k臺機器來唄。
(2)如果機器不夠,拋棄請求,拋棄50%(50%直接返回稍後再試),原則是要保護系統,不能讓所有用戶都失敗。

問題2、“控制了10w個肉雞,手裏有10w個uid,同時發請求” 這個問題怎麼解決哈?

答:上面說了,服務層寫請求隊列控制

問題3:限制訪問頻次的緩存,是否也可以用於搜索?例如A用戶搜索了“手機”,B用戶搜索“手機”,優先使用A搜索後生成的緩存頁面?

答:這個是可以的,這個方法也經常用在“動態”運營活動頁,例如短時間推送4kw用戶app-push運營活動,做頁面緩存。

問題4:如果隊列處理失敗,如何處理?肉雞把隊列被撐爆了怎麼辦?

答:處理失敗返回下單失敗,讓用戶再試。隊列成本很低,爆了很難吧。最壞的情況下,緩存了若干請求之後,後續請求都直接返回“無票”(隊列裏已經有100w請求了,都等着,再接受請求也沒有意義了)

問題5:站點層過濾的話,是把uid請求數單獨保存到各個站點的內存中麼?如果是這樣的話,怎麼處理多臺服務器集羣經過負載均衡器將相同用戶的響應分佈到不同服務器的情況呢?還是說將站點層的過濾放到負載均衡前?

答:可以放在內存,這樣的話看似一臺服務器限制了5s一個請求,全局來說(假設有10臺機器),其實是限制了5s 10個請求,解決辦法:

1)加大限制(這是建議的方案,最簡單)
2)在nginx層做7層均衡,讓一個uid的請求儘量落到同一個機器上

問題6:服務層過濾的話,隊列是服務層統一的一個隊列?還是每個提供服務的服務器各一個隊列?如果是統一的一個隊列的話,需不需要在各個服務器提交的請求入隊列前進行鎖控制?

答:可以不用統一一個隊列,這樣的話每個服務透過更少量的請求(總票數/服務個數),這樣簡單。統一一個隊列又複雜了。

問題7:秒殺之後的支付完成,以及未支付取消佔位,如何對剩餘庫存做及時的控制更新?

答:數據庫裏一個狀態,未支付。如果超過時間,例如45分鐘,庫存會重新會恢復(大家熟知的“回倉”),給我們搶票的啓示是,開動秒殺後,45分鐘之後再試試看,說不定又有票喲~

問題8:不同的用戶瀏覽同一個商品 落在不同的緩存實例顯示的庫存完全不一樣 請問老師怎麼做緩存數據一致或者是允許髒讀?

答:目前的架構設計,請求落到不同的站點上,數據可能不一致(頁面緩存不一樣),這個業務場景能接受。但數據庫層面真實數據是沒問題的。

問題9:就算處於業務把優化考慮“3k張火車票,只透3k個下單請求去db”那這3K個訂單就不會發生擁堵了嗎?

答:(1)數據庫抗3k個寫請求還是ok的;(2)可以數據拆分;(3)如果3k扛不住,服務層可以控制透過去的併發數量,根據壓測情況來吧,3k只是舉例;

問題10;如果在站點層或者服務層處理後臺失敗的話,需不需要考慮對這批處理失敗的請求做重放?還是就直接丟棄?

答:別重放了,返回用戶查詢失敗或者下單失敗吧,架構設計原則之一是“fail fast”。

問題11.對於大型系統的秒殺,比如12306,同時進行的秒殺活動很多,如何分流?

答:垂直拆分

問題12、額外又想到一個問題。這套流程做成同步還是異步的?如果是同步的話,應該還存在會有響應反饋慢的情況。但如果是異步的話,如何控制能夠將響應結果返回正確的請求方?

答:用戶層面肯定是同步的(用戶的http請求是夯住的),服務層面可以同步可以異步。

問題13、秒殺羣提問:減庫存是在那個階段減呢?如果是下單鎖庫存的話,大量惡意用戶下單鎖庫存而不支付如何處理呢?

答:數據庫層面寫請求量很低,還好,下單不支付,等時間過完再“回倉”,之前提過了
轉自:https://blog.csdn.net/liangkaiping0525/article/details/80836104

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章