Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

2.4. 網站靜態內容出版

2.4.1. 架構總覽

www 負責靜態檔案瀏覽, 台數不定, 可以採用零成本的DNS輪詢, 或者4層LVS, 或者7層HAProxy, 還可以用F5, Array 等負載均衡設備.

cms 負責靜態檔案生成. 生成後的檔案要同步到www中, 或者採用網絡共享, 再者使用分散式檔案系統, 總之將生成的檔案交給www伺服器, 根據你壓力橫向擴展即可

img 負責圖片檔案瀏覽. 通過給圖片加版本號, 結局圖片更新問題, 這樣更新網站不用頻繁刷新 CDN

這裡不談論負載均衡, 以及存儲方案, 有情緒可以延伸閲讀: http://netkiller.github.com/architect/index.html

你掌握了這個方案, 你可以很容易實現 向"京東商城", "VANCL凡客誠品", "走秀網" 這樣的網站

這些網站的特點是: 瀏覽量大, 數據存儲主要是圖片, 登錄/註冊與購物車,用戶中心 訪問量只占到 5% 使用負責均衡很容易解決.

靜態化網站可不避免的使用ajax做局部更新, ajax請求也要考慮緩存問題

靜態化另一個目的是改善SEO

首次訪問伺服器

  1. 訪問www伺服器

  2. nginx 判斷檔案是否存在,如果存在將檔案顯示出來

  3. 如果檔案不存在,去cms伺服器上查找, 如果存在便返回給www伺服器,並顯示出來

  4. 如果cms上檔案不存在,cms伺服器便使用rewrite生成該檔案, 同時將內容返回給www伺服器,www將內容緩存在自己的伺服器上,並將內容顯示出來

第二次訪問

  1. 訪問www伺服器

  2. nginx 判斷檔案是否存在,如果存在將檔案顯示出來

  3. 如果檔案不存在,去cms伺服器上查找, 如果存在便返回給www伺服器,並顯示出來

  4. 如果cms上檔案不存在,cms伺服器便使用rewrite生成該檔案, 同時將內容返回給www伺服器,www將內容緩存在自己的伺服器上,並將內容顯示出來

2.4.2. 靜態化實現手段有哪些?

靜態化方法包括:

  1. 生成方式

  2. 抓取方式

  3. 偽靜態化

  4. 混合方式

2.4.2.1. 生成方式

主要由程序實現

例如

			
content = "<html><title>my static</title><body>hello world</body></html>"
file = open( your static file)
file.write(content)
file.close()
			
			

2.4.2.2. 抓取方式

主要由程序實現

程序中抓取

			
content = get_url('http://netkiller.8800.org/index.php')
file = open( index.html)
file.write(content)
file.close()
			
			

使用軟件抓取,不僅限于wget。

			
wget http://netkiller.8800.org/index.php -O index.html
			
			

這時只給出簡單例子,使用複雜參數實現更複雜的拾取,然後將腳本加入crontab中可。

2.4.2.3. 偽靜態化

偽靜態化是主要是通過在URL上做一些手腳,使你看去是靜態的,實質上它是動態腳本。

偽靜態化實現主要包括兩種方法:

  1. Rewrite rule

  2. path_info

下面是一個PATH_INFO例子

http://netkiller.8800.org/zh-cn/photography/browse/2009.html

根本就不存在這個目錄'zh-cn/photography/browse/'和檔案'2009.html'

下面是一個Rewrite例子

http://example.org/bbs/thread-1003872-1-1.html

2.4.2.4. 混合方式

其實目前網站使用的基本上都是上面幾種方法混合方式。

例如首先將動態url(example.org/news.php?cid=1&id=1) 通過rewrite轉換為 (example.org/new_1_1.html)

接下來就比較容易解決了,一種方法是使用wget example.org/new_1_1.html,另一種方法你無需靜態化,直接使用squid規則配置讓他永不過期

2.4.2.5. 靜態化中的動態內容

在靜態化頁面中有一些內容是無法實現靜態的。像登錄信息,用戶評論等等

我們用三種方法實現靜態中嵌入動態內容:

  1. iframe - 靈活性差

  2. SSI - 消耗web伺服器資源

  3. Ajax - 依賴瀏覽器,穩定性差

2.4.3. cdn

如何使用 cdn 來緩存你的網站內容

讓你的網頁緩存在 cdn 節點上的方式有下面幾種

  1. 讓cdn的客服幫你配置緩存的規則, 他們很喜歡一刀切, 例如所有html都緩存2小時

  2. 在他們管理後台自行使用正則配置緩存的時間, 這個他們一般不會提供, 某些公司的CDN會提供這個功能. 非常方便.

  3. 通過HTTP頭自行控制緩存時間, 一般是使用 max-age / s-maxage / Last-Modified 判斷緩存時間

我比較喜歡最後一種, 通常我們使用max-age 與 s-maxage 同時使用, 這樣我可以按照我的意向來決定檔案的緩存時間 這裡有更詳細的解釋說明.

2.4.4. www 伺服器

下面給出一個精簡後的配置例子

如果檔案不存在就會連接後端cms伺服器生成檔案,並且顯示出來,同時加上緩存. 生成的檔案會從cms中同步到www伺服器上.

你可以採用

  1. rsync同步方案

  2. nfs/samba/gluster 共享方案

  3. iscsi 共享存儲方案

  4. 分散式檔案系統方案

