Odoo 只给你的集成每秒一次 API 调用,其余设计都由此推导
搜索 Odoo 集成建议,你会看到两类页面:合作伙伴的宣传册,或者教你从 Python 调用 search_read 的教程。两者都没有提到真正决定架构的东西。Odoo 公开了外部集成能做什么的硬性限制,其中两条还带着日期。
这篇文章来自构建一侧,写给那些必须把产品、客户门户、商城、定价引擎或内部工具接到 Odoo 数据库上,并让它在 Odoo 的发版列车中持续可用的团队。它假定 Odoo 会留下。这里没有任何内容是在主张替换一套已经承载你会计流程的 ERP。唯一值得回答的问题是:Odoo 与你自己拥有的软件之间,边界应该划在哪里。而 Odoo 自己的文档,只要读对那五页,就已经回答了其中大部分。
五条限制,一张表
| 限制 | Odoo 的说法 | 对设计的强制影响 |
|---|---|---|
| 套餐门槛 | 外部 API 访问包含在 Custom 套餐中,One App Free 与 Standard 套餐没有 | API 是 ERP 合同里的一项,不是免费能力。在界定集成范围之前先确认 |
| 调用预算 | 可接受使用政策把 Odoo 云上非持续、无并行、每秒约一次的调用称为可接受 | 批量读取、在服务端聚合,并让 Odoo 把事件推给你,而不是你去轮询它 |
| 事务边界 | 每个发往 JSON-2 端点的调用都在自己的 SQL 事务中运行,多个调用无法串联进一个事务 | 任何必须要么全成功要么全回滚的多步操作,都应放进自定义模块里的单一方法 |
| 协议时钟 | /xmlrpc、/xmlrpc/2 与 /jsonrpc 计划在 Odoo 22(2028 年秋)和 Odoo Online 21.1(2027 年冬)中移除 | 新开发一律走 /json/2。已有的 RPC 客户端需要一份带日期的迁移计划,而不是一张 backlog 卡片 |
| 升级时钟 | 每个大版本支持三年,Odoo Online 升级按计划强制执行,含自定义模块的数据库必须等模块适配后才能升级 | 自定义代码是一项带署名的持续维护义务,你的集成测试也是升级闸门的一部分 |
把最后两行连起来读,因为痛点正在那里。你的 RPC 客户端必须在 Odoo Online 走到 21.1 之前消失,而把你带到那里的升级,你并不能完全掌控。
限制一:外部 API 是套餐功能
第一个意外是商务上的,不是技术上的。Odoo 的 JSON-2 API 参考文档写明:通过外部 API 访问数据仅在 Custom 套餐中提供,One App Free 与 Standard 套餐不提供。
这一条注释会重排很多项目。一家因为看起来是稳妥中间选项而选了 Standard 套餐的公司,买到的是一套自家软件无法对话的 ERP。集成预算随后必须吸收按人、按月、长期存在的套餐升级成本。这是最便宜的核实项,也是第六周才发现时最贵的一项。
两点实务提示。第一,自托管的 Odoo Community 完全没有套餐门槛,因为它没有套餐,这也是自己运维为数不多的真实理由之一。第二,套餐名称和层级属于营销表面,会变动,所以把文档里的这条注释当成核对自己合同的理由,而不是可以贴进决策备忘录的引文。
限制二:每秒一次调用,且不能并行
Odoo 没有发布按端点的配额,也没有给出文档化的 429 阈值。它发布的是一项政策。Odoo 云的可接受使用政策把未加限流的 RPC 与 API 调用列为被禁止的网络滥用,指出导入场景已有批量 API,并写明:在非持续使用下,以每秒一次、无并行的速率发起的受限流调用通常是可接受的。它也点出了出口:在 Odoo.sh 上,独立主机模式可以作为解除该限制的替代方案。
政策上限比文档化配额更难设计,因为你无法通过测量把自己送进安全区。你必须假定这个数字就是这个数字。每秒一次、串行执行,如果每一秒都用满,一天约有 86,000 次调用,而真实集成不会这么用。于是设计问题就从"我们能跑多快"变成"这件事最少需要多少次调用"。
这样做能省下哪些调用
- 一次往返代替两次。
search_read取代先search再read。每个组合型 ORM 方法都是你没花掉的一次调用。 - 在 Odoo 内聚合。
read_group直接从数据库返回分组汇总。把 40,000 行订单明细拉出来在自己代码里求和,是同一个答案却付出百倍成本。 - 批量写入。把一组 id 传给一次写入是一次调用。对 id 循环则是每条记录一次调用,而 3,000 条记录的循环是你自己造出来的故障。
- 只请求需要的字段。
fields列表让响应保持精简,也避免客户端依赖你从未想要的列,这一点在升级时会再次得到回报。 - 绝不用轮询做变更检测。轮询是每秒一次调用预算的最大消耗者,而且它随客户数量增长,而不是随数据量增长。
轮询的替代方案已经在产品里。Odoo 的自动化规则包含 Send Webhook Notification 动作,把记录的所选字段 POST 到你指定的 URL,并提供 payload 预览;它们也可以由外部系统的入站 webhook 触发。Odoo 自己的文档为此加了一条警告:请让开发或解决方案架构角色参与,因为配置不当的 webhook 可能扰乱数据库。这条警告是对的,也正是 webhook 在你这一侧应当放在队列之后、而不是直接接进业务逻辑的原因。
能存活下来的结构很平淡。Odoo 推送事件。你的服务接收它,写入自有存储,快速返回 200,然后异步完成真正的工作,带重试策略和一个尊重每秒一次调用的限流器。你的产品从你的存储读,不从 Odoo 读。Odoo 依然是它所拥有那部分数据的记录系统。
产品路线图中间横着一套 Odoo,却没人负责那条边界?
界定集成工作范围限制三:每个调用都是独立事务
这条限制会制造出最昂贵的一类缺陷,而它在 JSON-2 参考文档的事务章节里写得很清楚:所有发往 JSON-2 端点的调用都在自己的 SQL 事务中运行,成功即提交、出错即丢弃,并且无法把多个调用串联进一个事务。文档进一步说明,在你的两次调用之间,数据库可能被其他并发事务修改,而这在预订与支付相关的操作中尤其危险。
把它当成架构指令来读,因为它就是。Odoo 推荐的解法就在同一段:始终调用一个在单一事务内完成所有相关操作的方法;如果不存在这样的方法,就在专门的模块里创建一个。
也就是说,"能不能不碰 Odoo 代码就做完"这个问题,诚实的答案往往是不能。一个先查库存、再预留、再确认订单的三次调用序列不是一个事务,而是三个事务加上两个窗口,另一个用户、另一套集成或一个计划任务都可能在窗口里改变你脚下的世界。故障不会表现为崩溃,而是重复预留、按过期价格完成的支付,或者一条没有单头的订单明细,一个月后由财务发现。
我们对每条 Odoo 写入路径都执行的三条规则
- 一个业务操作,一次调用。只要操作带有不变量,它就应该在自定义模块里拥有一个方法,而你的服务只调这一个方法。不变量活在数据库事务里,不在你的编排代码里。
- 所有创建动作都带幂等键。你的重试策略一定会重发一个你从未见到响应的调用。把自己的键存在 Odoo 记录上,创建前先检查,重试就从重复发票变成空操作。
- 对账,而不是信任。一个夜间任务用少量聚合读取把你的存储与 Odoo 比对,能抓住按调用做错误处理时漏掉的漂移。调用成本很低,而且这是唯一能找出无人记录的故障的手段。
限制四:RPC 协议已有下线日期
现存的 Odoo 集成几乎都在讲 XML-RPC,因为多年来那就是答案。External RPC API 参考文档现在开篇就是移除通告:位于 /xmlrpc、/xmlrpc/2 和 /jsonrpc 的 XML-RPC 与 JSON-RPC API 计划在 Odoo 22(2028 年秋)和 Odoo Online 21.1(2027 年冬)中移除,替代者是 External JSON-2 API。所暴露的三个服务 common、db 与 object 全部弃用。用 @route(type='jsonrpc') 声明的内部控制器明确不在该通告范围内。
两个日期,哪一个约束你取决于数据库在哪里。Odoo Online 先到线,在 2027 年冬,因为 Odoo Online 会发布小版本,比大版本线走得更快。自托管与 Odoo.sh 数据库有到 2028 年秋 Odoo 22 的时间,再加上你愿意停留在不受支持版本上的那段时间。
如果你的调用层是一个模块,迁移本身很小;如果 RPC 调用散落在整个代码库里,就会很痛。变化如下:
| 关注点 | XML-RPC 与 JSON-RPC | External JSON-2 |
|---|---|---|
| 端点 | 先 /xmlrpc/2/common,再 /xmlrpc/2/object | POST /json/2/<model>/<method> |
| 认证 | 先登录,再把返回的 user id 带进后续每一次调用 | 把 API key 作为 bearer token 放进 Authorization 头,没有登录往返 |
| 数据库选择 | 每次调用中的一个位置参数 | 可选的 X-Odoo-Database 头,在一台服务器托管多个数据库时需要 |
| 请求体 | 固定顺序的位置参数 | 带 ids、context 与命名参数的 JSON 对象 |
| 接口发现 | 去读你要调用的模型源码 | 每个数据库自带的 /doc 文档路由,由该数据库自身的模型生成 |
认证方式的变化值得规划,而不是机械照搬。JSON-2 的密钥可以通过 res.users.apikeys 以编程方式创建、吊销与轮换,其过期日期会按该用户角色所允许的最长密钥有效期进行校验。Odoo 的建议是:让自动化集成运行在一个专用的机器人用户下,权限最小、密码留空,从而关闭登录通道,并让访问日志记录机器人而不是冒用某个真人。每套集成做一次,密钥轮换就从迁移工作变成运维任务。
限制五:升级时钟属于 Odoo
升级文档是大多数集成方案跳过的一页,而它包含影响最久的约束。每个大版本支持三年。在 Odoo Online 上,大版本每两年强制升级一次,小版本则在下一个版本发布后几周内强制升级,而小版本大约每两个月发布一次。Odoo 的升级团队会为每个到期的数据库跑一次静默测试升级,若测试成功且耗时低于二十分钟,除非你在截止日前采取行动,自动升级就会继续。
接着是决定谁承担成本的两句话。含自定义模块的数据库,必须等到这些模块存在面向目标版本的版本后才能升级。而当新版本的变更破坏了某项定制时,让它重新兼容是该自定义模块维护者的责任。Odoo 自己的测试清单把与外部软件、API 和 EDI 的集成放在第一位。
这不是对 Odoo 的抱怨。这就是交易条件,而且是合理的条件。但它意味着你构建的每个自定义模块和每套集成都带着一份署名的持续成本,而署名必须是真实的人或真实的合同。19 版把这一点变得具体:res.users.groups_id 在 19.0 源码中已改为 group_ids,独立的合同模型也以 hr.version 的形式并入了 HR 核心。任何硬编码了这些字段名的外部客户端,会视调用不同而返回一个形状错误的干净 200,或者直接报错。两者都不会被一套 mock 掉 Odoo 的测试套件抓到。
廉价的防线是契约测试:一套小型测试,针对升级后的数据库副本跑你真实的读写路径,并断言你确实依赖的那些字段。这与对待任何你无法控制的API 是同一种纪律,它能把强制升级窗口从一场消防演习变成一个上午。
托管方式决定了争论
五条限制里有三条由数据库运行在哪里决定,这让托管成为架构决策,而不是采购决策。
| 选项 | 调用预算与 API 访问 | 升级控制权 | 适合的情况 |
|---|---|---|---|
| Odoo Online | 适用可接受使用上限,外部 API 与套餐层级绑定 | Rolling Release,按 Odoo 时间表强制升级,RPC 端点最迟随 2027 年冬的 21.1 消失 | 标准流程、集成较轻、不想碰基础设施 |
| Odoo.sh | 同一政策,独立主机模式是文档给出的解除限流途径 | 分级分支与测试构建,在支持窗口内由你触发升级 | 确有自定义模块、有 CI 习惯、集成流量会被共享上限掐住 |
| 自托管 Community | 没有套餐门槛也没有政策上限,容量和反向代理都归你 | 完全由你决定,包括决定脱离支持 | 重度集成、数据驻留要求、已有内部运维 |
这里有一个常被猜测的许可事实。Odoo Community 源码以 LGPL 第 3 版发布,因此在 Community 之上做一个专有自定义模块是常规做法,而不是一场法律辩论。Enterprise 版本与 Odoo 自家托管套餐另有商业条款,请对照你自己的情况去读,而不是从论坛帖子里继承一个观点。
自托管不是免费的。你用备份、升级、监控、一个现在由你自己限流的反向代理以及一位随时待命的人,换来速率上限的消失。这笔交易和我们在定制软件与标准软件对比指南中描述的是同一笔:为业务逻辑真正触碰的部分付费,其余的配置即可。
我们采用的参考结构
当五条限制都摆上桌面后,我们见过的每套 Odoo 集成都会收敛到同一种架构:一个由你的团队拥有的服务,坐在 Odoo 与其他一切之间。
- 一层防腐层。你代码库里的一个模块知道 Odoo 的模型名、字段名和各种怪癖,产品里没有别的地方导入它。当
groups_id变成group_ids,你只改一个文件。 - 你自己的读库。产品在热路径上读取的一切都放在你的数据库里,由事件填充并按计划对账。产品延迟不再取决于一套受调用上限约束的 ERP。
- 带队列的入站 webhook 端点。接收、落库、返回 200、异步处理、带退避重试。Odoo 的自动化规则是生产者,你的队列吸收同步处理器会丢弃的突发流量。
- 单车道的写入器。一个串行工作进程在同一处强制执行调用预算,携带幂等键,并让整套集成的调用量只需一个看板。
- 为不变量准备的自定义模块方法。小而具名、限定在一个事务内的方法,服务于不允许中途撕裂的操作。像产品代码一样评审和版本化,因为升级时它们就是你的责任。
- 针对真实升级后数据库的契约测试。正是这道闸门让 Odoo 的升级节奏变得可承受。
我们自己工作中最接近的类比根本不是 ERP。在 MyMerch 流程自动化项目中,客户在多个店铺上运行着一套正常工作的电商业务,却在两个具体环节持续流失利润。诱人的提案是重写。真正有效的做法是:点明手工交接环节、把它们自动化,并且不去动平台。同样的直觉适用于 ERP:价值在集成边界上,下面的平台通常没问题。这个论点的一般化版本写在不重写的架构整治一文里。
四件我们不会做的事
- 不要实时镜像 Odoo。在每秒一次调用的链路上双向同步共享可变状态,是一个披着集成外衣的分布式系统项目。按字段选定唯一写入方,并坚持下去。
- 不要把核心会计搬进你的产品。一旦发票、税务逻辑或科目表活在两套系统里,对账就永远归你。如果是法规义务把你推向那里,答案通常是 ERP 旁边的一层,而不是你应用内部的一层,这也是我们在围绕支付服务商做结构化电子发票一文中得到的相同结论。
- 不要把业务逻辑写进自动化规则。界面里的服务器动作与代码块对代码评审不可见、没有测试、没有版本管理。它们是极好的胶水,却是承载金钱规则的糟糕位置。
- 不要让集成成为没人负责的模块。无文档、无归属的 Odoo 定制是升级停滞最常见的原因,也是新服务商最常盲目继承的东西。我们针对这种交接局面的清单在从其他供应商手中接管软件项目一文中。
从未知走到安全的 30 天计划
- 第 1 到 3 天,确定事实。托管类型、确切版本、套餐层级,以及外部 API 在合同上是否可用。然后列出每一套现存集成以及它讲哪种协议。多数团队在第一天答不出最后这一项。
- 第 4 到 10 天,测量调用量。连续一周记录每次出站调用,按调用方和用途分组。轮询任务会排在这份清单最上面,也是最先被删掉的东西。
- 第 11 到 18 天,点明不变量。写下每一个必须要么全成功要么全回滚的操作。每一个要么变成一个带测试的自定义模块方法,要么变成一项被接受的风险,并在后面配一个对账任务。
- 第 19 到 25 天,建好边界。一个防腐模块、一个带幂等键的串行写入器、一个带队列的 webhook 端点。这个窗口里不做新功能。
- 第 26 到 30 天,规划 JSON-2 切换。一份带日期的计划,绑定你自己的升级窗口,而不是绑定 2027 年。一个专用机器人用户、按集成划分且带过期时间的 API 密钥,以及一份轮换手册。
三十天的前提是有人真正拥有这些决策。缺了这个人,工作会卡在第二步,这也是企业为一个界限清晰的集成项目引入Fractional CTO、而不是为此招人的常见原因。
Odoo 集成常见问题
Odoo 的 XML-RPC 被弃用了吗?
Odoo API 的速率限制是多少?
使用外部 API 需要付费的 Odoo 套餐吗?
可以把多个 Odoo API 调用放进同一个事务吗?
对接 Odoo 该用 webhook 还是轮询?
自定义模块会阻塞 Odoo 升级吗?
Odoo Online 数据库必须多久升级一次?
Odoo 19 中有哪些变更打断了集成?
可以在 Odoo Community 上保留专有自定义模块吗?
最终思考
Odoo 是一套不错的 ERP,它的外部契约刻意收窄,而且它把这份契约记录得很诚实。真正吃力的团队,是把它当成一个外挂了 REST API 的开放数据库。把这五条限制当作设计输入:给调用做预算,把不变量放进一个事务,按你自己的时间表而不是 2027 年切到 JSON-2,并给每个自定义模块指定一个在下次升级时还在的负责人。构建一个由你掌控的边界服务,ERP 就会从一个你被动应对的截止日期,变成一项你主动管理的依赖。
一手资料
- Odoo 19 文档,External JSON-2 API,含套餐限制、API 密钥管理与事务章节
- Odoo 19 文档,External RPC API,含 XML-RPC 与 JSON-RPC 端点的移除通告
- Odoo 可接受使用政策,网络与服务滥用,关于受限流调用速率
- Odoo 19 文档,升级,关于支持年限、强制升级、Rolling Release 与自定义模块责任
- Odoo 19 文档,自动化规则,含 Send Webhook Notification 动作
- Odoo 19.0 源码,res.users,其中的 group_ids 字段
- Odoo 19.0 源码,HR 核心中的 hr.version 模型
- Odoo 19.0 LICENSE 文件,Community 源码采用 LGPL 第 3 版
