iohao / ioGame

无锁异步化、事件驱动架构设计的 java netty 网络编程框架; 轻量级,无需依赖任何第三方中间件或数据库就能支持集群、分布式; 适用于网络游戏服务器、物联网、内部系统及各种需要长连接的场景; 通过 ioGame 你可以很容易的搭建出一个集群无中心节点、集群自动化、分布式的网络服务器;FXGL、Unity、UE、Cocos Creator、Godot、Netty、Protobuf、webSocket、tcp、socket;java Netty 游戏服务器框架;
http://game.iohao.com
GNU Affero General Public License v3.0
904 stars 201 forks source link

多次调用UserIdSettingKit.settingUserId,会出现逻辑服请求网关服超时的情况 #344

Closed dx1ngy closed 3 months ago

dx1ngy commented 3 months ago

你的问题

作者你好,使用单机多进程方式分别启动网关服、对外服、逻辑服,使用模拟客户端调用1-0路由,action方法中为UserIdSettingKit.settingUserId,第一次调用没问题正常返回,再次调用这个路由则会出现逻辑服请求网关服超时的情况 com.iohao.game.bolt.broker.client.kit.UserIdSettingKit.settingUserId:99 - Rpc invocation timeout[responseCommand TIMEOUT]! the address is 127.0.0.1:10200

复现步骤

game.zip

版本

dev分支最新代码

iohao commented 3 months ago

同一个连接只能调用一次登录。

dx1ngy commented 3 months ago

同一个连接只能调用一次登录。

为啥回报超时错误呢?感觉直接返回false是不是会好一点,这块是怎么考虑设计的?

iohao commented 3 months ago

正常业务不会登录两次。

方法内返回的 false,里面会打印错误日志。

dx1ngy commented 3 months ago

正常业务不会登录两次。

方法内返回的 false,里面会打印错误日志。

我大概跟了一下,因为登录完之后channelId为null,请求到逻辑服之后报错没返回导致超时,之后我又找到下面的判断

image

为什么登录之后不传channelId,传了会有什么问题么,可不可以解答一下。

iohao commented 3 months ago

登录后,该 channelId 就没有意义了。

see https://github.com/iohao/ioGame/blob/ba8867906eea529758324f64c6b1c24fdc4c7c8a/common/common-core/src/main/java/com/iohao/game/action/skeleton/protocol/HeadMetadata.java#L119-L129

dx1ngy commented 3 months ago

登录后,该 channelId 就没有意义了。

see

https://github.com/iohao/ioGame/blob/ba8867906eea529758324f64c6b1c24fdc4c7c8a/common/common-core/src/main/java/com/iohao/game/action/skeleton/protocol/HeadMetadata.java#L119-L129

channelId这个地方我明白了。还有一个我纠结的点在于SettingUserIdMessage这个消息对外服接收到之后,可以判断是否登录了,如果登录了就返回false,而不是现在这样因为channelId为null,导致超时不返回消息,逻辑服再捕获异常返false,感觉怪怪的。

iohao commented 3 months ago

同一个用户允许多次登录才是问题,建议在 action 中自己加个用户是否已经登录了的判断。

https://github.com/iohao/ioGame/commit/ba8867906eea529758324f64c6b1c24fdc4c7c8a

shuaiyinoo commented 3 months ago

本来我也遇到过这个问题但是规避了 从之前的 17 版本登录的时候就存在 后面就没有去管了 多少有点缺陷 登录之前先判断是否存在再顶号 我看作者在最新的版本已经修复了 很开心 #334 等我更新一波 看看是不是真的修复了 之前退出登录会报错导致登录不上或者再次登录需要 3-5 秒才能成功

shuaiyinoo commented 3 months ago
image

感谢由于断线重连一直很慢 看到这个 BUG 修复现在都是秒重新连接上 太开心了

iohao commented 3 months ago

使用技巧: 自定义注解配合 ioGame 线程相关 (yuque.com) 的线程编排,可以实现让登录方法交给虚拟线程处理,这样就不会阻塞用户线程。

1,在 loginVerify 方法上添加一个自定义的 VirtualThread 注解。

加上 VirtualThread 注解后,action 方法就能保持同样的编码风格,不需要显示的将代切换到其他线程中执行了。

@ActionController(LoginCmd.cmd)
public class LoginAction {
/**
* 登录验证
*
* @param loginVerify 登录
* @param flowContext flowContext
* @return 用户信息
*/
@VirtualThread
@ActionMethod(LoginCmd.loginVerify)
public UserInfo loginVerify(LoginVerify loginVerify, FlowContext flowContext) {
... ... 省略部分代码
}
}

2,重写 RequestMessageClientProcessorHook 接口,处理 VirtualThread 注解。

public final class MyRequestMessageClientProcessorHook implements RequestMessageClientProcessorHook {

    @Override
    public void processLogic(BarSkeleton barSkeleton, FlowContext flowContext) {
        ActionCommand actionCommand = flowContext.getActionCommand();
        var annotation = actionCommand.getAnnotation(VirtualThread.class);
        // 使用虚拟线程处理业务
        if (Objects.nonNull(annotation)) {
            TaskKit.executeVirtual(() -> barSkeleton.handle(flowContext));
            return;
        }

        ... ...
    }
}

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface VirtualThread {
}