精华内容
下载资源
问答
  • 由于Gru是按照集群设计的,所以架构上分了好几个模块,但若是对于十万级别的长连接来说,单纯一个长连接节点(Gru的长连接服务模块叫spear,详见代码目录下的spear目录)即可支持。感兴趣的同学可拿spear自行改造。 ...

    Github

    项目名称为Gru,取自《卑鄙的我》 项目地址: https://github.com/sumory/gru 示例-web chat:https://github.com/sumory/gru-example


    先上图

    • 示例项目是一个IM应用,基于Node.js和socket.io(协议实现由Gru提供),截图:

    输入图片说明

    输入图片说明

    • Gru集群提供状态监控,该模块使用Node.js实现

    输入图片说明

    输入图片说明

    介绍

    其实集群的主要实现是基于Java的,也用了大量的中间件,node.js实现了监控和示例项目,但稍微上点规模的项目都不是一个门语言能cover的,所以有兴趣的同学还是可以看下这个项目的。详情见Github,大致介绍如下:

    Gru

    Gru是一个长连接服务解决方案,可用于各种类型的实时交互应用。

    • 支持单点部署和集群模式部署
    • 采用socket.io协议
    • 各模块均支持水平扩展
    • 单节点可服务10W+以上长连接,具体为在不断发消息的情况下(1000条/秒),单长连接服务节点支持的稳定连接数量在10W+(8核16G)
    • 节点间通讯支持多种方式:进程内、redis、rocketmq

    安装:

    Gru-example

    gru-example基于Gru的示例项目,即时应用最典型的代表就是IM,这个示例是一个基于Gru的web聊天应用。

    安装:

    • 需首先配置好Gru集群
      • 集群配置详见Gru文档
      • 若不需要集群,Gru项目只需要部署一个模块(spear)即可
    • 本示例是一个Node.js项目,需先安装Node.js
    • clone下本示例
      • npm install相关依赖
      • 配置文件在config目录下,特别注意spearNode配置,此配置为一个可用的spear节点
      • 运行node app.js,默认会加载config/dev.js配置文件

    其它

    • 这个项目的前身是我原来的一个开源项目,后来用于公司内部某系统,就关了Github。最近抽空剥离了一些业务代码,内部实现改得更为通用后重新开放出来,支持点对点和广播消息。
    • 由于Gru是按照集群设计的,所以架构上分了好几个模块,但若是对于十万级别的长连接来说,单纯一个长连接节点(Gru的长连接服务模块叫spear,详见代码目录下的spear目录)即可支持。感兴趣的同学可拿spear自行改造。
    • gru-example提供了一个如何使用gru构建实时应用的示例,若要二次开发,集成用户系统进来,可在类似于gru-example的模块中实现。

    最后,欢迎star和fork.

    转载于:https://my.oschina.net/sumory/blog/524459

    展开全文
  • Spring boot 集群使用websocket长连接通讯引言基础使用创建一个基础的spring boot引入websocket需要的依赖编写websocket服务代码编写websocket客户端代码测试通过集群使用解决分布式session共享集群连接数限制环境...

    引言

    最近做了一个项目,涉及到玩家之间的交互。如果使用传统的http接口,客户端会频繁的请求服务端获取最新数据,最服务器会造成压力。这种场景正好符合websocket的使用场景,网上看了看demo感觉实现起来还行,就进了这个坑,给大家讲一下。

    基础使用

    创建一个基础的spring boot

    现在市面上最常见的后端服务框架之一,spring boot。本次也是使用服务端spring boot + 客户端websocket 去实现,首先我们需要搭建一个spring boot 工程。我这里用的社区版idea,有条件的可以用专业版直接创建spring boot。创建步骤不想看的可以跳过这部分

    Ctrl + N
    创建一个Maven项目
    填写项目标识
    完成创建
    生成代码结构如图
    代码结构图

    引入websocket需要的依赖

    Pom文件依赖如图所示

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-parent</artifactId>
            <version>2.2.1.RELEASE</version>
        </parent>
    
        <groupId>com.wpx.demo</groupId>
        <artifactId>websocket-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <java.version>1.8</java.version>
            <java.compile.version>1.8</java.compile.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
    
        <dependencies>
            <!-- spring boot 启动核心依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- websocket start-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>
            <!-- websocket end-->
        </dependencies>
    
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    

    编写websocket服务代码

    代码结构图
    代码结构图
    启动类Application代码

    package com.wpx.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * 启动类
     * @date 2021/1/12 16:55
     */
    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    开启websocket服务配置类

    package com.wpx.demo.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    /**
     * 开启websocket
     * @date 2021/1/12 18:52
     */
    @Configuration
    public class WebSocketConfig {
        /**
         * ServerEndpointExporter 作用
         *
         * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
         *
         * @return
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    

    对外提供websocket服务代码

    package com.wpx.demo.server;
    
    
    import org.springframework.stereotype.Component;
    
    import javax.websocket.*;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /***
     * 长连接服务
     * @date: 2021-1-12 16:58:49
     */
    @ServerEndpoint("/webSocket/{sid}")
    @Component
    public class WebsocketServer {
        //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
        private static AtomicInteger onlineNum = new AtomicInteger();
    
        //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
        private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();
    
        //发送消息
        public void sendMessage(Session session, String message) throws IOException {
            if(session != null){
                synchronized (session) {
    //                System.out.println("发送数据:" + message);
                    session.getBasicRemote().sendText(message);
                }
            }
        }
        //给指定用户发送信息
        public void sendInfo(String userName, String message){
            Session session = sessionPools.get(userName);
            try {
                sendMessage(session, message);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        //建立连接成功调用
        @OnOpen
        public void onOpen(Session session, @PathParam(value = "sid") String userName){
            sessionPools.put(userName, session);
            addOnlineCount();
            System.out.println(userName + "加入webSocket!当前人数为" + onlineNum);
            try {
                sendMessage(session, "欢迎" + userName + "加入连接!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //关闭连接时调用
        @OnClose
        public void onClose(@PathParam(value = "sid") String userName){
            sessionPools.remove(userName);
            subOnlineCount();
            System.out.println(userName + "断开webSocket连接!当前人数为" + onlineNum);
        }
    
        //收到客户端信息
        @OnMessage
        public void onMessage(String message) throws IOException{
            message = "客户端:" + message + ",已收到";
            System.out.println(message);
            for (Session session: sessionPools.values()) {
                try {
                    sendMessage(session, message);
                } catch(Exception e){
                    e.printStackTrace();
                    continue;
                }
            }
        }
    
        //错误时调用
        @OnError
        public void onError(Session session, Throwable throwable){
            System.out.println("发生错误");
            throwable.printStackTrace();
        }
    
        public static void addOnlineCount(){
            onlineNum.incrementAndGet();
        }
    
        public static void subOnlineCount() {
            onlineNum.decrementAndGet();
        }
    }
    

    编写websocket客户端代码

    编写一个html网页,用来与服务端交互

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>WebSocket</title>
    
    </head>
    <body>
    <h3>hello socket</h3>
    <p>【userId】:<div><input id="userId" name="userId" type="text" value="10"></div>
    <p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="20"></div>
    <p>【contentText】:<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
    <p>操作:<div><button onclick="openSocket()">开启socket</button></div>
    <p>【操作】:<div><button onclick="sendMessage()">发送消息</button></div>
    </body>
    <script>
        
    
        var socket;
        function openSocket() {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else{
                console.log("您的浏览器支持WebSocket");
                //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
                var userId = document.getElementById('userId').value;
    			var socketUrl="ws://127.0.0.1:8080/webSocket/"+userId;
                console.log(socketUrl);
                if(socket!=null){
                    socket.close();
                    socket=null;
                }
                socket = new WebSocket(socketUrl);
                //打开事件
                socket.onopen = function() {
                    console.log("websocket已打开");
                    //socket.send("这是来自客户端的消息" + location.href + new Date());
                };
                //获得消息事件
                socket.onmessage = function(msg) {
                    var serverMsg = "收到服务端信息:" + msg.data;
                    console.log(serverMsg);
                    //发现消息进入    开始处理前端触发逻辑
                };
                //关闭事件
                socket.onclose = function() {
                    console.log("websocket已关闭");
                };
                //发生了错误事件
                socket.onerror = function() {
                    console.log("websocket发生了错误");
                }
            }
        }
        function sendMessage() {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else {
                // console.log("您的浏览器支持WebSocket");
                var toUserId = document.getElementById('toUserId').value;
                var contentText = document.getElementById('contentText').value;
                var msg = '{"toUserId":"'+toUserId+'","contentText":"'+contentText+'"}';
                console.log(msg);
                socket.send(msg);
            }
        }
    
        </script>
    </html>
    

    测试通过

    启动我们的服务端,打开我们客户端的界面,按F12切换到Console控制台,点击开启socket,控制台输出如图,即表示测试通过

    测试网页

    集群使用

    解决分布式session共享

    Websocket通过连接注册的session保持会话,连接开启后,用户可以和节点双向通讯。
    目前,大多数服务部署都是采用的集群部署。集群部署websocket就会出现一个问题,session共享。
    用户开启连接后,session只会保存在当前节点中,如下图所示,如果用户1和用户2分别注册在了两个节点,用户1想给用户2发送信息,但因为用户2的session没有在SERVER 1中,SERVER 1无法向用户2发送信息。

    集群websocket问题
    如何实现跨节点的session通讯,就是我们在集群部署中要解决的大问题
    网上搜了好多session共享的实现,大多都是通过redis 的pub/sub 实现。本着各司其职的思想,我们的项目中有MQ,所以,这里就用MQ的消息队列实现session共享通讯。
    如下图所示,用户每次消息发送到服务端,服务端找不到用户2的session,转向发送到MQ,通过MQ的广播消费,实现跨节点的消息通讯

    集群websocket解决方案
    实现上述MQ消费,需要针对节点去创建队列名。每一个节点启动时创建自己的队列,服务销毁时将队列一并销毁

    附MQ配置类

    
    import org.springframework.amqp.core.Exchange;
    import org.springframework.amqp.core.ExchangeBuilder;
    import org.springframework.amqp.rabbit.connection.ConnectionFactory;
    import org.springframework.amqp.rabbit.core.RabbitAdmin;
    import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
    import org.springframework.amqp.support.converter.MessageConverter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    
    /***
     * MQ基础配置类,声明交换机和rabbitAdmin
     * @date: 2021-1-12 16:58:49
     */
    @Configuration
    public class CommonRabbitMQConfig {
        /**
         * 发送的消息体转换json序列化存储到mq中
         *
         * @return
         */
        @Bean
        public MessageConverter messageConverter() {
            return new Jackson2JsonMessageConverter();
        }
    
    
        @Bean
        public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
            RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
            rabbitAdmin.setAutoStartup(true);
            return rabbitAdmin;
        }
        /***
         * 声明交换机
         * 此交换机在没有被使用时会自动删除,内容全部遗弃,容器启动时会自动创建
         */
        @Bean(name = "websocketFanoutExchange")
        public Exchange topicExchange() {
            return ExchangeBuilder.fanoutExchange("WEBSOCKET_FANOUT_EXCHANGE"
    ).durable(true).build();
        }
    
    }
    

    声明队列的配置类,代码中的hostname根据自己的节点唯一标识注入

    
    import org.springframework.amqp.core.*;
    import org.springframework.amqp.rabbit.core.RabbitAdmin;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * rabbitMq config
     * 队列和交换机在没有消费者链接时会被删除,消息内容全部丢弃,消费者连接时会自动创建。
     * @date  2021-1-12 16:58:49
     * @version 1.0
     */
    @Configuration
    public class AnswerPkRabbitMQConfig {
    
        @Autowired
        RabbitAdmin rabbitAdmin;
    
        /**
         * hostname 机器名
         * 容器启动时会注入hostname
         */
        @Value("${hostname}")
        String hostname;
    
        /***
         * 声明队列
         */
        @Bean(name = "websocketQueue")
        public Queue websocketQueue(){
            return QueueBuilder.durable("WEBSOCKET_QUEUE_"+hostname).autoDelete().build();
        }
    
        /***
         * 队列绑定到交换机上
         */
        @Bean
        public Binding websocketFanoutExchangeBinding(@Qualifier("websocketQueue")Queue queue,
                                                 @Qualifier("websocketFanoutExchange")Exchange exchange){
            return BindingBuilder.bind(queue).to(exchange).with(MQConstants.WEBSOCKET_ROUTING_KEY).noargs();
        }
    
        @Bean
        public void createDeclare(){
            rabbitAdmin.declareQueue(websocketQueue());
        }
    }
    

    Mq发送和消费的代码不贴了,主要就是在操作session之前隔了一个mq,自行实现吧

    集群连接数限制

    在实际生产环境中,要防止websocket被连接撑爆节点服务。和http连接数限制一样,websocket也要控制长连接数量以保证服务的安全
    可以通过过滤器实现websocket连接中的握手校验,当达到系统设置的阈值时,拒绝握手
    以redis的incr实现连接数量的监控,当用户成功发起握手,触发 1.3 中的WebSocketServer中的OnOpen,对redis进行incr +1操作,当用户断开连接触发OnClose,对redis进程incr -1操作。在过滤器中判断redis值是否到达阈值来判断是否成功握手。

    附过滤器代码,代码中的preHandle为判断redis值是否到达阈值,自行实现

    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.http.MediaType;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    import sdk.common.util.JsonUtils;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.util.Objects;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 借过滤器实现对websocket的拦截
     * @date: 2020-12-15 17:21:40
     */
    @Slf4j
    @Component
    public class BodyRequestWrapperFilter extends OncePerRequestFilter {
    
        @Autowired
        private RedisUtil redisUtil;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
            try {
                boolean b = preHandle(request, response);
                if(!b){
                    return;
                }
            }catch (Exception e){
                log.error("doFilterInternal",e);
                responseError(response,"doFilterInternal"));
                return;
            }
            chain.doFilter(request, response);
        }
    }
    

    环境搭建

    Nginx配置

    如果你的项目用到了nginx,需要对nginx进行配置以支持websocket转发,配置如下表,在你的nginx配置中增加websocket的location

     location /webSocket/ {
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header  Connection "upgrade";
                    proxy_pass http://ip:host/uri;
            }
    

    如果你的项目中有F5,那么你的F5可能也需要支持websocket,去找你的网络人员需求支持吧

    Gateway配置

    如果你的项目中用到了gateway网关,那么你需要对你的gateway配置websocket转发以支持长连接,配置如下

    spring.cloud.gateway.routes[0].id =websocket-demo
    spring.cloud.gateway.routes[0].uri = lb:ws://websocket-demo
    spring.cloud.gateway.routes[0].predicates[0] = Path=/webSocket/**
    spring.cloud.gateway.routes[0].filters[0] = StripPrefix=0
    

    蓝绿发布irules配置

    如果你的项目中采用了irules蓝绿发布,那么irules规则也需要对websocket进行支持,针对传统http接口的irules规则不适用与websocket,我们采用url加标识,nginx做规则转发实现的蓝绿发布,时间紧我就不贴代码了

    进阶

    Session防止长时间连接

    Spring这一套websocket中,可以对session设置最大空闲时间,即当此连接闲置过长的时候,服务端会断开连接

    session.setMaxIdleTimeout(900000);//15分钟
    

    但这个并不能一劳永逸,如果你的服务遭到了恶意攻击,并没有将连接空闲,且你的服务设计并没有超过一段时间之外的支持,那么你就需要监听你的连接,当达到你设置的阈值时,强制断开连接。你需要设计一个守护线程,定时监控你服务中的session,当达到你设置的阈值,调用session.close();方法对非法的超时连接进行关闭

    展开全文
  • Kubernetes集群应用ipvs后长连接失效问题 最近在k8s集群升级 采取ipvs代理 部署后在调用gRPC服务时小概率出现Connection reset by peer的错误。 简单理解下:gRPC是基于HTTP/2协议的,gRPC的client和server在交互时...

    Kubernetes集群应用ipvs后长连接失效问题

    最近在k8s集群升级 采取ipvs代理 部署后在调用gRPC服务时小概率出现Connection reset by peer的错误。

    简单理解下:gRPC是基于HTTP/2协议的,gRPC的client和server在交互时会建立多条连接,为了性能,这些连接都是长连接并且是一直保活着的。

    参考问题:

    github有一篇关于docker swarm的连接网络长连接问题https://github.com/moby/moby/issues/31208

    究其原因是因为系统默认的TCP时间和ipvs的默认时间不同

    [root@node108 ~]# sysctl -a | grep net.ipv4.tcp_keepalive_time
    net.ipv4.tcp_keepalive_time = 7200
    # 一般操作系统默认时间为7200秒 2个小时
    [root@qtxian-k8s-master-1 ~]# ipvsadm -l --timeout
    Timeout (tcp tcpfin udp): 900 120 300
    # k8s使用的ipvs默认后的默认超时为900秒 15分钟
    

    TCP链接交互路径

    client --> linux_server --> k8s_proxy(ipvs) --> realy_pod_server
    

    在这个路径过程中 操作系统的连接断开时间会远远大于ipvs断开连接,所以会导致报错Connection reset by peer

    #修改以下内核参数

    # 只要设置时间比ipvs短就可以保证连接不超
    net.ipv4.tcp_keepalive_time=600
    net.ipv4.tcp_keepalive_intvl=30
    net.ipv4.tcp_keepalive_probes=10
    
    net.ipv4.ip_forward=1
    net.ipv4.tcp_tw_reuse=0 #允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
    net.ipv4.tcp_tw_recycle=0 #允许将TIME-WAIT sockets的快速回收,默认为0,表示关闭。
    
    [root@qtxian-k8s-master-1 ~]# sysctl -p
    
    展开全文
  • 移动应用软件有一些是长连接的,而服务器端的集群部署,有的是通过F5把每一次网络请求随机转发到集群中某一台应用服务器上的。要是想把某消息通过集群环境发送到移动端,那么集群中网络请求的随机转发与移动端长连接...

    移动应用软件有一些是长连接的,而服务器端的集群部署,有的是通过F5把每一次网络请求随机转发到集群中某一台应用服务器上的。要是想把某消息通过集群环境发送到移动端,那么集群中网络请求的随机转发与移动端长连接的特性会有矛盾。

    本文以Androidpn(网络协议为XMPP)为例,介绍一种后台集群部署解决移动端与服务器间长连接问题的方法。

     

    网络连接示意图:


    (上图省去了APN服务器与IME客户端之间的网络层)

    网络消息路由:F5APN(x):外部的网络请求会随机发送到集群中的一台APN服务器上。XMPP消息路由:APN01~IME01, APN(x)~IME(x):设备(x)只与一台服务器APN(x)保持长连接,是一一对应的关系。

     

    举例说明集群遇到的问题:

        假如APN01与IMEO1保持连接。当有外部网络请求、要向IME01发送消息时,F5首先接到网络请求,并将此请求随机转发给APN服务器,可能是APN02。而此时APN02服务器与IME01没有连接,此请求就无法发送出去了。


    解决方法:

        引入Rabbitmq 消息中间件 ,用于APN服务器间的消息转发、通信。每一台APN服务器,通过rabbitmq客户端与rabbitmq服务器进行通信。


    引入Memcache存服务器 ,胜于长连接客户端面与服务器的字典表,每次移动端登陆服务器应用时,就把移动端标识与其连接的服务器标识保存在缓存。每一台APN服务器通过MemCache客户端与MemCache服务器进行通信。


    接着上一个假设中的问题,APN02在接收到网络请求时,首先拿通过MemCache查询该移动端连接的APN服务器是哪个(MemCache中记录APN01与IMEO1保持连接),若是本机则处理消息,否则就将网络请求的消息发送到Rabbitmq服务器(rabbitmq路由机制及设计方法不是本文的重点),Rabbitmq服务器根据路由,将消息发送给与IMEO1保持连接的APN01服务器上的Rabbitmq客户端。APN01上的Rabbitmq客户端收到消息后进行处理,再调用APNO1与IME01的连接,将消息发送给设备IME01。


    发送集群非本节点消息流程图:


    附:Androidpn是韩国Sehwan No写的开源消息推送项目,很多大公司都用这个消息推送方式构建自己的消息推送服务,缺点是导致客户端比较耗电。通信机制分别由客户端和服务器完成。




    展开全文
  • mina ---- 分布式环境下长连接session共享的可行解决方案
  • 客户端连接和服务端连接的超时时间,这就有一个问题,因为客户端监听消息队列需要长久保持一个连接,但是haproxy却在客户端连接一段时间没有消息后就会断开连接,这样就没办法保证我的队列监听程序能够持续连接,这...
  • 启动生产者(客户端) 集群长连接 前面的实践中已经介绍过各个节点通过组播,互相发现彼此的tcp端口 三次握手 观测中发现双方一直在互相发数据(互相告诉当前节点的情况) 数据传输长连接 112节点未通过Event Bus传输...
  • 基于长连接的推送,集群,负载均衡

    千次阅读 2016-08-12 21:23:35
    基于长连接的推送,集群,负载均衡
  • java本地连接zk集群

    2020-12-19 14:31:30
    添加依赖后, pom.xml 这个样子: <project xmlns=...
  • 我做了一个消息推送系统,客户端与服务器建立长连接,但是由于客户端越来越多,服务器怎样做集群
  • 一、背景 近期在实现websocket长连接集群,需要用到集群通讯...
  • JestClient之HTTP长连接判断

    千次阅读 2018-05-16 18:09:55
    判断jestclient连接ES集群时,是HTTP短连接,还是HTTP长连接。 为什么要判断: 因为如果是短连接,我们需要连接池控制连接数,防止大量请求,重复创建连接导致系统崩溃;如果是长连接,我们需要注意如果长连接断开...
  • 就是后台跟前端浏览器要保持长连接,后台主动往前台推数据。 网上查了下,websocket stomp协议处理这个很简单。尤其是跟springboot 集成。 但是由于开始是单机玩的,很顺利。 但是后面部署到生产搞集群的话,就会...
  • centos LB负载均衡集群 三种模式区别 LVS/NAT 配置 LVS/DR 配置 LVS/DR + keepalived配置 nginx ip_hash 实现长连接 LVS是四层LB 注意down掉网卡的方法 nginx效率没有LVS高 ipvsadm命令集 测试LVS方法第三十三节课 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 811
精华内容 324
关键字:

长连接集群