QuickFIXGo 5.0订单状态同步及消息重传问题咨询
问题解决方案
一、数据库存储失败且对方不支持OrderStatusRequest(msgType=H)时,确保获取最新订单状态的方案
1. 本地持久化重试队列优先保障消息不丢失
当数据库写入失败时,不要直接丢弃消息,先存入本地持久化的重试队列(比如本地文件、LevelDB或嵌入式SQLite),后台异步重试写入数据库。即使数据库临时不可用,消息也不会丢失,后续能自动补全数据。
修改你的FromApp逻辑,加入重试队列处理:
func (e FixApp) FromApp(msg *quickfix.Message, sessionID quickfix.SessionID) (reject quickfix.MessageRejectError) { messageDTO := model.MessageDTO{Message: msg} executionReport, err := messageDTO.ToExecutionReport() if err != nil { return err } // 尝试写入数据库 if err := e.PersistExecutionReport(executionReport); err != nil { // 写入失败,加入本地重试队列 if queueErr := e.LocalRetryQueue.Add(executionReport); queueErr != nil { // 记录致命日志,此时消息面临丢失风险,需人工介入 log.Printf("Critical: Failed to enqueue execution report for retry: %v", queueErr) } // 不要返回错误,避免向对方发送Reject消息导致会话异常 return nil } return }
后台启动异步重试worker,用指数退避策略避免频繁重试:
func (e FixApp) InitRetryWorker() { go func() { for { select { case report := <-e.LocalRetryQueue.GetChan(): err := e.PersistExecutionReport(report) if err != nil { // 指数退避重试,最多重试10次后转入人工处理 retryDelay := time.Duration(math.Min(60, math.Pow(2, float64(report.RetryCount)))) * time.Second time.Sleep(retryDelay) report.RetryCount++ if report.RetryCount > 10 { log.Printf("Report %s exceeded max retries, mark for manual handling", report.OrderID) continue } // 重新加入队列 if requeueErr := e.LocalRetryQueue.Requeue(report); requeueErr != nil { log.Printf("Failed to requeue report %s: %v", report.OrderID, requeueErr) } } case <-e.ShutdownSignal: return } } }() }
2. 与对手方协商自定义状态查询机制
既然对方不支持标准的OrderStatusRequest,可私下约定替代方案:
- 用自定义字段扩展现有Admin消息(比如在
TestRequest中加入订单ID列表,对方返回对应ExecutionReport) - 约定使用
BusinessMessageReject消息携带特定标识,触发对方返回目标订单的最新状态 - 协商批量快照机制:每日固定时间或系统重启后,请求对方发送当前所有有效订单的状态快照
3. 利用FIX会话的Gap Fill机制
如果对手方支持会话层的Gap Fill配置,可开启该功能:当会话检测到序列号缺口时,对方会主动发送SequenceReset消息填充缺失的消息(包括ExecutionReport)。但此机制仅针对会话期间的消息丢失,若已经成功接收但数据库存储失败,需要配合本地重试队列使用。
二、系统重启后序列号重置,ResendRequest无法正常使用的解决方案
1. 持久化会话序列号(核心方案)
QuickFIXGo支持序列号持久化,无需手动维护:
- 文件存储:在会话配置文件中添加
FileStorePath=./fix_session_store,框架会自动将发送/接收序列号存入指定目录的文件,重启后自动加载,保持序列号连贯。 - 自定义存储:若需用数据库存储序列号,实现
quickfix.Store接口,将序列号写入数据库,替代默认的文件存储。
示例配置(config.cfg):
[DEFAULT] FileStorePath=./fix_store ConnectionType=initiator SenderCompID=YOUR_COMP_ID TargetCompID=COUNTERPARTY_COMP_ID [SESSION] BeginString=FIX.5.0 StartTime=08:00:00 EndTime=20:00:00 SocketConnectHost=counterparty.host SocketConnectPort=1234
2. 协商全量重传机制
如果对方允许,系统重启登录后,可发送ResendRequest请求从序列号1开始的所有消息(需对方支持全量重传),但此方案会产生较大流量,需提前沟通。
发送全量ResendRequest的示例代码:
func (e FixApp) SendFullResendRequest(sessionID quickfix.SessionID) error { resendReq := quickfix.NewMessage() resendReq.Header.SetField(quickfix.NewTag(35, "2")) // MsgType=ResendRequest resendReq.SetField(quickfix.NewTag(7, 1)) // BeginSeqNo=1 resendReq.SetField(quickfix.NewTag(16, 0)) // EndSeqNo=0(表示请求到最新序列号) return quickfix.SendToTarget(resendReq, sessionID) }
3. 重启后请求订单快照
若对方不支持全量重传,可在重启登录后,通过之前协商的自定义机制请求所有有效订单的状态快照,一次性补全重启期间可能遗漏的订单状态。
内容的提问来源于stack exchange,提问作者Fahimeh Fathian Rad
相关产品推荐
相关产品推荐

