Riemann 0.3.12 配置参数详解

Riemann 0.3.12 配置篇:riemann.config DSL 全部入口参数默认值(tcp/udp/ws/sse/graphite/opentsdb/index/periodically-expire/instrumentation/logging/include/async-queue)逐项详解,streams 组合器语义与 changed/expired/coalesce/stable/top/rate 实测回显、SIGHUP 热加载、生产配置模板与 FAQ。

RiemannRiemann自托管监控2026/11/01

Riemann 0.3.12 配置参数详解

目标:逐项讲清 Riemann 0.3.12 配置文件 /etc/riemann/riemann.config(Clojure DSL)的全部参数入口与默认值——传输监听(tcp/udp/ws/sse/graphite/opentsdb)、索引与过期(index/periodically-expire)、自监控(instrumentation)、日志(logging)、模块化(include/depend/async-queue)以及 streams 组合器(where/changed/expired/coalesce/stable/top/rate 等)的语义与实测行为。全部参数默认值取自 0.3.12 官方源码(riemann.config / riemann.streams 命名空间),全部命令与回显均已在 Ubuntu 22.04.5 + Riemann 0.3.12 服务器环境实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(Jammy,x86_64)
  • Riemann 版本:0.3.12(官方最新稳定版,2025-05-26 发布,查询日期 2026-11-01)
  • Java 运行时:OpenJDK 11.0.32.1
  • 配置文件:/etc/riemann/riemann.config(Clojure DSL,本文实测配置见第七章模板)
  • 模块化 include 文件:/etc/riemann/c1-include.clj
  • JVM 参数文件:/etc/default/riemann(EXTRA_JAVA_OPTS="-Xms256m -Xmx512m")
  • 日志文件:/var/log/riemann/riemann.log
  • 监听端口:5555/TCP+UDP(protobuf 事件)、5556/TCP(WebSocket/HTTP)、5557/TCP(SSE/HTTP 查询)、2003/TCP(Graphite 明文)、4242/TCP(OpenTSDB telnet)
  • 服务主机名:riemann-srv

安装过程(deb 包安装、systemd 托管、JVM 堆限)见 《Ubuntu 22.04 安装 Riemann 0.3.12 指南》,本文从配置层展开。

二、前置条件

Riemann 服务已按安装篇部署并处于运行状态,配置文件为 /etc/riemann/riemann.config(本文实测配置见第七章模板,另含 include 文件 /etc/riemann/c1-include.clj):

systemctl is-active riemann
active

事件注入与查询通道可用(本文所有实测均通过 5556 的 HTTP NDJSON 注入、5557 的 SSE 查询取证):

echo '{"host":"web1","service":"cpu","state":"ok","metric":10}' | \
  curl -s -X POST --data-binary @- http://127.0.0.1:5556/events
{}

{} 即摄入成功回执。查询 DSL 的语法细节与 CLI 工具用法见《Riemann 0.3.12 常用 CLI 命令与工具详解》。

三、参数查看与修改方式

3.1 配置文件即 Clojure 程序

Riemann 没有 ini/yaml 式参数文件——/etc/riemann/riemann.config 是一段 Clojure 源码,服务启动时在 riemann.config 命名空间里求值。因此「参数」的形态是 (函数 选项map) 调用,官方 riemann.config 命名空间提供全部入口函数(tcp-server、index、streams、include 等),riemann.streams 命名空间提供全部组合器(where、changed、rate 等)。查看当前生效配置直接看文件即可,无独立 dump 命令。

修改方式:编辑 /etc/riemann/riemann.config → 语法校验(3.2)→ 热加载(3.3)。

3.2 语法校验(不抢端口)

riemann test /etc/riemann/riemann.config
INFO [2026-11-01 10:31:06,286] main - riemann.bin - Loading /etc/riemann/riemann.config

Testing clojure.core

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.

test 模式加载配置并执行 riemann.test 用例,不占用监听端口,可与在线服务并存,用于上线前验证语法(launcher 完整说明见《Riemann 0.3.12 常用 CLI 命令与工具详解》)。

3.3 SIGHUP 热加载

改完配置无需重启,systemctl reload riemann(init 脚本发 SIGHUP)全量重载:

