Discuz!X集羣部署的系統方案和改造方式討論

多WEB部署時,面臨的核心問題是WEB服務器間的數據共享和同步。就數據存儲的方式而言,Discuz數據包含兩部分,一部分存儲在MySQL數據庫中(用戶、帖子等文本類、結構化的數據),一部分存儲爲文件(附件、緩存文件等)。其中存儲在MySQL中的數據能夠方便地在多服務器間共享,擴展和冗餘也已經有比較成熟的方案。這裏咱們主要討論Discuz文件類型的數據,部分涉及到多臺MySQL服務器的內容。

Discuz文件類型的數據都存儲於DISCUZ_ROOT/data目錄,各目錄主要功能以下:
data/p_w_upload 附件類
data/log 運行日誌
data/cache 配置參數類緩存文件(默認是sql,配置參數經過pre_common_syscache表緩存)、CSS緩存、部分JS緩存
data/template 模塊緩存

data/threadcache 論壇頁面緩存(針對遊客的優化)php


DISCUZ_ROOT/data目錄下有幾個重要的文件(文件鎖)
data/install.lock 安裝程序鎖定。若是該文件存在,DISCUZ_ROOT/install/中的安裝程序不能執行。

data/sendmail.lock 發送郵件鎖。Discuz默認經過相似home.php?mod=misc&ac=sendmail&rand=1379315574這個隱藏頁面調用,由用戶的瀏覽行爲觸發郵件發送流程(瀏覽器側用一個300秒的cookie控制頻率,服務器側經過sendmail.lock文件的mtime控制頻率5秒)。若是能夠控制 服務器,應該優化掉這個機制。前端

data/updatetime.lock 某管理後臺使用的鎖。

data/update.lock 系統升級鎖。執行版本升級程序(如x2升級到x3)時,會生成這個文件鎖。node


下面這些功能會涉及到多web服務器間的數據共享和同步,默認Discuz經過MySQL實現。
用戶session 表pre_common_session
管理面板session 表pre_common_admincp_session

系統配置項緩存 表pre_common_syscachemysql


咱們假設部署兩臺web服務器的場景(且web服務器也是php應用服務器)。咱們須要解決data目錄共享的問題,引入NFS服務能夠簡單解決這個問題。服務器複用,此處不表。這裏會有一個選擇,哪些目錄放置在NFS上,從上面的分析來看,將data目錄放置在NFS上便可,即各web服務器均獨立部署程序文件,將NFS掛載到data目錄節點,缺點是須要將程序文件部署到每一臺web服務器上,要解決程序文件更新部署的問題,優勢是能夠節省web服務器經過網絡取NFS上的程序文件的開銷。若是圖方便,也能夠把程序文件也放到NFS上,則全部文件都只有一個副本了,程序更新也很方便,缺點是會增長web服務器經過網絡取程序文件的開銷。這二者須要權衡,建議第一種。linux


上面的方案存在一些問題。當用戶訪問一個附件時,WEB服務器都須要經過網絡從NFS上取文件,這給內網網絡帶來了壓力和一些沒必要要的開銷,這能夠經過在web前端增長緩存機制來緩解(如squid,nginx的proxy cache等)。爲靜態資源配置單獨的域名供訪問也是值得實施的工做,Discuz能夠很簡單的作到這一點,經過配置「本地附件URL地址」項就能夠實現附件類(data/p_w_upload目錄裏的)文件URL重構,但後臺發佈的廣告不行,有BUG(在X3版本測試)。nginx


在使用文件鎖,且依賴於文件的mtime等時間值執行邏輯時,請務必保證服務器時鐘的一致性。web


上面的方案簡單,且對Discuz的改造很小,維護成本低,適合單臺服務器向多臺服務器(數量較少)擴展時選擇。隨着訪問量和web節點的增長,內網流量,NFS,MySQL均須要進行擴展。MySQL的擴展有較成熟的方案,如主重複制機制。NFS這個稍稍麻煩一點,且不少人詬病NFS的文件共享機制不安全;論壇附件以較小尺寸(幾百字節不等)的文件居多,而linux的ext文件系統的塊大小通常是4K,從而浪費了存儲空間,對inode的利用率也很差;從長遠來看,NFS終將成爲系統的瓶頸,咱們有必要從新規劃文件共享/同步機制。redis


