TT Lab
开始
学习 学习路径 课程

构建 EAI 中间层

按交易码决定去向的路由器

在 TT Lab 中继续学习

目标

把业务部门的接口清单整理成路由表(数据),并制作只看报文头来确定目的地的路由器,按规则优先级(ORG > EXACT > 较长的 PREFIX > NONE)运行。

为什么重要

枢纽的路由一旦出错,不会报错,而是报文悄悄地发往别的系统。所以要把规则放在表里而不是代码里,在规则重叠时由代码明确决定谁胜出、而不是靠行顺序,并把匹配了哪条规则留在审计日志里。对无法路由的不要吞掉,而是以 E101 响应返回,这也是枢纽的职责——如果吞掉,发送的渠道会一直等到超时。

步骤

  1. 阅读 /opt/lab/fixtures/eaimw/route/interfaces.csv 和 /opt/lab/fixtures/eaimw/route/ROUTING.md 第 2 节,创建 /root/eaimw/route/routes.csv。表头为 tx_code,target,mode,timeout_ms。只取状态为 운영(韩文,意为“运行”)的行,同一个交易码只取版本(整数)最大的那一行,交易码和目标去掉空白并转为大写,mode 为 SYNC/ASYNC,timeout_ms 为秒×1000 的整数。前缀规则(CD*)也原样载入,并按 tx_code 的字节顺序(LC_ALL=C sort)排序。
  2. 创建 /root/eaimw/route/router.py。python3 router.py --routes <표> lookup <거래코드>(占位符依次为表、交易码)找到完全一致的行,输出一行 JSON {"target": ..., "rule": "EXACT", "mode": ..., "timeout_ms": 정수}(其中最后的占位符为整数),并以 0 结束。如果没有,就输出 {"target": null, "rule": "NONE"},并以退出码 3 结束。即使表中有前缀行,也不能崩溃。省略 --routes 时使用 /root/eaimw/route/routes.csv。
  3. 加入前缀规则。如果交易码以 CD* 中的 CD 开头,rule 就是 PREFIX。完全一致胜过前缀,较长的前缀胜过较短的前缀。必须与表的行顺序无关。
  4. 加入机构规则。在 lookup <거래코드> --rcv-org <기관>(占位符依次为交易码、机构)中,如果机构不是 100,就与交易码无关,输出 {"target": "FEP", "rule": "ORG"} 并以 0 结束。省略 --rcv-org 时为本行(100)。
  5. 加入 python3 router.py --routes <표> route <전문파일>(占位符依次为表、报文文件)。用公共库 lhstd.parse 读取报文头,根据 TX_CODE·RCV_ORG 做出决定,并在第 4 步的 JSON 上加入 guid·tx_code·rcv_org,输出为一行(退出码与 lookup 相同)。如果是格式错误的报文,第一行输出 E102 <사유>(占位符为原因),退出码 2。
  6. 加入 route <전문파일> --reply <경로>(占位符依次为报文文件、路径)。如果决定为 NONE,就用 lhstd.reply 把响应码为 E101 的响应报文写入该路径,并以退出码 3 结束。对于已完成路由的报文,不创建文件。
  7. 加入全局选项 --audit <경로>(占位符为路径)。如果指定了它,则对 route 的每一次决定(包括 NONE),都向该文件追加一行 JSON——键为 ts(决定时刻)·guid·tx_code·rcv_org·rule·target。如果不指定,就什么都不写。
  8. 对收件箱 /opt/lab/fixtures/eaimw/route/inbox/ 的全部内容,用第 5 步的 route 运行,创建 /root/eaimw/route/dispatch.csv。表头为 file,tx_code,rule,target,按文件名排序。格式错误写成 파일,,E102,(首列为韩文,意为“文件”,其位置填文件名),NONE 则把 target 留空。

参考

把接口清单整理成路由表

从 /opt/lab/fixtures/eaimw/route/interfaces.csv 中只挑选运行中的最新版本,创建 /root/eaimw/route/routes.csv(tx_code,target,mode,timeout_ms)。

顺序很重要——先只保留状态为运行的行,然后对每个交易码(去空白、转大写),把版本转成 int,选出最大的那一行。有的交易码同时存在版本 9 和版本 10。超时中也有 1.5 这样的小数。

完全一致路由器

让 /root/eaimw/route/router.py 的 lookup 在表中找到完全相同的交易码并以 JSON 答复,如果没有,就以 NONE 和退出码 3 结束。

用 csv.DictReader 读取表,建立交易码 → 行的字典。以 * 结尾的行,在这一步先跳过。表的路径通过 --routes 接收——评分器会传入它自己的表。找不到目的地不是错误而是结果,所以输出 JSON 之后以 3 结束。

前缀规则——较长的胜出

把以 * 结尾的行当作前缀规则处理。完全一致 > 较长的前缀 > 较短的前缀,与表的行顺序无关。

把前缀列表按长度降序排好,第一个匹配的就是最长的。如果按文件顺序选“第一个匹配的行”,有人一排序表,结果就会改变。请用 startswith 比较——in 在中间出现也会匹配。

机构规则优先

给 lookup 加上 --rcv-org,如果接收机构不是 100,就与交易码无关,发往 FEP(rule ORG)。

ORG 是第 1 顺位,所以要在查表之前先看。即使是表中没有的交易码,只要是发往外部机构,结果就不是 NONE,而是 FEP。把 --rcv-org 的默认值设为本行 100,前面步骤的调用就能原样运行。

按报文头路由

让 route <报文文件> 通过 lhstd.parse 读取报文头,给出决定 JSON(包含 guid·tx_code·rcv_org),格式错误则以 E102 和退出码 2 拒绝。

用 open(文件, 'rb') 读取并传给 lhstd.parse,就能得到 TX_CODE·RCV_ORG·GUID。要看的是接收机构,而不是发送机构(SND_ORG)——保险公司发给我们的报文,不能发往外部系统。请捕获 lhstd.FrameError,并以 E102 结束。

把无法路由以 E101 返回

给 route 加上 --reply <路径>,如果是 NONE,就把 E101 响应报文写入该路径,并以退出码 3 结束。

lhstd.reply(报文头, 'E101') 会为你生成保持交易码和 GUID、互换机构的响应报文(bytes)。文件用 'wb' 写入。对于已完成路由的报文,不生成。

把路由决定留在审计日志里

如果指定了全局选项 --audit <路径>,则对 route 的每一次决定(包括 NONE),追加一行包含 ts·guid·tx_code·rcv_org·rule·target 的 JSON。

open(路径, 'a') 是追加,'w' 是清空后写入。审计日志不能被清掉。只留下目的地,就无法回答“为什么去了那里”——匹配了哪条规则(rule)才是关键。

分派收件箱的 30 笔

对整个收件箱用 route 运行,创建 /root/eaimw/route/dispatch.csv(file,tx_code,rule,target)。

对每个文件运行 route,并根据退出码区分——2 是格式错误,3 是 NONE。按目的地统计结果,就能看出集中到一边的地方。不要勉强写下格式错误报文的交易码。