如何实现单账号仅能登录一个共享数据库的Java游戏服务器?
限制单账号仅登录一台Java游戏服务器的方案
方案1:数据库新增在线状态标记
- 在用户账号表(比如
user_account)里加两个字段:is_online(布尔值,标记是否在线)、server_id(记录当前登录的服务器标识) - 登录逻辑改造成:
- 用户登录时先查数据库,看该账号
is_online是不是true - 要是已经在线,直接返回“账号已在其他服务器登录”
- 不在线的话,把
is_online设为true,server_id填当前服务器ID,再完成登录流程
- 用户登录时先查数据库,看该账号
- 异常情况处理:
- 用户主动登出时,把
is_online改回false,清空server_id - 加心跳检测:服务器每隔一段时间(比如30秒)检查客户端心跳,超过阈值没收到就重置该账号的在线状态
- 服务器启动时,批量把对应
server_id的账号在线状态重置,防止服务器宕机后状态残留
- 用户主动登出时,把
方案2:用行级锁避免并发登录竞态
- 针对同一账号的登录请求,用数据库行级锁确保同一时间只有一个请求能处理,防止两台服务器同时处理同一账号登录导致状态混乱
- Java代码示例:
// 登录时先获取账号行的排他锁,避免并发操作 String lockSql = "SELECT * FROM user_account WHERE username = ? FOR UPDATE"; try (Connection conn = getDbConnection()) { PreparedStatement lockStmt = conn.prepareStatement(lockSql); lockStmt.setString(1, loginUsername); ResultSet rs = lockStmt.executeQuery(); if (rs.next()) { if (rs.getBoolean("is_online")) { throw new RuntimeException("账号已在其他服务器登录"); } // 更新在线状态 String updateSql = "UPDATE user_account SET is_online = true, server_id = ? WHERE username = ?"; PreparedStatement updateStmt = conn.prepareStatement(updateSql); updateStmt.setString(1, currentServerId); updateStmt.setString(2, loginUsername); updateStmt.executeUpdate(); } } catch (SQLException e) { // 处理数据库异常 e.printStackTrace(); }
方案3:会话集中管理(适合多服务器扩展)
- 搭一个简单的会话管理中心,所有服务器的登录请求都先过这个中心验证
- 中心维护一个在线账号映射表,记录每个账号当前登录的服务器ID
- 登录时中心查映射表,已存在就拒绝登录;登出或异常断开时,中心移除对应记录
- 这种方式后续加新服务器时不用改每个服务器的逻辑,统一在中心管理
内容的提问来源于stack exchange,提问作者duy
相关产品推荐
相关产品推荐

