<-
Apache > HTTP 服务器 > 文档 > 版本 2.5 > Rewrite

使用 RewriteMap

可用语言:  de  |  en  |  es  |  fr  |  ja  |  ko  |  tr  |  zh-cn 

本文档是 mod_rewrite 参考文档的补充。它描述了 RewriteMap 指令的使用方法, 并提供了各种 RewriteMap 类型的示例。

请注意,这些示例中的许多在你的特定服务器配置中 不会原封不动地工作,因此理解它们非常重要, 而不是仅仅将示例复制粘贴到你的配置中。

参见

top

介绍

RewriteMap 指令定义了一个外部函数, 可以在 RewriteRuleRewriteCond 指令的上下文中调用,以执行过于复杂或过于专业化而无法仅通过正则表达式完成的重写。 此查找的来源可以是下面各节中列出的任何类型, 并在 RewriteMap 参考文档中有详细说明。

RewriteMap 指令的语法如下:

RewriteMap MapName MapType:MapSource

MapName 是你分配给映射的任意名称,稍后将在指令中使用。 通过以下语法将参数传递给映射:

${ MapName : LookupKey }
${ MapName : LookupKey | DefaultValue }

当出现这样的结构时,将查询映射 MapName 并查找键 LookupKey。如果找到该键, 映射函数结构将被替换为 SubstValue。 如果未找到该键,则替换为 DefaultValue, 如果未指定 DefaultValue 则替换为空字符串。

例如,你可以定义一个 RewriteMap 如下:

RewriteMap examplemap "txt:/path/to/file/map.txt"

然后你可以在 RewriteRule 中如下使用此映射:

RewriteRule "^/ex/(.*)" "${examplemap:$1}"

在映射中未找到任何内容时,可以指定默认值:

RewriteRule "^/ex/(.*)" "${examplemap:$1|/not_found.html}"

目录级和 .htaccess 上下文

RewriteMap 指令不能在 <Directory> 配置段或 .htaccess 文件中使用。你必须在服务器或虚拟主机上下文中声明映射。 创建映射后,你可以在这些范围内的 RewriteRuleRewriteCond 指令中使用它。只是不能在这些范围内声明它。

以下各节描述了可以使用的各种 MapType,并给出了每种类型的示例。

top

int:内部函数

当 MapType 为 int 时,MapSource 是可用的 RewriteMap 内部函数之一。模块作者可以通过使用 ap_register_rewrite_mapfunc API 注册来提供额外的内部函数。 默认提供的函数有:

要使用这些函数之一,创建一个引用 int 函数的 RewriteMap, 然后在你的 RewriteRule 中使用它:

将 URI 重定向到其全小写版本

RewriteMap lc int:tolower
RewriteRule "(.*)" "${lc:$1}" [R]

请注意,此处提供的示例仅用于说明目的,并非建议。 如果你想使 URL 不区分大小写,请考虑使用 mod_speling 代替。

top

txt:纯文本映射

当 MapType 为 txt 时,MapSource 是一个纯文本映射文件的 文件系统路径,每行包含一个以空格分隔的键/值对。 可选地,一行可以包含以 '#' 字符开头的注释。

有效的文本重写映射文件将具有以下语法:

# 注释行
MatchingKey SubstValue
MatchingKey SubstValue # 注释

当调用 RewriteMap 时, 在行的第一个参数中查找该参数,如果找到,则返回替换值。

例如,我们可以使用映射文件将产品名称转换为产品 ID, 以获得更易记的 URL,使用以下配方:

产品到 ID 的配置

RewriteMap product2id "txt:/etc/apache2/productmap.txt"
RewriteRule "^/product/(.*)" "/prods.php?id=${product2id:$1|NOTFOUND}" [PT]

我们假设 prods.php 脚本知道在收到 id=NOTFOUND 参数时如何处理——当在查找映射中找不到产品时。

文件 /etc/apache2/productmap.txt 包含以下内容:

