【问题标题】:Make cluster of netty-socketio server制作netty-socketio服务器集群
【发布时间】:2018-03-14 06:58:42
【问题描述】:

我正在使用 This 进行 socketio 设置。 我有 2 个不同的 socketio 服务器(比如说 server1 和 server2)在集群中运行,使用 RedissonStoreFactory

我的问题是,如果有任何客户端与 server1 连接,那么 server2 没有任何信息(如果已连接客户端)。 IE。如果 2 个客户端与 server1 连接,并且如果我在 server2 上执行 server.getAllClients(),它将返回空列表而不是 2 个计数的列表。

这是我在两台不同机器上运行的代码。

@SpringBootApplication
public class Application {


    private static final Logger LOGGER = Logger.getLogger(Application.class);

    @Value("${test.socketio.hostName}")
    private String socketIOHostName;

    @Value("${test.socketio.port}")
    private Integer socketIOport;

    @Value("${test.dedisson.redissonAddress}")
    private String redissonAddress;

    @Autowired
    private RedissonClient redissonClient;

    @Bean
    public SocketIOServer socketIOServer() {
        LOGGER.info("Socket server starting on host=" + socketIOHostName + ", port=" + socketIOport);
        Configuration config = new Configuration();
        config.setHostname(socketIOHostName);
        config.setPort(socketIOport);

        StoreFactory redissonStoreFactory = new RedissonStoreFactory(redissonClient);
        config.setStoreFactory(redissonStoreFactory);
        SocketIOServer server = new SocketIOServer(config);
        server.start();
        LOGGER.info( "Socket server started");
        return server;
    }

    /**
     * 
     * @return
     */
    @Bean
    public RedissonClient getRedissonClient(){
        LOGGER.info("creatting redisson client on redissonAddress="+redissonAddress);
        Config config = new Config();
        config.useSingleServer().setAddress(redissonAddress);
        RedissonClient redisson = Redisson.create(config);
        LOGGER.info("redisson client connected");
        return redisson;
    }


    public Application() {
        //Nothing to be done here
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
    }

    @Bean
    public SpringAnnotationScanner springAnnotationScanner(SocketIOServer ssrv) {
        return new SpringAnnotationScanner(ssrv);
    }
}

我更喜欢 2 个实例用于故障转移条件。如果 server1 关闭,则 server2 会将通知发送到连接的客户端,但在我的情况下,server2 不知道与 server1 连接的客户端。

【问题讨论】:

    标签: java spring-boot socket.io netty redisson


    【解决方案1】:

    【讨论】:

      【解决方案2】:

      如果 2 个客户端与 server1 连接,并且如果我在 server2 上执行 server.getAllClients(),它会返回空列表而不是 2 个计数的列表。

      这是意料之中的。 Server2 可能有一些关于与 server1 连接的客户端的信息(因为他们使用带有 pub/sub 的 redis),如 sessionId(查看类BaseStoreFactory)等,但 server2 未与这些客户端连接,server2 和客户端之间不存在通道与 server1 连接。考虑一下,在低级别,当没有 tcp 套接字连接存在时,server2 是如何与客户端通信的。

      我更喜欢 2 个实例用于故障转移条件。如果 server1 关闭,则 server2 会将通知发送到连接的客户端,但在我的情况下,server2 不知道与 server1 连接的客户端。

      使用 Nginx(带有iphash)或集群顶部的任何代理,一旦一台服务器出现故障,客户端将尝试重新连接,而 nginx 会将其重定向到另一台服务器。从 clinet 的角度来看,不会有太多的延迟。

      【讨论】:

        【解决方案3】:

        我遇到过类似的问题,并通过我的“自我协议”解决了这个问题。

        有一个server Aclient X相连,server Bclient Y相连。我需要从 服务器 A客户端 Y 发送 私人消息。解决方案是通过在每台服务器中映射所有服务器主机的列表,并为每台服务器主机创建一个“内部客户端 ”。

        每个内部客户端负责通知其各自的服务器,然后服务器可以检查客户端目标是否存在于其自身的客户端列表中。

        给定一个服务器A,如果你想列出一个集群的所有客户端,你可以要求每个内部客户端发送一个数据包,例如“REQUEST”为其连接服务器,然后为“RESPONSE”消息创建侦听器并将结果添加到全局列表中。

        build.gradle

        implementation group: 'io.socket', name: 'socket.io-client', version: '1.0.0'
        

        服务器

        Configuration config = new Configuration();
        config.setHostname(LOCALHOST);
        config.setPort(PORT);
        config.setTransports(Transport.WEBSOCKET); // DONT FORGET THIS LINE !!!
        
        Config redissonConfig = new Config();
        redissonConfig.useSingleServer().setAddress("redis://192.168.0.24:6379")
                    .setPassword("myPass");
        Redisson redisson = (Redisson) Redisson.create(redissonConfig);
        RedissonStoreFactory redisStoreFactory = new RedissonStoreFactory(redisson);
        config.setStoreFactory(redisStoreFactory);
        
        SocketIOServer server = new SocketIOServer(config);
        
        server.addEventListener("REQUEST", Object.class, (client, data, ackSender) -> {
            server.getBroadcastOperations().sendEvent("RESPONSE", server.getAllClients());
        });
        

        内部客户

        List<SocketIOClient> allClients = new ArrayList<>();
        List<Socket> internalClients = new ArrayList<>();
        String[] hostnames = { "http://localhost:8081", "http://localhost:8082" };
        
        for (String hostname : hostnames) {
            IO.Options opts = new IO.Options();
            opts.transports = new String[] { WebSocket.NAME };  // DONT FORGET THIS LINE !!!
            Socket socket = IO.socket(hostname, opts);
        
            socket.on("RESPONSE", args -> {
                List<SocketIOClient> currentList = (List<SocketIOClient>) args[0];
                allClients.addAll(currentList);
            });
        
            socket.connect();
            internalClients.add(socket);
        }
        
        for (Socket socket : internalClients) {
            socket.emit("REQUEST", "foo"); // THIS LINE WILL FILL CLIENTS LIST, VIA CALLBACK, FOR EACH SERVER
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-09
          • 2012-08-04
          • 2016-04-06
          • 2013-10-13
          • 1970-01-01
          相关资源
          最近更新 更多