针对一个客户端的物理连接,OBProxy 维持自身到后端多个 OBServer 的连接,采用基于版本号的增量同步方案维持每个 OBServer 的连接在同一状态,保证了客户端高效访问各个 OBServer。连接管理的另外一个功能是连接保持,在 OBServer 宕机/升级/重启时,客户端与 OBProxy 的连接不会断开,OBProxy 可以迅速切换到健康的 OBServer 上,对应用透明。
创建连接
OBProxy 的 Session 分为两类:Client Session 和 Server Session。Client Session 指的是客户端与 OBProxy 之间建立的连接;Server Session 指的是 OBProxy 与 OBServer 之间创建的连接。当 OBProxy 向 OBServer 转发客户端请求时,如果与 OBServer 之间没有创建连接,则需要初始化一个 Session 实例。
创建 Session 时需要做认证操作,OBProxy 无法决定该 Session 将来要访问哪些 OBServer,所以认证过程中,OBProxy 只能任意选择一台 OBServer 进行认证,将客户端发送的认证数据包转发给 OBServer,并将 OBServer 返回的结果转发给客户端,同时将客户端认证的数据包都缓存在 Client Session 内部,以便 OBProxy 与其它 OBServer 建立该 Client Session 关联的 Server Session 时,将这些认证数据表发送给 OBServer,便于与 OBServer 顺利进行认证。
存储连接
每当客户端向 OBProxy 发送请求,需要根据客户端连接信息查询获取 Client Session。在非连接池模式下,Server Session 不能脱离 Client Session 独立存在,只有根据 Client Session 来查询其关联的 Server Session,或者查询某个 Server Session 是否是与某个 Client Session 关联。
OBProxy 接收到客户端 SQL 请求时,会通过查询 partition table cache 来获取到 OBServer 的地址,然后查询 Client Session 保存的 Server Session 有没有与该 OBServer 关联的 Server Session 存在,如果存在则使用该 Server Session,如果不存在,则与该 OBServer 创建连接,并将 Server Session 加入 Client Session 关联的 Server Session 存储结构中。
事务状态维护
OBProxy 以事务来绑定 Client Session 与 Server Session。事务的第一条语句到达 OBProxy 时,OBProxy 选择一个 Server Session,并绑定到 Client Session 上,以后在整个事务处理过程中,都使用该 Server Session 转发数据到 OBServer,在事务中不能切换 Server Session,所以需要记录事务的状态到 Client Session中。
在事务开启时,记录事务开启状态到 Client Session,事务结束时将该事务状态重置。那么怎么来判断事务是否结束了呢?如果使用 autocommit,每个请求都要解析 OBServer 的返回包,如果一旦数据转发完成,就将该请求的事务状态重置。如果是长事务,只需要在 commit/rollback 请求后解析 OBServer 数据包来判断事务是否结束。
维护事务状态的目的主要是保证在事务中不切换 Server Session,也就是说,如果在事务中,OBServer 宕机,OBProxy 需要感知 OBServer 状态,并将未完结的事务结束掉。因为 MySQL 协议没有超时机制,OBProxy 必须主动通知客户端事务已经终止。OBServer 也没有对事务做同步,暂时也不能实现事务迁移功能,也就是说事务只能由接受请求的那台 OBServer 负责响应结果,换一台 OBServer 就不能处理,基于这个原因,在 OBProxy 做主备切换时,一定要保证正在处理的事务完成后才能切换,或者返回特殊错误,便于 OBProxy 通知客户端终止事务。
维护事务状态还有一个目的就是在 OBProxy 升级时,旧 OBProxy 经历一段时间,活跃的 Client Session 比较少时,采用 Kill 旧 OBProxy 的方式停止服务,停止服务也要保证在所有事务完结后才能进行,尽量减小对用户的影响。
连接变量管理
Client Session 需要记录客户端在该 Session 上所有设置过的变量,每次修改一个变量,在 Client Session 中记下修改时间。当 OBProxy 选择一个 Server Session 转发请求时,首先检查该 Server Session 上 Session 变量的修改时间是否大于 Client Session 中记录的修改时间。若小于,则意味着该 Session 没有使用最新的 Session 变量。OBProxy 会先重置该 Server Session 上的所有 Session 变量,批量将当前 Client Session 中保存的 Session 变量设置到该 Server Session 上后,再通过该 Server Session 转发请求;反之,则直接通过 Server Session 转发。
对于一些常见的 Session 变量,如:autocommit 等,客户端可能频繁设置,所以不能使用根据 modify time 来决定是否批量重置,而是每个 Server Session 存储这些常见 Session 变量的值,每次请求都对比 Client Session 中的变量值与 Server Session 中的变量值是否一致,不一致则重新设置这些变量值。
闪断避免
闪断避免指的是 OBProxy 与 OBServer 的 Server Session 异常不会被客户端感知,客户端与 OBProxy 之间的 Client Session 正常,客户端能够正常的读写数据。需要处理 Server Session 异常的情况如下:
OBServer 发生 Leader 切换,OBProxy 还没有获取到新的 Leader。这种情况主要由 OBServer 来处理,Leader 切换一定要保证将正在处理的事务都完成,OBProxy 只是尽力保证将请求发送给数据所在的 OBServer,OBServer 发生了 Leader 切换,那么即使数据不在该 OBServer 上,该 OBServer 也需要负责处理该请求,并把结果返回给 OBProxy。
OBServer 发生宕机,那么 OBProxy 与该 OBServer 的连接就会断开。如果该 Server Session 正在处理事务中,那么 OBProxy 需要发送一个错误响应给客户端;如果该 Server Session 处于空闲状态,只需将该 Server Session 从其对应的 Client Session 中标记删除即可。新的请求将不再使用该 Server Session 转发请求。
OBProxy 与 OBServer 通信超时,MySQL 协议本没有超时机制,但 OBServer 有超时机制,所以 OBserver 超时会通知 OBProxy,OBProxy 将错误通知给客户端。如果 OBServer 发现 Server Session 长时间不活动,也会 Kill 该 Session,这种情况的处理参考第 2 项的处理方式。
OBProxy 与 OBServer 之间的网络连接断开或发生网络分区,这种情况处理参考第 2 项,即使发生网络分区,如果 Server Session 上有事务正在执行,OBServer 在一段时间后终止该 Session,所以 OBProxy 在 Server Session 断开一段时间后,给客户端报错,结束未完成的事务,避免 Session 长时间被挂住。
OBProxy 升级,新启动的 OBProxy 将负责客户端发起的新 Session,旧 OBProxy 上的 Session 数量会越来越少,当旧 OBProxy 上的 Session 数量低于某个阈值时,需要将旧 OBProxy 上的 Session 全部都终止掉(当然要保证正在处理的事务完成,长事务需要等一段时间,超时仍然没有完成也只能强制 Kill 掉),然后停止旧 OBProxy。这个过程无法完全避免客户端连接闪断。
OBProxy 宕机,这种情况下,OBProxy 上的连接都会断掉,可能很快 OBProxy 被重新启动,或者该 OBProxy 负责的连接被其它的 OBProxy 处理,闪断无法避免。
使用 OBProxy 能够避免大部分异常情况下的连接闪断,特别是在 OBServer 发生 Leader 切换、主备集群切换、OBProxy 升级等后端维护的情况下,能保持客户端连接正常,对客户端的影响比较小。
连接复用
当 Client Session 关闭后,与 Client Session 关联的 Server Session 是否需要全部关闭?如果按常理处理,会关闭所有关联的 Server Session,但如果同一个客户端再次来连接 OBProxy,那么 Server Session 又需要重新建立一遍。创建 Session 是比较耗时的操作,特别是有应用的客户端代码,创建一个 Client Session,发送一条 SQL 请求获取到数据后关闭 Client Session,反复创建/关闭 Session 给 OBProxy 带来比较大的资源消耗。其中一种解决办法就是 Session 复用。
Session 复用是在 Client Session 关闭后,不关闭与之关联的 Server Session,而是将该 Server Session 加入 free list 中,如果该 Client 再创建 Client Session,则先查询是否该 Client 有空闲的 Client Session 可以使用,如果有,则重用该 Client Session,当需要使用与之关联的 Server Session 时,需要重置其 Session 变量,并设置新的 Session 变量后,就可以使用该 Server Session 转发客户端请求。
通过 Session 复用可以减少 OBProxy 与 OBServer 创建 Session 的频率,是一种优化的手段,并且 OBProxy 支持 Client Session 与 Server Session 不强绑定,作为一个公共的池子(连接池),所有的 Client Session 都可以从该连接池中获取可用的连接,当不再使用时,就将该连接释放归还连接池,从而可以被别的请求使用。
Kill Session 处理
MySQL 协议没有超时机制,但 MySQL 会 Kill 长时间不活跃的 Session,如果客户端一直等不到服务端的响应,会一直等待,遇到这种挂住的情况,一般 DBA 会介入,调用 MySQL 的 Kill Session 命令将 Session 关闭。
OBServer 有超时机制,一般情况下,无论是服务端宕机,还是网络分区,或者 OBProxy 与服务端的连接断开,OBProxy 都能够通知 Client Session 事务处理失败。但如果超时时间设置得过长,或者 OBProxy 处理有遗漏,Client Session 确实挂住了,那么应用就会要求做 Kill Session 操作。OBProxy 需要能够对 Kill Session 做特殊处理,不仅要处理 OBProxy 上记录的 Client Session 状态,还需要通知 OBServer关闭 Server Session。




