如何插件化?

解读

国内面试官问“CouchDB 如何插件化”,并不是想听你泛泛地说“装插件”,而是考察三件事:

  1. 你是否清楚 CouchDB 的进程模型与 Erlang/OTP 架构
  2. 你是否能把“插件”映射到 Erlang 世界的Release、Application、Module、BEAM 文件
  3. 你是否能在不破坏官方升级路径的前提下,把自研功能做成可插拔、可灰度、可回滚的组件,满足国内金融、政企对稳定性与审计的硬性要求。

一句话:要你证明“我既懂 CouchDB 的 Erlang 内核,又能在国内严苛的生产环境里安全地插拔功能”。

知识点

  1. Erlang/OTP Release 结构:CouchDB 本身是一个 OTP Release,由 couch.app.src 描述,插件必须作为独立 OTP Application 存在,才能被 release 打包。
  2. 启动顺序与依赖couch_sup 是根监督者,插件需要把自己的顶层监督者挂在 couch_sup 下,或挂在自定义的 sup 下再被 couch_sup 引用;必须在 .app 文件里声明 applications: [kernel, stdlib, couch],保证CouchDB 先启动
  3. 配置注入:CouchDB 使用 config 这个 ETS 表,插件用 config:listen 动态监听变更,实现热配置;国内政企要求“配置可审计”,因此要走 local.ini 并加 Git 版本管理。
  4. HTTP 接口扩展couch_httpd 提供 couch_httpd:register_handler/2,插件可在 couchdb.httpd.extra_handlers 段注册新的 {<<"_myapi">>, myapi_handler}无需改源码
  5. 安全与灰度
    • 用 Erlang 的 code:load_file/1热升级,但必须先做 couch_epi 插件注册,确保模块版本号couch_db_update 事件匹配;
    • 国内等保要求“失败可回滚”,因此要把插件打为 relx overlay,生成 couchdb-3.x.x+plugin.v1.tar.gz,升级时走蓝绿部署,回滚直接切回旧 Release。
  6. 国内镜像与合规:由于 GitHub 访问不稳定,企业 Nexus 私服需镜像 hexpmerlang-solutions;插件里若含加密代码,要走国密算法并通过商用密码产品认证

答案

CouchDB 的插件化本质上是OTP 应用级扩展,标准流程分五步:

  1. rebar3 new app my_plugin 创建独立 OTP 应用,把 my_plugin.app.srcapplications 列表加上 couch,保证启动顺序。
  2. my_plugin_sup.erl 实现监督者,内部挂 worker 或子 sup;在 my_plugin_app.erlstart/2 里调用 couch_httpd:register_handler(<<"_myapi">>, fun myapi_handler:handle/1),把 REST 端子插进去。
  3. 配置项写在 priv/my_plugin.ini,安装时通过 local.d 软链到 etc/couchdb/local.d/,CouchDB 启动会自动加载;用 config:listen("my_plugin", fun changes/2) 实现热更新。
  4. 打包阶段把 my_plugin 加到 CouchDB 源码根目录的 rebar.config.scriptdeps 列表,再执行 make release,relx 会把插件 BEAM 与启动脚本一起打进出 rel/couchdb;生成 couchdb-3.3.3+myplugin.v1.tar.gz 后,走 Ansible 蓝绿发布。
  5. 回滚时直接在负载均衡层把流量切到旧节点,或执行 bin/couchdb downgrade my_plugin-1.0.0,OTP 会自动回退到旧版本代码,零数据迁移

通过以上步骤,即可在国内生产环境实现可灰度、可审计、可回滚的 CouchDB 插件化方案。

拓展思考

  1. 如果插件需要拦截文档写入做国密 SM4 加密,应订阅 couch_db_update 事件,并在 process_change/2 里返回 {ok, NewDoc};此时必须打开 couch_epibefore_doc_update 钩子,并做性能压测,确保单节点写入 QPS 下降不超过 5%。
  2. 在多活数据中心场景,插件里带自定义冲突解析逻辑,需在 couch_replicatorfilter 阶段注入 my_merge_fun/2,并把该函数注册到 couch_episervice 段,保证双向同步时冲突解决策略版本一致。
  3. 国内信创环境要求运行在鲲鹏 + 麒麟 OS 上,编译插件时必须使用 erlang-24.3.4.2-1.el8.aarch64 的 RPM,且 BEAM 文件要加 -fno-omit-frame-pointer 重新编译,才能在银河麒麟 V10 上通过工信部兼容性测试

把这三点想透,面试时就能从“会用插件”跃迁到“能主导企业级 CouchDB 插件架构设计”,稳稳拿到 Offer。