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 rivetApache rivetTclhttpdApache2026/10/16

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.12026-09-05官方 rivet-current,建议新部署使用
3.2(维护线)3.2.82026-09-05兼容性维护

💡 技术拓展:服务端脚本嵌入 httpd 的主流方案对比——

方案语言工作方式现状
mod_phpPHPhttpd 进程内嵌 PHP 解释器主流,生态庞大
mod_perlPerlhttpd 进程内嵌 Perl 解释器维护中,Perl Web 生态收缩
mod_rivet(本文)Tclhttpd 进程/线程内嵌 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 的场景:

  1. 存量 Tcl 资产复用:大量 Tcl 业务代码、Tcl 工具脚本希望低成本 Web 化;
  2. httpd 中心化部署:已经重度使用 httpd(反代、静态资源、TLS 终端),希望动态页在同进程体系内完成,避免多一层 FPM/应用网关;
  3. 低流量内部系统:管理后台、设备管理页、嵌入式 Web 控制台(Tcl 解释器内存占用远小于 JVM/Node);
  4. 模板即页面:页面以 HTML 为主、逻辑点缀,.rvt 模板体验接近 PHP。

限制与风险(均为实测或源码层面确认):

  1. 生态小众:无主流 ORM/中间件客户端生态,数据库访问依赖自带 DIO 或 tcllib;
  2. 3.x 兼容性断点:命令命名空间化(默认不导入)、var 子命令化、RivetUserConf 键白名单化,老代码/老教程需迁移(见《Apache rivet 配置参数详解》实测对照);
  3. 上游包存在实际缺陷:实测 3.3.1 的 load_cookies.tcl 无法解析任何 k=v 形式 cookie(导致 Session 无法恢复会话)、dio.tcl 的 update 生成空 SET 子句 SQL——均需一行补丁修复,采用前应评估跟版本策略;
  4. 进程内嵌解释器的通病:应用随 httpd 重启而重启,长连接/后台任务能力弱;
  5. 社区节奏慢:发布以年计,安全响应不如 mod_php 活跃,建议锁定 LTS 内核 + httpd 发行版安全通道。

六、同类选型对比

维度Apache Rivet 3.3.1mod_php 8.xmod_perl 2.xmod_tcl独立 Tcl 应用(tclhttpd/Wub)
语言Tcl 8.6PHPPerlTclTcl
与 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 可用性并确认补丁策略,再进入开发。

七、参考链接