简体   繁体   English

制作 netty-socketio 服务器集群

[英]Make cluster of netty-socketio server

I am using This for socketio setup.我正在使用进行 socketio 设置。 I have 2 different socketio server(let say server1 and server2) running in cluster with using of RedissonStoreFactory我有 2 个不同的 socketio 服务器(比如 server1 和 server2),使用RedissonStoreFactory在集群中运行

My issue is, If any client connects with server1, then server2 has no information if connected client.我的问题是,如果任何客户端与 server1 连接,则 server2 没有连接客户端的信息。 ie.即。 If 2 clients connect with server1 and If I execute server.getAllClients() on server2 it returns the empty list instead of the list with 2 counts.如果 2 个客户端与 server1 连接并且如果我在 server2 上执行server.getAllClients()它返回空列表而不是具有 2 个计数的列表。

Here is my code running on 2 different machines.这是我在 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);
    }
}

I prefer 2 instances for failover condition.If server1 is down then server2 will send the notification to connected clients, but in my case, server2 has no idea of the connected client with server1.我更喜欢故障转移条件的 2 个实例。如果 server1 关闭,则 server2 会将通知发送到连接的客户端,但在我的情况下,server2 不知道与 server1 连接的客户端。

If 2 clients connect with server1 and If I execute server.getAllClients() on server2 it returns the empty list instead of the list with 2 counts.如果 2 个客户端与 server1 连接,并且如果我在 server2 上执行 server.getAllClients() 它返回空列表而不是具有 2 个计数的列表。

This is expected.这是预期的。 Server2 might have some information about connected clients with server1 (as they use redis with pub/sub) like sessionId (look into class BaseStoreFactory )and etc but server2 is not connected with those clients, there is no Channel present between server2 and clients connected with server1. Server2 可能有一些有关与 server1 连接的客户端的信息(因为它们将 redis 与 pub/sub 一起使用),例如 sessionId(查看类BaseStoreFactory )等,但 server2 未与这些客户端连接,server2 和客户端之间不存在 Channel服务器 1。 Give it a thought, on low level, when no tcp socket connection is present then how come server2 can communicate with client.想一想,在底层,当没有 tcp 套接字连接存在时,server2 怎么可以与客户端通信。

I prefer 2 instances for failover condition.If server1 is down then server2 will send the notification to connected clients, but in my case, server2 has no idea of the connected client with server1.我更喜欢故障转移条件的 2 个实例。如果 server1 关闭,则 server2 会将通知发送到连接的客户端,但在我的情况下,server2 不知道与 server1 连接的客户端。

Use Nginx (with iphash )or any proxy on top of your cluster, as soon as one server goes down, the client will try to reconnect and nginx will redirect it to the other server.使用 Nginx(带有iphash )或集群顶部的任何代理,一旦一台服务器出现故障,客户端将尝试重新连接,nginx 会将其重定向到另一台服务器。 From clinet point of view, there won't be much delay in that.从客户的角度来看,不会有太多延迟。

I've had a similar problem and solved that with my "self-protocol".我遇到了类似的问题,并用我的“自协议”解决了这个问题。

There was a server A connected with client X , and server B connected with client Y .服务器 A客户端 X连接,服务器 B客户端 Y连接。 I needed to send a private message from server A to client Y .我需要从服务器 A客户端 Y发送私人消息 The solution was made by mapping, in each server, a list of all server hosts , and for every server host we create a " internal client ".该解决方案是通过在每个服务器中映射所有服务器主机的列表来实现的,并且我们为每个服务器主机创建一个“内部客户端”。

Each internal client is responsible for notifying its respective server , then the server could check if client-target exists on its self-list of clients.每个内部客户端负责通知其各自的服务器,然后服务器可以检查 client-target 是否存在于其自身的客户端列表中。

Given a server A , if you want to list all clients of a cluster, you can ask for each internal client to send a packet eg " REQUEST " for its connection server, then create a listener for " RESPONSE " message and add result to a global list.给定服务器 A ,如果您想列出集群的所有客户端,您可以要求每个内部客户端为其连接服务器发送一个数据包,例如“ REQUEST ”,然后为“ RESPONSE ”消息创建一个侦听器并将结果添加到全球名单。

build.gradle :构建.gradle

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

Server服务器

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());
});

Internal Clients内部客户

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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM