Apache rivet 介绍与选型指南
介绍 Apache Rivet(httpd 模块 mod_rivet 构成的服务端 Tcl Web/模板框架)的定位、核心架构、功能特性、适用限制与同类方案选型对比,实测环境为 Ubuntu 22.04 + Apache 2.4.52 + Tcl 8.6.12 + Rivet 3.3.1。
Apache rivet 介绍与选型指南
目标:讲清 Apache Rivet 是什么、解决什么问题、内部如何工作、与 PHP/mod_perl 等同类方案如何取舍,帮助读者在"老牌 Tcl 技术栈维护与新项目选型"场景下做出正确判断。本文所有结论均在 Ubuntu 22.04 + Apache 2.4.52 + Tcl 8.6.12 + Rivet 3.3.1 环境实测验证。
一、环境信息
- 操作系统:Ubuntu 22.04 LTS(jammy,x86_64)
- Web 服务器:Apache/2.4.52(Ubuntu 软件源版本 2.4.52-1ubuntu4.25,event MPM)
- Tcl 解释器:Tcl 8.6.12(tcl8.6 8.6.12+dfsg-1build1)
- Apache Rivet:3.3.1(2026-09-05 发布,当前最新稳定版;源码编译安装,安装目录
/usr/local/apache2/lib/rivet3.3.1/) - 模块文件:
/usr/lib/apache2/modules/mod_rivet.so(约 449 KB) - 验证命令与实测回显:
apache2 -v
Server version: Apache/2.4.52 (Ubuntu)
Server built: 2026-10-08T19:59:46
echo "info patchlevel" | tclsh
8.6.12
ls /usr/local/apache2/lib/rivet3.3.1/
default_request_handler.tcl
init.tcl
librivetlib.so
librivetparser.so
mpm
packages
pkgIndex.tcl
rivet-tcl
二、软件概述
Apache Rivet 是 Apache 软件基金会(ASF)Tcl 项目下维护的服务端 Web 编程框架,本质是一个 Apache httpd 模块(mod_rivet)。它把 Tcl 解释器直接嵌入 httpd 进程/线程中,让 .rvt 模板文件(HTML 与 Tcl 混排)由 httpd 直接解释执行,无需外部 FastCGI/CGI 网关。定位与 mod_php 之于 PHP 完全同构:"httpd 内嵌解释器 + 页面内嵌代码标签 + 一组请求级命令库"。
Rivet 的前身是 NeoWebScript(1997 年 Karl Lehenbauer 开发),2000 年前后进入 ASF,2002 年以 Rivet 名称首发,是 ASF 体系内历史最长的 Web 框架之一。当前版本线有两个:
| 版本线 | 最新版本 | 发布时间 | 说明 |
|---|---|---|---|
| 3.3(当前稳定线) | 3.3.1 | 2026-09-05 | 官方 rivet-current,建议新部署使用 |
| 3.2(维护线) | 3.2.8 | 2026-09-05 | 兼容性维护 |
💡 技术拓展:服务端脚本嵌入 httpd 的主流方案对比——
方案 语言 工作方式 现状 mod_php PHP httpd 进程内嵌 PHP 解释器 主流,生态庞大 mod_perl Perl httpd 进程内嵌 Perl 解释器 维护中,Perl Web 生态收缩 mod_rivet(本文) Tcl httpd 进程/线程内嵌 Tcl 解释器 ASF 维护,小众但稳定 FastCGI/PHP-FPM 任意 解释器独立进程池,httpd 反代 当前主流架构方向 模板引擎(如 Tcl 的 tclhttpd) 各异 独立应用服务器自行监听 已被反向代理架构取代 本文主角 Rivet 属于"进程内嵌解释器"派别;该派别请求开销最低,但应用生命周期与 httpd 绑定。新项目若非存量 Tcl 资产复用,也应对照表中 FastCGI 一行权衡进程隔离性。
版本获取方式(不依赖本文写作时点):
curl -s https://archive.apache.org/dist/tcl/rivet/ | grep -oE 'rivet-[0-9.]+\.tar\.gz' | sort -uV | tail -3
实测输出节选(首尾):
rivet-3.2.8.tar.gz
rivet-3.3.0.tar.gz
rivet-3.3.1.tar.gz
三、核心架构
3.1 总体结构
Rivet 的安装产物分为三层,以 3.3.1 实测目录结构为准:
| 层 | 位置 | 内容 |
|---|---|---|
| httpd 模块层 | /usr/lib/apache2/modules/mod_rivet.so | 模块主体,实现 Apache hook、指令、内容处理器 |
| MPM 桥接层 | rivet3.3.1/mpm/ 下的 rivet_prefork_mpm.so、rivet_worker_mpm.so、rivet_lazy_mpm.so | 把模块适配到 httpd 三种多进程/多线程模型 |
| Tcl 库层 | /usr/local/apache2/lib/rivet3.3.1/(init.tcl、default_request_handler.tcl、rivet-tcl/、packages/) | 请求处理脚本、命令库与扩展包(Session、DIO、form 等) |
各 MPM 桥的实际加载情况可在运行中查看:
ls /usr/local/apache2/lib/rivet3.3.1/mpm/
librivetmpm.so
rivet_lazy_mpm.so
rivet_prefork_mpm.so
rivet_worker_mpm.so
💡 技术拓展:httpd 的 MPM(Multi-Processing Module,多处理模块)决定服务器并发模型,Rivet 必须为其适配——
MPM 模型 对 Rivet 的影响 prefork 多进程,每进程单线程 每进程一个 Tcl 解释器,最稳,内存开销大 worker/event 多进程多线程 单进程内多线程共享脚本缓存,解释器按线程/请求分配 动态选择 lazy bridge 按运行时 MPM 自动装载对应桥 本文实测环境为 Ubuntu 默认 event MPM;Rivet 源码 configure 输出会明确报告 worker 桥的工作模式。
3.2 一次 .rvt 请求的生命周期
以实测页面 /var/www/html/hello.rvt 为例:
<? set title "Hello Rivet" ?>
<html>
<head><title><?= $title ?></title></head>
<body>
<h1><?= $title ?></h1>
</body>
</html>
curl -s -o /dev/null -w '%{http_code}\n' http://localhost/hello.rvt
200
处理流程为:httpd 根据 AddType application/x-httpd-rivet .rvt 把请求交给 mod_rivet → 模块检查脚本缓存(CacheSize 控制)→ 未命中则调用 librivetparser.so 把模板编译成一段纯 Tcl 脚本 → 在 ::request 命名空间内执行 → 输出回写 httpd 输出过滤器。
模板编译这一步是可观测的:模板解析失败时,Rivet 默认错误页会把生成的脚本原样打出来。实测一个含未定义变量的页面:
<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: 之后的内容可以看到,HTML 静态文本被编译成 puts -nonewline "..." 序列、<? ?> 内代码原样保留——这印证了"Rivet 页面本质是 Tcl 脚本"的架构结论。
3.3 命名空间与命令布局
Rivet 3.x 把自有命令收敛到 ::rivet 命名空间(3.2 早期版本直接放全局),实测枚举:
curl -s http://localhost/cmds.rvt | head -2
RIVET-NS: abort_code abort_page after apache_log_error apache_table append apply array ...(Tcl 内建命令略)... escape_sgml_chars escape_shell_command escape_string ... headers include ... load load_env load_headers ... makeurl namespace ... parse ... raw_post ... upload url_script var var_post var_qs ...
(单行长列表,上面为节选;其中 Rivet 专属、非 Tcl 内建的命令包括:var/var_qs/var_post(请求参数)、load_env/env(环境变量)、load_headers/headers(请求/响应头)、cookie、upload、include、makeurl、abort_page/abort_code、apache_log_error、inspect、parse、escape_sgml_chars 等。)
是否把这些命令导入全局命名空间由 ImportRivetNS 指令控制,实测默认值为 0(不导入):
curl -s http://localhost/inspectall.rvt | head -3
ServerInitScript {} GlobalInitScript {} ChildInitScript {} ... HonorHeaderOnlyRequests 1 MpmBridge {} RequestHandler {} ExportRivetNS 1 ImportRivetNS 0
(inspect -all 返回单行平铺字典,上面为首尾节选;完整默认值表见《Apache rivet 配置参数详解》。)
因此 3.3 默认写法是限定名 ::rivet::var get name;设 RivetServerConf ImportRivetNS true 后才能像老教程那样直接写 var get name。这一差异是 3.x 升级兼容性的最大坑点,细节见《Apache rivet 配置参数详解》。
四、功能特性
以下特性均在实测环境中逐项验证过,代码可直接运行。
4.1 模板标签
两类标签覆盖"代码"与"插值"两种诉求,实测:
<? set t "X" ?><h1><?= $t ?></h1>
curl -s http://localhost/min.rvt
<h1>X</h1>
HTML 静态区内 $、[、]、{、}、" 均按字面文本处理(编译期自动转义),实测 HTML 区写入 [clock format [clock seconds]] 得到的就是原样字符,而不是求值结果——需要执行命令替换必须放在 <? ?> 内。
4.2 请求参数与环境变量
<? set out {} ?>
<? lappend out "var get GET_name = [::rivet::var get GET_name default-Tom]" ?>
<? lappend out "var exists GET_name = [::rivet::var exists GET_name]" ?>
<? lappend out "var names = [::rivet::var names]" ?>
<? foreach item $out { puts "$item<br/>" } ?>
curl -s 'http://localhost/api1.rvt?GET_name=Tom&a=1'
var get GET_name = Tom<br/>
var exists GET_name = 1<br/>
var names = GET_name a<br/>
注意 3.x 的 var 命令是子命令式(get/list/exists/names/number/all),与 2.x 直接 var foo 的写法不兼容。环境变量经 load_env 装载到 ::request::env 数组,实测:
<? ::rivet::load_env ?>
<? foreach k {SERVER_NAME REQUEST_METHOD QUERY_STRING SERVER_SOFTWARE} {
puts "$k = $::request::env($k)<br/>" } ?>
SERVER_NAME = localhost<br/>
REQUEST_METHOD = GET<br/>
QUERY_STRING = a=1<br/>
SERVER_SOFTWARE = Apache/2.4.52 (Ubuntu)<br/>
4.3 会话与数据访问扩展包
packages/ 目录自带 Session(itcl 实现的会话对象,持久化经 DIO 数据库抽象层)、DIO(支持 Sqlite/MySQL/PostgreSQL/Oracle/Tdbc)、form/formbroker(表单构建与校验)、Calendar、AsciiGlyphs、RivetEntities 等。实测包索引:
grep -oE 'package ifneeded [a-zA-Z0-9]+ [0-9.]+' /usr/local/apache2/lib/rivet3.3.1/pkgIndex.tcl | sort -u
package ifneeded AsciiGlyphs 0.1
package ifneeded Calendar 1.2
package ifneeded DIO 1.1
package ifneeded DIO 1.2.3
package ifneeded DIODisplay 1.0
package ifneeded Rivet 3.3
package ifneeded RivetEntities 1.0.1
package ifneeded Session 1.1
package ifneeded form 1.0
package ifneeded form 2.1
package ifneeded form 2.2
package ifneeded formbroker 1.0.1
package ifneeded rivetlib 3.3.1
package ifneeded rivetparser 0.2
Session 实测(cookie 会话保持 + 数据缓存):
第一次请求: session id: A94D28BC96652354A4712C946EF5BE48 is_new: 1
带 cookie 再请求:session id: A94D28BC96652354A4712C946EF5BE48 is_new: 0
完整搭建步骤(含 3.3.1 需要注意的两个上游包缺陷及修复)见《Apache rivet 在 Ubuntu 22.04 集成使用指南》。
4.4 脚本缓存与调试支撑
- 脚本缓存:编译后的 .rvt 脚本缓存在内存(
CacheSize条目数),实测经inspect server字典读取CacheSize 200生效值,重复请求不再重新解析模板文件。 - 内省命令
inspect:无参返回当前请求生效的完整配置字典,inspect server返回服务器级信息,inspect -all返回全部指令及默认值,inspect script返回当前脚本路径。 - 错误处理:
RivetServerConf ErrorScript指向自定义 Tcl 处理脚本,实测触发除零错误后页面被接管:
<h1>站点自定义错误页</h1><p>错误信息:</p><pre>divide by zero
invoked from within
"expr {1/0}" ...</pre>
- 文件上传:
upload命令族(exists/filename/size/save/data/type等),配合UploadDirectory/UploadMaxSize指令,实测 24 字节文件上传保存成功。
五、适用场景与限制
适合 Rivet 的场景:
- 存量 Tcl 资产复用:大量 Tcl 业务代码、Tcl 工具脚本希望低成本 Web 化;
- httpd 中心化部署:已经重度使用 httpd(反代、静态资源、TLS 终端),希望动态页在同进程体系内完成,避免多一层 FPM/应用网关;
- 低流量内部系统:管理后台、设备管理页、嵌入式 Web 控制台(Tcl 解释器内存占用远小于 JVM/Node);
- 模板即页面:页面以 HTML 为主、逻辑点缀,
.rvt模板体验接近 PHP。
限制与风险(均为实测或源码层面确认):
- 生态小众:无主流 ORM/中间件客户端生态,数据库访问依赖自带 DIO 或 tcllib;
- 3.x 兼容性断点:命令命名空间化(默认不导入)、
var子命令化、RivetUserConf键白名单化,老代码/老教程需迁移(见《Apache rivet 配置参数详解》实测对照); - 上游包存在实际缺陷:实测 3.3.1 的
load_cookies.tcl无法解析任何k=v形式 cookie(导致 Session 无法恢复会话)、dio.tcl的update生成空 SET 子句 SQL——均需一行补丁修复,采用前应评估跟版本策略; - 进程内嵌解释器的通病:应用随 httpd 重启而重启,长连接/后台任务能力弱;
- 社区节奏慢:发布以年计,安全响应不如 mod_php 活跃,建议锁定 LTS 内核 + httpd 发行版安全通道。
六、同类选型对比
| 维度 | Apache Rivet 3.3.1 | mod_php 8.x | mod_perl 2.x | mod_tcl | 独立 Tcl 应用(tclhttpd/Wub) |
|---|---|---|---|---|---|
| 语言 | Tcl 8.6 | PHP | Perl | Tcl | Tcl |
| 与 httpd 关系 | 模块内嵌 | 模块内嵌 | 模块内嵌 | 模块内嵌 | 独立进程 |
| 模板体验 | .rvt 标签,接近 PHP | .php 原生 | Mason/ Embperl | 仅能整页输出脚本 | 各框架自带 |
| 会话/表单/数据库包 | 内置 Session+DIO+form | 生态最全 | CPAN 生态 | 无 | 需自建 |
| 维护方 | ASF Tcl 项目 | PHP 官方 | ASF Perl 项目 | 单人维护 | 社区 |
| 适用判断 | Tcl 存量复用、轻量内管 | 通用 Web 项目 | Perl 存量复用 | 极简定制脚本 | Tcl 全栈独立部署 |
关键差异与如何选型:
- 已有 Tcl 代码与 Tcl 程序员 → Rivet 是唯一由 ASF 维护、带完整命令库与模板体验的选项;mod_tcl 只提供裸脚本输出,无模板与请求库。
- 新项目、团队无 Tcl 背景 → 优先 mod_php(生态)或语言无关的 FastCGI/反向代理架构;Rivet 的学习收益主要兑现在 Tcl 存量资产上。
- 需要"独立应用进程 + httpd 仅做反代" → 选 tclhttpd/Wub 等,Rivet 不适合长驻后台逻辑。
- 对会话/上传等能力有强依赖时,务必先按本文 4.3 节实测 Session 可用性并确认补丁策略,再进入开发。
七、参考链接
- Apache Rivet 官方主页与文档:https://rivet.apache.org/
- 源码与版本归档(含 sha512/签名):https://archive.apache.org/dist/tcl/rivet/
- Apache Tcl 项目说明:https://tcl.apache.org/
- Apache httpd MPM 文档:https://httpd.apache.org/docs/2.4/mpm.html
- 相关文档:《Ubuntu 22.04 安装 Apache rivet 指南》《Apache rivet 在 Ubuntu 22.04 集成使用指南》《Apache rivet 配置参数详解》