日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

大型电商网站设计——商品详情页面静态化

發(fā)布時間:2023/12/9 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 大型电商网站设计——商品详情页面静态化 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

分布式系統(tǒng)的cap理論首先把分布式系統(tǒng)中的三個特性進行了如下歸納:
1.一致性? : 在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時刻是否是同樣的值(等同于所有節(jié)點訪問同一份最新的數(shù)據(jù)副本)
2.可用性(A) : 在集群中一部分節(jié)點故障后,集群整體是否還能響應客戶端的讀寫請求(對數(shù)據(jù)更新具備高可用性)
3.分區(qū)容錯§ : 以實際效果而言,分區(qū)相當于對通信的時限要求。系統(tǒng)如果不能在時限內達成數(shù)據(jù)一致性,就意味著發(fā)生了分區(qū)的情況,必須就
當前操作在C和A之間做出選擇。

應用數(shù)據(jù)靜態(tài)化架構高性能單頁web應用

在電商網(wǎng)站中,單頁web是非常常見的一種形式,比如首頁,廣告頁等都屬于單頁應用。這種頁面是由模板+數(shù)據(jù)組成,傳統(tǒng)的構建方式一般通過靜態(tài)化實現(xiàn)。
而這種方式的靈活性不是很好,比如頁面模板部分變更了需要重新全部生成。因此最好能有一種實現(xiàn)方式是可以實時動態(tài)渲染的,以支持模板的多樣性。另外
也需要考慮如下問題:
1.動態(tài)化模板渲染支持
2.數(shù)據(jù)和模板的多版本化:生產版本,灰度版本和預發(fā)布版本
3.版本回滾問題
4.異常問題,假設渲染模板時遇到了異常情況如何處理
5.灰度發(fā)布問題,比如切20%量給灰度版本
6.預發(fā)布問題,目的是在正式版本測試數(shù)據(jù)和模板的正確性

1.整體架構

靜態(tài)化單頁如圖,直接將生成的靜態(tài)頁推送到相關服務器即可。使用這種方式要考慮文件操作的原子性,即從老版本切換到新版本如何做到文件操作原子化。

而動態(tài)化方案的整體架構如下,分為三大系統(tǒng):CMS系統(tǒng),控制系統(tǒng)和前端展示系統(tǒng)。

CMS系統(tǒng)

在cms系統(tǒng)中可以配置頁面的模板和數(shù)據(jù)。模板動態(tài)在cms系統(tǒng)中維護,即模板不是一個靜態(tài)文件,而是存儲在cms中的一條數(shù)據(jù),最終發(fā)布到’發(fā)布數(shù)據(jù)存儲redis中,前端展示系統(tǒng)從redis中獲取該模板進行渲染,從而前端展示系統(tǒng)更換了模板也不需要重啟,純動態(tài)維護模板數(shù)據(jù)。
原始數(shù)據(jù)存儲到’元數(shù)據(jù)存儲mysql’中即可,比如頻道頁一般需要前端訪問的url,分類,輪播圖,商品樓層數(shù)據(jù)等,這些數(shù)據(jù)按照相應維度存儲在cms系統(tǒng)中。cms系統(tǒng)提供發(fā)布到’發(fā)布數(shù)據(jù)存儲redis’的控制。將cms系統(tǒng)中的原始數(shù)據(jù)和模板數(shù)據(jù)組裝成聚合數(shù)據(jù)(json存儲)同步到’發(fā)布數(shù)據(jù)存儲redis’,以便前端展示系統(tǒng)獲取進行展示。此處提供3個按鈕:正式版本,灰度版本和預發(fā)布版本。

cms目前存在幾個問題:
1.用戶訪問諸如 http://channel.jd.com/fashion.html 怎么定位到對應的聚合數(shù)據(jù)呢?我們可以在cms元數(shù)據(jù)中定義url作為key,如果沒有url,則用id
作為key,或者自動生成一個url。
2.多版本如何存儲呢?使用redis的Hash結構存儲即可,key作為url,字段按照維度存儲:正式版本使用當前時間戳存儲(這樣前端系統(tǒng)可以根據(jù)時間戳排序然后獲得
最新的版本),預發(fā)布版本使用’predeploy’作為字段,灰度版本使用’abVersion’作為字段即可,這樣就區(qū)分開了多版本。
3.灰度版本如何控制呢?可以通過控制系統(tǒng)的開關來控制如何灰度
4.如何訪問預發(fā)布版本呢?比如在url參數(shù)總是帶上predeploy=true,另外可以限定只有內網(wǎng)可以訪問或者訪問時帶上訪問密碼,比如pwd=abc234
5.模板變更的歷史數(shù)據(jù)校驗問題?比如模板變更了,但是使用歷史數(shù)據(jù)渲染該模板會出現(xiàn)問題,即模板總是要兼容歷史數(shù)據(jù)的;此處的方案不存在這個問題,因為每次存儲的是當時的模板快照,即數(shù)據(jù)快照和模板快照推送到’發(fā)布數(shù)據(jù)存儲redis’中。

