socketio+nginx负载均衡配置

本文介绍了在项目中使用nginx作为反向代理和负载均衡器的场景,特别是针对socketio服务端绑定域名的情况。当面临第三方系统回调时,前端socket-client无法确定连接的socket-server。解决方案是通过nginx配置支持长连接的websocket负载均衡,并将服务端绑定地址设为0.0.0.0。同时,服务端利用redis进行消息队列和topic监听,实现消息群发。

项目场景:

项目中使用socketIo推送结果到前端页面,开发环境调试完后,部署到测试和生产环境遇到问题

问题描述:

  1. 测试和生产环境用了nginx做反向代理和负载均衡

  2. 测试和生产环境socketio服务端绑定的是域名

  3. 当第三方系统调用结果回调前端时,不知道前端socket-client连的时哪台socket-server

解决方案:

  1. nginx做http请求的负载均衡的时候是采用轮询的方式,但是websocket需要保持长连接,所以Nginx必须配置支持长连接
 upstream  socket{
 		//websocket的负载均衡必须使用Iphash的策略来保证,客户端是连的同一个服务端
         ip_hash;
         server xxx.xxx.xxx:9092;
         server xxx.xxx.xxx:9093;
    }
     server {
        listen       8080;
        server_name  www.baidu.com;
 			location  /socket.io {
       			 proxy_set_header X-Real_IP $remote_addr;
       			 proxy_set_header Host $host;
       			 proxy_set_header X_Forward_For $proxy_add_x_forwarded_for;
       		 //显示设置http协议1.1,这个版本才支持长连接
       			 proxy_http_version 1.1;
       		 //连接从HTTP连接升级到WebSocket连接
       			 proxy_set_header Upgrade $http_upgrade;
			        proxy_set_header Connection 'upgrade';
        			proxy_pass http://socket;
   	   		 }
    }
  1. 当服务部署到阿里云时,服务端绑定的主机名要改成0.0.0.0
public class SocketServer {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private SocketIOServer server;
    //9092
    public static String WSS_PORT = PropUtil.getProperty("wss.port");
    //0.0.0.0
    public static String WSS_HOST = PropUtil.getProperty("wss.host");
    public void run() {
        if (server == null) {
            SocketConfig socketConfig = new SocketConfig();
            socketConfig.setReuseAddress(true);
            Configuration config = new Configuration();
            config.setSocketConfig(socketConfig);
            //设置主机
            config.setHostname(WSS_HOST);
            //设置端口
            config.setPort(Integer.parseInt(WSS_PORT));
            //添加链接身份验证监听器
            config.setAuthorizationListener(new SocketIoAuthListener());
            //添加错误处理监听器
            config.setExceptionListener(new SocketIoExListener());

            server = new SocketIOServer(config);
        }
        //添加客户端断开连接监听器
        server.addDisconnectListener(new SocketIoDisConnListener());
        //添加客户端连接监听器
        server.addConnectListener(new SocketIoConnListener());

        server.start();
        logger.info("================Socket服务端启动成功==============");
    }
}
  1. 服务端采用redis做消息队列,监听一个topic,来进行消息群发,然后每个服务器拿到消息后都尝试往client发送。
<!-- redis -->
	<bean id="remindListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
		<constructor-arg>
			<bean class="com.xxx.xxx.websocket.CallBackMessageListener"/>
		</constructor-arg>
	</bean>
	<bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
		<property name="connectionFactory" ref="jedisConnectionFactory"/>
		<property name="messageListeners">
			<map>
				<entry key-ref="remindListener">
					<bean class="org.springframework.data.redis.listener.ChannelTopic">
						<constructor-arg value="socketio:topic"/>
					</bean>
				</entry>
			</map>
		</property>
	</bean>
public class CallBackMessageListener implements MessageListener {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void onMessage(Message message, byte[] bytes) {
        try {
            byte[] body = message.getBody();
            //反序列化
            String str = (String) redisTemplate.getValueSerializer().deserialize(body);
            String channel = (String) redisTemplate.getValueSerializer().deserialize(bytes);
            logger.info("redis监听到消息内容:" + str);
            logger.info("消息监听通道:" + channel);
            String[] msg = str.split(":");
            if(msg.length == 2){
                SocketIOClient client
                        = SocketIoClientCache.getSocketIOClientByClientID(msg[0]);
                if (client == null) {
                    logger.info("没有找到socket client,clientId:{}", msg[0]);
                    return;
                }
                SocketIoClientCache.sendMessageToClient(client, EventContanst.CALL_BACK_EVENT, msg[1]);
            }
        } catch (Exception e) {
            logger.error("-----------------------消息提醒redis监听处理失败-------------------------");
            logger.error(e.getMessage(), e);
        }
    }
}
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值