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 }