产品到 ID 的映射

##
## productmap.txt - 产品到 ID 的映射文件
##

television 993
stereo 198
fishingrod 043
basketball 418
telephone 328

因此,当请求 http://example.com/product/television 时, RewriteRule 被应用, 请求在内部被映射到 /prods.php?id=993

注意:.htaccess 文件

此处给出的示例是为在服务器或虚拟主机范围内使用而设计的。 如果你计划在 .htaccess 文件中使用, 则需要从重写模式中删除前导斜杠才能匹配任何内容:
RewriteRule "^product/(.*)" "/prods.php?id=${product2id:$1|NOTFOUND}" [PT]

缓存查找

查找的键会被 httpd 缓存,直到映射文件的 mtime (修改时间)发生变化或 httpd 服务器重启。 这确保了被许多请求调用的映射有更好的性能。

top

rnd:随机纯文本

当 MapType 为 rnd 时,MapSource 是一个纯文本映射文件的 文件系统路径,每行包含一个键和一个或多个由 | 分隔的值。如果键匹配,将随机选择其中一个值。

例如,你可以使用以下映射文件和指令通过反向代理在多个后端服务器之间 提供随机负载均衡。图像被发送到 'static' 池中的某个服务器, 而其他所有内容被发送到 'dynamic' 池中的某个服务器。

重写映射文件

##
## map.txt -- 重写映射
##

static www1|www2|www3|www4
dynamic www5|www6

配置指令

RewriteMap servers "rnd:/path/to/file/map.txt"

RewriteRule "^/(.*\.(png|gif|jpg))" "http://${servers:static}/$1"  [NC,P,L]
RewriteRule "^/(.*)"                "http://${servers:dynamic}/$1" [P,L]

因此,当请求图像并且第一条规则匹配时, RewriteMap 在映射文件中查找字符串 static, 随机返回指定主机名之一,然后将其用于 RewriteRule 目标。

如果你想让某个服务器更有可能被选中(例如, 如果某个服务器比其他服务器有更多内存,因此可以处理更多请求), 只需在映射文件中多次列出它即可。

static www1|www1|www2|www3|www4

top

dbm:DBM 哈希文件

当 MapType 为 dbm 时,MapSource 是一个 DBM 数据库文件的文件系统路径,其中包含用于映射的键/值对。 其工作方式与 txt 映射完全相同,但速度更快, 因为 DBM 是有索引的,而文本文件没有索引。 这允许更快地访问所需的键。

你可以选择指定特定的 dbm 类型:

RewriteMap examplemap "dbm=sdbm:/etc/apache/mapfile.dbm"

类型可以是 sdbmgdbmndbmdb。但建议你使用 Apache HTTP Server 提供的 httxt2dbm 工具, 因为它会使用正确的 DBM 库,与构建 httpd 本身时使用的库相匹配。

要创建 dbm 文件,首先按照 txt 节中的描述创建文本映射文件。然后运行 httxt2dbm

$ httxt2dbm -i mapfile.txt -o mapfile.map

然后你可以在 RewriteMap 指令中引用生成的文件:

RewriteMap mapname "dbm:/etc/apache/mapfile.map"

请注意,对于某些 dbm 类型,会生成多个文件,它们具有共同的基本名称。 例如,你可能有两个名为 mapfile.map.dirmapfile.map.pag 的文件。这是正常的,你只需在 RewriteMap 指令中使用基本名称 mapfile.map 即可。

缓存查找

查找的键会被 httpd 缓存,直到映射文件的 mtime (修改时间)发生变化或 httpd 服务器重启。 这确保了被许多请求调用的映射有更好的性能。

top

prg:外部重写程序

当 MapType 为 prg 时,MapSource 是一个可执行程序的 文件系统路径,该程序将提供映射行为。这可以是编译的二进制文件, 也可以是 Python 或 Perl 等解释型语言的程序。

