3. 会话管理基础:会话的定义、生命周期与状态机
聊到交易网关,很多人第一反应是「连接」。但说实话,连接只是最底层的东西。真正让系统跑起来的,是会话(Session)。
我刚开始做量化交易系统时,也犯过这个错误——把连接和会话混为一谈。结果呢?连接断了重连,但业务状态全丢了,订单都不知道该不该继续发。嗯,那场面挺尴尬的。
今天我们就来把会话这件事彻底讲清楚。
3.1 会话(Session)到底是什么?
简单说,会话是交易网关与交易所之间一次完整的、有状态的交互过程。
你可以把连接想象成一根电话线,而会话是电话里正在进行的对话。电话线断了可以重新接上,但对话的内容、上下文、当前进行到哪一步了——这些是会话要管的事。
在我的定义里,一个会话至少包含以下要素:
- 会话ID:全局唯一标识,比如
session_20240101_001 - 认证信息:API Key、Token、签名等
- 业务上下文:当前持仓、未成交订单、交易限额等
- 状态:当前会话处于哪个阶段(后面会细讲)
- 元数据:创建时间、最后活跃时间、所属用户等
核心观点:会话是业务层面的抽象,连接是传输层面的抽象。两者可以一一对应,也可以多对一。
3.2 会话的生命周期
一个会话从生到死,大概经历这么几个阶段:
- 创建:用户发起登录或认证请求,网关分配会话ID
- 认证:验证身份,交换密钥,建立信任
- 激活:认证通过,会话进入可用状态
- 维持:持续收发消息,心跳保活,更新上下文
- 挂起:连接断开但会话保留,等待重连恢复
- 关闭:主动登出或超时,释放所有资源
我个人习惯把生命周期画成一条时间线,每个阶段都有明确的进入条件和退出条件。这样写代码时就不会模棱两可。
避坑指南:我曾经在一个项目中,把「会话创建」和「会话激活」合并成了一个步骤。结果认证失败时,会话已经被分配了ID,导致后续清理逻辑非常混乱。后来我强制拆成两步,问题就解决了。
3.3 会话与连接的关系
这是很多人搞混的地方。我直接画个图来说明:
你看,会话和连接之间可以是一对一,也可以是多对一。什么意思呢?
- 一对一:一个会话独占一个连接。这是最常用的模式,简单可靠。
- 多对一:多个会话复用同一个连接。比如同一个TCP连接上,多个用户会话通过不同的消息ID来区分。这种模式节省资源,但实现复杂。
我个人更推荐一对一模式。你想想看,交易场景下,一个会话出问题不应该影响其他会话。复用连接虽然省资源,但排查问题时真的很头疼。
3.4 会话状态机模型
状态机是会话管理的核心。没有状态机,会话就是一盘散沙。
我设计过一套通用的会话状态机,分享给你:
这个状态机有几个关键点:
- 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