3.前端展示系統(tǒng)
前端展示系統(tǒng)可以獲取當前url,使用url作為key首先從本機’發(fā)布數(shù)據(jù)存儲redis’獲取數(shù)據(jù)。如果沒有數(shù)據(jù)或者異常則從主’發(fā)布數(shù)據(jù)存儲redis’獲取。如果主’發(fā)布數(shù)據(jù)
存儲redis’也發(fā)生了異常,那么會直接調用cms系統(tǒng)暴露的api直接從元數(shù)據(jù)存儲mysql中獲取數(shù)據(jù)進行處理。
前端展示系統(tǒng)的偽代碼如下:

--1.加載lua模塊庫local template=require("resty.template")template.load=function(s) return s end--2.動態(tài)獲取模板local myTemplate="<html>{*title*}</html>"--3.動態(tài)獲取數(shù)據(jù)local data={title="iphone6s"}--4.渲染模板local func=template.compile{myTemplate}local content=func(data)--5.通過ngx api 輸出內容ngx.say(content)

從上述代碼可知,模板和數(shù)據(jù)都是動態(tài)獲取的,然后使用動態(tài)獲取的模板和數(shù)據(jù)進行渲染。
此處假設最新版本的模板和數(shù)據(jù)都有問題怎么辦?可以從流程上避免:
a)首先進行預發(fā)布版本發(fā)布,測試人員嚴重沒問題后進行下一步
b)接著發(fā)布灰度版本,在灰度的時候自動去掉cdn功能(即不設置頁面的緩存時間),發(fā)布驗證
c)最后發(fā)布正式版本,正式版本發(fā)布的前5分鐘內是不設置頁面緩存的,這樣就可以防止發(fā)版時遇到問題,但是若問題版本已經(jīng)在cdn上,問題會影響到全部用戶,且無法快速回滾。

控制系統(tǒng)

控制系統(tǒng)是用于版本降級和灰度發(fā)布的,當然也可以把這個功能放在cms系統(tǒng)中。
a)版本降級:假設當前線上的版本遇到問題了,想要快速切換到上個版本,可以使用控制系統(tǒng)實現(xiàn),選擇其中一個歷史版本然后通知給前端展示系統(tǒng),使用url和當前版本的字段即可,
這樣前端展示系統(tǒng)就可以自動切換到選中的那個版本;當問題修復后,再刪除該降級配置切換回最新版本。
b)灰度發(fā)布:在控制系統(tǒng)控制哪些url需要灰度發(fā)布和灰度發(fā)布的比例,與版本降級類似,將相關的數(shù)據(jù)推送到前端展示系統(tǒng)即可,當不想灰度發(fā)布時刪除相關數(shù)據(jù)。

數(shù)據(jù)和模板動態(tài)化

我們將數(shù)據(jù)和模板都進行動態(tài)化存儲,這樣可以在cms進行數(shù)據(jù)和模板的變更;實現(xiàn)了前端和后端人員的分離;前端開發(fā)人員進行cms數(shù)據(jù)配置和模板開發(fā),而后端開發(fā)人員只進行
系統(tǒng)的維護。另外,因為模板的動態(tài)存儲,每次發(fā)布新的模板不需要重啟前端展示系統(tǒng),后端開發(fā)人員更好的得到了解放。
模板和數(shù)據(jù)可以是一對多的關系,即一個模板可以被多個數(shù)據(jù)使用。假設模板發(fā)生變更后,我們可以批量推送模板關聯(lián)的數(shù)據(jù),首先進行預發(fā)布版本的發(fā)布,由測試人員進行驗證,驗證沒問題發(fā)布正式版本。

多版本機制

我們將數(shù)據(jù)和模板分為多版本后,可以實現(xiàn):
1.預發(fā)布版本:更容易讓測試人員在實際環(huán)境進行驗證
2.灰度版本:只需要簡單的開關,就可以進行ab測試
3.正式版本:存儲多個歷史正式版本,假設最新的正式版本出現(xiàn)問題,可以非??焖俚那袚Q回之前的版本

應用多級緩存模式支撐海量讀服務

多級緩存介紹

所謂的多級緩存,即在整個系統(tǒng)架構的不同系統(tǒng)層級進行數(shù)據(jù)緩存,以提升訪問效率
1.首先接入nginx將請求負載均衡到應用nginx,此處常用的負載均衡算法是輪詢或者一致性哈希,輪詢可以使服務器的請求更加均衡,而一致性哈??梢蕴嵘龖胣ginx的緩存命中率。
相對于輪詢,一致性哈希會存在單機熱點問題,一種解決辦法是熱點直接推送到接入層nginx,另一種辦法是設置一個閾值,當超過閾值,改為輪詢。
2.接著應用nginx讀取本地緩存,如果本地緩存命中則直接返回。應用nging本地緩存可以提升整體的吞吐量,降低后端的壓力,尤其應對熱點問題非常有效。本地緩存可以使用Lua Shared Dict,Nginx Proxy Cache(磁盤/內存),Local Redis實現(xiàn)
3.如果nginx本地緩存沒有命中,則會讀取相應的分布式緩存(如redis緩存,另外可以考慮主從架構來提升性能和吞吐量),如果分布式緩存命中則直接返回相應的數(shù)據(jù)(并回寫到nginx
本地緩存)
4.如果分布式緩存也沒有命中,則會回源到tomcat集群,在回源到tomcat集群時也可以使用輪詢或一致性哈希作為負載均衡算法
5.在tomcat應用中,首先讀取本地堆緩存,如果有則直接返回(并會寫到主redis集群)
6.作為可選部分,如果步驟4沒有命中可以再嘗試一次讀取主redis集群,目的是防止從redis集群有問題時的流量沖擊
7.如果所有的緩存都沒有命中,只能查詢db或相關服務獲取數(shù)據(jù)并返回
8.步驟7返回的數(shù)據(jù)異步寫到主redis集群,此處可能有多個tomcat實例同時寫主redis集群,造成數(shù)據(jù)混亂。

