精华内容
下载资源
问答
  • springboot+websocket集群session共享

    千次阅读 2018-07-06 15:23:26
     # 下面处理websocket  # Origin不能少,否则会爆403  proxy_http_version 1.1;  proxy_set_header Upgrade $http_upgrade;    proxy_set_header Connection "upgrade";  proxy_set_header Origin ...

    1.安装redis(省略)

    外网访问权限更改redis.conf 文件

    bind 127.0.0.1
    
    protected-mode yes

    更改为

    # bind 127.0.0.1
    
    protected-mode no

    启动redis命令:    ./redis-server ../redis.conf

    2.安装nginx(省略)

    更改nginx.conf 文件

        upstream springboots{
                ip_hash;	#同一个ip负载到同一个服务器	
    	    server 192.168.1.204:8001;
    	    server 192.168.1.204:8002;
        }
        server {
            listen       80;
            server_name  localhost;
    
            location / {
                proxy_pass http://springboots;             # 下面处理websocket            # Origin不能少,否则会爆403
    	    proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
        	    proxy_set_header Connection "upgrade";
    	    proxy_set_header Origin "";
            }
    

    3.stringboot 代码

    Maven配置

            <!-- springboot - Redis -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <!--spring session 与redis应用基本环境配置,需要开启redis后才可以使用,不然启动Spring boot会报错 -->
            <dependency>
                <groupId>org.springframework.session</groupId>
                <artifactId>spring-session-data-redis</artifactId>
            </dependency>

    Application.properties

    application.properties 
    特别注意此处需要spring.session.store-type

    ########################  Redis ###################################
    spring.redis.database=0
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.password=
    # 连接池最大连接数
    spring.redis.pool.max-active=8
    spring.redis.pool.max-wait=-1
    # 连接池中的最大空闲连接
    spring.redis.pool.max-idle=8
    # 连接池中的最小空闲连接
    spring.redis.pool.min-idle=0
    # 连接超时时间(毫秒)
    spring.redis.timeout=0
    
    spring.session.store-type=redis

    Config

    /**
     * @description 分布式Session,使用spring.session.store-type=redis自动配置
     */
    @EnableRedisHttpSession(maxInactiveIntervalInSeconds= 1800)
    public class SessionConfig {
    
    }
    
    • 1
    • 2








    展开全文
  • 看了网上很多springwebsocket的帖子,关于用户的session都是存在map或List里 比如 private static Map, WebSocketSession> users = new ConcurrentHashMap,WebSocketSession>(); 但是在做集群的时候,用户肯定不会...
  • Nginx-tomcat 负载均衡配置 以及redis-session共享 websocket集群, Nginx是一个高性能的HTTP和反向代理web服务器,返现代理指代表外部网络用户向内部服务器发出请求,即接收来自Internet上用户的连接请求,并将...

    Nginx-tomcat 负载均衡配置 以及redis-session共享 websocket集群,

    Nginx是一个高性能的HTTP反向代理web服务器,返现代理指代表外部网络用户向内部服务器发出请求,即接收来自Internet上用户的连接请求,并将这些请求转发给内部网络上的服务器,然后将从内部服务器上得到的响应返回给Internet上请求连接的客户:执行反向代理服务的服务器称为反向代理服务器

    Nginx的负载均衡策略大概是有六种,即weight权重,ip_hash根据ip分配的方式,least_conn最少连接方式,轮询(默认方式),fair(第三方)响应时间,url_hash(第三方)根据url来分配

    1. 轮询故名思意就是说的会随机进行分配
    2. 权重weight在轮询策略的基础上指定轮询的几率(具体配置请参考百度)
    3. Ip_hash指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。
    4. least_conn把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。

    第三方的需要安装第三方插件不做具体阐述(如果有需要请参考百度)

    首先说一下nginx反向代理tomcat集群

    打开nginx-1.16.1/conf/nginx.conf

    配置好之后,nginx -s reload 重新加载nginx配置即可

    访问时只需要localhsot/工程名称,即可

     

    接下来说一下redis-session 共享

    之前工程部署为单节点,所以没必要考虑session共享的问题,但是根据业务需要改为双节点部署,所以就需要考虑session共享的问题

    废话不多说,直接上代码

    首先在pom.xml中添加

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-redis</artifactId>
        <version>1.4.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
        <version>2.0.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session</artifactId>
        <version>1.3.3.RELEASE</version>
    </dependency>

    然后redissessionconfig类

    @Configuration
    @EnableRedisHttpSession(redisNamespace = "newseditor")
    public class RedisSessionConfig {
    
        @Bean
        public static ConfigureRedisAction configureRedisAction() {
            return ConfigureRedisAction.NO_OP;
        }
    }

    在配置文件中,添加redis连接信息

    spring.redis.database=1		//可指定库,默认为db0
    spring.redis.host=127.0.0.1
    spring.redis.password=
    spring.redis.port=6379

    最后写一个测试类

    @RequestMapping("/")
    public class RedisSessionController {
        @RequestMapping(value = "/query", method = RequestMethod.GET)
        public Object sessions (HttpServletRequest request){
            Map<String, Object> map = new HashMap<>();
            map.put("sessionId", request.getSession().getId());
            map.put("testKey", request.getSession().getAttribute("testKey"));
            return map;
        }
    }

    然后浏览器调用query测试类,就会发现两个不同端口的同一个工程所访问到的sessionid为同一个

     

    Websocket集群以及nginx反向代理Websocket集群

    首先了解一下websocket是什么,为什么会用到websocket?(找度娘)

    https://baike.baidu.com/item/WebSocket/1953845?fr=aladdin

    Websocket集群搭建的代码就不给贴出来了自行百度吧

     

     

    正在进行中的开源项目:https://github.com/canxin0523/thesixsectorTeam

    qq技术交流群:602953488

    本人微信公众号,不写技术,只写一些经典语录,欢迎关注

    展开全文
  • 大家都知道集群中会有websocketsession不一致的问题,会造成消息推送不成功,用redis发布订阅模式解决websocket session问题其实算不上session共享,因为websocketsession没法序列化存到redis,只能换一种方式实现...

    每天分享java干货小知识,欢迎关注转发评论交流,每天一点进步!

    9e51b8341e9319bf02570974c12ceace.png

    大家都知道集群中会有websocketsession不一致的问题,会造成消息推送不成功,用redis发布订阅模式解决websocket session问题其实算不上session共享,因为websocketsession没法序列化存到redis,只能换一种方式实现,模拟session共享

    • 在redis的配置类里面加上,如下面(redis的基本配置我不截图了,在我的历史文章有怎么整合redis的 自己去看下)
    395fd3ea8c3bd09dff27950fe413de90.png

    订阅webSocketSessionShare的频道消息,监控到这个频道有消息后,会调用消息监听适配器listenerAdapter, listenerAdapter会传入WebSocketRedisMsgInterface这个业务接口(这个接口就是处理业务逻辑的,有消息后你会做什么),以及通过反射调用WebSocketRedisMsgInterface的方法 receiveMessage。

    • 下面是WebSocketRedisMsgInterface是业务接口
    d0141abca1017daa8f2183b49a353693.png
    • 我们让我们的websocket处理类实现这个WebSocketRedisMsgInterface接口
    afc4cea615eb1b2cc59c107c53aacb7f.png
    • 实现这个接口的逻辑就是具体要发送消息的操作(监控到redis这个频道有消息就会触发)
    5f1c10878d7d756e72793c95965caf17.png
    • 下面是发送消息先存到redis的这个频道中(之前是直接推送到前端,会有sessiong不一致问题)
    aba0b7c5ff00ee22e6faba2828f14a5b.png

    好了,不知道各位老铁看懂没 欢迎交流!

    展开全文
  • 最近做项目时遇到了需要多用户之间通信的问题,涉及到了WebSocket握手请求,以及集群中WebSocket Session共享的问题。 期间我经过了几天的研究,总结出了几个实现分布式WebSocket集群的办法,从zuul到spring cloud ...

    问题起因

    最近做项目时遇到了需要多用户之间通信的问题,涉及到了WebSocket握手请求,以及集群中WebSocket Session共享的问题。

    期间我经过了几天的研究,总结出了几个实现分布式WebSocket集群的办法,从zuul到spring cloud gateway的不同尝试,总结出了这篇文章,希望能帮助到某些人,并且能一起分享这方面的想法与研究。

    以下是我的场景描述

    • 资源:4台服务器。其中只有一台服务器具备ssl认证域名,一台redis+mysql服务器,两台应用服务器(集群)
    • 应用发布限制条件:由于场景需要,应用场所需要ssl认证的域名才能发布。因此ssl认证的域名服务器用来当api网关,负责https请求与wss(安全认证的ws)连接。俗称https卸载,用户请求https域名服务器(eg:https://oiscircle.com/xxx),但真实访问到的是http+ip地址的形式。只要网关配置高,能handle多个应用
    • 需求:用户登录应用,需要与服务器建立wss连接,不同角色之间可以单发消息,也可以群发消息
    • 集群中的应用服务类型:每个集群实例都负责http无状态请求服务与ws长连接服务

    系统架构图

    clipboard.png

    在我的实现里,每个应用服务器都负责http and ws请求,其实也可以将ws请求建立的聊天模型单独成立为一个模块。从分布式的角度来看,这两种实现类型差不多,但从实现方便性来说,一个应用服务http+ws请求的方式更为方便。下文会有解释

    本文涉及的技术栈

    • Eureka 服务发现与注册
    • Redis Session共享
    • Redis 消息订阅
    • Spring Boot
    • Zuul 网关
    • Spring Cloud Gateway 网关
    • Spring WebSocket 处理长连接
    • Ribbon 负载均衡
    • Netty 多协议NIO网络通信框架
    • Consistent Hash 一致性哈希算法

    相信能走到这一步的人都了解过我上面列举的技术栈了,如果还没有,可以先去网上找找入门教程了解一下。下面的内容都与上述技术相关,题主默认大家都了解过了...
    这里是描述一致性Hash算法最易懂的文章传送门

    技术可行性分析

    下面我将描述session特性,以及根据这些特性列举出n个解决分布式架构中处理ws请求的集群方案

    WebSocketSession与HttpSession
    在Spring所集成的WebSocket里面,每个ws连接都有一个对应的session:WebSocketSession,在Spring WebSocket中,我们建立ws连接之后可以通过类似这样的方式进行与客户端的通信:

    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
       System.out.println("服务器接收到的消息: "+ message );
       //send message to client
       session.sendMessage(new TextMessage("message"));
    }

    那么问题来了:ws的session无法序列化到redis,因此在集群中,我们无法将所有WebSocketSession都缓存到redis进行session共享。每台服务器都有各自的session。于此相反的是HttpSession,redis可以支持httpsession共享,但是目前没有websocket session共享的方案,因此走redis websocket session共享这条路是行不通的
    有的人可能会想:我可不可以将sessin关键信息缓存到redis,集群中的服务器从redis拿取session关键信息然后重新构建websocket session...我只想说这种方法如果有人能试出来,请告诉我一声...

    以上便是websocket session与http session共享的区别,总的来说就是http session共享已经有解决方案了,而且很简单,只要引入相关依赖:spring-session-data-redisspring-boot-starter-redis,大家可以从网上找个demo玩一下就知道怎么做了。而websocket session共享的方案由于websocket底层实现的方式,我们无法做到真正的websocket session共享。

    解决方案的演变

    Netty与Spring WebSocket

    刚开始的时候,我尝试着用netty实现了websocket服务端的搭建。在netty里面,并没有websocket session这样的概念,与其类似的是channel,每一个客户端连接都代表一个channel。前端的ws请求通过netty监听的端口,走websocket协议进行ws握手连接之后,通过一些列的handler(责链模式)进行消息处理。与websocket session类似地,服务端在连接建立后有一个channel,我们可以通过channel进行与客户端的通信

       /**
        * TODO 根据服务器传进来的id,分配到不同的group
        */
       private static final ChannelGroup GROUP = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);
    
       @Override
       protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
           //retain增加引用计数,防止接下来的调用引用失效
           System.out.println("服务器接收到来自 " + ctx.channel().id() + " 的消息: " + msg.text());
           //将消息发送给group里面的所有channel,也就是发送消息给客户端
           GROUP.writeAndFlush(msg.retain());
       }

    那么,服务端用netty还是用spring websocket?以下我将从几个方面列举这两种实现方式的优缺点

    • 使用netty实现websocket

      玩过netty的人都知道netty是的线程模型是nio模型,并发量非常高,spring5之前的网络线程模型是servlet实现的,而servlet不是nio模型,所以在spring5之后,spring的底层网络实现采用了netty。如果我们单独使用netty来开发websocket服务端,速度快是绝对的,但是可能会遇到下列问题:
      1.与系统的其他应用集成不方便,在rpc调用的时候,无法享受springcloud里feign服务调用的便利性
      2.业务逻辑可能要重复实现
      3.使用netty可能需要重复造轮子
      4.怎么连接上服务注册中心,也是一件麻烦的事情
      5.restful服务与ws服务需要分开实现,如果在netty上实现restful服务,有多麻烦可想而知,用spring一站式restful开发相信很多人都习惯了。

    • 使用spring websocket实现ws服务

      spring websocket已经被springboot很好地集成了,所以在springboot上开发ws服务非常方便,做法非常简单
      第一步:添加依赖

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>

      第二步:添加配置类

      @Configuration
      public class WebSocketConfig implements WebSocketConfigurer {
      @Override
      public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
          registry.addHandler(myHandler(), "/")
              .setAllowedOrigins("*");
      }
      
      @Bean
       public WebSocketHandler myHandler() {
           return new MessageHandler();
       }
      }

      第三步:实现消息监听类

      @Component
      @SuppressWarnings("unchecked")
      public class MessageHandler extends TextWebSocketHandler {
         private List<WebSocketSession> clients = new ArrayList<>();
      
         @Override
         public void afterConnectionEstablished(WebSocketSession session) {
             clients.add(session);
             System.out.println("uri :" + session.getUri());
             System.out.println("连接建立: " + session.getId());
             System.out.println("current seesion: " + clients.size());
         }
      
         @Override
         public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
             clients.remove(session);
             System.out.println("断开连接: " + session.getId());
         }
      
         @Override
         protected void handleTextMessage(WebSocketSession session, TextMessage message) {
             String payload = message.getPayload();
             Map<String, String> map = JSONObject.parseObject(payload, HashMap.class);
             System.out.println("接受到的数据" + map);
             clients.forEach(s -> {
                 try {
                     System.out.println("发送消息给: " + session.getId());
                     s.sendMessage(new TextMessage("服务器返回收到的信息," + payload));
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
             });
         }
      }

      从这个demo中,使用spring websocket实现ws服务的便利性大家可想而知了。为了能更好地向spring cloud大家族看齐,我最终采用了spring websocket实现ws服务。
      因此我的应用服务架构是这样子的:一个应用既负责restful服务,也负责ws服务。没有将ws服务模块拆分是因为拆分出去要使用feign来进行服务调用。第一本人比较懒惰,第二拆分与不拆分相差在多了一层服务间的io调用,所以就没有这么做了。

    从zuul技术转型到spring cloud gateway

    要实现websocket集群,我们必不可免地得从zuul转型到spring cloud gateway。原因如下:

    zuul1.0版本不支持websocket转发,zuul 2.0开始支持websocket,zuul2.0几个月前开源了,但是2.0版本没有被spring boot集成,而且文档不健全。因此转型是必须的,同时转型也很容易实现。
    
    在gateway中,为了实现ssl认证和动态路由负载均衡,yml文件中以下的某些配置是必须的,在这里提前避免大家采坑
    server:
      port: 443
      ssl:
        enabled: true
        key-store: classpath:xxx.jks
        key-store-password: xxxx
        key-store-type: JKS
        key-alias: alias
    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          httpclient:
            ssl:
              handshake-timeout-millis: 10000
              close-notify-flush-timeout-millis: 3000
              close-notify-read-timeout-millis: 0
              useInsecureTrustManager: true
          discovery:
            locator:
              enabled: true
              lower-case-service-id: true
          routes:
          - id: dc
            uri: lb://dc
            predicates:
            - Path=/dc/**
          - id: wecheck
            uri: lb://wecheck
            predicates:
            - Path=/wecheck/**

    如果要愉快地玩https卸载,我们还需要配置一个filter,否则请求网关时会出现错误not an SSL/TLS record

    @Component
    public class HttpsToHttpFilter implements GlobalFilter, Ordered {
      private static final int HTTPS_TO_HTTP_FILTER_ORDER = 10099;
      @Override
      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
          URI originalUri = exchange.getRequest().getURI();
          ServerHttpRequest request = exchange.getRequest();
          ServerHttpRequest.Builder mutate = request.mutate();
          String forwardedUri = request.getURI().toString();
          if (forwardedUri != null && forwardedUri.startsWith("https")) {
              try {
                  URI mutatedUri = new URI("http",
                          originalUri.getUserInfo(),
                          originalUri.getHost(),
                          originalUri.getPort(),
                          originalUri.getPath(),
                          originalUri.getQuery(),
                          originalUri.getFragment());
                  mutate.uri(mutatedUri);
              } catch (Exception e) {
                  throw new IllegalStateException(e.getMessage(), e);
              }
          }
          ServerHttpRequest build = mutate.build();
          ServerWebExchange webExchange = exchange.mutate().request(build).build();
          return chain.filter(webExchange);
      }
    
      @Override
      public int getOrder() {
          return HTTPS_TO_HTTP_FILTER_ORDER;
      }

    }

    这样子我们就可以使用gateway来卸载https请求了,到目前为止,我们的基本框架已经搭建完毕,网关既可以转发https请求,也可以转发wss请求。接下来就是用户多对多之间session互通的通讯解决方案了。接下来,我将根据方案的优雅性,从最不优雅的方案开始讲起。

    session广播

    这是最简单的websocket集群通讯解决方案。场景如下:
    教师A想要群发消息给他的学生们

    1. 教师的消息请求发给网关,内容包含{我是教师A,我想把xxx消息发送我的学生们}
    2. 网关接收到消息,获取集群所有ip地址,逐个调用教师的请求
    3. 集群中的每台服务器获取请求,根据教师A的信息查找本地有没有与学生关联的session,有则调用sendMessage方法,没有则忽略请求

    clipboard.png

    session广播实现很简单,但是有一个致命缺陷:计算力浪费现象,当服务器没有消息接收者session的时候,相当于浪费了一次循环遍历的计算力,该方案在并发需求不高的情况下可以优先考虑,实现很容易。

    spring cloud中获取服务集群中每台服务器信息的方法如下
    @Resource
    private EurekaClient eurekaClient;
    
    Application app = eurekaClient.getApplication("service-name");
    //instanceInfo包括了一台服务器ip,port等消息
    InstanceInfo instanceInfo = app.getInstances().get(0);
    System.out.println("ip address: " + instanceInfo.getIPAddr());
    服务器需要维护关系映射表,将用户的id与session做映射,session建立时在映射表中添加映射关系,session断开后要删除映射表内关联关系

    一致性哈希算法实现(本文的要点)

    这种方法是本人认为最优雅的实现方案,理解这种方案需要一定的时间,如果你耐心看下去,相信你一定会有所收获。再强调一次,不了解一致性哈希算法的同学请先看这里,现先假设哈希环是顺时针查找的。

    首先,想要将一致性哈希算法的思想应用到我们的websocket集群,我们需要解决以下新问题:

    1. 集群节点DOWN,会影响到哈希环映射到状态是DOWN的节点。
    2. 集群节点UP,会影响到旧key映射不到对应的节点。
    3. 哈希环读写共享。
    在集群中,总会出现服务UP/DOWN的问题。

    针对节点DOWN的问题分析如下:

    一个服务器DOWN的时候,其拥有的websocket session会自动关闭连接,并且前端会收到通知。此时会影响到哈希环的映射错误。我们只需要当监听到服务器DOWN的时候,删除哈希环上面对应的实际结点和虚结点,避免让网关转发到状态是DOWN的服务器上。
    实现方法:在eureka治理中心监听集群服务DOWN事件,并及时更新哈希环。

    针对节点UP的问题分析如下:

    现假设集群中有服务CacheB上线了,该服务器的ip地址刚好被映射到key1和cacheA之间。那么key1对应的用户每次要发消息时都跑去CacheB发送消息,结果明显是发送不了消息,因为CacheB没有key1对应的session。

    clipboard.png

    此时我们有两种解决方案。
    方案A简单,动作大:
    eureka监听到节点UP事件之后,根据现有集群信息,更新哈希环。并且断开所有session连接,让客户端重新连接,此时客户端会连接到更新后的哈希环节点,以此避免消息无法送达的情况。
    方案B复杂,动作小:
    我们先看看没有虚拟节点的情况,假设CacheCCacheA之间上线了服务器CacheB。所有映射在CacheCCacheB的用户发消息时都会去CacheB里面找session发消息。也就是说CacheB一但上线,便会影响到CacheCCacheB之间的用户发送消息。所以我们只需要将CacheA断开CacheCCacheB的用户所对应的session,让客户端重连。

    clipboard.png

    接下来是有虚拟节点的情况,假设浅色的节点是虚拟节点。我们用长括号来代表某段区域映射的结果属于某个Cache。首先是C节点未上线的情况。图大家应该都懂吧,所有B的虚拟节点都会指向真实的B节点,所以所有B节点逆时针那一部分都会映射到B(因为我们规定哈希环顺时针查找)。

    clipboard.png

    接下来是C节点上线的情况,可以看到某些区域被C占领了。

    clipboard.png

    由以上情况我们可以知道:节点上线,会有许多对应虚拟节点也同时上线,因此我们需要将多段范围key对应的session断开连接(上图红色的部分)。具体算法有点复杂,实现的方式因人而异,大家可以尝试一下自己实现算法。

    哈希环应该放在哪里?

    1. gateway本地创建并维护哈希环。当ws请求进来的时候,本地获取哈希环并获取映射服务器信息,转发ws请求。这种方法看上去不错,但实际上是不太可取的,回想一下上面服务器DOWN的时候只能通过eureka监听,那么eureka监听到DOWN事件之后,需要通过io来通知gateway删除对应节点吗?显然太麻烦了,将eureka的职责分散到gateway,不建议这么做。
    2. eureka创建,并放到redis共享读写。这个方案可行,当eureka监听到服务DOWN的时候,修改哈希环并推送到redis上。为了请求响应时间尽量地短,我们不可以让gateway每次转发ws请求的时候都去redis取一次哈希环。哈希环修改的概率的确很低,gateway只需要应用redis的消息订阅模式,订阅哈希环修改事件便可以解决此问题。
    至此我们的spring websocket集群已经搭建的差不多了,最重要的地方还是一致性哈希算法。现在有最后一个技术瓶颈,网关如何根据ws请求转发到指定的集群服务器上?答案在负载均衡。spring cloud gateway或zuul都默认集成了ribbon作为负载均衡,我们只需要根据建立ws请求时客户端发来的user id,重写ribbon负载均衡算法,根据user id进行hash,并在哈希环上寻找ip,并将ws请求转发到该ip便完事了。流程如下图所示:

    clipboard.png

    接下来用户沟通的时候,只需要根据id进行hash,在哈希环上获取对应ip,便可以知道与该用户建立ws连接时的session存在哪台服务器上了!

    spring cloud Finchley.RELEASE 版本中ribbon未完善的地方

    题主在实际操作的时候发现了ribbon两个不完善的地方......

    1. 根据网上找的方法,继承AbstractLoadBalancerRule重写负载均衡策略之后,多个不同应用的请求变得混乱。假如eureka上有两个service A和B,重写负载均衡策略之后,请求A或B的服务,最终只会映射到其中一个服务上。非常奇怪!可能spring cloud gateway官网需要给出一个正确的重写负载均衡策略的demo。
    2. 一致性哈希算法需要一个key,类似user id,根据key进行hash之后在哈希环上搜索并返回ip。但是ribbon没有完善choose函数的key参数,直接写死了default

    clipboard.png

    难道这样子我们就没有办法了吗?其实还有一个可行并且暂时可替代的办法!
    如下图所示,客户端发送一个普通的http请求(包含id参数)给网关,网关根据id进行hash,在哈希环中寻找ip地址,将ip地址返回给客户端,客户端再根据该ip地址进行ws请求。

    clipboard.png

    由于ribbon未完善key的处理,我们暂时无法在ribbon上实现一致性哈希算法。只能间接地通过客户端发起两次请求(一次http,一次ws)的方式来实现一致性哈希。希望不久之后ribbon能更新这个缺陷!让我们的websocket集群实现得更优雅一点。

    后记

    以上便是我这几天探索的结果。期间遇到了许多问题,并逐一解决难题,列出两个websocket集群解决方案。第一个是session广播,第二个是一致性哈希。这两种方案针对不同场景各有优缺点,本文并未用到ActiveMQ,Karfa等消息队列实现消息推送,只是想通过自己的想法,不依靠消息队列来简单地实现多用户之间的长连接通讯。希望能为大家提供一条不同于寻常的思路。

    展开全文
  • 问题起因最近做项目时遇到了需要多用户之间通信的问题,涉及到了WebSocket握手请求,以及集群中WebSocket Session共享的问题。期间我经过了几天的研究,总结出了几个实现分布式WebSocket集群的办法,从zuul到spring ...
  • 分布式WebSocket集群解决方案

    千次阅读 2018-12-08 15:14:50
    最近做项目时遇到了需要多用户之间通信的问题,涉及到了WebSocket握手请求,以及集群中WebSocket Session共享的问题。 期间我经过了几天的研究,总结出了几个实现分布式WebSocket集群的办法,从zuul到spring cloud ...
  • 环境:nginx+Tomcat服务器ABC 问题:如果用户 1 访问由服务器 A ...(socket 集群由 nginx 分配)由于用户 1,2没法决定自己访问哪一台服务器,所以不可能实时同时访问一台服务器。 情况一:用户1,2进入系统时...
  • 关于websocket做一次全面的总结。...实现WebSocket集群的2种方式 用redis的订阅/推送功能实现的。(推荐) 用redis存客户端连接对应的服务器的IP+端口,再用http去调用对应服务器的接口。 用redi.
  • Spring Boot+NettySocketIo+RabbitMQ实现websocket集群 效果 连接到不同的服务器的两个客户端可以互发消息 websocket的连接Client是不能序列化的,所以不能使用session共享那样的方法来做websocket的集群 netty...
  • 微服务分布式多应用场景中,由于ws的session无法序列化到redis,所以集群中,无法将所有WebSocket Session都缓存到redis进行session共享。Session 都存储在各应用当中 解决办法-基于Redis 的发布订阅 redis 订阅配置...
  • 解决websocket集群 session共享问题 (给用户页面推送消息的websocket服务未必是与该用户建立websocket连接的服务)
  • Spring boot 集群使用websocket长连接通讯引言基础使用创建一个基础的spring boot引入websocket需要的依赖编写websocket服务代码编写websocket客户端代码测试通过集群使用解决分布式session共享集群连接数限制环境...
  • 前面文章介绍了利用nginx的ip_hash和redis实现共享session,这里继续讨论session,...它不单单解决了共享session,在其它场景也可以使用,比如webSocket等等,这里只是简单介绍其作为共享session的使用。  原理  
  • 本文聊天室基于 websocket 进行实现,同时也为解决websocket session集群部署服务时的无法共享导致的收发消息问题。 问题引入     当我们使用 websocket 实现聊天时,后端服务会将所有的 ...
  • session共享,本质上就是存储容器的变动,但如何得到最优存取结构、如何准确清理过期会话,以及如何整合WebSocket等无法回避。Spring Session就是专门用于解决集群会话问题的,它不仅为集群会话提供了非常完善的...
  • Spring Session 提供了一个 API 和实现,用于管理用户的会话信息,同时使其在不依赖于特定于应用程序容器的解决方案的情况下,可以轻松地支持集群会话。它还提供了透明的集成: HttpSession 允许以中立的方式替换...
  • 文章目录净菜加工系统后台集群部署前言集群部署带来的问题集群部署落地实现解决方案问题一:跨域问题二:Session共享问题三:分布式WebSocket方案落地跨域Session共享消息驱动WebSocket使用`Nginx`对后台服务负载...
  • 前言 最近负责的一个项目需要使用 WebSocket 做前后端通信,我使用了Spring提供支持的STOMP协议,它可以解决...这是WebSocket集群状态下必然要面对的问题。 传统的做法是使用分布式session、redis或者通过消息队列来...
  • 集群Session共享、敏感词自动过滤、Java8 等等;   A.  基于Elastic Search实现首页列表数据的初始化加载、首页全文检索; B.  基于缓存Redis缓存首页朋友圈“是否已点赞、收藏...
  • 项目单点登录:使用nginx作为负载均衡,使用redis存储tomcat session,来实现集群中tomcat session共享,使用redis作为cas ticket的仓库,来实现集群中cas ticket的一致性。OA已经对接CAS,admin工程暂时不对接CAS...
  • FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
    2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户...

空空如也

空空如也

1 2
收藏数 26
精华内容 10
关键字:

websocket集群session共享