Contents
1 前言
在部署 WordPress 多活节点方案时,一个至关重要的技术点是读写分离:即将所有涉及”写入数据库”的操作(如发布文章、修改内容、修改插件配置、提交评论等)统一路由到主写节点完成,而其他节点则作为只读副本,提供内容展示、缓存服务等。这种架构的好处是显而易见的:
- 它避免了多点写入可能引发的数据冲突、同步延迟和一致性问题;
- 同时也降低了主数据库的负载压力,使各节点能各司其职,提升整体系统的可扩展性与稳定性。
实现读写分离的核心在于将所有可能触发写操作的前端请求,准确且稳定地引导至主写节点。对于某些写操作,比如管理员发布文章、修改内容或配置插件,这类行为通常发生在后台操作环境中,可以通过强制访问主写节点来规避问题,因此相对可控。但也有一些写入行为难以人为干预,例如访客在文章下方直接发表评论,这类请求发生在普通页面中,无法要求用户特意跳转至某个专用节点来完成操作,因此不可控。
基于以上描述,在 WordPress 多活节点架构中,如何处理评论请求的写入路径,是实现读写分离时最具挑战性的问题之一,也是整体架构设计的关键所在。早期最直接的做法是将所有用户评论请求强制引导至主节点数据库写入,确保数据源的一致性和集中化管理。而为了实现这一目标,常见的思路包括:
- 跨域写入方案:在主节点的 WordPress 中配置 CORS 头,允许其他只读节点通过 JavaScript 的 AJAX 请求,将评论直接提交到主节点(如 comment.tangwudi.com)。这种方式实现相对简单,但涉及浏览器跨域限制,需妥善配置 Access-Control-Allow-Origin 等响应头,并处理好 Cookie 的作用域问题。
- 反向代理回写方案:在每个只读节点的 Web Server 中,将用户对 admin-ajax.php 或 wp-comments-post.php 的 POST 请求反代至主节点。该方法对用户透明,无需修改前端代码,也不存在跨域问题,但在实践中需关注会话保持、缓存穿透等细节,否则容易出现认证丢失或 CSRF 校验失败等问题。
- Worker 中转方案:借助 Cloudflare Worker 等边缘计算平台拦截评论请求,并将其统一转发至主节点。这种方式能够在不修改客户端代码的前提下,灵活实现后端写入路径的集中控制,同时天然具备跨域屏蔽和安全隔离优势。
2 基于”跨域写入”的实现方式
基于跨域的方案实现起来其实并不复杂,以我的博客(采用argon主题)为例,原本评论请求是发送到 “https://blog.tangwudi.com/admin-ajax.php
“,只需通过 PHP 层稍作修改,将请求地址改为 “https://comment.tangwudi.com/admin-ajax.php
“,就能将评论流量定向到指定节点,实现集中写入。但是这种方案伴随着几个潜在的坑点,值得特别注意:
- 浏览器的同源策略限制
默认情况下,浏览器会阻止网页向不同源(即不同域名、协议或端口)的服务器发起带有 Cookie 的请求。这意味着,如果前端页面尝试将评论请求发送到主写节点的另一个子域,可能会因为缺乏凭证而被 WordPress 拒绝,尤其是在用户已登录或需要 CSRF 校验的场景下。
- CORS 配置较难统一
要实现跨域请求,目标节点(即主写节点)需要正确设置 CORS 头部,比如 Access-Control-Allow-Origin、Access-Control-Allow-Credentials 等。虽然技术上可以通过 PHP 或服务器配置添加这些响应头,但在多节点环境中,为了确保代码统一维护,所有节点也需要保持相同的配置,即使某些只读节点本身并不处理写请求。
- 安全性引入新风险
允许跨域请求时,一旦设置不当,容易造成 API 暴露、权限绕过或 CSRF 攻击等安全隐患。例如,设置了 Access-Control-Allow-Origin: * 并允许带上 Cookie,将直接导致会话凭证泄露风险。
- 调试和监控变得复杂
一旦跨域请求失败,浏览器端往往只是抛出一个模糊的 CORS 错误,具体细节藏在网络层,定位和排查问题较为困难。同时,日志记录也会分布在多个节点,增加了运维难度。
- 兼容性与边缘行为不可预期
某些缓存层(如 Cloudflare APO)、前端框架或插件,可能默认假设所有请求都是同源的,跨域方案容易触发异常行为,例如 CSRF token 失效、用户状态丢失、功能报错等。
总结来说,基于跨域的方案虽然在技术上可行,但在涉及认证、缓存、安全、代码统一性等方面存在一定复杂度。在多节点部署场景中,除非具备严格控制跨域访问行为的能力,通常建议更谨慎地选择该方案,反正我是不会选择这种了,一看就知道要折腾很久~~。
注:对”跨域”概念不熟悉的朋友,可以参考我之前的一篇文章中的相关描述”家庭数据中心系列 由Ajax跨域问题开始的对当下Web开发核心基础概念的梳理(非程序员视角)”
3 基于”反向代理回写”的实现方式
另一种常见的思路是通过 Web Server 层的”反向代理”来实现评论请求的回写。具体来说,可以在每个只读节点的 Web Server(如 Nginx 或基于它的 Nginx Proxy Manager)中设置规则,将对 admin-ajax.php 的特定 POST 请求,反向代理至主写节点,例如https://comment.tangwudi.com/admin-ajax.php
。从浏览器角度看,请求依旧发送给原本的域名(如 https://blog.tangwudi.com/admin-ajax.php
),但实际由 Web Server 接手并完成请求的”引流”。
这种方式的优势在于结构简单、部署灵活。既不需要浏览器支持跨域访问,也无需引入额外的中间层工具。只要反向代理配置得当,整个过程对前端用户和 WordPress 插件层都是完全透明的,甚至可以与 APO、CDN 缓存共存,不涉及复杂的 Worker 路由逻辑。
但它也存在一些细节挑战,主要集中在以下几个方面:
- Cookie 和会话保持:反向代理后的请求是否能够携带用户登录状态,取决于 Cookie 域配置是否正确、主写节点是否识别这些 Cookie;
- 缓存干扰:如果前端或 CDN 对 admin-ajax.php 的请求做了缓存,可能会导致代理请求没有到达主写节点,需谨慎设置 Cache-Control 与相关策略;
- 错误排查难度:一旦请求在代理层被拦截或重写失败,排查起来可能不如 Worker 那样直观,尤其是在 UI 层没有明显报错时;
- 只适用于 HTTP 层转发:它无法做复杂的请求重写、响应重组或身份标识透传,相比 Worker 可操作空间较小。
总体来看,这种方式更接近”传统反向代理”的思路,适合已有 Nginx 管理经验、希望在现有 Web Server 层完成改造的用户。它也是一种”在节点边缘处理写入请求”的技术方案,部署简洁,对 WordPress 主体结构无侵入,是实现读写分离的一种稳妥手段。
4 基于”Worker 中转”的实现方式
使用这种方式可以完全绕过跨域相关的问题,因为在浏览器视角中,评论请求仍然是向原域名发起的,例如本例中的 “https://blog.tangwudi.com/admin-ajax.php
“。Worker 会拦截这类请求,并在边缘网络节点将其中转至另一个真正负责写入操作的节点,比如 “https://comment.tangwudi.com/admin-ajax.php
“,从而实现评论写入的集中化。
这种方式的最大优势在于对前端完全透明,无需修改浏览器侧逻辑,也不需要客户端支持 CORS 或配置复杂的跨域头。它还能避免暴露真实写入节点的地址,提升安全性。
当然,这种方案也存在一些需要注意的地方,比如:
- Cloudflare Worker 的 CPU 执行时间和响应体积都有上限,尽管评论请求通常不会触发限制,但仍需保持关注;
- Worker 代理的是 HTTP 层 的请求转发,不能处理某些需要与源站保持长连接或状态同步的高级场景(例如实时 WebSocket、特定的 Cookie 域属性问题);
- 若你启用了 APO(Automatic Platform Optimization),Worker 的处理可能与其缓存策略存在冲突,需特别设定 Worker 的路由匹配规则,避免干扰到 APO 所维护的页面缓存。
总体而言,Worker 是一种非常优雅的”在节点外部解决跨域写入问题”的思路,特别适用于 WordPress 多活部署场景下,将”用户产生的数据”有序引流至唯一主写节点。
5 使用”worker中转”方式实现多活wordpress节点的评论一致性
5.1 使用worker实现评论只写入主节点
这种方案逻辑最为简单,也最传统,遵循了”读写完全分离”的思路。只需确保评论统一写入位于家庭数据中心 macmini 上的 WordPress 主节点(假设对应于域名”comment.tangwudi.com”),其余的同步工作则由主节点上的数据库变更检测机制自动完成。
具体流程如下:主节点运行的定时检测脚本会周期性地检查 MariaDB 中的 WordPress 数据库是否发生变更;一旦检测到更新,便自动导出当前数据库为 wordpress.sql 文件。这个导出的 SQL 文件随后通过多种渠道(如 scp、syncthing 等)同步至芝加哥的wordpress从节点上的指定目录。芝加哥从节点上运行的数据检测机制(通常基于 inotify 等文件监控方式)会捕捉到指定目录中的新文件(wordpress.sql)到达的事件,自动触发数据库导入动作,将 wordpress.sql 文件中的内容导入其本地 MariaDB,从而完成主从节点之间 WordPress 数据的同步。
具体的worker创建步骤在之前的很多文章我都写过了,这里就不重复了,除了worker路由要配置正确,其他也没什么需要设置的,以我博客为例,worker的入口路由:
blog.tangwudi.com/wp-admin/admin-ajax.php*