這裏咱們先討論 文件共享問題MySQL擴展涉及的問題後面一點再說明。

目前不少公司都有解決大量小文件存儲的方案,如國內某大互聯網公司使用基本MangoDB的GridFS等。實現的細節不在討論範圍,其基本思想就是構建文件存儲服務,把附件類靜態文件存儲到遠端(遠端系統返回一個URL供訪問),而且由這個遠端系統處理用戶訪問的各類優化等。咱們討論若是已經有了這樣的一個服務,Discuz接入到這樣的一個服務須要注意些什麼。sql


在這以前,咱們來看一下Discuz的附件上傳、存儲流程。

帖子附件的上傳和帖子的發佈是異步的。附件上傳後的實體文件會被存儲到相似data/p_w_upload/forum/201307/20/路徑,同時會在附件表中(pre_forum_p_w_upload_unused)添加相應記錄,發佈帖子時,這些記錄被散列分佈到相應的附件表(pre_corum_p_w_upload_[0-9])中,並標識其所屬的pid, tid。這是附件本地存儲的流程。數據庫


遠程附件

Discuz支持「遠程附件」功能(全局-上傳設置-遠程附件)。「遠程附件」功能支持將附件經過FTP的方式存儲到遠端系統,若是網站當前沒有文件存儲服務,但又想將文件存儲分離,使用這個內建功能也是一種不錯的選擇,畢竟FTP很好維護,也不須要對Discuz進行改造。雖然Discuz默認只支持FTP方式,但遠端存儲在功能接口層面基本是共同的概念,添加、刪除之類。因此當要把「遠程附件」擴展到自有的遠端文件存儲服務時,一個比較好的實踐是繼承Discuz的ftp類,用遠端文件存儲的功能重寫Discuz的ftp類定義的各方法,而後在ftp類實例化的地方,調用這個新的子類;若是不打算保留默認的FTP機制,甚至能夠直接修改Discuz的ftp類實現,這樣連ftp類實例化的地方也不用修改了。這樣的處理對Discuz的改造最小,細節都隱藏在了ftp類的實現中,遵照與ftp相同的行爲模式。


遠程附件將大部分的靜態資源流量分離,咱們能夠分別的優化兩個系統。但Discuz的遠程附件的行爲模式仍須要咱們注意,咱們必須清楚它是怎樣運行的,是否有某種陷阱,以便於系統的某些功能行爲表現異常時,內心有底。


1)開啓遠程附件時,附件上傳、存儲流程
附件異步上傳階段與本地存儲是同樣的,附件始終會先被上傳到相似data/p_w_upload/forum/201307/20/路徑,同時會在附件表中(pre_forum_p_w_upload_unused)添加相應記錄,發佈帖子時,這些記錄被散列分佈到相應的附件表(pre_corum_p_w_upload_[0-9])中,並標識其所屬的pid, tid。而後向遠端上傳附件數據,上傳完成後,會刪除掉本地的副本,並將附件表(pre_corum_p_w_upload_[0-9])的remote字段標識爲遠程附件類型。
2)在什麼時間點執行向遠端上傳
附件上傳到遠端的時間點是發佈帖子的時候,且在該進程週期內,上傳該帖子的全部附件(阻塞模型)。這會有一些風險點,若是上傳附件到無故系統花費的時間較長(一個帖子的附件不少、很大,或網絡帶寬限制),用戶界面的響應體驗會有很明顯的卡頓感,甚至頁面超時失敗。
3)已知會被存儲到遠端的數據
帖子附件
4)已知不會存儲到遠端的數據(或者說會被存儲在本地)
  • 帖子附件在上傳到無故以前,會存儲在本地。
  • 「遊客查看小圖」(須要啓用「遊客登錄查看大圖」功能),小圖是用相似forum.php?mod=p_w_picpath&aid=4&size=100x100&key=05869b37379ff990&type=1的調用生成的。小圖生成並存儲在相似data/p_w_upload/p_w_picpath/000/00/00/123.gif ,且會在本地保留副本。
  • 活動貼的封面不會存儲爲遠程附件。
  • 編輯帖子時,顯示的縮略圖(所見即所得)是即時生成的(經過網絡訪問遠端獲取原圖),生成這個縮略圖的過程當中,會把圖片存儲到data/p_w_upload/temp目錄,圖片數據內容輸出到瀏覽器後,該位置存儲的的縮略圖被會刪除。
  • 圖片附件的縮略圖都是經過forum.php?mod=p_w_picpath&xxx 這個模塊生成的。用戶能夠經過拼裝請求,使服務器側執行生成遠程附件的本地副本的流程,因此這裏可能存在某種風險。