應用整體分了3部分的緩存:應用nginx本地緩存,分布式緩存,tomcat堆緩存。每一層緩存都用來解決相關的問題,如應用nginx本地緩存用來解決熱點緩存問題,分布式緩存用來減少訪問回源率,tomcat堆緩存用于防止緩存失效/崩潰之后的沖擊。

如何緩存數(shù)據(jù)

過期與不過期

對于緩存的數(shù)據(jù)我們可以考慮不過期緩存和帶過期時間緩存,什么場景應該選擇哪種模式則需要根據(jù)業(yè)務和數(shù)據(jù)量等因素來決定。
a)不過期緩存場景一般思路如下:
業(yè)務邏輯:
1.開始事務
2.執(zhí)行sql
3.提交事務
4.寫緩存
使用cache-aside 模式,首先寫數(shù)據(jù)庫,如果成功,則寫緩存。這種場景下存在事務成功,緩存寫失敗但無法回滾事務的情況。另外,不要把寫緩存放在事務中,尤其寫分布式緩存,因為網(wǎng)絡抖動可能導致寫緩存響應時間很慢,引起數(shù)據(jù)庫事務阻塞。如果對緩存數(shù)據(jù)一致性要求不是很高,數(shù)據(jù)量也不是很大,則可以考慮定期全量同步緩存。也有提到以下思路:先刪緩存,然后執(zhí)行數(shù)據(jù)庫事務;不過這種操作對于如商品這種查詢非常頻繁的業(yè)務不適用,因為你在刪緩存的同時,已經(jīng)有另外一個系統(tǒng)讀緩存了,此時事務 還沒提交。當然對于如用戶維度的業(yè)務是可以考慮的。
不過為了更好的解決以上多個事務的問題,可以考慮使用訂閱數(shù)據(jù)庫日志的架構,如使用canal訂閱mysql的binlog實現(xiàn)緩存同步。
對于長尾訪問的數(shù)據(jù),大多數(shù)數(shù)據(jù)訪問頻繁都很高的場景,若緩存空間足夠則可以考慮不過期緩存,如用戶,分類,商品,價格,當緩存滿了可以考慮LRU機制驅逐老的緩存數(shù)據(jù)。
b)過期緩存機制,即采用懶加載,一般用于緩存其他系統(tǒng)的數(shù)據(jù)(無法訂閱變更消息或者成本很高),緩存空間有限,低頻熱點緩存等場景。常見的步驟是:首先讀取緩存,如果不命中
則查詢數(shù)據(jù),然后異步寫入緩存并設置過期緩存,設置過期時間,下次讀取將命中緩存。熱點數(shù)據(jù)經(jīng)常使用,即在應用系統(tǒng)上緩存比較短的時間。這種緩存可能存在一段時間的數(shù)據(jù)
不一致,需要根據(jù)場景來決定如何設置過期時間。如庫存數(shù)據(jù)可以在前端應用上緩存幾秒鐘,短時間的不一致是可以忍受的。

維度化緩存與增量緩存

對于電商系統(tǒng),一個商品可能拆成諸如基礎屬性,圖片列表,上下架,規(guī)格參數(shù),商品介紹等;如果商品變了,要把這些數(shù)據(jù)都更新一遍,那么整個更新成本(接口調用量和帶寬)很高。因此最好將數(shù)據(jù)進行維度化并增量更新(只更新變更的部分)。尤其是上下架這種只是一個狀態(tài)變更,但是每天頻繁調用的,維度化后能減少服務很大的壓力。按照不同維度接收MQ進行更新。

大value緩存

要警惕緩存中的大value,尤其是使用redis的時候。遇到這種情況時可以考慮使用多線程實現(xiàn)的緩存(如memcached)來緩存大value;或者對大value進行壓縮;或者將value拆分為多個小value,客戶端再進行查詢,聚合。

熱點緩存

對于那些訪問非常頻繁的熱點緩存,如果每次都去遠程緩存系統(tǒng)中獲取,可能會因為訪問量太大導致遠程緩存系統(tǒng)請求太多,負載過高或者帶寬過高等問題,最終可能會導致緩存響應慢, 使客戶端超時。一種解決辦法是通過掛更多的從緩存,客戶端通過負載均衡機制讀取從緩存系統(tǒng)數(shù)據(jù)。不過也可以在客戶端所在的應用/代理層本地存儲一份,從而避免訪問遠程緩存,即使像庫存這種數(shù)據(jù),在有些應用系統(tǒng)中也可以進行幾秒的本地緩存,從而降低遠程系統(tǒng)的壓力。

