按交易码决定去向的路由器
目标
把业务部门的接口清单整理成路由表(数据),并制作只看报文头来确定目的地的路由器,按规则优先级(ORG > EXACT > 较长的 PREFIX > NONE)运行。
为什么重要
枢纽的路由一旦出错,不会报错,而是报文悄悄地发往别的系统。所以要把规则放在表里而不是代码里,在规则重叠时由代码明确决定谁胜出、而不是靠行顺序,并把匹配了哪条规则留在审计日志里。对无法路由的不要吞掉,而是以 E101 响应返回,这也是枢纽的职责——如果吞掉,发送的渠道会一直等到超时。
步骤
- 阅读
/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)排序。 - 创建
/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。 - 加入前缀规则。如果交易码以
CD*中的CD开头,rule就是PREFIX。完全一致胜过前缀,较长的前缀胜过较短的前缀。必须与表的行顺序无关。 - 加入机构规则。在
lookup <거래코드> --rcv-org <기관>(占位符依次为交易码、机构)中,如果机构不是100,就与交易码无关,输出{"target": "FEP", "rule": "ORG"}并以 0 结束。省略--rcv-org时为本行(100)。 - 加入
python3 router.py --routes <표> route <전문파일>(占位符依次为表、报文文件)。用公共库lhstd.parse读取报文头,根据 TX_CODE·RCV_ORG 做出决定,并在第 4 步的 JSON 上加入guid·tx_code·rcv_org,输出为一行(退出码与 lookup 相同)。如果是格式错误的报文,第一行输出E102 <사유>(占位符为原因),退出码 2。 - 加入
route <전문파일> --reply <경로>(占位符依次为报文文件、路径)。如果决定为 NONE,就用lhstd.reply把响应码为E101的响应报文写入该路径,并以退出码 3 结束。对于已完成路由的报文,不创建文件。 - 加入全局选项
--audit <경로>(占位符为路径)。如果指定了它,则对route的每一次决定(包括 NONE),都向该文件追加一行 JSON——键为ts(决定时刻)·guid·tx_code·rcv_org·rule·target。如果不指定,就什么都不写。 - 对收件箱
/opt/lab/fixtures/eaimw/route/inbox/的全部内容,用第 5 步的 route 运行,创建/root/eaimw/route/dispatch.csv。表头为file,tx_code,rule,target,按文件名排序。格式错误写成파일,,E102,(首列为韩文,意为“文件”,其位置填文件名),NONE 则把 target 留空。
参考
- 读取公共库:先
sys.path.insert(0, "/opt/lab/fixtures/eaimw/lib")再import lhstd。报文头解析在第 1 模块中已经亲自做过,所以这里使用只有一套的公共模块。 interfaces.csv是从 Excel 导出的文件,所以开头有 BOM。Python 要用encoding="utf-8-sig"打开。- 常见错误:把版本按字符串比较(
"9" > "10")。选取表中第一个匹配的行(一排序,结果就会改变)。 - 评分器每次都会用新的交易码和目的地名称生成随机表,并通过
--routes传入。如果把目的地写死在代码里,是无法通过的。 - 全局选项(
--routes·--audit)位于子命令(lookup·route)之前。使用 argparse 的 subparsers 就会是这个形状。
把接口清单整理成路由表
从 /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。按目的地统计结果,就能看出集中到一边的地方。不要勉强写下格式错误报文的交易码。