從上面關於「遠程附件」的討論咱們看到,即便啓用了「遠程附件」機制,但Discuz仍然會在衆多功能上用到data目錄下的多個子目錄,且有的子目錄還必須在多個web服務器間共享(如data/p_w_upload)。因此若是不對這些功能點進行改造,咱們仍然須要NFS這個設備,畢竟遠程附件已經將大部分的流量分走了,NFS是保證業務正常運行的最簡單的辦法,誰知道還有多少其它功能會依賴於此呢?


MySQL擴展
在Discuz的業務模型上,對MySQL進行擴展使用最普遍的機制是「主從複製」、「讀寫分離」。Discuz自己也支持這些機制,最近的版本還支持分庫的部署,咱們不討論這些機制部署的細節,咱們討論將Discuz部署到這樣的環境中時,須要作出的一些調整。
1)複製延遲形成的主庫、從庫數據不一樣步。
複製機制的延遲雖然很小,但總存在,即便是這種很小延遲,也足以給系統帶來行爲異常,特別是實施「讀寫分離」的場景。在Disuz業務場景中,如下狀況是曾經出現過的:在後臺配置的參數不起做用,有多是配置項寫入了主庫(pre_common_setting),同時在主庫刪除了配置的緩存(pre_common_syscache),但在數據同步到從庫以前,另外一個請求發現(pre_common_syscache)沒有緩存,因而觸發了生成緩存的流程,這個流程會在從庫中讀到舊的配置項(「讀」操做是在從庫)。相似的場景還有不少,如刷積分,管理員進後臺異常等。解決相似這種與時序相關的場景,每每須要針對個例去處理,就緩存這個問題,建議是在程序更新時間點,統一輩子成好緩存,且避免線上對配置的操做。

2)僅將需持久化的數據同步到從庫。

如pre_common_session、pre_common_admincp_session, pre_forum_threadaddviews這類數據是不該該同步到從庫的,它們更新很是頻繁,且都是臨時性的,它們應該被配置爲忽略同步的表(replicate_wild_ignore_table選項),或者更好的辦法是經過其它機制處理(如memcache、redis等)來實現,從而完全從數據庫中分離。從庫同步主庫的寫操做時,一樣會使用寫鎖,而這些性能開銷是沒必要要的、應該優化,以使從庫最大限度的服務於核心內容的讀取查詢。


其它一些多web部署時要注意的問題

改造內置計劃任務。

默認Discuz內置的計劃任務是經過用戶瀏覽行爲觸發的,若是能控制 服務器,這應該改爲用操做系統的計劃任務驅動,Discuz!提供了api.php?mod=cron,稍做改造便可。僅在某一臺WEB服務器上部署,而且將Discuz的每一個任務單獨部署成操做系統計劃任務的一項,有一種選擇是隻部署一個「每分鐘」週期的計劃任務,而後由這個任務每分鐘的輪詢操做來驅動Discuz內置的計劃任務機制,不建議這種作法,計劃任務的數量畢竟是頗有限的、執行的頻率也是有計劃的。