分布式緩存與應用負載均衡

此處說的分布式緩存一般采用分片實現(xiàn),即將數(shù)據(jù)分散到多個實例或多臺服務器。算法一般采用取模和一致性哈希。如之前說的做不過期緩存機制可以考慮取模機制,擴容時一般是新建一個集群;而對于可以丟失的緩存數(shù)據(jù)則可以采用一致性哈希,即使其中一個實例出問題只是丟失一小部分,對于分片實現(xiàn)可以考慮客戶端實現(xiàn),或者使用如Twemproxy中間件進行代理(分片對 客戶端是透明的)。如果使用redis可以考慮使用redis-cluster分布式集群方案。
應用負載均衡一般采用輪詢和一致性哈希,一致性哈希可以根據(jù)應用請求的url或者url參數(shù)將相同的請求轉發(fā)到同一個節(jié)點;而輪詢即將請求均勻的轉發(fā)到每一個服務器。
整體流程如下:
1.首先請求進入接入層nginx
2.根據(jù)負載均衡算法將請求轉發(fā)給應用nginx
3.如果應用nginx本地緩存命中,則直接返回數(shù)據(jù),否則讀取分布式緩存或者回源到tomcat集群。
輪詢的優(yōu)點是:應用nginx的請求更加均勻,使得每個服務器負載基本均衡,不會因為熱點問題導致其中一臺服務器負載過重。
輪詢的缺點是:隨著應用nginx服務器的增加,緩存的命中率會下降。
一致性哈希的優(yōu)點是:相同的請求都會轉發(fā)到同一臺服務器,命中率不會因為服務器的增加而降低。
一致性哈希的缺點是:因為相同的請求都會轉發(fā)到同一臺服務器,因此可能造成某臺服務器負載過重。
那么如何選擇:
a)負載低的時候選擇一致性哈希,比如普通商品訪問
b)熱點請求時降級一致性哈希為輪詢,比如京東首頁的商品訪問
當然,某些場景是將熱點數(shù)據(jù)直接推送到接入層nginx,直接響應給用戶,比如秒殺。

熱點數(shù)據(jù)與更新緩存

1.打擊全量緩存+主從
如圖所示,所有緩存都存儲在應用本地,回源之后把數(shù)據(jù)更新到主redis集群,然后通過主從復制到其他從redis集群。緩存的更新可以采用懶加載或者訂閱消息進行同步。


2.分布式緩存+應用本地熱點
對于分布式緩存,我們需要在nginx+lua 應用中進行應用緩存來減少redis集群的訪問沖擊,即首先查詢應用本地緩存,如果命中則直接返回,如果沒有命中則接著查詢redis集群,回源到tomcat,然后將數(shù)據(jù)緩存到應用本地。此處到應用nginx的負載機制:正常情況下才有一致性哈希,如果某個請求類型訪問量突破了一定閾值,則自動降級為輪詢機制。另外對于一些秒殺活動之類的熱點我們是可以提前知道的,可以把相關的數(shù)據(jù)預先推送到接入層nginx并將負載均衡機制降級為輪詢。

另外可以考慮建立實時熱點發(fā)現(xiàn)系統(tǒng)來發(fā)現(xiàn)熱點:
1.接入層nginx將請求轉發(fā)給應用nginx
2.應用nginx首先讀取本地緩存,如果命中則直接返回,不命中會讀取分布式緩存,回源到tomcat集群
3.應用nginx會將請求上報給實時熱點發(fā)現(xiàn)系統(tǒng)(如使用udp上報請求,或者將請求寫到kafka,或者使用flume訂閱本地nginx日志),上報給實時熱點發(fā)現(xiàn)系統(tǒng)后,它將進行
熱點統(tǒng)計(可以考慮storm實時計算)
4.根據(jù)設計的閾值將熱點數(shù)據(jù)推送到應用nginx本地緩存。
因為做了本地緩存,因此對數(shù)據(jù)一致性需要我們去考慮,即何時失效或更新緩存:
1.如果可以訂閱數(shù)據(jù)變更消息,那么可以訂閱變更消息進行緩存更新
2.如果無法訂閱或者成本比較高,并且對短暫的數(shù)據(jù)一致性要求不嚴格(比如在商品詳情頁看到的庫存,可以短暫不一致,只要保證下單時一致即可),那么可以設置合理的過期時間,過期后再查詢更新數(shù)據(jù)。
3.如果是秒殺之類的,可以訂閱活動開啟消息,將相關數(shù)據(jù)提取推送到前端應用,并將負載機制降級為輪詢
4.建立實時熱點發(fā)現(xiàn)系統(tǒng)來對熱點進行統(tǒng)一推送和更新

更新緩存與原子性

