Apache rivet 配置参数详解

逐项实测讲解 Apache Rivet 3.3.1 的 RivetServerConf、RivetDirConf、RivetUserConf 三条 Apache 指令及其全部配置键,覆盖脚本类钩子、上传控制、缓存、多解释器隔离与调试参数,附配置层级、错误页与日志行为的实测记录。

Apache rivetApache rivethttpdUbuntu 22.04Linux系统2026/10/16

Apache rivet 配置参数详解

目标:把 Apache Rivet 在 httpd 配置文件中的全部指令、配置键、生效层级与默认值一次讲清,每项均给出现实可用的配置片段与实测结果,并记录错误页与日志行为,作为生产部署的配置字典使用。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS(x86_64)
  • Apache:2.4.52(event MPM)
  • Apache Rivet:3.3.1(源码编译,模块 /usr/lib/apache2/modules/mod_rivet.so,Tcl 运行库 /usr/local/apache2/lib/rivet3.3.1/)
  • Tcl:8.6.12
  • 配置文件:/etc/apache2/conf-available/*.conf(服务器级)、/etc/apache2/sites-available/*.conf(虚拟主机级)

版本与指令集核对(指令名提取自模块本体,作为全文依据):

apache2 -v | head -1
Server version: Apache/2.4.52 (Ubuntu)
grep -rnE '"Rivet[A-Za-z]+"' /usr/local/apache2/lib/rivet3.3.1/ 2>/dev/null | head -2
strings /usr/lib/apache2/modules/mod_rivet.so | grep -E '^Rivet[A-Z]' | sort -u
RivetDirConf
RivetServerConf
RivetUserConf

结论先行:Rivet 3.3.1 对外只暴露三条 Apache 指令——RivetServerConf、RivetDirConf、RivetUserConf,均为"指令名 + 配置键 + 值"的两段式写法(键名大小写敏感)。所有功能差异都体现在键集与作用域上。

💡 技术拓展:与多数 httpd 模块的"一个功能一条指令"不同,Rivet 用"同一条指令、不同键名"收敛配置面,代价是键名拼错只会得到一条运行期报错而不是配置语法错(httpd 启动仍 Syntax OK),因此配置校验必须结合实际请求与第六章的 inspect 内省命令。

二、前置条件

  1. mod_rivet 已安装并启用(见《Ubuntu 22.04 安装 Apache rivet 指南》);
  2. apachectl -M | grep rivet 输出 rivet_module (shared);
apachectl -M | grep rivet
 rivet_module (shared)
  1. 准备一个测试站点目录与样例页面,用于验证各参数的生效情况:
mkdir -p /var/www/html
cat > /var/www/html/hello.rvt <<'EOF'
<? set title "Hello Rivet" ?><h1><?= $title ?></h1>
EOF
curl -s -o /dev/null -w '%{http_code}\n' http://localhost/hello.rvt
200

三、三条指令与配置层级

指令httpd 作用域说明
RivetServerConfserver config / virtual host服务器级(可放入全局段或某 <VirtualHost>)
RivetDirConfdirectory / .htaccess目录级,写在 <Directory>、<Location> 内
RivetUserConfserver config / virtual host / directory同样可服务器或目录级,但键集是白名单(见 3.3)

三条指令读取的键集合不同,先给全量键清单(来源:3.3.1 模块源码键名校验逻辑,并经配置实测报错信息印证):

3.1 RivetServerConf 键集(18 个)

类别键
生命周期脚本ServerInitScript、GlobalInitScript、ChildInitScript、ChildExitScript
请求期脚本BeforeScript、AfterScript、AfterEveryScript、AbortScript、ErrorScript
上传控制UploadDirectory、UploadMaxSize、UploadFilesToVar
脚本缓存CacheSize
解释器隔离SeparateVirtualInterps、SeparateChannels
兼容/开关HonorHeadRequests(旧名 HonorHeaderOnlyRequests)、MpmBridge、RequestHandler
命名空间ExportRivetNS、ImportRivetNS

3.2 RivetDirConf 键集

BeforeScript、AfterScript、AbortScript、AfterEveryScript、UploadDirectory、ErrorScript,以及 Debug 系列(Debug、DebugIp、DebugSubst、DebugSeparator)。

3.3 RivetUserConf 键集(白名单)

BeforeScript、AfterScript、AbortScript、AfterEveryScript、UploadDirectory、ErrorScript、Debug、DebugIp、DebugSubst、DebugSeparator。

实测向 RivetUserConf 提交白名单外的键(这是与老版本"任意 key=value"最大的行为差异):

cat > /etc/apache2/conf-available/rivet-user-test.conf <<'EOF'
RivetUserConf site_name "Rivet Demo Site"
EOF
a2enconf rivet-user-test
apachectl -k restart
AH00526: Syntax error on line 1 of /etc/apache2/conf-enabled/rivet-user-test.conf:
Rivet configuration error: 'site_name' not valid for RivetUserConf
Action '-k restart' failed.

实测行为要点:校验发生在配置加载期——apachectl -k restart 直接失败(Action failed),旧进程继续对外服务(期间 curl 仍返回 200),并不会出现运行期 500。报错信息本身即键集白名单的权威提示。移除非法键后恢复正常加载:

a2disconf rivet-user-test && apachectl -k restart
curl -s -o /dev/null -w '%{http_code}\n' http://localhost/hello.rvt
200

四、脚本类配置键(生命周期与请求期钩子)

4.1 四个生命周期脚本

键时机典型用途
ServerInitScript主服务器进程启动一次全局一次性初始化
GlobalInitScript每个解释器创建时(进程级一次)加载全局库
ChildInitScript每个 httpd 子进程启动时创建 DIO/Session 等长寿命对象(集成指南已实测)
ChildExitScript子进程退出时清理资源

实测 ChildInitScript 创建 Session 对象(效果见《Apache rivet 在 Ubuntu 22.04 集成使用指南》第六章):

RivetServerConf ChildInitScript "package require Itcl; package require DIO; package require Session; ::DIO::handle Sqlite DIO -db /var/lib/rivet/sessions.db; Session SESSION -dioObject DIO -entropyFile /dev/urandom -entropyLength 10 -debugMode 0"
(重启后每个 apache2 子进程持有 SESSION 对象,请求页可见 session id 输出)

4.2 请求期脚本钩子

键执行点实测验证
BeforeScript页面脚本执行前注入 puts -nonewline <!--B> 后每个 .rvt 响应开头出现 <!--B>
AfterScript页面脚本执行后(每请求)同法可观测尾部输出
AfterEveryScript同 AfterScript,但可目录级覆盖RivetDirConf AfterEveryScript "puts -nonewline <!--D>" 实测响应尾部出现 <!--D>
AbortScriptabort_page 触发时执行用于中止页的统一出口

实测组合(BeforeScript 服务器级 + AfterEveryScript 目录级):

RivetServerConf BeforeScript "puts -nonewline <!--B>"
RivetUserConf BeforeScript "puts -nonewline <!--B>"
<Directory "/var/www/html">
    RivetDirConf AfterEveryScript "puts -nonewline <!--D>"
</Directory>
curl -s http://localhost/abort.rvt
<!--B><!--D>

(abort.rvt 内容是 <? ::rivet::abort_page ... ?>,正文被中止,因此只看到两个钩子输出——恰好把 BeforeScript 与 AfterEveryScript 的执行点验证干净。)

4.3 ErrorScript:错误页接管

实测明确三点行为:

  1. 键值是 Tcl 代码(模块内以 uplevel #0 执行),不是文件路径;
  2. 引用文件用 source,且被引用的必须是纯 Tcl 脚本——指向 .rvt 模板会把 HTML 当 Tcl 执行而失败(实测报 invalid command name "<h1>自定义错误页</h1>" 并回退默认错误页);
  3. 值内含空格时要用 双引号 包裹:Apache 配置解析不认识 Tcl 花括号分组,写 {source ...} 会报 RivetServerConf takes two arguments(实测)。
RivetServerConf ErrorScript "source /var/www/html/errhandler.tcl"
cat > /var/www/html/errhandler.tcl <<'EOF'
puts -nonewline "<h1>站点自定义错误页</h1>"
puts -nonewline "<p>错误信息:</p><pre>$::errorInfo</pre>"
EOF
cat > /var/www/html/divzero.rvt <<'EOF'
<? set x [expr {1/0}] ?>
never reached
EOF
apachectl -k restart
curl -s http://localhost/divzero.rvt
<h1>站点自定义错误页</h1><p>错误信息:</p><pre>divide by zero
    invoked from within
"expr {1/0}"
    ("::try" body line 5)
    (in namespace eval "::request" script line 4)
    invoked from within
"namespace eval ::request $script"
    ("::try" body line 15)
</pre>

HTTP 状态实测为 500(curl -s -o /dev/null -w '%{http_code}' 输出 500)。

五、运行行为类配置键

5.1 上传控制:UploadDirectory / UploadMaxSize / UploadFilesToVar

RivetServerConf UploadDirectory /var/lib/rivet/uploads
RivetServerConf UploadMaxSize 2000000

实测:24 字节文件经 curl -F getfile=@/tmp/ufile.txt 上传,::rivet::upload save 落盘到 /var/lib/rivet/uploads/ufile.txt,内容一致:

cat /var/lib/rivet/uploads/ufile.txt
hello rivet upload test

UploadFilesToVar(实测默认 1)控制上传内容暂存方式:1 表示上传体先存为内存变量供 upload data 读取,0 表示走临时文件。目录不存在或不可写时上传保存报 couldn't open ... permission denied,生产上应把该目录属主设为 httpd 运行用户(www-data)并独立于 DocumentRoot。

5.2 脚本缓存:CacheSize

模板编译产物在内存中按 LRU 缓存,CacheSize 设定条目上限:

RivetServerConf CacheSize 200
curl -s http://localhost/inspect.rvt | grep CacheSize
CacheSize = 200

命中率无需精确估计的场景给个"站点 .rvt 文件总数 + 20%"即可;设 0 表示禁用缓存(每次请求都重新读文件编译,仅开发调试用)。

5.3 解释器隔离:SeparateVirtualInterps / SeparateChannels

键实测默认作用
SeparateVirtualInterps0虚拟主机间是否隔离 Tcl 解释器;0 共享(省内存),1 每虚拟主机独立解释器(配置与全局变量互不污染)
SeparateChannels0隔离 Tcl 通道(文件/网络句柄),防止跨请求误用他人打开的句柄

多租户/多站点共享一台 httpd 时建议二者开 1;单站点内部系统可保持 0 换取内存开销最低。

5.4 命名空间:ExportRivetNS / ImportRivetNS

实测全量配置快照(inspect -all)中的默认值:

ExportRivetNS 1 ImportRivetNS 0
  • ExportRivetNS 1(默认):::rivet 命名空间里的命令对 namespace import 可见;
  • ImportRivetNS 0(默认):不把 var、include 等命令导入全局/请求命名空间。

因此 3.3 默认写法必须带 ::rivet:: 前缀(实测裸写 var get name 报 invalid command name "var")。要兼容 2.x 老代码:

RivetServerConf ImportRivetNS true

随后裸命令可用。两开关组合的取舍:导入后命令直接可见但可能与应用自己的 Tcl 过程重名;不导入则每处都要限定名(本文档与集成指南的代码均用限定名风格)。

5.5 请求处理开关:HonorHeadRequests / RequestHandler / MpmBridge

  • HonorHeadRequests(实测默认 1,旧名 HonorHeaderOnlyRequests):对 HTTP HEAD 请求只回响应头。实测开启时 curl -I http://localhost/hello.rvt 返回 200 与完整头部、无响应体:
curl -s -I http://localhost/hello.rvt | head -4
HTTP/1.1 200 OK
Date: Thu, 16 Oct 2026 04:15:22 GMT
Server: Apache/2.4.52 (Ubuntu)
Last-Modified: ...
  • RequestHandler:替换默认请求处理脚本(default_request_handler.tcl),高级定制用,常规部署留空(实测默认 {});
  • MpmBridge:强制指定 MPM 桥(可选 prefork/worker),默认空表示按运行时 MPM 自动选择(实测 event MPM 下自动工作正常)。

5.6 调试系列:Debug / DebugIp / DebugSubst / DebugSeparator

调试四键用于把请求级调试输出限制到指定来源 IP(DebugIp)、指定替换标记(DebugSubst/DebugSeparator)。这些键在 RivetUserConf 与 RivetDirConf 均可用(属白名单)。生产保持关闭;排障时可临时在目录级打开,避免全站影响。

六、配置的查看与排障:inspect 命令

Rivet 把"当前请求实际生效的配置"暴露为 Tcl 命令,比翻配置文件更可靠:

调用返回实测用途
::rivet::inspect字典 {dir {...} server {...} user {...}}一次性看全三层配置
::rivet::inspect server服务器级字典核对 ServerConf 键值
::rivet::inspect dir目录级字典核对 DirConf 覆盖
::rivet::inspect userUserConf 字典核对 UserConf
::rivet::inspect -all全量平铺字典(含默认值)查默认值
::rivet::inspect script当前脚本路径确认请求落到哪个文件

实测页面:

cat > /var/www/html/inspect.rvt <<'EOF'
<? set d [::rivet::inspect] ?>
<? foreach k [lsort [dict keys $d]] { puts "$k = [dict get $d $k]<br>" } ?>
EOF
curl -s http://localhost/inspect.rvt
dir = AfterEveryScript {puts -nonewline <!--D>} BeforeScript {SESSION activate}<br>
server = ErrorScript {source /var/www/html/errhandler.tcl} CacheSize 200 UploadDirectory /var/lib/rivet/uploads UploadMaxSize 2000000 HonorHeadRequests on ChildInitScript {package require Itcl; package require DIO; package require Session; ::DIO::handle Sqlite DIO -db /var/lib/rivet/sessions.db; Session SESSION -dioObject DIO -entropyFile /dev/urandom -entropyLength 10 -debugMode 0}<br>
user = BeforeScript {puts -nonewline <!--B>}<br>

三层配置同时可见,层级覆盖关系一目了然。注意 3.3.1 的 inspect 只接受 0 个或 1 个参数(server/dir/user/-all/script),写 inspect server CacheSize 这类两参数形式会报 wrong # args(实测),取单个键用 dict get。

七、虚拟主机级配置实践

RivetServerConf 可放入 <VirtualHost>,实现"每站点独立配置"。模板:

<VirtualHost *:80>
    ServerName site1.example.com
    DocumentRoot /var/www/site1

    <Directory /var/www/site1>
        AddType application/x-httpd-rivet .rvt
        AddType application/x-rivet-tcl .tcl
        RivetDirConf BeforeScript "puts -nonewline <!--site1-->"
    </Directory>

    RivetServerConf ImportRivetNS true
    RivetServerConf UploadDirectory /var/lib/rivet/uploads-site1
    RivetServerConf CacheSize 100
</VirtualHost>

验证(本机用 Host 头模拟):

echo '<? puts "<p>site1 ok</p>" ?>' > /var/www/site1/index.rvt
apachectl -k restart
curl -s -H 'Host: site1.example.com' http://localhost/index.rvt
<!--site1--><p>site1 ok</p>

目录级钩子输出出现在站点响应之前,且该配置不影响默认站点(http://localhost/hello.rvt 输出无 <!--site1-->,实测比对通过)。

八、错误页与日志行为

8.1 默认错误页格式

未配置 ErrorScript 时,模板执行异常返回 500,页面内容包含错误信息 + 完整调用栈 + OUTPUT BUFFER(模板已编译脚本),实测片段:

<pre>no such variable
    (read trace on "env(SERVER_NAME)")
    invoked from within
"puts -nonewline  $env(SERVER_NAME) "
    (in namespace eval "::request" script line 4)
    invoked from within
"namespace eval ::request $script"
    ("::try" body line 15)<hr/><p>OUTPUT BUFFER:</p>puts -nonewline "<html><body>
..."
</pre>

⚠️ 安全提示:默认错误页会泄漏脚本内容与路径(OUTPUT BUFFER 段),生产环境必须配置 ErrorScript 接管,或至少在 httpd 层把 5xx 响应体替换为统一错误页。

8.2 模块日志与 httpd 日志

Rivet 的配置级错误在 httpd 启动/重载时以 AH00526 语法错误形式出现(3.3 节实测)。应用侧主动写日志用 apache_log_error 命令,第一个参数是严重级别,实测可选值为 emerg|alert|crit|err|warning|notice|info|debug(写 error 会直接触发错误页,正确写法是 err):

<? ::rivet::apache_log_error err "board app: user submitted" ?>
tail -1 /var/log/apache2/error.log
[Fri Oct 16 04:38:28.197035 2026] [:error] [pid 12334:tid 127052681983552] board app: user submitted

8.3 崩溃类日志特征

模块与 Tcl 库版本不配套时(本文安装指南第九章实测),error.log 特征为连续的 AH00051: child pid ... exit signal Segmentation fault (11)。看到该模式先核对 /usr/lib/apache2/modules/mod_rivet.so 与 Tcl 运行库目录(如 /usr/local/apache2/lib/rivet3.3.1、/usr/lib/tcltk/rivet3)版本是否同源,而不是盲目重启。

九、生产配置模板

综合以上实测,给出一套可直接套用的生产基线(含逐行实测依据索引):

<IfModule rivet_module>
    AddType application/x-httpd-rivet .rvt
    AddType application/x-rivet-tcl .tcl

    # —— 生命周期(集成指南第六章实测) ——
    RivetServerConf ChildInitScript "package require Itcl; package require DIO; package require Session; ::DIO::handle Sqlite DIO -db /var/lib/rivet/sessions.db; Session SESSION -dioObject DIO -entropyFile /dev/urandom -entropyLength 10 -debugMode 0"
    RivetServerConf ChildExitScript ""

    # —— 错误页(本文 4.3 实测:值是 Tcl 代码,双引号,引用 .tcl 纯脚本) ——
    RivetServerConf ErrorScript "source /var/www/html/errhandler.tcl"

    # —— 上传(本文 5.1 实测) ——
    RivetServerConf UploadDirectory /var/lib/rivet/uploads
    RivetServerConf UploadMaxSize 2000000

    # —— 缓存与隔离(本文 5.2/5.3 实测) ——
    RivetServerConf CacheSize 200
    RivetServerConf SeparateVirtualInterps 1
    RivetServerConf SeparateChannels 1

    # —— 兼容与调试(本文 5.4/5.5 实测默认值) ——
    RivetServerConf ImportRivetNS false
    RivetServerConf HonorHeadRequests on
</IfModule>

上线前用第六章 inspect 做一遍配置核对清单:

#核对项方法通过标准
1三层配置符合预期::rivet::inspect 字典server/dir/user 与计划一致
2默认值未被意外覆盖::rivet::inspect -allImportRivetNS=0、HonorHeadRequests=1 等
3错误页接管请求一个除零页面返回自定义页,无 OUTPUT BUFFER 泄漏
4上传落盘curl -F 小文件文件出现在 UploadDirectory
5会话可用两次请求(带 cookie)会话 ID 一致、is_new 变 0

十、参考链接

  • Rivet 官方配置文档:https://rivet.apache.org/
  • 默认请求处理脚本(RequestHandler 替换对象):/usr/local/apache2/lib/rivet3.3.1/default_request_handler.tcl
  • Apache httpd 2.4 配置段说明:https://httpd.apache.org/docs/2.4/sections.html
  • 相关文档:《Apache rivet 介绍与选型指南》《Ubuntu 22.04 安装 Apache rivet 指南》《Apache rivet 在 Ubuntu 22.04 集成使用指南》