systemctl reload riemann
tail -3 /var/log/riemann/riemann.log
INFO [2026-11-01 10:28:21,734] SIGHUP handler - riemann.bin - Caught SIGHUP, reloading
INFO [2026-11-01 10:28:21,735] SIGHUP handler - riemann.pubsub - Sweeping transient subscriptions.
INFO [2026-11-01 10:28:21,779] SIGHUP handler - riemann.core - Hyperspace core online

Caught SIGHUP, reloading → Hyperspace core online 即重载完成;进程 PID 不变、索引保留(若新旧配置等价则沿用旧索引)。

四、参数总览

riemann.config 命名空间全部入口函数与默认值(0.3.12 源码实测口径):

入口函数作用关键默认值
(tcp-server opts)protobuf/TCP 事件摄入:host "127.0.0.1"、:port 5555(TLS 时 5554)、:so-backlog 50
(udp-server opts)protobuf/UDP 事件摄入:host "127.0.0.1"、:port 5555、:max-size 16384
(ws-server opts)WebSocket/HTTP 接口:host "127.0.0.1"、:port 5556
(sse-server opts)SSE/HTTP 查询接口:host "127.0.0.1"、:port 5558(发行版配置常显式 5557)
(graphite-server opts)Graphite 明文监听:host "127.0.0.1"、:port 2003、:protocol :tcp、:tags false
(opentsdb-server opts)OpenTSDB telnet 监听:host "127.0.0.1"、:port 4242
(index)核心索引NonBlockingHashMap,键 [host service],default-ttl 60
(periodically-expire interval opts)过期清扫线程interval 默认 10 秒;:keep-keys [:host :service]
(instrumentation opts)自监控事件:interval 10、:enabled? true、ttl=2×interval、tag riemann
(streams & things)追加处理流—
(include path)加载其他配置/目录目录递归收 *.clj/*.config
(logging/init opts) / (logging/set-level ...)日志:rotate-count 10、:layout :riemann
(async-queue! name pool-opts & children)异步队列流:core-pool-size 1、:max-pool-size 128、:keep-alive-time 10ms、:queue-size 1000
(publish channel) / (subscribe channel f)核心内 pubsub—
(delete-from-index)按字段删索引—
(reinject)事件回流当前核心警告:易死循环
(service! svc) / (clear!) / (apply!) / (start!) / (stop!)手动核心过渡配合 depend/REPL
(depend plugin artifact version opts)pomegranate 拉依赖:exit-on-failure true、仓库默认 clojars
(local-repo path)Maven 本地仓库—
(repl-server opts)nREPL 远程调试—
(update-index ...)已废弃,index 本身即流—

⚠️ 注意:deb 默认配置只写 tcp-server/udp-server/ws-server 且绑 127.0.0.1,默认未启用 sse-server/graphite-server/opentsdb-server——远程摄入与 HTTP 查询都需显式加配置并热加载。

五、核心参数分类详解

本章所有默认值均对照 0.3.12 源码核实,实测回显来自本文第七章配置(已部署生效)。

5.1 tcp-server:protobuf/TCP 摄入

(tcp-server {:host "0.0.0.0" :port 5555})
  • :host 默认 "127.0.0.1",生产需改 "0.0.0.0" 才能收远程 agent;
  • :port 默认 5555;开启 TLS 时默认 5554;
  • :so-backlog 默认 50(ss -ltn 回显中 Recv-Q 列的 50 即此值);
  • TLS::tls? true 且必须同时给 :key(PKCS8 私钥)、:cert、:ca-cert 三项,缺一 assert 报错。

5.2 udp-server:protobuf/UDP 摄入

(udp-server {:host "0.0.0.0" :port 5555})
  • :host/:port 默认同 TCP;
  • :max-size 数据报上限默认 16384 字节,超限的包直接丢弃(protobuf parse error,不报错给客户端)——大批量事件应走 TCP 或减小单批条数;
  • :so-rcvbuf 可调接收缓冲。

5.3 ws-server 与 sse-server:HTTP/WebSocket 接口

(ws-server  {:host "0.0.0.0" :port 5556})
(sse-server {:host "0.0.0.0" :port 5557})
  • ws-server 默认 5556:WebSocket 订阅 + HTTP POST /events(NDJSON,每行一个 JSON 对象,不是数组);
  • sse-server 代码默认端口 5558,本文与 deb 常见配置显式改为 5557:纯 HTTP GET /index?query=... 返回 SSE 流(data: 行);
  • :headers 可附加响应头,默认已含 Content-Type: text/event-stream、Cache-Control: no-cache、Connection: keepalive。

实测注入与查询闭环(5556 进、5557 出):

echo '{"host":"web1","service":"cpu","state":"ok","metric":10}' | \
  curl -s -X POST --data-binary @- http://127.0.0.1:5556/events
curl -s --max-time 2 "http://127.0.0.1:5557/index?query=service%20%3D%20%22cpu%22"
{}
data: {"host":"web1","service":"cpu","state":"ok","metric":0,"time":"2026-11-01T10:27:20.438Z"}

(此处回显为第七章 rate 流覆盖索引后的最终态,见 6.7。)

5.4 graphite-server:收 Graphite 明文

(graphite-server {:host "0.0.0.0" :port 2003})
  • 默认 :host "127.0.0.1"、:port 2003、:protocol :tcp(可 :udp);:parser-fn 自定义解析;:tags 把 Graphite tag 转事件属性,默认 false。

实测:向 2003 写一行 Graphite 明文(metric.path value timestamp),即时查询:

ts=$(date +%s)
exec 3<>/dev/tcp/127.0.0.1/2003; printf "c1.graphite.value2 42 %s\n" "$ts" >&3; exec 3<&- 3>&-
curl -s --max-time 2 "http://127.0.0.1:5557/index?query=service%20%3D%20%22c1.graphite.value2%22"
data: {"host":null,"service":"c1.graphite.value2","state":null,"description":null,"metric":42.0,"tags":["c1"],"time":"2026-11-01T10:29:00.000Z","ttl":60}

两个实测要点:

  1. host 为 null——Graphite 明文行只有 path/value/ts,没有主机信息,事件不带 host;索引键 [nil service],查询按 host 过滤会漏(见第八章 8.4)。
  2. service 保留点分原名(c1.graphite.value2),不是空格分隔;用 service = "c1 graphite value2" 查询恒为空。

5.5 opentsdb-server:收 OpenTSDB telnet

(opentsdb-server {:host "0.0.0.0" :port 4242})

实测 put 命令(带 host= 标签)注入与查询:

ts=$(date +%s)
exec 4<>/dev/tcp/127.0.0.1/4242; printf "put c1.opentsdb.value2 %s 7.5 host=web1\n" "$ts" >&4; exec 4<&- 4>&-
curl -s --max-time 2 "http://127.0.0.1:5557/index?query=service%20%3D%20%22c1.opentsdb.value2%22"
data: {"host":"web1","service":"c1.opentsdb.value2","state":null,"description":"c1.opentsdb.value2","metric":7.5,"tags":["c1"],"time":"2026-11-01T10:29:00.000Z","ttl":60}

与 graphite 的差异:OpenTSDB 行协议自带标签,put ... host=web1 的 host 标签会成为事件 host;description 回填为 metric 全名。:parser-fn 可自定义行解析。

5.6 index 与 periodically-expire:瞬态状态与过期

(index)
(periodically-expire 5 {:keep-keys [:host :service :tags]})
  • index 返回核心索引流(NonBlockingHashMap),键 [host service],同键新事件覆盖旧事件;default-ttl 60——事件无 ttl 且流里未补时按 60 秒存活;
  • periodically-expire 是清扫线程:默认每 10 秒扫一轮(本文显式 5 秒),把 age > ttl 的事件置 state="expired" 流回 streams 并发布到 "index" pubsub 通道,随后从索引删除;
  • :keep-keys 默认 [:host :service]——过期合成事件只保留这两个字段,加 :tags 可保留标签;:all 保留全部字段;
  • 0.3.12 无 :jitter 选项(较新代码才有,配置里写了不报错但无抖动效果)。

💡 技术拓展:内存态索引的淘汰策略常见对比——

策略机制代表
TTL 过期(本文所用)每键存最新值,age>ttl 即删Riemann index
LRU按最近访问淘汰Redis allkeys-lru、本地缓存
LFU按访问频率淘汰Redis allkeys-lfu
容量上限+拒绝满后拒写/驱逐各类 ring buffer

Riemann 选 TTL 过期,因为「当前状态」的语义就是「最近 ttl 秒内报告过才算活着」,agent 停止上报 = 状态消失,无需容量调参。

实测短命事件(ttl 2)的完整生命周期——注入后约 5 秒(清扫周期 5s)过期:

echo '{"host":"web1","service":"shortlived","state":"ok","metric":1,"ttl":2}' | \
  curl -s -X POST --data-binary @- http://127.0.0.1:5556/events
sleep 6
grep shortlived /var/log/riemann/riemann.log | tail -2
INFO [2026-11-01 10:27:06,849] riemann task 3 - riemann.config - C1-CHANGED shortlived expired
INFO [2026-11-01 10:27:06,863] riemann task 3 - riemann.config - C1-EXPIRED shortlived

再查索引即消失:

curl -s --max-time 2 "http://127.0.0.1:5557/index?query=service%20%3D%20%22shortlived%22"
(无输出——expired 事件已从索引删除)

5.7 instrumentation:自监控

(instrumentation {:interval 10})
  • :interval 默认 10 秒;:enabled? 默认 true(false 则不发自身健康事件);事件 ttl 默认 2×interval;tag riemann;
  • 采样内核与传输层服务:index size、streams rate/latency、各 server 的 in latency / conns / threads active / queue size 等,service 前缀 "riemann ",rate/latency 后缀(如 riemann streams latency 0.99)。

实测(配置里给全流打了 c1 标签后按标签查询,节选):

curl -s --max-time 2 "http://127.0.0.1:5557/index?query=tagged%20%22c1%22" | grep 'riemann-srv' | head -3
data: {"host":"riemann-srv","service":"riemann server udp 0.0.0.0:5555 in latency 0.95","state":"ok","description":null,"metric":null,"tags":["c1","riemann"],"time":"2026-11-01T10:28:10.421Z","ttl":20}
data: {"host":"riemann-srv","service":"riemann server tcp 0.0.0.0:2003 conns","state":"ok","description":null,"metric":1,"tags":["c1","riemann"],"time":"2026-11-01T10:28:10.421Z","ttl":20}
data: {"host":"riemann-srv","service":"riemann streams latency 0.99","state":null,"description":null,"metric":0.6470349999999999,"tags":["c1","riemann"],"time":"2026-11-01T10:28:10.420Z","ttl":20}

⚠️ 注意:源码注释称 instrumentation 事件 host=localhost,0.3.12 实测 host 是服务器主机名(riemann-srv)。按 host = "localhost" 查询自监控事件恒为空(见第八章 8.2)。

5.8 logging:日志参数

(logging/init {:file "/var/log/riemann/riemann.log"})
  • :file 单文件日志;:files 多文件各配 layout;:console?/:console-layout 控制台输出;
  • :layout 取 :riemann(默认)或 :json;
  • :logsize-rotate 给定字节数则按大小轮转,否则按时间轮转;:rotate-count 默认 10;
  • JVM 属性 -Dlogback.configurationFile 优先于此处的配置;
  • 运行时调级:(logging/set-level :warn)。

配置内 (info ...) 写入的自定义日志(本文实测各组合器取证即靠它):

INFO [2026-11-01 10:27:02,762] worker-3 - riemann.config - C1-INCLUDE include-test

5.9 include:模块化拆分

(include "/etc/riemann/c1-include.clj")
  • 加载其他配置文件或目录(目录递归收集 *.clj/*.config);相对路径基于当前 *config-file* 所在目录;
  • include 文件在 riemann.config 命名空间求值,可直接调 streams/where 等。

实测 include 文件 /etc/riemann/c1-include.clj:

; C1 include 验证:被主配置 (include ...) 加载
(streams
  (where (service "include-test")
    (fn [e] (info "C1-INCLUDE" (:service e)))))

注入匹配事件后日志回显:

echo '{"host":"web1","service":"include-test","state":"ok","metric":1}' | \
  curl -s -X POST --data-binary @- http://127.0.0.1:5556/events
grep C1-INCLUDE /var/log/riemann/riemann.log | tail -1
INFO [2026-11-01 10:27:02,762] worker-3 - riemann.config - C1-INCLUDE include-test

5.10 其余入口参数速查

  • (async-queue! name pool-opts & children):为子流建独立线程池异步队列,防慢下游阻塞摄入。:core-pool-size 默认 1、:max-pool-size 默认 128、:keep-alive-time 默认 10 毫秒、:queue-size 默认 1000;每个调用创建一个 threadpool-service,滥用会线程膨胀。
  • (publish channel) / (subscribe channel f):核心内 pubsub 总线,periodically-expire 即向 "index" 通道发布过期事件。
  • (delete-from-index):([] [host service]) 删指定键;([fields]) 按字段删。
  • (reinject):事件回流当前核心——配置不当会形成死循环,慎用。
  • (service! svc):热加载时复用服务实例不打断连接;(clear!)/(apply!)/(start!)/(stop!) 手动核心过渡,配合 depend 或 REPL。
  • (depend plugin artifact version opts) + (local-repo path):pomegranate 运行时拉依赖并 load-plugin;:exit-on-failure 默认 true(拉不到直接退出),仓库默认仅 clojars(HTTPS)。
  • (repl-server opts):开 nREPL 端口远程调试配置。
  • (update-index ...):已废弃,index 本身即流,直接写进 streams 即可。

六、streams 组合器详解

riemann.streams 命名空间的组合器是配置的灵魂。本章逐个讲语义,并给出 0.3.12 实测回显(测试流均落在 (fn [e] (info "C1-..." ...)) 写日志的探针上)。

6.1 where:谓词过滤(0.3.12 无 when)

(where (service "cpu") ...)          ; 等值
(where (host #"^web") ...)            ; 正则
(where (or (service "cpu") (service "mem")) ...)
(where (not (tagged "batch")) ...)    ; 反义过滤
(where (service "cpu") ... (else ...)) ; else 子句兜底
  • 谓词符号可重写:host/service/state/metric/metric_f/time/ttl/description/tags/tagged/tagged-all/tagged-any;(metric 1 2 3) 表任一相等;(service #{...}) 集合成员。
  • 0.3.12 没有 when——网上旧文档/博客里的 (when ...) 写法在本版本直接报 Unable to resolve symbol: when,反义过滤一律用 (where (not expr))(见第八章 8.3)。

6.2 changed:状态变化才放行

(changed :state (fn [e] (info "C1-CHANGED" (:service e) (:state e))))
  • 默认按 [host service] 记忆上一值,字段变化才放行;:init 设初值,:pairs? true 时子流收到 [旧 新] 向量;(changed-state) 是 (changed :state) 的别名。

实测回显(cpu 从 ok 翻 critical、flap 三连翻转、graphite 首见、shortlived 过期翻转,全部只输出「变化」时刻):

INFO [2026-11-01 10:27:02,749] worker-1 - riemann.config - C1-CHANGED cpu critical
INFO [2026-11-01 10:27:02,756] worker-2 - riemann.config - C1-CHANGED shortlived ok
INFO [2026-11-01 10:27:03,797] worker-3 - riemann.config - C1-CHANGED flap critical
INFO [2026-11-01 10:27:04,804] worker-4 - riemann.config - C1-CHANGED flap ok
INFO [2026-11-01 10:27:04,822] defaultEventExecutorGroup-2-4 - riemann.config - C1-CHANGED c1.graphite.value nil
INFO [2026-11-01 10:27:06,849] riemann task 3 - riemann.config - C1-CHANGED shortlived expired

注意 cpu 事件先注入两条 ok(重复值被吞、无日志),第三条 critical 才打出——这就是 changed 的去重语义;graphite 事件 state 为 nil,首见即「变化」。

6.3 expired / not-expired:过期事件流

(expired (fn [e] (info "C1-EXPIRED" (:service e))))
  • expired? 判据:state == "expired" 或 age 超过 ttl(ttl 缺省用 index 的 default-ttl 60)。

实测(5.6 的 shortlived 事件过期时):

INFO [2026-11-01 10:27:06,863] riemann task 3 - riemann.config - C1-EXPIRED shortlived

6.4 coalesce:时间窗合并

(where (service "coalesce-test")
  (coalesce 5 (fn [es] (info "C1-COALESCE" (count es)))))
  • 每 5 秒把窗口内事件合并成一个事件向量交给子流一次(子流收 es 向量而非单事件)。

实测:注入 h1/h2/h3 三条 coalesce-test 后,日志每 5 秒输出一轮(窗口内剩余条数递减,空窗输出 0 条向量被探针 count 显示):

INFO [2026-11-01 10:27:02,784] riemann task 1 - riemann.config - C1-COALESCE 3
INFO [2026-11-01 10:27:07,783] riemann task 3 - riemann.config - C1-COALESCE 3
INFO [2026-11-01 10:27:12,783] riemann task 2 - riemann.config - C1-COALESCE 3
INFO [2026-11-01 10:27:17,783] riemann task 1 - riemann.config - C1-COALESCE 3

6.5 stable:持续稳定才放行(防抖)

(where (service "flap")
  (stable 5 :state (fn [e] (info "C1-STABLE" (:state e)))))
  • 字段值连续 5 秒不变才放行,期间缓冲;用于过滤抖动(flapping)。

实测:flap 事件 1 秒内 ok→critical→ok 三连翻转,6.2 的 changed 流每条都打,而 stable 流全程静默;直到第 9 秒连续 ok 超窗才放行,之后每轮稳定重复输出:

INFO [2026-11-01 10:27:09,803] riemann task 0 - riemann.config - C1-STABLE ok
INFO [2026-11-01 10:27:13,814] worker-1 - riemann.config - C1-STABLE ok

6.6 top:前 K 名打标(惰性降级)

(where (tagged "rank")
  (top 2 :metric
    (tag "top" index)
    index))
  • (top k f top-stream [bottom-stream [demote?]]):按 f 排序前 k 名进 top-stream,其余进 bottom-stream(内部由 dual 实现,排名变化时惰性降级)。

实测:注入 ranker 的 r1=1 / r2=5 / r3=9 后查询——r3、r2 带上了 top 标签,r1 只有 rank:

data: {"host":"ranker","service":"r3","state":"ok","metric":9,"tags":["top","rank"],"time":"2026-11-01T10:27:02.773Z"}
data: {"host":"ranker","service":"r2","state":"ok","metric":5,"tags":["top","rank"],"time":"2026-11-01T10:27:02.773Z"}
data: {"host":"ranker","service":"r1","state":"ok","metric":1,"tags":["rank"],"time":"2026-11-01T10:27:02.772Z"}

按标签反查 tagged "top" 命中 r2/r3 两条;host = "ranker" and not tagged "top" 在 ttl 60 过期后查询为空:

curl -s --max-time 2 "http://127.0.0.1:5557/index?query=host%20%3D%20%22ranker%22%20and%20not%20tagged%20%22top%22"
(无输出——ttl 60 已过期被清扫)

6.7 rate:事件速率流

(where (service "cpu")
  (by :host
    (rate 5 index)))
  • (rate interval & children):每 interval 秒把窗口内 metric 求和 ÷ interval 合成一个 rate 事件,模板取窗口内最近一个带 metric 的事件,ttl 随窗口递减。
  • 关键陷阱:rate 事件与原始事件 [host service] 键相同,写入同一 index 后互相覆盖——索引里看到的是 rate 值而非原始 metric(实测取证见《Riemann 0.3.12 常用 CLI 命令与工具详解》,轮询序列 10→10→6→6→6→0→0→0,6.0=30/5)。

实测:向 web2 注入 3 条 cpu metric 10,6 秒后查询(rate 窗口已过、空窗归零):

printf '{"host":"web2","service":"cpu","state":"ok","metric":10}\n{"host":"web2","service":"cpu","state":"ok","metric":10}\n{"host":"web2","service":"cpu","state":"ok","metric":10}\n' | \
  curl -s -X POST --data-binary @- http://127.0.0.1:5556/events
sleep 6
curl -s --max-time 2 "http://127.0.0.1:5557/index?query=host%20%3D%20%22web2%22%20and%20service%20%3D%20%22cpu%22"
{}
{}
{}
data: {"host":"web2","service":"cpu","state":"ok","metric":0,"time":"2026-11-01T10:29:10.810Z"}

6.8 tag / with / default / adjust / scale:字段改写

(tag "c1" index)                     ; 加标签(distinct,实测全流事件均带 ["c1"])
(untag "c1" ...)                     ; 去标签
(with :state "critical" ...)         ; 强制改字段(nil=删字段),仅子流可见
(default :ttl 60 ...)                ; 仅当字段为 nil 才填(第七章模板主链即用它)
(adjust [:metric (* 2)] ...)         ; 基于当前值改;smap 别名
(scale 1000 ...)                     ; metric 乘系数

事件不可变:with/default/adjust 只影响子流收到的副本,上游与其他并行流不受影响——这是理解 Riemann 流「分支各改各的」的关键。

6.9 其余组合器速查(0.3.12 源码实际存在清单)

组合器语义备注
(match f value & children)自定义谓词匹配riemann.common/match
(split / split* / splitp)首个匹配谓词分流奇数形式带默认流
(by fields & children)按字段值分叉子流字段可为一元函数;无 GC,无界值会泄漏
(by-builder [sym field] forms)fork 时按值构造子流—
(runs len field & children)连续 n 个事件字段全同才放行防抖另一形态
(throttle n dt)dt 秒最多放行 n 条立即丢弃余量、零延迟
(rollup n dt)dt 秒最多调用子流 n 次传事件向量、溢出滚存
(batch n dt)满 n 条或 dt 秒成批传向量
(moving-event-window n) / (window n) / (fixed-event-window n) / (moving-time-window n) / (fixed-time-window n) / (fixed-offset-time-window n)事件向量窗口—
(fold / fold-all)对窗口 metrics 聚合mean/median/mode(s)/sum/product/minimum/maximum/std-dev/count/sorted-sample
(percentiles interval points & children)分位数service 追加 " 0.95" 等后缀
(ewma-timeless r) / (ewma halflife)指数加权移动平均—
(counter [init])metric 累加计数tag reset 重置
(ddt [n] & children)metric 对时间求导—
(fill-in interval) / (fill-in-last interval update) / (interpolate-constant interval)空洞补发 / 固定间隔重发—
(fold-interval interval event-key folder & children)窗口聚合回写字段—
(periodically-until-expired [interval [delay] f])事件驱动周期任务,ttl 到期停—
(part-time-fast / part-time-simple interval ...)时间分箱基础件percentiles 的底层
(execute-on executor & children)线程池执行子流配 service!/threadpool-service
(exception-stream ex & children)子流异常转事件送 ex默认仅 warn 日志
(bit-bucket)丢弃—
(dual pred t f)镜像双流另一边收 expire;top 的实现
(append ref) / (register ref)收集到 atom测试用
(forward client)经 riemann.client 转发—
(project fields & children)只保留指定字段—
(apdex) / (clock-skew dt f) / (predict-linear ...)APDEX / 时钟偏移 / 线性预测—
(smap f) / (smap* f) / (smapcat) / (sflatten) / (sreduce f [val]) / (sdo)函数式组合nil 丢弃/透传有别
(sum-over-time) / (mean-over-time)已废弃用 counter/ewma-timeless
(synchronize) / (convert-strings) / (retry) / (timeout) / (tsv) / (partition) / (concat) / (average) / (percentile)0.3.12 不存在勿照旧文档抄写

测试辅助(riemann.test):(deftest name body) 全新时间上下文+清索引、(tests & body)、(inject! events)、(run-stream / test-stream / lookup)、(tap name & children) 记录到 *results*、(io & children) 测试期抑制副作用——配合 riemann test 子命令做配置回归测试。

七、生产配置模板

适用场景:4 核 8GB 内存、50GB SSD 的单机 Riemann,监控约 1000 台主机、峰值约 5 万事件/分钟,需要收编 Graphite 明文与 OpenTSDB 上报、事件入索引供 SSE 查询、异常事件经邮件/PagerDuty 告警(告警出站函数需按环境配 SMTP/PagerDuty 凭据,模板中以注释标注)。模板即本文实测部署的 /etc/riemann/riemann.config(探针日志行可按需替换为真实告警流):

; -*- mode: clojure; -*-
; vim: filetype=clojure
; 生产模板:4C8G 单机 / ~1000 主机 / ~5万事件每分钟(实测于 0.3.12)

(logging/init {:file "/var/log/riemann/riemann.log"})

; 摄入面:TCP/UDP 5555, WS/HTTP 5556, SSE/HTTP 5557, Graphite 2003, OpenTSDB 4242
; 内存预算:JVM 堆 256-512MB(/etc/default/riemann),索引仅存最新态,磁盘占用主要是日志
(let [host "0.0.0.0"]
  (tcp-server {:host host :port 5555})
  (udp-server {:host host :port 5555})
  (ws-server  {:host host :port 5556})
  (sse-server {:host host :port 5557})
  (graphite-server {:host host :port 2003})
  (opentsdb-server {:host host :port 4242}))

; 过期清扫:5 秒一轮;过期事件保留 tags 便于按标签告警路由
(periodically-expire 5 {:keep-keys [:host :service :tags]})

; 业务流按团队拆分到独立文件,目录可整体 include
(include "/etc/riemann/c1-include.clj")

(let [index (index)]
  (streams
    ; 1. 主链:补 ttl + 打标 + 全量入索引
    (default :ttl 60
      (tag "prod" index))

    ; 2. 状态翻转告警(changed 去重,避免每事件一封邮件)
    (changed :state
      (where (tagged "critical-ok")
        (fn [e] (info "ALERT" (:service e) (:state e)))))

    ; 3. 过期事件:agent 停报即告警(生产替换为 email/pagerduty 流)
    (expired
      (fn [e] (info "EXPIRED" (:host e) (:service e))))

    ; 4. CPU 速率:按 host 分叉,5 秒窗口(注意 rate 覆盖索引原值,见 6.7)
    (where (service "cpu")
      (by :host
        (rate 5 index)))

    ; 5. Top-2 热点服务打标(惰性降级,见 6.6)
    (where (tagged "rank")
      (top 2 :metric
        (tag "top" index)
        index))

    ; 6. 高频事件合并输出(5 秒一批,防告警风暴,见 6.4)
    (where (tagged "batch")
      (coalesce 5
        (fn [es] (info "BATCH" (count es)))))

    ; 7. 抖动过滤:稳定 5 秒才告恢复(防 flap 刷屏,见 6.5)
    (where (tagged "flap")
      (stable 5 :state
        (fn [e] (info "RECOVERED" (:state e)))))))

逐项要点::ttl 60 主链兜底 agent 漏报 ttl;keep-keys 加 :tags 让过期事件仍可被 tagged 谓词路由;rate/top/coalesce/stable 四条治理流分别对应「速率、热点、合并、防抖」四类生产刚需;出站告警(email/pagerduty/graphite 转发)在对应 (where (tagged ...)) 子树里挂接,凭据类参数走环境变量注入,不落配置文件明文。

八、常见问题(FAQ)

8.1 刚注入的事件查不到了?

Q: 事件明明发过,几分钟后 GET /index 查不到? A: 正常行为——Riemann 索引是瞬态状态,age > ttl 即被 periodically-expire 清扫(本文实测:shortlived ttl 2 秒后过期、graphite/opentsdb 事件 ttl 60 秒后查询为空)。查历史请外接 Graphite/时序库;要事件「活着」就保证 agent 上报间隔小于 ttl,或显式加大 ttl。

8.2 查不到 Riemann 自监控事件?

Q: host = "localhost" and service = "riemann streams rate" 返回空? A: 0.3.12 实测 instrumentation 事件 host 是服务器主机名(本文环境为 riemann-srv),不是源码注释里的 localhost。改用 host = "riemann-srv" 或直接 tagged "riemann" 查询。

8.3 配置里写 (when ...) 报错?

Q: 照博客示例写 (when (service "cpu") ...),加载报 Unable to resolve symbol: when? A: 0.3.12 的 riemann.streams 没有 when,只有 where。需要「不满足条件时走另一支」用 (where expr ... (else ...)),需要反义过滤用 (where (not expr) ...)。

8.4 Graphite 转发进来的事件按 host 查不到?

Q: (graphite-server) 收上来的数据,host = "web1" 恒为空? A: Graphite 明文行只有 path value ts,事件 host 为 null(实测回显见 5.4)。按 service 查,或在转发链上游用 (with :host "web1" ...) 补;OpenTSDB 通道则用 put ... host=web1 标签带上。

8.5 旧文档里的组合器/选项不生效?

Q: (synchronize)、(percentile ...)、(periodically-expire 10 {:jitter 2}) 报错或无效果? A: 0.3.12 源码清单里没有 synchronize/convert-strings/retry/timeout/tsv/partition/concat/average/percentile(单数)这些函数;periodically-expire 无 :jitter 选项。以 6.9 速查表为准,写前先 riemann test 校验。

8.6 热加载后端口没变/连接断了?

Q: 改了 :port reload 后旧端口还在监听? A: reload 是全量过渡:旧 server shut down、新 server online(安装篇 8.3 实测日志)。若新配置求值失败,核心保持旧配置运行——看 /var/log/riemann/riemann.log 是否有 Hyperspace core online 收尾;等价配置热加载会保留旧索引,(service!) 复用可避免连接打断。

九、参考链接

Riemann 0.3.12 配置参数详解 - Quzuo