如果多個應用同時操作一份數(shù)據(jù)可能造成緩存是臟數(shù)據(jù),解決的辦法有:
1.更細數(shù)據(jù)時使用更新時間戳或者版本對比,如果使用redis可以利用其單線程機制進行原子化更新
2.使用如canal訂閱數(shù)據(jù)庫binlog,此時把mysql看成發(fā)布者,binlog是發(fā)布的內容,canal看成消費者,canal訂閱binlog然后更新到redis。
3.將更新請求按照相應的規(guī)則分散到多個隊列,然后每個隊列進行單線程更新,更新時拉取最新的數(shù)據(jù)保存
4.用分布式鎖,更新前獲取相關的鎖。

緩存崩潰與快速恢復

當我們使用分布式緩存的時候,應該考慮如何對應其中一部分緩存實例宕機的情況。當數(shù)據(jù)可丟失的情況,我們可以選擇一致性哈希。

1.取模
對于取模機制如果其中一個實例故障,如果摘除此實例將導致大量緩存不命中,瞬間大流量可能導致后端db/服務出現(xiàn)問題。對于這種情況可以采用主從機制來避免實例故障的問題,即其中一個實例故障可以用從/主頂上來。但是取模機制下如果增加一個節(jié)點將導致大量緩存不命中,所以一般是建立另一個集群,然后把數(shù)據(jù)遷移到新集群,然后把流量遷移過去。
2.一致性哈希
對于一致性哈希機制如果其中一個實例故障,摘除此實例只影響一致性哈希的部分緩存不命中,不會導致瞬間大量回源到后端db/服務,但是也會產生一定影響。

快速恢復
  • 主從機制,做好冗余
  • 如果因為緩存導致應用可用性已經(jīng)下降可以考慮:部分用戶降級,然后慢慢減少降級量后臺通過worker預熱緩存數(shù)據(jù)。
    也就是說如果整個緩存集群故障,而且沒有備份,那么只能去慢慢將緩存重建。為了讓部分用戶還是可用的,可以根據(jù)系統(tǒng)的承受能力,通過降級方案讓一部分用戶先用起來,將這些用戶的相關緩存重建。另外根據(jù)后臺worker進行緩存數(shù)據(jù)的預熱。

構建需求響應式億級商品詳情頁

商品詳情頁架構,主要包括三部分:
1.商品詳情頁系統(tǒng) : 負責靜態(tài)部分
2.商品詳情頁同意服務系統(tǒng) : 負責動態(tài)部分
3.商品詳情頁動態(tài)服務系統(tǒng) : 給內網(wǎng)其他系統(tǒng)提供一些數(shù)據(jù)服務

商品詳情頁前端結構

前端展示可以分為這么幾個維度:商品維度(標題,圖片,屬性等),主商品維度(商品介紹,規(guī)格參數(shù)),分類維度,商家維度,店鋪維度等,另外還有一些實時性要求比較高的如實時價格,實時促銷,廣告詞,配送至,預售等都是通過異步加載的。

單品頁技術架構發(fā)展

單品頁技術架構經(jīng)歷了4個階段的發(fā)展:
架構1.0
IIS+C#+SQL Server ,最原始的架構,直接調用商品庫獲取響應的數(shù)據(jù),扛不住時加一層memcached來緩存數(shù)據(jù)。

架構2.0
該方案采用了靜態(tài)化技術,按照商品維度生成靜態(tài)化html。
主要思路:
1.通過MQ得到變更通知
2.通過java worker調用多個依賴系統(tǒng)生成詳情頁html
3.通過rsync同步到其他機器
4.通過nginx直接輸出靜態(tài)頁
5.接入層負責負載均衡
該方案的缺點是:
1.假設只有分類,面包屑變了,那么所有相關的商品都要重新刷
2.隨著商品數(shù)量的增加,rsync會成為瓶頸
3.無法迅速響應一些頁面需求變更,大部分都是通過JavaScript動態(tài)改變頁面元素

隨著商品數(shù)量的增加,這種架構的存儲容量達到了瓶頸,而且按照商品維度生成整個頁面會存在如分類維度變更就要全部刷一遍這個分類下所有信息的問題,因此又改版了
一次按照尾號路由到多臺機器。
主要思路:
1.容量問題通過按照商品尾號做路由分散到多臺機器,按照自營商品單獨一臺,第三方商品按照尾號分散到11臺。
2.按維度生成html片段,而不是一個大的html
3.通過nginx SSI 合并片段并輸出
4.接入層負責負載均衡
5.多機房部署也無法通過rsync同步,而是使用部署多套相同的架構來實現(xiàn)
該方案的缺點是:
1.碎片文件太多,導致無法rsync
2.機械盤做SSI合并時,高并發(fā)時性能差,此時我們還沒嘗試SSD
3.模板如果要變更,數(shù)億商品需要數(shù)天才能刷完
4.到達容量瓶頸時,我們會刪除一部分靜態(tài)化商品,然后通過動態(tài)渲染輸出,動態(tài)渲染輸出在高峰時會導致依賴系統(tǒng)壓力大,扛不住
5.還是無法迅速響應一些業(yè)務需求
我們的痛點是:
1.之前的架構的問題存在容量問題,很快就會出現(xiàn)無法全量靜態(tài)化問題,所以還是需要動態(tài)渲染;不過對于全量靜態(tài)化渲染還可以通過分布式文件系統(tǒng)解決,沒嘗試。
2.最主要的問題是隨著業(yè)務的發(fā)展,無法迅速滿足變化的需求

