社交媒体去中心化描述的是没有单一运营者同时控制身份、内容存储、信息流排序和审核的架构。联邦协议(如 ActivityPub)、基于中继的网络(如 Nostr)和区块链支持的社交图谱之间的实际差异体现在延迟、可用性、审核和数据可移植性上。本文构建这些架构的心智模型,然后逐步介绍可复现的流程,用于度量中继响应时间、节点可用性和导出完整性。它将有文档记录的协议行为与运营建议以及你必须在自己的基础设施上收集的数字区分开来。
范围与三种去中心化模型
去中心化社交媒体不是一种设计。该术语至少涵盖三种不同的架构,将它们混为一谈会导致糟糕的工程决策。联邦式网络(基于 ActivityPub 的系统,如 Mastodon、Lemmy 和 PixelFed)运行在独立运营的服务器上,这些服务器之间交换消息。基于中继的网络(Nostr)将签名事件发布到多个中继,由客户端选择读取和写入哪些中继。区块链支持的社交网络将身份、社交图谱或内容引用存储在分布式账本上,内容本身通常存储在 IPFS 或类似存储中。
每种模型去中心化的东西不同。联邦制去中心化托管和审核,但身份仍与主服务器绑定。中继网络去中心化发布和抗审查能力,但将过滤推给客户端。区块链模型去中心化身份和所有权声明,但增加了共识延迟和成本。对于任何平台,一个有用的问题是:身份、内容存储、信息流排序和审核中,哪些可以由单一运营者单方面更改?
本文假设你能运行命令行工具、发起 HTTP 请求并阅读 JSON。它不假设你运营社交服务器。在端点选择重要之处,具体的提供商选择属于 RPC Assistant;本页侧重于方法论和可复现的度量。
- 联邦式:共享协议、独立服务器、服务器本地审核。
- 基于中继:签名事件、多个中继、客户端过滤。
- 区块链支持:账本锚定的身份或内容引用,负载存储在外部。
心智模型:身份、存储、排序和审核
将每个社交平台视为四个可分离的功能。身份是账户如何命名和认证。存储是帖子和媒体所在之处。排序是信息流如何排序。审核是谁可以删除或限制内容。中心化平台将这四个功能捆绑在一个运营者之下。去中心化设计将它们解绑,而解绑模式预示了你将遇到的运营问题。
在联邦制中,身份是主服务器上的句柄(例如 user@instance),存储是主服务器加上远程服务器上的缓存,排序通常是按时间顺序或客户端侧,审核在主服务器和每个接收服务器之间分割。在中继网络中,身份是公钥,存储是每个接受该事件的中继,排序由客户端定义,审核是中继策略加上客户端静音列表。在区块链模型中,身份可能是钱包或注册名称,存储通常在链下,排序由应用定义,审核通常在客户端或索引器层执行。
这个模型解释了一个常见的意外:一篇帖子可能在一台服务器上可见而在另一台上不可见,因为接收服务器选择不进行联邦或缓存内容的方式不同。因此,可用性是服务器之间路径的属性,而不是单一源站的属性。
协议机制:ActivityPub、中继和账本锚定
ActivityPub 定义了一种服务器到服务器的协议,其中参与者将活动(Create、Follow、Announce)发布到收件箱和发件箱。投递是基于推送的:源服务器将签名活动 POST 到每个关注者的收件箱。如果远程收件箱宕机,投递可能会重试或丢弃,具体取决于实现。这就是为什么联邦可靠性取决于重试队列和许多独立服务器的健康状态。
Nostr 反转了流程。客户端用私钥签名事件,并通过 WebSocket 发布到一个或多个中继。读者订阅中继并在本地过滤事件。没有主服务器;有一组中继,它们可能根据策略接受或拒绝事件。延迟主要取决于中继往返次数以及客户端查询的中继数量。
区块链支持的社交系统在链上锚定一条最小记录,例如个人资料注册或内容哈希,并将负载存储在其他地方。读取需要索引器或对链的 RPC 访问。写入需要交易,这引入了区块时间和费用波动。有关这些读取背后节点访问模式的更广泛讨论,请参阅区块链节点托管指南。
先决条件和可复现的测试计划
在度量任何东西之前,定义你要测试什么。一个有用的计划涵盖三个维度:可用性(客户端能否访问服务)、延迟(读取或写入需要多长时间)和可移植性(用户能否导出并重新导入其数据)。记录每次运行的客户端版本、网络条件和一天中的时间。
对所有端点使用占位符。将 RELAY_URL、INSTANCE_URL 和 RPC_URL 替换为你自己的基础设施或你选择的提供商的值。不要将公共端点硬编码到共享脚本中;它们会变化,并可能对你进行速率限制。
每次测量至少运行十次,并报告中位数和第 95 百分位数,而不是单个样本。单个快速响应不是可靠性的证据。
- 可用性:HTTP 状态和 WebSocket 连接成功率。
- 延迟:读取的首字节时间,写入的往返时间。
- 可移植性:导出完整性和重新导入成功。
- 一致性:同一篇帖子是否从多个观察点可见。
使用 curl 度量中继和实例延迟
对于基于 HTTP 的联邦端点,curl 可以快速了解可达性和首字节时间。以下脚本查询 WebFinger 端点(联邦服务器为参与者发现而暴露的端点),并报告计时。将 INSTANCE_URL 和资源替换为你的目标。
预期的验证结果是 HTTP 200,JSON 正文包含链接,输出中包含计时值。404 表示该实例上不存在该参与者;超时表示从你的网络无法访问该实例。至少从两个网络重复,以区分本地路由问题和服务器故障。
#!/usr/bin/env bash
# Measure WebFinger latency for a federated actor
INSTANCE_URL="https://INSTANCE_URL"
RESOURCE="acct:user@INSTANCE_URL"
for i in $(seq 1 10); do
curl -sS -o /dev/null \
-w "%{http_code} %{time_namelookup} %{time_connect} %{time_starttransfer} %{time_total}\n" \
"${INSTANCE_URL}/.well-known/webfinger?resource=${RESOURCE}"
done使用 JavaScript 度量中继往返
基于中继的网络使用 WebSocket 连接。下面的脚本打开到 RELAY_URL 的连接,发送订阅过滤器,并记录直到第一个匹配事件到达的时间。它使用 ws 包和一个占位中继。对多个中继运行它以比较首个事件延迟。
预期验证:脚本为每个中继打印以毫秒为单位的延迟,如果连接失败则打印错误。连接但在超时内从未返回事件的中继应记录为超时,而不是零延迟。将结果汇总到表格中,列包括中继、连接时间、首个事件时间和状态。
const WebSocket = require('ws');
const RELAY_URL = 'wss://RELAY_URL';
const TIMEOUT_MS = 5000;
function measure(relayUrl) {
return new Promise((resolve) => {
const start = Date.now();
const ws = new WebSocket(relayUrl);
let connectedAt = null;
const timer = setTimeout(() => {
ws.terminate();
resolve({ relayUrl, status: 'timeout', connectedAt, firstEventAt: null });
}, TIMEOUT_MS);
ws.on('open', () => {
connectedAt = Date.now() - start;
ws.send(JSON.stringify(['REQ', 'probe', { kinds: [1], limit: 1 }]));
});
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
if (msg[0] === 'EVENT') {
clearTimeout(timer);
ws.close();
resolve({ relayUrl, status: 'ok', connectedAt, firstEventAt: Date.now() - start });
}
});
ws.on('error', () => {
clearTimeout(timer);
resolve({ relayUrl, status: 'error', connectedAt, firstEventAt: null });
});
});
}
(async () => {
const results = await Promise.all([
measure(RELAY_URL),
measure('wss://RELAY_URL_2')
]);
console.table(results);
})();结果表及其解读
将每次运行记录在具有相同列的表格中,以便结果在中继、实例和日期之间具有可比性。下表是一个模板;用你自己的测量值填充它,而不是从其他地方借用数字。
| 目标 | 协议 | 运行次数 | 连接中位数 (ms) | 连接 p95 (ms) | 首个事件中位数 (ms) | 首个事件 p95 (ms) | 状态 |
|---|---|---|---|---|---|---|---|
| RELAY_URL | Nostr | 10 | |||||
| RELAY_URL_2 | Nostr | 10 | |||||
| INSTANCE_URL | ActivityPub | 10 |
解读表格时先比较中位数,再比较 p95。中位数低但 p95 高的中继在负载下不稳定。中位数高但 p95 紧凑的中继持续缓慢,对于后台读取可能仍然可以接受。状态应区分 ok、timeout 和 error,这样失败就不会被平均到延迟中。
测试数据可移植性和导出完整性
可移植性是去中心化的实际测试。联邦平台通常提供关注列表的导出,有时也提供帖子的导出。中继网络依赖客户端重新发布事件。区块链模型可能暴露链上记录,但将媒体留在链下。可移植性测试检查导出是否包含你期望的每个项目,以及重新导入是否恢复社交图谱。
流程:导出你的账户数据,按类型计数记录,然后导入到不同服务器或客户端上的第二个账户。比较计数并抽查时间戳。记录任何媒体、回复或关注关系的丢失。这是一个手动流程,但它产生了关于锁定效应的最具决策相关性的证据。
对于区块链支持的系统,等效的检查是链上记录是否可以在没有原始前端的情况下解析。通过索引器或 RPC 端点查询链,并确认内容哈希解析为可检索的数据。如果哈希只能通过一家公司的网关解析,那么该系统在名称上是去中心化的,但在检索上不是。
排查常见的去中心化故障
远程服务器上帖子缺失。 联邦投递可能失败,或者远程服务器可能阻止了源站。检查源站的投递日志和远程服务器的审核策略。你无法从客户端触发全局重试。
中继接受写入但读取返回空。 中继可能接受事件然后将其修剪,或者可能应用过滤器。查询多个中继并比较。如果只有一个中继返回该事件,则将该中继视为该内容的单点故障。
从区块链支持的个人资料读取缓慢。 瓶颈通常是 RPC 端点或索引器,而不是链本身。针对你的 RPC_URL 度量首字节时间并比较提供商。对于端点选择,使用 RPC Assistant;有关 RPC 行为的背景,请参阅 RPC 端点指南。
身份无法迁移。 如果你的句柄绑定到主服务器,迁移取决于协议的移动支持。在承诺使用服务器之前测试它。相比之下,基于中继的身份是你控制的密钥对,但丢失密钥就丢失了身份。
每种模型的局限性和权衡
联邦制提高了托管多样性和本地审核,但不保证可用性:一篇帖子取决于源服务器以及每个接收服务器缓存它的意愿。中继网络提高了发布弹性,但将过滤和垃圾邮件防御转移到客户端,这增加了客户端复杂性。区块链模型提供可验证的身份和所有权,但增加了交易成本、延迟以及实际读取对索引器的依赖。
这些模型都没有消除审核;它们只是转移了审核。相关的工程问题是:谁承担审核的成本,以及该成本有多透明。必须遵守当地法律的服务器管理员与可以在本地静音的客户端有不同的约束。设计评审应说明哪一方可以删除内容以及在什么条件下。
性能也因架构而异。联邦延迟主要取决于服务器到服务器的投递和缓存。中继延迟主要取决于 WebSocket 往返和中继数量。区块链读取主要取决于 RPC 和索引器性能。分别度量每个,而不是将“去中心化”视为单一性能类别。
后续步骤:建立度量基线
首先选择一个你实际使用的联邦实例和两个中继,然后每周运行上述脚本,并以相同的表格格式存储结果。每季度添加一次可移植性测试。如果你运营服务器,监控投递队列和出站请求延迟;如果你消费区块链支持的社交数据,监控你的 RPC 提供商的错误率和 p95 延迟。
有关这些度量背后的基础设施选择,请查看 RPC 定价 和 专用节点,以了解成本和隔离权衡。网络特定行为记录在支持的网路下,托管 API 访问在 API 服务 概述中描述。对于具体的端点和提供商建议,使用 RPC Assistant,而不是从公共列表中猜测。
目标不是宣布一种架构优越。而是用你自己的数字了解每个系统在哪里失败,以及你的社区是否能容忍这种失败模式。