iohao / ioGame

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

Home Page:http://game.iohao.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

心跳钩子被多次执行

KING754 opened this issue · comments

commented

你的问题 | 使用场景

在对外服上加了钩子。然后通过Client连过去。发现callback被执行了三次。
hook:

@Slf4j
public class HeartBeatHook implements SocketIdleHook {
    @Override
    public boolean callback(UserSession userSession, IdleStateEvent idleStateEvent) {
        log.info("heart check id:{}",userSession.getUserId());
        return false;
    }

    @Override
    public void pongBefore(BarMessage idleMessage) {log.info("receive pongBefore:{}",idleMessage);
        idleMessage.setData(DataCodecKit.encode(TimeKit.currentTimeMillis()));
    }
}

ExternalServerConfig

@Component
@RequiredArgsConstructor
public class ExternalServerConfig {
    private final AppConfig appConfig;
    private final ServerUtil serverUtil;

    public ExternalServer createExternalServer() {
        if (!serverUtil.checkServerConfig(appConfig, true, true)) {
            System.exit(0);
        }

        IoGameGlobalConfig.openTraceId = true;
        IoGameGlobalConfig.externalLog = true;
        IoGameGlobalConfig.broadcastLog = true;
        IoGameGlobalConfig.openLog = true;

        // 路由访问权限控制;https://www.yuque.com/iohao/game/nap5y8p5fevhv99y
        extractedIgnore();

        // 游戏对外服 - 构建器;https://www.yuque.com/iohao/game/ea6geg
        DefaultExternalServerBuilder builder = DefaultExternalServer.newBuilder(appConfig.getPort())
                .externalJoinEnum(this.getProtoByConfigOrDefault(appConfig.getProtoName()))             //协议类型
                .brokerAddress(new BrokerAddress(appConfig.getGatewayIP(), appConfig.getGatewayPort()));

        builder.setting().setUserHook(new LogOutHook());
        builder.setting().setIdleProcessSetting(this.getHeartBeatSetting());

        // 构建游戏对外服
        return builder.build();
    }

    private void extractedIgnore() {
        AccessAuthenticationHook accessAuthenticationHook = ExternalGlobalConfig.accessAuthenticationHook;
        accessAuthenticationHook.addIgnoreAuthCmd(E_ModuleId.E_M_Login_Id.value(), E_MsgId.E_Login.value());

    }

    private IdleProcessSetting getHeartBeatSetting(){
        return new IdleProcessSetting()
                // 心跳整体时间设置包括:readerIdleTime、writerIdleTime、allIdleTime
                .setIdleTime(30)
                .setIdleHook(new HeartBeatHook());
    }

    private ExternalJoinEnum getProtoByConfigOrDefault(String protoType) {
        if (StrUtil.isEmpty(protoType)) {
            return ExternalJoinEnum.TCP;
        }

        for (ExternalJoinEnum proto : ExternalJoinEnum.values()) {
            if (proto.getName().equals(protoType)) {
                return proto;
            }
        }

        return ExternalJoinEnum.TCP;
    }
}

InitServer

@Slf4j
@Component
@RequiredArgsConstructor
public class InitServer  implements CommandLineRunner {
    private final ExternalServerConfig gameExternal;

    @Override
    public void run(String... args) throws Exception {
        ExternalServer server = gameExternal.createExternalServer();
        server.startup();
    }
}

boot:
@componentscan(basePackages = "com.bitdragon.*")
@SpringBootApplication
public class ExternalApplication {
public static void main(String[] args) {
SpringApplication.run(ExternalApplication.class, args);
}
}

client:

    public static ClientRunOne initClient(){
        ClientRunOne client = new ClientRunOne();
        client.setJoinEnum(TCP);
        client.setInputCommandRegions(InitClient.getAllCmdAction());
        client.setConnectAddress(EXTERNAL_IP);
        client.setConnectPort(EXTERNAL_PORT);
//        client.idle(20);

        return client;
    }

    private static List<InputCommandRegion> getAllCmdAction(){
       return List.of(
         new LoginCmd(),
         new FriendListCmd(),
         new TaskListCmd()
       );
    }

结果每隔30秒日志:

18:01:25.481 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:01:25.524 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:01:25.524 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:01:55.482 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:01:55.535 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:01:55.535 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:02:25.482 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:02:25.536 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928
18:02:25.536 [iohao.com:external-netty-server-worker-1] [] INFO  c.b.e.h.HeartBeatHook.callback(HeartBeatHook.java:17) heart check id:1779812137254780928

预期值

每30秒,应该只执行一次。

实际值

每30秒,执行了三次。

可以提供一个能运行的可复现的 demo 和复现步骤吗

commented

上面的代码, 就是我有点问题的代码.然后好像没有特别的步骤.就是启动好几个服务之后,然后客户端启动起来,连过去.啥也不做(不发心跳).

commented

我明天看一下,能不能拉一个单独工程试一下.

commented

https://github.com/KING754/ioGameDemo

我刚刚用这个试了,还是三次。

启动server-gateway,server-external,server-login
然后启动客户端
30秒之后,server-external就会有三次日志出来了。

上传一个 zip 到这里吧。

commented

我刚刚试了example/example-hook,也能复现出来.
修改1:DemoIdleHook.java的callback方法:

   @Override
    public boolean callback(UserSession userSession, IdleStateEvent event) {
        log.info("receive heart beat call back!!!");
        return false;
    }

修改2:DemoHookClient.java

    public static void main(String[] args) {
        ClientUserConfigs.closeLog();

        // 模拟请求数据
        List<InputCommandRegion> inputCommandRegions = List.of(
                new InternalRegion()
        );

        // 启动模拟客户端
        new ClientRunOne()
                .setInputCommandRegions(inputCommandRegions)
                // 开启心跳,每 3 秒向服务器发送一次心跳消息
//                .idle(3)                   //第二处修改,注释掉心跳发送
                .startup();
    }

然后运行:
DemoHookApplication.java
再运行:DemoHookClient.java

观察:DemoHookApplication控制台即可.

默认心跳配置的是 READER_IDLE、WRITER_IDLE、ALL_IDLE,触发时间都是大致相同的。

建议你在打印时,把 IdleStateEvent 也打印一下。

commented

确实是这样,三个事件。因为我用的是setIdleTime(30)方法。

那像这样的话,我想想,我应该怎么使用。
没有注意到心跳有三种类型。

我只处理一种,其它两个不处理就好了。

谢谢。