架構3.0
我們要解決的問題:
1.能迅速響應瞬變的需求和其他需求
2.支持各種垂直化頁面改版
3.頁面模塊化
4.ab測試
5.高性能,水平擴容
6.多機房多活,異地多活
主要思路:
1.數(shù)據(jù)變更還是通過mq
2.數(shù)據(jù)異構worker得到通知,然后按照一些維度進行數(shù)據(jù)存儲,存儲到數(shù)據(jù)異構JIMDB集群(JIMDB:redis+持久化引擎),存儲的數(shù)據(jù)都是未加工的原子化數(shù)據(jù),如商品基本信息,商品擴展屬性,商品其他一些信息,商品規(guī)格參數(shù),分類,商家信息等。
3.數(shù)據(jù)異構worker存儲成功后,會發(fā)送一個mq給數(shù)據(jù)同步worker,數(shù)據(jù)同步worker也可以叫做數(shù)據(jù)聚合worker,按照相應的維度聚合數(shù)據(jù)存儲到相應的JIMDB集群;三個維度信息:基本信息(基本信息+擴展屬性等的一個聚合),商品介紹(PC版,移動版),其他信息(分類,商家等維度,數(shù)據(jù)量小,直接redis存儲)
4.前端展示分為2個:商品詳情頁和商品介紹,使用nginx+lua技術獲取數(shù)據(jù)并渲染模板輸出。
另外我們目前的架構的目標不僅僅是為商品詳情頁提供數(shù)據(jù),只要是key-value結構獲取而非關系結構的我們都可以提供服務,我們叫做動態(tài)服務系統(tǒng)。該動態(tài)服務分為前端和后端,即公網(wǎng)還是內網(wǎng),如目前該動態(tài)服務為列表頁,商品對比,微信單品頁,總代等提供相應的數(shù)據(jù)來滿足和支撐其業(yè)務。

3.詳情頁架構設計原則
1.數(shù)據(jù)閉環(huán)
數(shù)據(jù)閉環(huán)即數(shù)據(jù)的自我管理,或者說是數(shù)據(jù)都在自己的系統(tǒng)里維護,不依賴于任何其他系統(tǒng),去依賴化。這樣的好處是別人抖動跟我沒關系。
a)數(shù)據(jù)異構是數(shù)據(jù)閉環(huán)的第一步,將各個依賴系統(tǒng)的數(shù)據(jù)拿過來,按照自己的要求存儲起來
b)數(shù)據(jù)原子化,數(shù)據(jù)異構的數(shù)據(jù)是原子化數(shù)據(jù),這樣未來我們可以對這些數(shù)據(jù)進行再加工再處理而想要變化的需求
c)數(shù)據(jù)聚合,將多個原子化的數(shù)據(jù)聚合為一個大json數(shù)據(jù),這樣前端展示只需要一次get,當然要考慮系統(tǒng)架構,比如我們使用的redis改造,redis又是單線程系統(tǒng),我們需要
部署更多的redis來支持更高的并發(fā),另外存儲的值要盡可能的小。
d)數(shù)據(jù)存儲,我們使用JIMDB,redis持久化存儲引擎,可以存儲超過內存N倍的數(shù)據(jù)量,我們目前一些系統(tǒng)是redis+LMDB引擎的存儲,是配合ssd進行存儲;另外我們使用hash tag
機制把相關的數(shù)據(jù)哈希到同一個分片,這樣mget時不需要跨分片合并。
我們目前的異構數(shù)據(jù)是鍵值結構的,用于按照商品維度查詢,還有一套異構是關系結構的,用于關系查詢使用。

數(shù)據(jù)維度化

對于數(shù)據(jù)應該按照維度和作用進行維度化,這樣可以分離存儲,進行更有效的存儲和使用。

拆分系統(tǒng)

將系統(tǒng)拆分為多個子系統(tǒng)雖然增加了復雜度,但是可以得到更多的好處,比如數(shù)據(jù)異構系統(tǒng)存儲的數(shù)據(jù)是原子化數(shù)據(jù),這樣可以按照一些維度對外提供服務;而數(shù)據(jù)同步系統(tǒng)存儲的 是聚合數(shù)據(jù),可以為前端展示提供高性能的讀取。前端展示系統(tǒng)分離為商品詳情頁和商品介紹,可以減少互相影響;目前商品介紹系統(tǒng)還提供其他的一些服務,比如全站異步腳本服務。

worker無狀態(tài)化+任務化

worker無狀態(tài)化+任務化,可以幫助系統(tǒng)做水平擴展。
1.數(shù)據(jù)異構和數(shù)據(jù)同步worker無狀態(tài)化設計,這樣可以水平擴展
2.應用雖然是無狀態(tài)的,但是配置文件是有狀態(tài)的,每個機房一套配置,這樣每個機房只讀取當前機房數(shù)據(jù)
3.任務多隊列化,等待隊列,排重隊列,本地執(zhí)行隊列,失敗隊列
4.隊列優(yōu)先級化,分為:普通隊列,刷數(shù)據(jù)隊列,高優(yōu)先級隊列,例如一些秒殺商品會走高優(yōu)先級隊列保證快速執(zhí)行。
5.副本隊列,當上線后業(yè)務出現(xiàn)問題時,修正邏輯可以回放,從而修復數(shù)據(jù);可以按照比例如固定大小隊列或者小時隊列設計
6.在設計消息時,按照維度更新,比如商品信息變更和商品上下架分離,減少每次變更接口的調用量,通過聚合worker去做聚合。