使用「文件」緩存配置項數據。
$_config['cache']['type'] = 'file'
Discuz默認是使用的MySQL存儲配置緩存(表pre_common_syscache)。
$_config['cache']['type'] = 'sql '
若是開啓了內存緩存機制,如memcache,這些緩存數據會放到緩存中,以緩解數據庫的壓力。這些緩存項是每一個動態頁面都須要調用的,因此若是瀏覽量高的話,這種方式在網絡等方面的開銷累計起來就很可觀了。

建議使用文件緩存這部分數據,緩存文件跟着程序文件一塊兒部署,每套程序文件都有一套配置項的緩存文件的副本,從而徹底優化掉了這部分開銷。優勢:將配置項的緩存寫入文件是Discuz內置的機制,無需改造;磁盤文件IO穩定性是最好的,成本是最低的,避免中心節點故障帶來的風險;上線時間點全部配置項緩存已經生成,客觀上達到了「暖緩存」效果。缺點:在系統上線部署以前,須要生成所有的配置項緩存文件,Discuz默認的生成緩存文件的策略(Discuz默認的策略是「找不到緩存再生成」,對於短期併發較高的系統,這種策略每每會形成多個處理進程同時觸發寫緩存的狀況)不能知足這個需求,這須要一些開發量。


在具體實施時,有一些建議。生成的緩存文件同程序源代碼同樣歸入版本控制,以便於跟蹤配置變化。而經過後臺UI操做,存儲在數據庫中的配置數據則沒有這麼方便。配置項緩存的生成比較簡單,按照pre_common_syscache表的記錄生成便可。


同理,模板緩存文件也能夠在上線部署前完成生成,只是模板緩存文件初始化會麻煩一些,建議收集最經常使用頁面的入口,創建腳原本觸發。


若是要覆蓋Discuz默認的配置項的值,建議啓用一個配置文件,用新值覆蓋舊值,儘可能避免管理後臺UI操做,特別是線上環境,由於配置參數最終須要與配置項緩存文件同步才能起做用。


全部WEB服務器上部署程序的路徑要徹底同樣。Discuz的緩存項,會生成一些絕對路徑,一個例子是「本地附件保存位置」配置項(經過管理後臺「全局-上傳設置-基本設置」操做)。
mysql> select * from pre_common_setting where skey='attachdir';
+-----------+-------------------+
| skey | svalue |
+-----------+-------------------+
| attachdir | ./data/p_w_upload |
+-----------+-------------------+

表pre_common_syscache cname=' setting'
mysql> SELECT * FROM pre_common_syscache WHERE `data` LIKE '%p_w_upload%';
在輸出結果中查找'attachdir',有相似下面的內容
s:9:"attachdir";s:39:"D:/Apache/htdocs/./data/p_w_upload/";

這些絕對路徑須要在全部WEB服務器上存在,且功能匹配。若是網站的部署路徑發生了變動,從新生成這些緩存項值。

「論壇頁面緩存」 :「論壇首頁緩存」(只針對遊客有效)、緩存帖子;這些緩存數據默認被存儲於data/threadcache目錄,特別的,這些緩存數據沒有過時刪除機制,每每會生成大量緩存文件,須要注意監控緩存目錄的狀況。有些建議是使用相似memcache這樣的有過時控制機制的設備來改造這個緩存,可能須要權衡網絡的負載。
「優化更新主題瀏覽量」、「附件下載量延遲更新」選項。若是是經過寫文件的方式緩存這部分數據,建議把這些文件存儲在各web服務器上,而不是寫到NFS上(以減小網絡開銷,各類鎖),在把數據彙總到數據庫中時,需清理每臺WEB服務器上的相關日誌。最近的X版本經過「pre_forum_threadaddviews」表來緩存主題瀏覽量,我的感受寫文件的方式對於均衡性能方面更好。
經過上面的優化步驟後,只有data/p_w_upload目錄須要在各web服務器間共享,因此把NFS掛載到該目錄便可。保持這個位置能夠在WEB服務器間共享,能夠在最小改造下避免衆多已知和不預知的問題場景出現。
mount /PATH/TO/DISCUZ_ROOT/data/p_w_upload NFS_SERVER:/PATH
相關文章
相關標籤/搜索