參考閲讀: 分散式檔案系統 , Netkiller Linux Storage 手札

		
upstream  cms.mydomain.com  {
  server   192.168.2.11		weight=5	max_fails=3  fail_timeout=30s;
  server   192.168.2.21     weight=5	max_fails=3  fail_timeout=30s;
  server   192.168.2.23 backup;
  server   192.168.2.23 down;
}

server {
    listen       80;
    server_name  www.mydomain.com;

    charset utf-8;
    access_log  /var/log/nginx/www.mydomain.com.access.log  main;

    location / {
        root   /www/mydomain.com/www.mydomain.com;
        index  index.html index.htm;

		if ($request_uri ~* "\.(ico|css|js|gif|jpe?g|png|html)$") {
            expires 1d;
        }
        if ($request_uri ~* "\.(xml|json)$") {
            expires 1m;
        }

		valid_referers none blocked *.mydomain.com;
		if ($invalid_referer) {
			#rewrite ^(.*)$  http://www.mydomain.com/cn/$1;
			return 403;
		}

        proxy_intercept_errors  on;
        if (!-f $request_filename) {
          proxy_pass http://cms.mydomain.com;
          break;
        }
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}
		
		

2.4.5. cms 伺服器

CMS 內容管理系統的主要功能

  1. 內容分類管理

  2. 內容模板管理

  3. 內容編輯與發佈

  4. 內容生成

服務應該實現

  1. 當發現目錄中檔案不存, 通過rewrite生成html, 這樣可能根據需要生成html頁面

  2. 當頁面更新的時候,應該通過api 刷新cdn的緩存, 圖片的版本好應該加一

  3. 將頁面分成多個模組, 通過SSI拼裝頁面, 避免有重大改版時, 整站生成HTML.

  4. 避免使用seesion技術, 這樣在負載均衡的時候可以使用最小連接數算法

例如:

		
rewrite ^/product/(phone|notebook)/(\d+).html /product/$1.php?id=$2 last;
		
		

URL 唯一, url設計要考慮唯一性, 不要出現同一個url處理兩個任務, 例如下面的例子, 每個用戶的profile一個URL, 當被訪問的時候便可以緩存在CDN或者用戶瀏覽器上.

		
http://www.mydomain.com/profile/neo.html
http://www.mydomain.com/profile/jam.html
		
		
		
server {
    listen       80;
    server_name  www.mydomain.com;

    #charset koi8-r;
    access_log  /var/log/nginx/www.mydomain.com.access.log  main;

    location / {
        root   /www/mydomain.com/www.mydomain.com;
        index  index.html;
    }
}

server {
    listen       80;
    server_name  cms.mydomain.com;

    charset utf-8;
    access_log  /var/log/nginx/cms.mydomain.com.access.log  main;

    location / {
        root   /www/mydomain.com/cms.mydomain.com;
        index  index.html index.php;

    }

    location ~ ^/(cn|tw)/(comment|affiche)/.*\.html {
		root /www/mydomain.com/www.mydomain.com;
		if (!-f $request_filename) {
			rewrite ^/(cn|tw)/(comment|affiche)/(\d+).html /publish/$2.php?id=$3&lang=$1 last;
		}
    }
    location /xml/ {
		root /www/mydomain.com/www.mydomain.com/xml;
    }
    location ~ ^/(config|include|crontab)/ {
		deny all;
		break;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
	root		/www/mydomain.com/cms.mydomain.com;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /www/mydomain.com/cms.mydomain.com$fastcgi_script_name;
        include        fastcgi_params;
		fastcgi_param  DOCUMENT_ROOT /www/mydomain.com/cms.mydomain.com;
		fastcgi_param  HOSTNAME cms.mydomain.com;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
}
		
		

2.4.6. img

img.mydomain.com

		
server {
    listen       80;
    server_name  img.mydomain.com;

    charset utf-8;
    access_log  /var/log/nginx/img.mydomain.com.access.log  main;

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ico)$
    {
		expires      7d;
    }
    location ~ .*\.(js|css)$
    {
		expires      1d;
    }
    location ~ .*\.(html|htm)$
    {
		expires      15m;
    }

    location / {
        root   /img/mydomain.com/img.mydomain.com;
        index  index.html;

        rewrite  "/theme/([0-9] {4})([0-9] {2})([0-9] {2})/(.+)\.(.+)\.(.+)" /theme/$1/$2/$3/$4.$6;
        rewrite  "/news/([0-9] {4})([0-9] {2})([0-9] {2})/(.+)\.(.+)\.(.+)" /news/$1/$2/$3/$4.$6;
        rewrite  "/product/([0-9] {4})([0-9] {2})([0-9] {2})/(.+)\.(.+)\.(.+)" /product/$1/$2/$3/$4.$6;
    }

}
		
		

/theme/2012/08/15/images.1.jpg 實際上就是 /theme/2012/08/15/images.jpg 檔案

/theme/2012/08/15/images.2.jpg 也是 /theme/2012/08/15/images.jpg

/theme/2012/08/15/images.3.jpg 也是 /theme/2012/08/15/images.jpg

但CDN與你的瀏覽器會每次下載新的檔案, 這樣只要更新CDN中的html頁面即可, 不用去理睬圖片, 你的瀏覽器會用新的地址下載圖片. 這樣就解決了煩瑣的刷新工作.

2.4.7. Ajax 局部更新與緩存

例如我的新聞評論頁面,需要使用ajax技術, 將用戶回覆的品論顯示來, ajax 載入json數據然後局部更新, 我對他做了1分鐘的緩存

		
if ($request_uri ~* "\.(xml|json)$") {
	expires 1m;
}
		
		

如果有新的提交我們可以為json增加版本是控制例如: http://api.mydomain.com/news.json?1.0