3. FIX会话管理
在FIX协议订单路由与成交回报的实战中,会话层是消息可靠传输的地基。无论上层业务是NewOrderSingle、ExecutionReport还是OrderCancelRequest,它们都依赖会话层提供的序号管理、心跳保活、断线重连与消息重传能力。如果会话层没有管理好,订单可能重复发送、成交回报可能丢失,甚至出现“客户端认为已报单、交易所却从未收到”的严重不一致。本节围绕FIX会话管理的核心机制展开,帮助你在实际对接中定位和解决会话级问题。
一、核心原理:序号驱动的可靠会话
FIX会话的本质是一个双向、有序、可恢复的消息流。每一方向独立维护自己的发送序号(MsgSeqNum)和期望接收序号。发送方每发一条消息,序号加一;接收方检查收到的MsgSeqNum是否等于自己期望的序号。若相等,则处理并期望值加一;若大于期望值,说明中间有消息丢失,触发ResendRequest;若小于期望值,通常是重复消息,按PossDupFlag决定是否处理。
会话层消息分为两类:管理消息和业务消息。管理消息包括Logon、Logout、Heartbeat、TestRequest、ResendRequest、SequenceReset、Reject。它们不承载订单业务,但决定了业务消息能否可靠流动。所有消息共享同一个序号空间,因此一条业务消息丢失也会影响后续管理消息的序号连续性。
二、方法流程:会话建立、保活与恢复
一个完整的FIX会话生命周期通常包括:建立连接、Logon协商、心跳保活、异常检测、重传恢复、正常或异常Logout。下面按流程说明关键动作。
- Logon:发起方发送Logon,携带HeartBtInt、ResetSeqNumFlag、EncryptMethod等字段。接收方校验通过后回复Logon,会话正式建立。若ResetSeqNumFlag=Y,双方将序号重置为1。
- Heartbeat:在空闲时按HeartBtInt周期发送,证明连接存活。若在HeartBtInt加一定宽限时间内未收到任何消息,则发送TestRequest。
- TestRequest:要求对方立即回复Heartbeat,且该Heartbeat必须携带TestReqID。若未收到,则判定会话异常,主动断线并重连。
- ResendRequest:当收到的MsgSeqNum大于期望值时,发送ResendRequest,要求对方重发从期望序号到实际序号减一之间的消息。
- SequenceReset:重发时可用GapFill模式跳过无需重发的管理消息,或用Reset模式强制重置序号。GapFill必须保持MsgSeqNum递增,否则会被拒绝。
- Logout:正常结束会话时发送Logout,对方回复Logout后断开。若直接断线,则视为异常,需按重连流程处理。
三、案例场景:断线重连后的序号错位
假设交易客户端与券商网关已建立会话,客户端发送序号到120,网关期望收到121。此时网络抖动导致连接断开。客户端重连后发送Logon,MsgSeqNum=121。网关侧因为之前已收到120,期望值也是121,于是正常建立会话。但如果客户端在断线前发送了121、122两条订单消息,而网关只收到121,那么重连后客户端从123开始发送,网关期望122,就会触发ResendRequest。
客户端重连 Logon: MsgSeqNum=123
网关期望: 122
网关发送: ResendRequest(BeginSeqNo=122, EndSeqNo=122)
客户端重发: MsgSeqNum=122, PossDupFlag=Y, 原订单消息
网关处理: 期望值变为123,会话恢复
如果客户端在重连时错误地使用了ResetSeqNumFlag=Y,双方序号被重置为1,那么之前未确认的订单状态就可能永久丢失。因此,生产环境通常禁止在非约定场景下随意重置序号。
四、误区与复盘:会话级拒绝与重传边界
会话级Reject(MsgType=3)用于拒绝无法按业务规则处理的消息,例如缺少必需字段、序号异常、CompID不匹配等。它与业务级拒绝不同:业务级拒绝通常用ExecutionReport或OrderCancelReject表达,而会话级Reject表示消息在会话层就未被接受。收到Reject后,不应盲目重发同一条消息,而应先检查RefSeqNum、SessionRejectReason和Text,定位是序号问题、字段问题还是权限问题。
| 机制 | 触发条件 | 关键字段 | 处理要点 |
|---|---|---|---|
| Logon | 建立会话 | HeartBtInt、ResetSeqNumFlag | 校验通过后回复Logon |
| Heartbeat | 空闲超时或响应TestRequest | TestReqID | 必须携带TestReqID |
| TestRequest | 长时间未收到消息 | TestReqID | 对方须立即回Heartbeat |
| ResendRequest | MsgSeqNum大于期望值 | BeginSeqNo、EndSeqNo | 重发时置PossDupFlag=Y |
| SequenceReset | 跳过管理消息或强制重置 | NewSeqNo、GapFillFlag | GapFill保持序号递增 |
| Reject | 会话层无法处理 | RefSeqNum、SessionRejectReason | 先定位原因再决定是否重发 |
复盘时建议重点检查三类日志:一是Logon中的序号与ResetSeqNumFlag;二是ResendRequest与SequenceReset的配对情况;三是Heartbeat与TestRequest的时间间隔是否与HeartBtInt一致。很多“订单状态不一致”的问题,根源并不在订单业务逻辑,而在会话层序号没有对齐。把会话管理做扎实,订单路由与成交回报的可靠性才有真正保障。