10.1期間,一位朋友問我一個apache的 Rewrite規則中一個高級語法RewriteMap的用法問題。其想要實現的是這個功能,有個多用戶的blog,用戶訪問的時候,是用三級域名訪問的。比如http://cfc4n1.blog.cnxct.com,http://cfc4n2.blog.cnxct.com這種三級域名。在服務器上,是對三級域名做泛解析。每個三級域名都生成了一個靜態的html主頁文件。由于用戶數量較多,linux ext硬盤格式上同一目錄文件太多,檢索文件的速度會有折扣,遂將文件打散到不同的目錄下。打散方式是以用戶名【三級域名中的cfc4n1,cfc4n2等】的MD5值的每隔兩位作為一個目錄。MD5的默認長度是32位,每隔兩位分一次目錄的話,那就有16級目錄。每級目錄的目錄名是2個字符,每個字符的有16種可能【0-9a-f】,那么每級目錄的目錄數為256個目錄,16級的話就有4096個目錄。每個目錄存1000個文件的話,可以存放4096000個文件,這樣做,即可以把文件均勻打散到各個小目錄中,同時,每個目錄下的文件數又不是很多。當用戶訪問的時候,取目錄里的用戶名,計算MD5hash,做字符分割,重寫到對應的目錄下的文件中,如果文件不存在,則重寫到生成這個文件的動態頁面中。
到apache手冊里找了下RewriteMap的用法
RewriteMap MapName MapType:MapSource
遂順手在.htaccess里這么寫了
查看源代碼打印幫助1 RewriteMap cfc prg:/var/www/1.php
2 RewriteRule ^([a-f0-9-]+)\.blog\.cnxct\.com ${cfc:$1} [L,PT]
然后訪問一個三級域名試試。結果提示500 http 錯誤。到apache日志里看到如下
/var/www/.htaccess: RewriteMap not allowed here
搜了半天,不知道是什么錯誤,只好再次看手冊,這時候,才發現rewritemap的作用域卻是server config, virtual host,真汗了一下。自己沒認真看手冊。
改到virtual host里之后,重啟apache,結果,還是http 500。再到錯誤日志里查個究竟。里面記錄的確實(13)Permission denied: mod_rewrite: could not start RewriteMap program /var/www/1.php。呃,權限,權限。。趕緊chmod了一下。再次啟動,卻提示404。。。 /0a/c1/…./…html那種MD5字符串切割之后的文件找不到。但目錄里確實是存在的啊。又到日志里查看,原來卻是/0a/c1/…../…html\r 文件無法找到了。為什么地址后面多個\r呢?打開MapSource的腳本文件,才看到里面PHP操作流的結束字符里是“\r\n”了,去掉\r 才可以。
總結一下使用APACHE URL REwrite的RewriteMap方法要注意以下幾點:
作用域-server config, virtual host,其他配置里無效。
自定義規則MapSource中流的結束符要跟操作系統符合,linux的要用“\n”,同時,切記在win平臺編輯腳本傳到linux上的時候,文件換行符要用linux格式的,不然,同樣會出現問題。
要給apache賦予對腳本的讀權限。
apache會在啟動的時候,將自定義規則的腳本讀取到內存中,之后,再次修改腳本時,不會立刻生效,需要重啟 apache
apache 的error.log中會記錄[warn] mod_rewrite: Running external rewrite maps without defining a RewriteLock is DANGEROUS!這樣的錯誤日志,在apache2.conf【我的系統是ubuntu,其他linux在httpd.conf中】中添加RewriteLock /etc/apache2/script/cfc.lock來指定RewriteLock的文件位置。記得給apache對script目錄下有讀寫權限。
自定義腳本的代碼格式如下:
查看源代碼打印幫助1 <?php
2 while($in = trim(fgets(STDIN)))
3 fputs(STDOUT, getfile($in)."\n");
4 function($str)
5 {
6 //函數判斷文件是否存在等邏輯
7 }