You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:04:59