3. 测试环境搭建:模拟交易服务器、客户端工具与日志捕获回放
说实话,做金融协议解析的测试,环境搭建这块儿我踩过的坑比写过的代码还多。很多人一上来就想着连生产环境,结果不是把对方系统搞崩了,就是被合规部门请去喝茶。我的建议是——先搭一套能完全掌控的模拟环境。
这一章,我就把压箱底的经验掏出来,跟你聊聊怎么搭一套靠谱的测试环境。
3.1 模拟交易服务器:从零开始造一个对手方
模拟交易服务器,说白了就是假装自己是交易所或者券商。你想想看,真实环境里对方系统动不动就断连、发乱序消息、甚至给你塞个非标字段——这些在开发阶段都得靠模拟器来复现。
我个人习惯用 QuickFIX/J 自带的 Executor 作为起点。它是个现成的模拟服务器,能处理基本的订单请求并返回执行报告。但注意,它太「乖」了,不会主动搞事情。
核心改造点:
- 注入随机延迟:模拟网络抖动,延迟范围 50ms~500ms
- 乱序消息:随机打乱部分消息的到达顺序
- 异常响应:比如拒绝订单、返回错误代码
我曾经在改造模拟器时,故意让它在每天下午 3:00 准时断连 30 秒。结果发现客户端的重连逻辑有个 bug——重试次数超过 5 次后居然不重置会话状态。嗯,这种边界情况,生产环境里可能一年都碰不到一次,但在模拟器里可以天天测。
下面是我常用的模拟服务器启动配置片段:
// 模拟服务器配置(QuickFIX/J)
[default]
FileStorePath=target/data/server
ConnectionType=acceptor
SocketAcceptPort=9878
StartTime=00:00:00
EndTime=00:00:00
HeartBtInt=30
ReconnectInterval=5
[session]
BeginString=FIX.4.4
SenderCompID=SIMULATOR
TargetCompID=CLIENT1
这里有个小技巧:ReconnectInterval 设成 5 秒,比生产环境短一些。为什么?因为测试时你等不起 30 秒重连,5 秒能让你快速迭代调试。
3.2 客户端工具:QuickFIX/J 的正确打开方式
客户端工具这块,QuickFIX/J 是绕不开的。但很多人把它当黑盒用,出了问题就抓瞎。我建议你至少搞清楚它的消息流转链路。
来看一个典型的客户端初始化流程:
// 客户端启动示例
public class FixClient {
public static void main(String[] args) throws Exception {
SessionSettings settings = new SessionSettings("client.cfg");
Application app = new MyApplication(); // 你的业务逻辑
FileStoreFactory storeFactory = new FileStoreFactory(settings);
LogFactory logFactory = new FileLogFactory(settings);
MessageFactory messageFactory = new DefaultMessageFactory();
Initiator initiator = new SocketInitiator(
app, storeFactory, settings, logFactory, messageFactory
);
initiator.start();
}
}
注意看 LogFactory 这行。很多人图省事不配日志,结果出了问题连消息都没法回溯。我建议你配成 FileLogFactory,它会生成两个文件:
| 文件 | 内容 | 用途 |
|---|---|---|
| messages.log | 原始 FIX 消息(含心跳) | 协议解析验证 |
| events.log | 会话事件(连接、断连、重连) | 异常排查 |
我的习惯:在开发阶段把日志级别调到 DEBUG,虽然文件会大一些,但你能看到每个字段的解析过程。等上线前再切回 INFO。
3.3 日志捕获与回放:让 Bug 无处遁形
日志捕获这块,我吃过不少亏。早期我直接用 System.out 打日志,结果线上出了问题,日志文件被滚没了,连现场都保不住。
后来我总结了一套「三层捕获法」:
- 网络层:用 tcpdump 或 Wireshark 抓原始报文
- 应用层:QuickFIX/J 的 messages.log
- 业务层:自定义的业务日志,记录订单状态变更
三层对不上的情况,基本就是 Bug 所在。我曾经遇到过一个诡异问题:应用层日志显示订单已成交,但业务层显示还是「新单」。最后发现是消息解析时 Tag 顺序搞错了,导致成交报告被当成垃圾消息丢弃。
日志回放工具,我推荐用 FIX Antenna 或者自己写个简单的回放脚本。核心逻辑就三步:
// 伪代码:日志回放核心逻辑
while (有下一条消息) {
1. 读取原始消息字符串
2. 解析为 FIX 消息对象
3. 按原始时间戳延迟发送
}
注意:回放时一定要保留原始的时间戳和序列号。否则对方系统可能会因为 MsgSeqNum 不连续而拒绝连接。我见过有人回放时忘了重置序列号,结果被对方当成重放攻击给封了 IP。
3.4 知识体系总览
下面这张图是我自己整理的测试环境搭建逻辑。你看一眼就能明白各个组件之间的关系:
这张图里,我特意把「日志回放引擎」放在底部,因为它是个逆向过程——把捕获的日志重新喂给客户端,验证解析逻辑是否正确。说白了,就是让历史重现。
避坑指南:我曾经在回放时发现日志里有一条消息的 BodyLength 字段算错了。查了半天,原来是 QuickFIX/J 的某个版本在计算长度时把 SOH 字符也算进去了。这种问题,不靠回放根本发现不了。
嗯,环境搭建这块儿就先聊到这儿。记住一个原则:模拟环境越「坏」,你的系统就越「稳」。别怕把模拟器搞崩溃,反正它又不会投诉你。