此程序在 Apache HTTP Server 启动时启动一次, 然后通过 STDINSTDOUT 与重写引擎通信。对于每次映射函数查找, 键被写入程序的 STDIN,后跟换行符。 程序应从 STDIN 读取一行(直到并包括换行符), 并在 STDOUT 上写入其响应作为单个以换行符终止的行。 键永远不会包含换行符;如果遇到包含换行符的键,查找将失败。

如果没有对应的查找值,映射程序应返回四字符字符串 "NULL" 来表示这一点。请注意,此比较不区分大小写, 因此 "null"、"Null" 等也被视为查找失败。 因此,映射程序不可能返回字面量字符串 "NULL" 作为映射值。

程序的 STDERR 继承自 httpd 父进程, 因此程序写入 STDERR 的任何内容都将与 httpd 自身的错误输出出现在相同的位置(通常是 ErrorLog)。

如果外部重写程序定义在未将 RewriteEngine 设置为 on 的上下文中,则不会启动。

默认情况下,外部重写程序以启动 httpd 的用户:组身份运行。 在 UNIX 系统上,可以通过将用户名和组名作为第三个参数传递给 RewriteMap, 以 username:groupname 格式进行更改。

此功能使用 rewrite-map 互斥锁, 这是与程序可靠通信所必需的。互斥锁机制和锁文件可以通过 Mutex 指令进行配置。

这里展示了一个简单的示例,它将请求 URI 中的所有破折号替换为下划线。

重写配置

RewriteMap d2u "prg:/www/bin/dash2under.py" apache:apache
RewriteRule "-" "${d2u:%{REQUEST_URI}}"

dash2under.py

#!/usr/bin/env python3
import sys

for line in sys.stdin:
    print(line.strip().replace('-', '_'), flush=True)

注意!

  • 保持你的重写映射程序尽可能简单。如果程序挂起, 它将导致 httpd 无限期地等待映射的响应, 这反过来会导致 httpd 停止响应请求。
  • 确保在你的程序中关闭缓冲。在上面的 Python 示例中, 这是通过向 print() 传递 flush=True 来完成的。 缓冲的 I/O 将导致 httpd 等待输出,从而挂起。
  • 请记住,程序只有一个副本,在服务器启动时启动。 所有请求都需要通过这一个瓶颈。如果许多请求必须通过此进程, 或者脚本本身非常慢,这可能会导致显著的性能下降。
  • 如果映射程序终止,它不会自动重启。后续查找将失败, 直到服务器重启。
  • 无论相关配置指令是否发生更改,映射程序在任何服务器重启 (优雅重启或其他方式)时都会被终止并重新启动。 关闭时,程序会收到 SIGTERM 信号; 如果在 3 秒内未退出,则会收到 SIGKILL 信号。
top

dbd 或 fastdbd:SQL 查询

当 MapType 为 dbdfastdbd 时, MapSource 是一个接受单个参数并返回单个值的 SQL SELECT 语句。

需要配置 mod_dbd 指向正确的数据库才能执行此语句。

此 MapType 有两种形式。使用 dbd 的 MapType 会在每次映射请求时执行查询,而使用 fastdbd 则在内部缓存数据库查找结果。因此,虽然 fastdbd 更高效、更快,但它不会在服务器重启之前获取数据库的更改。

如果查询返回多行,将从结果集中随机使用一行。

示例

RewriteMap myquery "fastdbd:SELECT destination FROM rewrite WHERE source = %s"

注意

查询名称作为 SQL 预处理语句的标签传递给数据库驱动程序, 因此需要遵循数据库要求的任何规则(例如大小写敏感性)。

top

总结

RewriteMap 指令可以出现多次。对于每个映射函数,使用一个 RewriteMap 指令来声明其重写映射文件。

虽然你不能在目录级上下文(.htaccess 文件或 <Directory> 块)中 声明映射,但可以在目录级上下文中使用此映射。

可用语言:  de  |  en  |  es  |  fr  |  ja  |  ko  |  tr  |  zh-cn