## 从 Next.js 到 1C-Bitrix 的异步线索传输:无需队列和工作进程的模式
通过 REST API 同步提交线索到 CRM 会引发严重问题:用户需要等待外部请求完成才能收到响应,而 1C-Bitrix API 的不稳定性会导致超时和错误。我们在 Next.js 16 上提出了一种解决方案,它消除了队列和后台进程。该实现将线索保存到本地的 PostgreSQL 数据库,立即向用户响应,并通过 after() 处理与 Bitrix 的集成——这是一个在发送 HTTP 响应后进行后台处理的机制。
无队列架构:三层职责分离
核心原则——将操作分离为同步和异步部分。接收表单时:
- 使用 zod 进行数据验证
- 保存到 PostgreSQL,bitrix_id=NULL
- 立即返回 200 OK 响应
- 通过 after() 在后台发送到 Bitrix
这保证了:
- 独立于 Bitrix REST API 的稳定性
- 不阻塞用户界面
- 可以通过 SELECT * FROM leads WHERE bitrix_id IS NULL 手动处理未送达的线索
该方法的关键特性是摒弃 Redis/BullMQ。所有操作都融入 Next.js API 路由,使用内置的 after() 进行响应后处理。这降低了基础设施复杂度,并简化了通过 systemd/nginx 在 VPS 上的部署。
Next.js 16 中的 after():技术实现
after() 方法解决了无服务器环境中“发送后不管”的问题。与 Promise.resolve().then() 不同,它确保即使在发送响应后任务也能执行,且不会在操作完成前终止进程。表单处理示例:
export async function POST(request: Request) {
// ... validatsiya and antidubl
const [lead] = await pgQuery(...);
after(async () => {
try {
const bitrixId = await sendToBitrix24(payload);
await pgQuery(
`UPDATE leads SET bitrix_id = $1 WHERE id = $2`,
[bitrixId, lead.id]
);
} catch (error) {
console.error("[Leads API] Error otpravki in Bitriks:", error);
}
});
return Response.json({ ok: true, id: lead.id });
}
关键细节:
- 防重复通过 PostgreSQL 中的 10 分钟窗口实现,而不是通过 Bitrix(CRM 会无警告接受重复项)
- bitrix_id 存储为可空 text——反映了操作的异步特性
- 集成错误记录到 journalctl,不会影响用户响应
适用于 1C-Bitrix 的可靠 HTTP 客户端
与不稳定 API 稳定协作的关键参数:
- MAX_ATTEMPTS = 2——可靠性和负载之间的平衡
- REQUEST_TIMEOUT_MS = 8000——高于 Bitrix p95 延迟(400-700 ms)
- RETRY_DELAY_MS = 1500——线性退避,避免指数复杂度
客户端实现包括:
async function bitrixRequest(method: string, payload: Record<string, unknown>) {
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const response = await fetch(url, {
signal: controller.signal,
// ...
});
const text = await response.text();
const json = text ? JSON.parse(text) : {};
if (!response.ok || json.error) {
throw new Error(json.error_description || `HTTP ${response.status}`);
}
return json;
} catch (error) {
if (attempt >= MAX_ATTEMPTS) throw error;
await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
} finally {
clearTimeout(timer);
}
}
}
三个关键点:
- 使用 response.text() 而非 response.json() 进行手动解析——绕过 Bitrix 空响应的错误
- 处理两种错误:HTTP 状态码和 CRM 内部错误
- finally 中保证清理定时器——防止内存泄漏
认证特性与数据模式
对于集成,选择传入 webhook 而非 OAuth:
- 无需依赖 Marketplace 和审批
- 简单管理 BITRIX_WEBHOOK_URL 环境变量
- 在 Bitrix 管理面板中配置权限范围
URL 中令牌泄露风险通过规则最小化:日志和代码仅显示 API 方法,而非完整 URL。
leads 表模式:
CREATE TABLE leads (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
name text NOT NULL,
phone text NOT NULL,
source text,
bitrix_id text,
created_at timestamptz NOT NULL DEFAULT now()
);
为什么 bitrix_id 是 text 而非 integer?
- Bitrix 在 JSON 中返回 ID 为字符串
- 无需类型转换
- PostgreSQL 中的 UUID 确保幂等性
关键要点
- 单一真相来源——本地数据库:在联系 Bitrix 之前保存线索,即使 CRM 失败也能确保数据
- 响应后处理:after() 取代队列,消除基础设施复杂度
- 错误处理:带超时的重试和 journalctl 日志确保可观测性
- 数据库级防重复:按电话的 10 分钟窗口防止重复,无论 CRM 如何
- 安全认证:webhook 需要严格的 URL 日志控制
此模式适用于每分钟最多 50 个请求的项目。对于高负载系统,可通过 SELECT COUNT(*) FROM leads WHERE bitrix_id IS NULL 添加监控,并通过 cron 实现自动重试。在当前实现中,手动错误处理优于自动重试——它降低了临时故障期间线索重复的风险。
— Editorial Team
暂无评论。