異步化+并發(fā)化

我們系統(tǒng)使用了大量的異步化,通過異步化機制提升并發(fā)能力。首先使用了消息異步化進行系統(tǒng)解耦,通過消息通知變更,然后再調用相應接口獲取相關數(shù)據(jù);之前老系統(tǒng)使用同步推送機制,這種方式系統(tǒng)是緊耦合的,出問題需要聯(lián)系各個負責人重新推送還要考慮失敗重試機制。數(shù)據(jù)更新異步化,更新緩存時同步調用服務,然后異步更新緩存??墒沟萌蝿詹⑿谢?br /> 商品數(shù)據(jù)來源有多處,但是可以并發(fā)調用聚合,經(jīng)過這種方式我們可以把原先串行1s的時間提示到300ms內。異步請求合并,一次請求調用就能拿到所有的數(shù)據(jù)。前端服務異步化/聚合,實時價格,實時庫存異步化,使用如線程或協(xié)程機制將多個可并發(fā)的服務聚合。異步化還有一個好處是可以對異步請求做合并,原來的n個調用可以合并為一次,還可以做請求的排重。

多級緩存化
1.瀏覽器緩存,當頁面之間來回跳轉時走local cache,或者打開頁面時拿著Last-Modified去CDN驗證是否過期,減少來回傳輸?shù)臄?shù)據(jù)量2.cdn緩存,用戶去離自己最近的cdn節(jié)點拿數(shù)據(jù),而不是全部回源到北京機房獲取數(shù)據(jù),提升訪問性能3.服務端應用本地緩存,我們使用nginx+lua架構,使用HttpLuaModule模塊的shared dict做本地緩存(reload不丟失)或內存級Proxy Cache,從而減少帶寬。另外我們還可以使用一致性哈希(如商品編號/分類)做負載均衡內部對url重寫提升命中率我們對mget做了優(yōu)化,如取商品其他維度數(shù)據(jù),分類,面包屑,商家等差不多8個維度數(shù)據(jù),如果每次mget獲取性能差而且數(shù)量很大,30kb以上;而這些數(shù)據(jù)緩存半小時也是沒有問題的,那么我們可以設計為先讀local cache,然后把不命中的再回源到remote cache獲取,這個優(yōu)化減少了一半的remote cache 流量。4.服務端分布式緩存,我們使用內存+ssd+JIMDB持久化存儲
動態(tài)化
數(shù)據(jù)獲取動態(tài)化,商品詳情頁:按維度獲取數(shù)據(jù),如商品基本數(shù)據(jù),其他數(shù)據(jù);而且還可以根據(jù)數(shù)據(jù)屬性,按需做邏輯,比如虛擬商品需要自己定制的詳情頁,那么我們就可以跳轉走,比如全球購的需要走jd.hk域名,也是沒問題。模板渲染實時化,支持隨時變更模板需求。重啟應用秒級化,使用nginx+lua架構,重啟速度快,重啟不丟失共享字段緩存數(shù)據(jù);需求上線速度化,因為我們使用了nginx+lua架構,可以快速上線和重啟應用,不會產生抖動;另外lua本身是一個腳本語言,我們也嘗試把代碼如何版本化存儲,直接內部驅動lua代碼更新而不需要重啟nginx。
彈性化
我們所有的應用業(yè)務都接入了docker容器,存儲還是物理機。我們會制作一些基礎鏡像,把需要的軟件打成鏡像,這樣就不用每次去運維那安裝部署軟件了。未來可以支持自動擴容,比如按照cpu或帶寬自動擴容機器。
降級開關
推送服務器推送降級開關,開關集中化維護,然后通過推送機制推送到各個服務器??山导壍亩嗉壸x服務為:前端數(shù)據(jù)集群 -> 數(shù)據(jù)異構集群 -> 動態(tài)服務(調用依賴系統(tǒng)),這樣就可以保證服務質量,假設前端數(shù)據(jù)集群壞了一個磁盤,還可以回源到數(shù)據(jù)異構集群獲取數(shù)據(jù)。開關前置化,如nginx->tomcat,在nginx做開關,請求就到不了后端,減少后端壓力??山导壍臉I(yè)務線程池隔離。我們可以把處理過程分解為一個個的事件。通過這種將請求劃分事件的方式我們可以進行更多的控制。如,我們可以為不同的業(yè)務再建立不同的線程池進行控制:即我們只依賴tomcat線程池進行請求的解析,對請求的處理可以交給我們自己的線程池去完成。這樣tomcat線程池就不是我們的瓶頸了。通過使用這種異步化事件模型,我們可以提高整體的吞吐量,不讓慢速的A業(yè)務影響到其他業(yè)務。慢的還是慢,但是不影響其他業(yè)務。
多機房多活
應用無狀態(tài),通過在配置文件中配置各個資方的數(shù)據(jù)集群來完成數(shù)據(jù)讀取。數(shù)據(jù)集群采用一主三從結構,防止當一個機房掛了,另一個機房壓力大產生抖動。
多種壓測方案
線下壓測使用apache ab,apache Jmeter,這種方式是固定url壓測,一般通過訪問日志收集一些url進行壓測,可以簡單壓測單機峰值吞吐量,但是不能作為最終的壓測結果,因為這種壓測會存在熱點問題。線上壓測,可以使用Tcpcopy直接把線上流量導入到壓測服務器,這種方式可以壓測出機器的性能,還可以把流量放大,也可以使用nginx+lua協(xié)程機制把流量分發(fā)到多臺壓測服務器,或者直接在頁面埋點,讓用戶壓測,此種壓測方式可以不給用戶返回內容。

