3. 会话管理基础:会话的定义、生命周期与状态机

聊到交易网关,很多人第一反应是「连接」。但说实话,连接只是最底层的东西。真正让系统跑起来的,是会话(Session)。

我刚开始做量化交易系统时,也犯过这个错误——把连接和会话混为一谈。结果呢?连接断了重连,但业务状态全丢了,订单都不知道该不该继续发。嗯,那场面挺尴尬的。

今天我们就来把会话这件事彻底讲清楚。

3.1 会话(Session)到底是什么?

简单说,会话是交易网关与交易所之间一次完整的、有状态的交互过程

你可以把连接想象成一根电话线,而会话是电话里正在进行的对话。电话线断了可以重新接上,但对话的内容、上下文、当前进行到哪一步了——这些是会话要管的事。

在我的定义里,一个会话至少包含以下要素:

  • 会话ID:全局唯一标识,比如 session_20240101_001
  • 认证信息:API Key、Token、签名等
  • 业务上下文:当前持仓、未成交订单、交易限额等
  • 状态:当前会话处于哪个阶段(后面会细讲)
  • 元数据:创建时间、最后活跃时间、所属用户等

核心观点:会话是业务层面的抽象,连接是传输层面的抽象。两者可以一一对应,也可以多对一。

3.2 会话的生命周期

一个会话从生到死,大概经历这么几个阶段:

  1. 创建:用户发起登录或认证请求,网关分配会话ID
  2. 认证:验证身份,交换密钥,建立信任
  3. 激活:认证通过,会话进入可用状态
  4. 维持:持续收发消息,心跳保活,更新上下文
  5. 挂起:连接断开但会话保留,等待重连恢复
  6. 关闭:主动登出或超时,释放所有资源

我个人习惯把生命周期画成一条时间线,每个阶段都有明确的进入条件和退出条件。这样写代码时就不会模棱两可。

避坑指南:我曾经在一个项目中,把「会话创建」和「会话激活」合并成了一个步骤。结果认证失败时,会话已经被分配了ID,导致后续清理逻辑非常混乱。后来我强制拆成两步,问题就解决了。

3.3 会话与连接的关系

这是很多人搞混的地方。我直接画个图来说明:

会话层(Session Layer) 会话A(已认证) 会话B(挂起中) 会话C(已关闭) 连接层(Connection Layer) TCP连接 #1(活跃) TCP连接 #2(活跃) 1:1 1:1 N:1(复用) 会话 连接

你看,会话和连接之间可以是一对一,也可以是多对一。什么意思呢?

  • 一对一:一个会话独占一个连接。这是最常用的模式,简单可靠。
  • 多对一:多个会话复用同一个连接。比如同一个TCP连接上,多个用户会话通过不同的消息ID来区分。这种模式节省资源,但实现复杂。

我个人更推荐一对一模式。你想想看,交易场景下,一个会话出问题不应该影响其他会话。复用连接虽然省资源,但排查问题时真的很头疼。

3.4 会话状态机模型

状态机是会话管理的核心。没有状态机,会话就是一盘散沙。

我设计过一套通用的会话状态机,分享给你:

INIT AUTHENTICATING ACTIVE SUSPENDED CLOSED TIMEOUT 发起认证 认证成功 连接断开 重连成功 主动关闭 超时未恢复 主动登出 正常流转 异常流转 终止流转

这个状态机有几个关键点:

  • INIT:会话刚创建,还没开始认证。此时不持有任何资源。
  • AUTHENTICATING:正在认证中。这个状态要设置超时,防止一直卡在这里。
  • ACTIVE:正常工作的状态。所有业务操作都在这个状态下进行。
  • SUSPENDED:连接断了,但会话还在。这是交易网关里最重要的状态——它给了你重连的机会。
  • CLOSED:正常关闭。资源全部释放。
  • TIMEOUT:超时关闭。比如挂起超过30秒还没恢复,就强制关闭。

特别注意:SUSPENDED状态一定要有超时机制。我曾经见过一个系统,会话挂起后一直不清理,结果内存里堆积了几万个僵尸会话,最后OOM了。嗯,血的教训。

3.5 代码实现要点

最后,我贴一段核心的状态机实现思路。这不是完整代码,但骨架都在了:

class Session:
    def __init__(self, session_id):
        self.id = session_id
        self.state = SessionState.INIT
        self.connection = None
        self.created_at = time.time()
        self.last_active = time.time()
    
    def authenticate(self, credentials):
        if self.state != SessionState.INIT:
            raise InvalidStateError()
        self.state = SessionState.AUTHENTICATING
        # 执行认证逻辑...
        if success:
            self.state = SessionState.ACTIVE
        else:
            self.state = SessionState.CLOSED
    
    def disconnect(self):
        if self.state == SessionState.ACTIVE:
            self.state = SessionState.SUSPENDED
            self.connection = None
    
    def reconnect(self, new_conn):
        if self.state != SessionState.SUSPENDED:
            raise InvalidStateError()
        self.connection = new_conn
        self.state = SessionState.ACTIVE
    
    def close(self):
        self.state = SessionState.CLOSED
        self.connection = None
        # 清理资源...

你看,每个状态转移都有明确的检查。非法操作直接抛异常,这样就不会出现「会话都关闭了还在发订单」这种低级错误。

我的习惯:状态机里每个状态都配一个超时定时器。比如AUTHENTICATING状态5秒超时,SUSPENDED状态30秒超时。定时器触发后自动转移到TIMEOUT状态。这样系统就不会被僵尸会话拖垮。

好了,会话管理的基础就这些。说白了,就是搞清楚「会话是什么」「它怎么活」「它怎么死」「它和连接什么关系」「状态怎么流转」。把这五点吃透了,后面讲会话池、会话复用、会话恢复,你就能轻松跟上。


公众号:蓝海资料掘金营,微信deep3321