不同的wordpress主题在实现评论功能时所采用的技术方式并不完全相同。以 Argon 主题为例,它使用的是基于 AJAX 的异步提交方式,因此评论提交请求最终会被发送到 WordPress 的 “/wp-admin/admin-ajax.php” 接口,通过后台的 wp_ajax_nopriv_* 钩子进行处理,这种方式的优点是用户提交评论后无需刷新页面,体验更流畅。
然而,也有不少主题(尤其是一些偏原生的、轻量化的主题)采用的是 WordPress 默认的评论处理机制,即直接将评论 POST 到 wp-comments-post.php。这种方式虽然更为传统,但提交后通常会触发整页刷新,用户体验略逊一筹。此外,由于这些请求并不经过 AJAX 管道,也就无法方便地在前端捕捉提交结果或统一错误处理逻辑。
这一差异在实际架构设计中需要特别留意,尤其是在部署自定义 Worker、反向代理或 Webhook 处理逻辑时。如果只针对某一种提交路径做了拦截或重写,而忽略了另一种路径的可能,就可能出现部分主题评论无法写入或写入失败的问题。因此,在多节点部署场景中,建议结合实际使用的主题和评论插件情况,针对不同的评论提交入口做适配处理,确保所有路径都能被正确地路由或处理,我这里为了偷懒就只考虑”admin-ajax.php” 这一种情况了,毕竟”wp-comments-post.php”我也没法验证。
具体的worker代码如下:
export default {
async fetch(request, env, ctx) {
// 解析请求的 URL
const url = new URL(request.url);
// 如果请求路径不是以 /admin-ajax.php 结尾,返回 404,并标记未命中 Worker
if (!url.pathname.endsWith("/admin-ajax.php")) {
return new Response("Not found", {
status: 404,
headers: {
"content-type": "text/plain",
"X-Worker-Hit": "no" // 自定义标头,方便调试是否命中 Worker
}
});
}
// 目标代理地址,即评论请求的实际处理端
const targetUrl = "https://comment.tangwudi.com/wp-admin/admin-ajax.php";
// 克隆请求对象,避免后续 body 被多次读取导致报错
const reqClone = request.clone();
// 克隆请求头,并移除 Referer,避免原始域名暴露或被 WAF 拦截
const newHeaders = new Headers(reqClone.headers);
newHeaders.delete("referer");
// 构造一个新的请求对象,指向代理目标地址
const proxiedRequest = new Request(targetUrl, {
method: reqClone.method,
headers: newHeaders,
// GET 和 HEAD 请求没有 body,其他方法则读取原始 body 进行转发
body: reqClone.method === "GET" || reqClone.method === "HEAD" ? null : await reqClone.arrayBuffer(),
redirect: "manual" // 不跟随重定向,交由浏览器自行处理
});
// 发送代理请求
const response = await fetch(proxiedRequest);
// 克隆响应头并添加自定义标记
const respHeaders = new Headers(response.headers);
respHeaders.set("X-Worker-Hit", "yes"); // 用于确认 Worker 生效
// 返回代理后的响应,同时保留原响应体和状态码
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: respHeaders
});
}
};
在以上这段 Worker 代码中,我加入了两个小功能,以便增强代理请求的可控性和可观测性,这两个小功能不仅提升了调试效率,也让整个评论请求的代理链路变得更透明和更可验证:
- 清除原有 Referer:为了避免将原始页面的来源信息(即 blog.tangwudi.com)暴露给目标服务端,我在 Worker 中显式删除了请求头中的 Referer 字段。这有助于提升一定程度的隐私保护,也可以避免某些服务器端逻辑因为 Referer 不一致而拒绝请求(其实对于我的场景而言没啥用,因为我在所有WordPress节点去除了默认的域名访问限制),尤其在 WordPress 的安全插件、WAF 或某些 CDN 策略存在时。
- 添加自定义响应头 X-Worker-Hit:为了便于确认请求是否真正经过了 Worker,我在响应头中添加了标头”X-Worker-Hit: yes”。借助这个标头,可以在浏览器开发者工具或 curl 等调试工具中快速判断 Worker 是否生效,避免因路由未命中、缓存绕过等问题而产生的误判。在路径未匹配时,Worker 还会返回 404,并附带 X-Worker-Hit: no,便于分辨处理路径是否正确。
5.2 多节点同时写入评论的实现(进阶版)
5.2.1 把”不可控写”变为”可控写”
上一节提到的”评论写入主节点 + 数据库变更检测 + 自动导出 wordpress.sql 同步至从节点“这套机制,整体稳定,部署也不复杂。但由于依赖定时检测与文件传输,在评论这种对实时性要求较高的场景下,天然存在同步延迟问题:用户可能遇到这样的情况:评论提交后提示成功,但页面上迟迟看不到更新。这往往是因为回源节点不同导致的——如果恰好被 Cloudflare Tunnel 分发到主节点,就能看到最新评论;但大多数情况下会被分发到地理位置更近、但尚未同步的芝加哥从节点,从而出现”评论成功但不可见”的错觉,影响用户体验。
在已经实现了”评论写入主节点”的基础上,我更进一步,用了 Cloudflare Worker 实现多点同步写入 的方案:在评论提交阶段直接向主节点和从节点同时发起请求,实现写入冗余。这种方式不仅提升了可见性,还从根本上避免了依赖”主节点变更检测”这种间接手段:因为把”不可控”的评论写入采用多节点同时写入的办法强行变为了”可控”。
5.2.2 具体实现的worker代码
假设主节点对应”comment1.tangwudi.com”,芝加哥从节点对应”comment2.tangwudi.com”,具体的worker代码如下:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// 如果路径不是 /admin-ajax.php,返回 404(防止滥用)
if (!url.pathname.endsWith("/admin-ajax.php")) {
return new Response("Not found", {
status: 404,
headers: {
"content-type": "text/plain",
"X-Worker-Hit": "no" // 自定义响应头,用于标记未命中
}
});
}
// 缓冲请求体(避免多次读取失败)
const reqBody = request.method === "GET" || request.method === "HEAD"
? null
: await request.arrayBuffer();
// 拷贝请求头,去除 Referer(有些服务会校验)
const commonHeaders = new Headers(request.headers);
commonHeaders.delete("referer");
// 构建主节点请求(comment1:macmini 上的主写节点)
const primaryRequest = new Request("https://comment1.tangwudi.com/wp-admin/admin-ajax.php", {
method: request.method,
headers: commonHeaders,
body: reqBody,
redirect: "manual" // 防止重定向被自动跟随
});
// 构建从节点请求(comment2:芝加哥从读节点)
const secondaryRequest = new Request("https://comment2.tangwudi.com/wp-admin/admin-ajax.php", {
method: request.method,
headers: commonHeaders,
body: reqBody,
redirect: "manual"
});
// 主节点请求:同步执行,并作为最终响应返回
const primaryResponse = await fetch(primaryRequest);
// 从节点请求:异步执行,失败也不会影响主流程
ctx.waitUntil(
fetch(secondaryRequest).catch((err) => {
console.log("Secondary write failed:", err);
})
);
// 添加自定义响应头,标记命中 Worker
const respHeaders = new Headers(primaryResponse.headers);
respHeaders.set("X-Worker-Hit", "yes");
// 返回主节点的响应结果
return new Response(primaryResponse.body, {
status: primaryResponse.status,
statusText: primaryResponse.statusText,
headers: respHeaders
});
}
};
目标 | 实现方式 |
---|---|
主节点写入成功后返回响应 | await fetch(primaryRequest) |
从节点异步写入,失败不影响主流程 | ctx.waitUntil(fetch(secondaryRequest)) |
保持 Worker 快速响应 | 所有非主流程操作都通过 ctx.waitUntil() |
支持评论双写、简化数据同步 | 一次提交即同步两个节点,无需后续检测机制 |
为了实现评论的多wordpress节点写入,Worker 脚本在收到评论请求后,并不将其简单转发到单一节点,而是构造出多个并发的 HTTP 请求,将同一条评论数据同时发送到主节点和从节点的接口,这些请求通常是异步触发、彼此独立,互不影响。借助边缘计算的高可用特性,这种”多点并发写入 “的方案,兼顾了性能、可靠性和一致性,大幅降低了主节点单点故障或同步延迟对用户体验的影响。
注:这种方式可以支持WordPress多活架构中添加更多节点,比如comment3.tangwudi.com,comment4.tangwudi.com等,代码做简单的修改即可。
虽然通过 Worker 实现 WordPress 多节点评论同时写入,大幅提升了评论系统在多活架构下的可用性与一致性,但它仍绕不开 WordPress 的一个底层限制:评论审核状态和评论 ID 仅在本地数据库生效,主从节点互不感知。
这带来的问题不仅仅是”首次发表评论后需要在主节点批准并同步数据库“,更关键的是:如果访客后续尝试回复某条评论,而该评论在不同节点中的 ID 不一致(由于 APO 缓存或回源时访问的节点不可控,页面上的评论内容不管是来自主节点还是从节点,对于另一方而言评论 ID都不一致,这个坑点折腾了我好久,因为当时对关系型数据库的”自增”逻辑理解不到位),就可能出现”无法回复未审核评论”或”引用 ID 不存在”等错误。
原因很简单:WordPress 的评论回复逻辑依赖于已有评论的唯一 ID,而这个 ID 是各节点数据库中独立自增生成的。如果主从不同步,那每个节点上的 ID 分配就可能出现偏差,导致一个节点上回复成功,另一个节点却报错失败。
为了解决这个问题,我采取了一个相对稳妥的策略:所有评论审核和回复统一在主节点进行,一旦操作完成,立即手动触发一次主节点向从节点的数据库同步。这样可以确保评论数据和状态在所有节点保持一致,避免交互错误,实现多活架构下的”既快又稳”的评论写入体验。
虽然每次批准或回复评论都要导出一次 wordpress.sql,有点像”为了同步一条评论就搬一次整库”,但在目前评论量不大的前提下,这种”轻量手动同步”依然是性价比较高的选择:比起重构整个评论系统或引入外部平台,这显然更务实也更透明。
当然,这也只是一个阶段性的解决方案,毕竟现在评论量还不多,如果以后真的做大做强了,日常评论很多,那就得考虑恢复之前”数据库变更感知 + 自动同步”的机制了——真到那时候,也算是成长的烦恼了吧。:)
注:其实也可以考虑只同步wp_comments这一个表,而不是同步整个库,只是我目前懒得维护两个不同的脚本,所以就偷下懒。
6 新增评论通知(可选)
6.1 碎碎念
我其实一直对 WordPress 自带的新评论邮件通知不太满意。平时我手机大多数时间是静音的,各类通知也几乎都关闭了,心情好的时候才会去翻一翻邮箱,所以经常是隔了好几个小时甚至半天,才发现有人在博客下留言。这种延迟感,对一个想认真运营博客、和读者保持及时互动的人来说挺糟糕的。
这次借着评论请求改为通过 Cloudflare Worker 中转的机会,我就想,要不干脆把通知机制也一并升级:当有新评论产生时,直接通过 Bark(不熟悉bark的朋友可以参看文章:docker系列 搭建基于bark server的消息推送服务器) 把通知推送到我的手机和家里的 Mac mini 上,让我几秒钟之内就能知道有人留言了。
不过这里也遇到了一点麻烦:相比起服务端 shell 脚本、定时任务或 webhook 那类方式,Worker 本身运行在边缘节点,是无状态的、事件驱动的,处理逻辑越轻越好,不太适合直接做包含身份密钥的 Bark 请求:这既是安全方面的考虑,也是为了让 Worker 保持尽可能简洁。
所以我计划让 Worker 在捕捉到评论请求之后,向一个预先部署在另一个1核心1G内存的小鸡-圣何塞节点的轻量级 Webhook 接口发送通知信号。这个接口的职责非常单一:它不会直接去调用 Bark,而是作为一个中转站,将事件传递给本地运行的 shell 脚本。脚本中预先配置了 Bark 的 API Key 和推送模板,真正负责将通知精准地推送到我的手机和 Mac mini 上。
这样的设计一方面把敏感的密钥保留在可信的本地环境中,不暴露给 Worker 端,避免潜在的安全风险;另一方面也让我可以灵活控制通知行为,比如根据评论内容做关键词过滤、合并重复通知,甚至加入通知节流逻辑,避免短时间内被评论轰炸。
同时,Webhook + 本地脚本的组合也非常易于扩展:未来如果我想改为通过 Telegram Bot、企业微信、或是桌面端弹窗提醒,只需要在脚本层面稍作修改即可,无需动 Worker 的一行代码。整体来看,这是一个将实时性、安全性与可维护性三者平衡得比较不错的方案。
6.2 4. Python Flask 实现轻量级本地 Webhook
6.2.1 flask介绍
之前使用宝塔面板时,我注意到宝塔的软件商店里有现成的 Webhook 软件可以直接安装,使用起来非常方便省事。但现在我已经换成了 1panel 面板,经过一番查找,貌似没有类似的现成插件或应用可用(网上倒是有很多朋友在给1panel提这个需求,都几年了~),只能自己动手写一个了。考虑到需求尽量轻量化、易于维护且灵活扩展,我最终选择了基于 Python 的 Flask 框架来搭建这个本地 Webhook 服务。
Flask 是一个非常流行的轻量级 Web 框架,核心代码只有几千行,设计简洁且灵活,适合快速开发各种 Web 服务和接口:它没有过多复杂的依赖和强制的项目结构,开发者可以根据实际需求自由组织代码和功能,此外,Flask 拥有丰富的扩展库支持,能够轻松集成数据库、认证、日志等功能,未来如果有需求,也可以方便地进行功能拓展。它的社区活跃,文档齐全,上手非常简单,非常适合搭建像本地 Webhook 这样的小型服务。
6.2.2 flask安装
apt update
apt install -y python3-pip
pip3 install flask
6.2.3 创建基于 Flask 的 Webhook 服务脚本
1、新建目录
mkdir -p ~/script
2、创建服务脚本
cd ~/script
vim webhook_server.py
将以下的代码的代码粘贴并保存:
# filename: webhook_server.py
from flask import Flask, request
import subprocess
app = Flask(__name__)
# 预设 Webhook Token
EXPECTED_TOKEN = "your-token"
@app.route("/webhook/comment", methods=["POST"])
def comment_webhook():
# 校验 Token
received_token = request.headers.get("X-Webhook-Token", "")
if received_token != EXPECTED_TOKEN:
return "Invalid token", 403
# 调用 Bark 通知脚本
try:
subprocess.run(["/usr/local/bin/bark_notify.sh"], check=True)
return "Bark notified", 200
except subprocess.CalledProcessError as e:
return f"Notification failed: {str(e)}", 500
if __name__ == "__main__":
# 监听 IP 和指定端口
app.run(host="127.0.0.1", port=9527)
上面是一个使用 Flask 框架编写的 Python Webhook 服务示例,它在”127.0.0.1″的9527端口上监听 /webhook/comment 路径上的 POST 请求,并触发本地脚本(/usr/local/bin/bark_notify.sh)执行bark通知逻辑。
至于bark_notify.sh脚本按照如下流程创建:
vim /usr/local/bin/bark_notify.sh
将以下内容复制粘贴并保存(请根据自己的实际环境修改):
#!/bin/bash
# Bark 通知地址,替换成自己的域名、token和通知格式
bark_success_macmini="https://xxxx.tangwudi.com/your-token/wordpress/wordpress-new-comment"
bark_success_iphone="https://xxxx.tangwudi.com/your-token/wordpress/wordpress-new-comment"
# Bark 成功通知
curl --max-time 5 -s -A "your-useragent" "bark_success_macmini">/dev/null
curl --max-time 5 -s -A "your-useragent" "bark_success_iphone" > /dev/null
赋予执行权限:
chmod +x /usr/local/bin/bark_notify.sh
之所以将 bark_notify.sh 创建在路径 /usr/local/bin 下,是出于权限和可访问性的考虑。虽然该脚本本质上只是一个简单的通知触发器,但在实际部署中,Flask 应用并不一定始终以 root 用户身份运行,尤其是在生产环境中,Flask 更可能以 www-data、nobody、或通过 systemd 指定的普通用户身份启动。
如果将脚本放在 “/root” 目录下,Flask 进程由于权限限制可能无法访问或执行该路径中的脚本,导致触发通知失败,且不易在日志中直接发现问题(这个问题折腾了我几个小时,之前我是图方便直接放在”/root/script”路径下,结果一直返回内部500错误)。相比之下,”/usr/local/bin” 是 Linux 系统中为用户自定义的可执行文件预留的位置,它默认具有合适的执行权限,并且多数用户和服务在 PATH 环境变量中都能直接找到该路径下的命令或脚本,因此更加稳妥与通用。
简言之:/usr/local/bin 是一个兼顾权限、安全性与系统可维护性的合理放置点,适合作为系统级服务调用脚本的统一入口。
6.2.4 work代码的通知部分
这部分发送Webhook通知的内容只需要附加到之前worker代码的ctx.waitUntil内容之后,举例如下如下:
// 并发从节点请求,但不影响主流程
ctx.waitUntil(
fetch(secondaryRequest).catch((err) => {
console.log("Secondary write failed:", err);
})
);
// 成功时发送 Webhook 通知(非阻塞)
if (primaryResponse.ok) {
ctx.waitUntil(
fetch("https://notification.tangwudi.com/webhook/comment", {
method: "POST",
headers: {
"X-Webhook-Token": "your-token"
}
}).catch((err) => {
console.log("Webhook failed:", err);
})
);
}
需要提前为安装部署flask的、用来接收worker发送请求的设备分配一个域名,在本例中是”notification.tangwudi.com”,另外webhook_server.py中的”your-token”要和worker中的”X-Webhook-Token”的值一致。
7 后话
除了上述几种方案,也有人选择使用第三方评论系统(如 Disqus、Twikoo、Valine 等)绕过 WordPress 自身的评论机制,从而规避多活架构中涉及的读写分离复杂性。这类方案在静态博客或不需要强后台交互的网站中确实较为常见,部署简单、前后端解耦,甚至还具备一定的抗攻击能力(因为服务端压力转移到第三方平台上了)。
然而,对于以 WordPress 为核心构建的网站而言,评论功能往往不仅仅是”留言板”这么简单。它既是用户互动的一部分,也是 SEO 和内容生态的有机组成。使用第三方系统,虽然从表面上看简化了部署,但本质上却牺牲了数据控制权、后期可迁移性与系统一致性,而这些,恰恰是在多活架构下最应该强调的几个点,更何况,许多第三方评论系统并不完全”好用”:Disqus 日益商业化,广告繁多;Twikoo、Valine 等系统虽开源自由,但依赖 LeanCloud、CloudBase 等平台,也存在运营稳定性问题;而且从最终体验来看,加载速度、数据延迟、安全合规性等各方面,往往也难以与自托管方案相提并论。
因此,在多活架构中,绕过原生评论机制看似”捷径”,实则是一种放弃结构优化的妥协。相比之下,借助 Cloudflare Worker 实现轻量级、可控的多点写入,不仅保持了 WordPress 原生体系的完整性,也大大增强了架构的灵活性与可持续性——这是第三方服务所难以提供的价值。