遇到的一些問題

SSD性能差
使用SSD做KV存儲時發(fā)現(xiàn)磁盤IO非常低。初步懷疑:線上系統(tǒng)用的是消費級硬盤。RAID卡設置,write back 和 write through策略。實驗用的是dd壓測,嚴格測試應該用FIO等工具。
鍵值存儲選型壓測
對于存儲選型,嘗試過LevelDB,RocksDB,BeansDB,LMDB,Riak等,最終選擇LMDB。
數(shù)據(jù)量大時JIMDB同步不動
JIMDB 數(shù)據(jù)同步時要 dump 數(shù)據(jù),ssd磁盤容量用了50%以上,dump到同一塊磁盤容量不足。切換主從之前的架構是一主二從(主機房一主一從,備機房一從)切換到備機房時,只有一個主服務,讀寫壓力大時有抖動,因此改造為一主三從。
分片配置
之前的架構分片邏輯分散到多個子系統(tǒng)的配置文件中,切換時需要很多系統(tǒng),解決方案:1.引入Twemproxy中間件,我們使用本地部署的 Twemproxy 來維護分片邏輯2.使用自動部署系統(tǒng)推送配置和重啟應用,重啟之前暫停mq消費保證數(shù)據(jù)一致性3.用unix domain socket減少連接數(shù)和端口占用不釋放啟動不了服務的問題
模板元數(shù)據(jù)存儲HTML
起初不確定lua做邏輯和渲染模板性能如何,就盡量減少for,if/else之類的邏輯;通過java worker組成html片段存儲到jimdb,html片段會存儲諸多問題,假設未來變了也是需要全量刷出的,因此存儲的內容最好就是元數(shù)據(jù)。因此通過線上不斷壓測,最終jimb只存儲元數(shù)據(jù),lua做邏輯和渲染,邏輯代碼在3000行以上,模板代碼1500以上,其中包括大量for,if/else語句,目前渲染性能可以接受庫存接口訪問量600w/分鐘,因為是詳情頁展示的數(shù)據(jù),緩存幾秒是可以接受的,因此開啟nginx proxy cache 來解決該問題。目前用的是nginx+lua架構改造服務,數(shù)據(jù)過濾,url重寫等在nginx層完成。 通過url重寫+一致性哈希負載均衡,不怕隨機url,一些服務提升了10%以上的緩存命中率。
微信接口調用鏈暴增
通過訪問日志發(fā)現(xiàn)某ip頻繁抓取,而且按照商品編號遍歷,但是會有一些不存在的編號,解決方案是:1.讀取kv存儲的部分不限流2.回源到服務接口的進行請求限流,保證服務質量
開啟nginx proxy cache 性能不升反降
開啟nginx proxy cache 后,性能下降,而且過一段時間內存使用率達到98%,解決方案是:1.對于內存占有率高的問題是內核問題,內核使用lru機制,本身不是問題,不過可以修改內核參數(shù):sysctl -w vm.extra_free_bytes=6436787sysctl -w vm.vsf_cache_pressure=100002.使用proxy cache在機械盤上性能差可以通過tmpfs緩存或nginx共享字典緩存元數(shù)據(jù),或者使用ssd,目前使用內存文件系統(tǒng)。
配送至’讀服務因依賴太多,響應時間偏慢
'配送至'服務每天有數(shù)十億調用量,響應時間慢,解決方案是:1.串行獲取變并發(fā)獲取,這樣一些服務可以并發(fā)調用2.預取依賴數(shù)據(jù)回傳
網(wǎng)絡抖動時,返回502錯誤
Twemproxy 配置的timeout時間太長,之前設置5s,而且沒有分別對連接,讀,寫設置超時。后來我們減少了超時時間,內網(wǎng)設置150ms以內,當超時時訪問動態(tài)服務。
機器流量太大
雙11期間,服務器網(wǎng)卡流量到了400Mbps,cpu30%左右。原因是我們所有的壓縮都在接入層完成,因此接入層不再傳入相關請求頭到應用,隨著流量的增大,接入層壓力過大。因此我們把壓縮下方到各個業(yè)務應用,添加了相應的請求頭,nginx gzip壓縮級別在2~4吞吐量最高,應用服務器流量降了差不多5倍。

總結

以上是生活随笔為你收集整理的大型电商网站设计——商品详情页面静态化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。