制作netty-socketio服务器集群

时间:2017-10-03 06:38:01

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

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

我的问题是,如果任何客户端与server1连接,则server2在连接客户端时没有信息。 即。如果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。

3 个答案:

答案 0 :(得分:1)

  

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

这是预期的。 Server2可能有一些关于连接客户端与server1的信息(因为他们使用带有pub / sub的redis),如sessionId(查看类BaseStoreFactory)等,但server2没有与那些客户端连接,server2之间没有Channel和客户端连接server1。在没有tcp套接字连接的情况下,在低级别给出一个想法,那么server2如何与客户端通信。

  

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

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

答案 1 :(得分:1)

答案 2 :(得分:0)

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

服务器A客户端X相连,服务器B客户端Y相连。我需要从服务器 A客户端 Y 发送私人消息。该解决方案是通过在每个服务器中映射所有服务器主机的列表来制定的,并且对于每个服务器主机,我们创建一个“内部客户端 ”。

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

给定一个服务器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
}