乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      分布式WebSocket集群解決方案

       雪山紅柳 2020-01-10

      問(wèn)題起因

      最近做項(xiàng)目時(shí)遇到了需要多用戶(hù)之間通信的問(wèn)題,涉及到了WebSocket握手請(qǐng)求,以及集群中WebSocket Session共享的問(wèn)題。

      期間我經(jīng)過(guò)了幾天的研究,總結(jié)出了幾個(gè)實(shí)現(xiàn)分布式WebSocket集群的辦法,從zuul到spring cloud gateway的不同嘗試,總結(jié)出了這篇文章,希望能幫助到某些人,并且能一起分享這方面的想法與研究。

      以下是我的場(chǎng)景描述

      • 資源:4臺(tái)服務(wù)器。其中只有一臺(tái)服務(wù)器具備ssl認(rèn)證域名,一臺(tái)redis+mysql服務(wù)器,兩臺(tái)應(yīng)用服務(wù)器(集群)
      • 應(yīng)用發(fā)布限制條件:由于場(chǎng)景需要,應(yīng)用場(chǎng)所需要ssl認(rèn)證的域名才能發(fā)布。因此ssl認(rèn)證的域名服務(wù)器用來(lái)當(dāng)api網(wǎng)關(guān),負(fù)責(zé)https請(qǐng)求與wss(安全認(rèn)證的ws)連接。俗稱(chēng)https卸載,用戶(hù)請(qǐng)求https域名服務(wù)器(eg:https:///xxx),但真實(shí)訪(fǎng)問(wèn)到的是http+ip地址的形式。只要網(wǎng)關(guān)配置高,能handle多個(gè)應(yīng)用
      • 需求:用戶(hù)登錄應(yīng)用,需要與服務(wù)器建立wss連接,不同角色之間可以單發(fā)消息,也可以群發(fā)消息
      • 集群中的應(yīng)用服務(wù)類(lèi)型:每個(gè)集群實(shí)例都負(fù)責(zé)http無(wú)狀態(tài)請(qǐng)求服務(wù)與ws長(zhǎng)連接服務(wù)

      系統(tǒng)架構(gòu)圖

      clipboard.png

      在我的實(shí)現(xiàn)里,每個(gè)應(yīng)用服務(wù)器都負(fù)責(zé)http and ws請(qǐng)求,其實(shí)也可以將ws請(qǐng)求建立的聊天模型單獨(dú)成立為一個(gè)模塊。從分布式的角度來(lái)看,這兩種實(shí)現(xiàn)類(lèi)型差不多,但從實(shí)現(xiàn)方便性來(lái)說(shuō),一個(gè)應(yīng)用服務(wù)http+ws請(qǐng)求的方式更為方便。下文會(huì)有解釋

      本文涉及的技術(shù)棧

      • Eureka 服務(wù)發(fā)現(xiàn)與注冊(cè)
      • Redis Session共享
      • Redis 消息訂閱
      • Spring Boot
      • Zuul 網(wǎng)關(guān)
      • Spring Cloud Gateway 網(wǎng)關(guān)
      • Spring WebSocket 處理長(zhǎng)連接
      • Ribbon 負(fù)載均衡
      • Netty 多協(xié)議NIO網(wǎng)絡(luò)通信框架
      • Consistent Hash 一致性哈希算法

      相信能走到這一步的人都了解過(guò)我上面列舉的技術(shù)棧了,如果還沒(méi)有,可以先去網(wǎng)上找找入門(mén)教程了解一下。下面的內(nèi)容都與上述技術(shù)相關(guān),題主默認(rèn)大家都了解過(guò)了...
      這里是描述一致性Hash算法最易懂的文章傳送門(mén)

      技術(shù)可行性分析

      下面我將描述session特性,以及根據(jù)這些特性列舉出n個(gè)解決分布式架構(gòu)中處理ws請(qǐng)求的集群方案

      WebSocketSession與HttpSession
      在Spring所集成的WebSocket里面,每個(gè)ws連接都有一個(gè)對(duì)應(yīng)的session:WebSocketSession,在Spring WebSocket中,我們建立ws連接之后可以通過(guò)類(lèi)似這樣的方式進(jìn)行與客戶(hù)端的通信:

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

      那么問(wèn)題來(lái)了:ws的session無(wú)法序列化到redis,因此在集群中,我們無(wú)法將所有WebSocketSession都緩存到redis進(jìn)行session共享。每臺(tái)服務(wù)器都有各自的session。于此相反的是HttpSession,redis可以支持httpsession共享,但是目前沒(méi)有websocket session共享的方案,因此走redis websocket session共享這條路是行不通的。
      有的人可能會(huì)想:我可不可以將sessin關(guān)鍵信息緩存到redis,集群中的服務(wù)器從redis拿取session關(guān)鍵信息然后重新構(gòu)建websocket session...我只想說(shuō)這種方法如果有人能試出來(lái),請(qǐng)告訴我一聲...

      以上便是websocket session與http session共享的區(qū)別,總的來(lái)說(shuō)就是http session共享已經(jīng)有解決方案了,而且很簡(jiǎn)單,只要引入相關(guān)依賴(lài):spring-session-data-redisspring-boot-starter-redis,大家可以從網(wǎng)上找個(gè)demo玩一下就知道怎么做了。而websocket session共享的方案由于websocket底層實(shí)現(xiàn)的方式,我們無(wú)法做到真正的websocket session共享。

      解決方案的演變

      Netty與Spring WebSocket

      剛開(kāi)始的時(shí)候,我嘗試著用netty實(shí)現(xiàn)了websocket服務(wù)端的搭建。在netty里面,并沒(méi)有websocket session這樣的概念,與其類(lèi)似的是channel,每一個(gè)客戶(hù)端連接都代表一個(gè)channel。前端的ws請(qǐng)求通過(guò)netty監(jiān)聽(tīng)的端口,走websocket協(xié)議進(jìn)行ws握手連接之后,通過(guò)一些列的handler(責(zé)鏈模式)進(jìn)行消息處理。與websocket session類(lèi)似地,服務(wù)端在連接建立后有一個(gè)channel,我們可以通過(guò)channel進(jìn)行與客戶(hù)端的通信

         /**
          * TODO 根據(jù)服務(wù)器傳進(jìn)來(lái)的id,分配到不同的group
          */
         private static final ChannelGroup GROUP = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);
      
         @Override
         protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
             //retain增加引用計(jì)數(shù),防止接下來(lái)的調(diào)用引用失效
             System.out.println("服務(wù)器接收到來(lái)自 " + ctx.channel().id() + " 的消息: " + msg.text());
             //將消息發(fā)送給group里面的所有channel,也就是發(fā)送消息給客戶(hù)端
             GROUP.writeAndFlush(msg.retain());
         }

      那么,服務(wù)端用netty還是用spring websocket?以下我將從幾個(gè)方面列舉這兩種實(shí)現(xiàn)方式的優(yōu)缺點(diǎn)

      • 使用netty實(shí)現(xiàn)websocket

        玩過(guò)netty的人都知道netty是的線(xiàn)程模型是nio模型,并發(fā)量非常高,spring5之前的網(wǎng)絡(luò)線(xiàn)程模型是servlet實(shí)現(xiàn)的,而servlet不是nio模型,所以在spring5之后,spring的底層網(wǎng)絡(luò)實(shí)現(xiàn)采用了netty。如果我們單獨(dú)使用netty來(lái)開(kāi)發(fā)websocket服務(wù)端,速度快是絕對(duì)的,但是可能會(huì)遇到下列問(wèn)題:
        1.與系統(tǒng)的其他應(yīng)用集成不方便,在rpc調(diào)用的時(shí)候,無(wú)法享受springcloud里feign服務(wù)調(diào)用的便利性
        2.業(yè)務(wù)邏輯可能要重復(fù)實(shí)現(xiàn)
        3.使用netty可能需要重復(fù)造輪子
        4.怎么連接上服務(wù)注冊(cè)中心,也是一件麻煩的事情
        5.restful服務(wù)與ws服務(wù)需要分開(kāi)實(shí)現(xiàn),如果在netty上實(shí)現(xiàn)restful服務(wù),有多麻煩可想而知,用spring一站式restful開(kāi)發(fā)相信很多人都習(xí)慣了。

      • 使用spring websocket實(shí)現(xiàn)ws服務(wù)

        spring websocket已經(jīng)被springboot很好地集成了,所以在springboot上開(kāi)發(fā)ws服務(wù)非常方便,做法非常簡(jiǎn)單
        第一步:添加依賴(lài)

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

        第二步:添加配置類(lèi)

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

        第三步:實(shí)現(xiàn)消息監(jiān)聽(tīng)類(lèi)

        @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("斷開(kāi)連接: " + 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("接受到的數(shù)據(jù)" + map);
               clients.forEach(s -> {
                   try {
                       System.out.println("發(fā)送消息給: " + session.getId());
                       s.sendMessage(new TextMessage("服務(wù)器返回收到的信息," + payload));
                   } catch (Exception e) {
                       e.printStackTrace();
                   }
               });
           }
        }

        從這個(gè)demo中,使用spring websocket實(shí)現(xiàn)ws服務(wù)的便利性大家可想而知了。為了能更好地向spring cloud大家族看齊,我最終采用了spring websocket實(shí)現(xiàn)ws服務(wù)。
        因此我的應(yīng)用服務(wù)架構(gòu)是這樣子的:一個(gè)應(yīng)用既負(fù)責(zé)restful服務(wù),也負(fù)責(zé)ws服務(wù)。沒(méi)有將ws服務(wù)模塊拆分是因?yàn)椴鸱殖鋈ヒ褂胒eign來(lái)進(jìn)行服務(wù)調(diào)用。第一本人比較懶惰,第二拆分與不拆分相差在多了一層服務(wù)間的io調(diào)用,所以就沒(méi)有這么做了。

      從zuul技術(shù)轉(zhuǎn)型到spring cloud gateway

      要實(shí)現(xiàn)websocket集群,我們必不可免地得從zuul轉(zhuǎn)型到spring cloud gateway。原因如下:

      zuul1.0版本不支持websocket轉(zhuǎn)發(fā),zuul 2.0開(kāi)始支持websocket,zuul2.0幾個(gè)月前開(kāi)源了,但是2.0版本沒(méi)有被spring boot集成,而且文檔不健全。因此轉(zhuǎn)型是必須的,同時(shí)轉(zhuǎn)型也很容易實(shí)現(xiàn)。
      
      在gateway中,為了實(shí)現(xiàn)ssl認(rèn)證和動(dòng)態(tài)路由負(fù)載均衡,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卸載,我們還需要配置一個(gè)filter,否則請(qǐng)求網(wǎng)關(guān)時(shí)會(huì)出現(xiàn)錯(cuò)誤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來(lái)卸載https請(qǐng)求了,到目前為止,我們的基本框架已經(jīng)搭建完畢,網(wǎng)關(guān)既可以轉(zhuǎn)發(fā)https請(qǐng)求,也可以轉(zhuǎn)發(fā)wss請(qǐng)求。接下來(lái)就是用戶(hù)多對(duì)多之間session互通的通訊解決方案了。接下來(lái),我將根據(jù)方案的優(yōu)雅性,從最不優(yōu)雅的方案開(kāi)始講起。

      session廣播

      這是最簡(jiǎn)單的websocket集群通訊解決方案。場(chǎng)景如下:
      教師A想要群發(fā)消息給他的學(xué)生們

      1. 教師的消息請(qǐng)求發(fā)給網(wǎng)關(guān),內(nèi)容包含{我是教師A,我想把xxx消息發(fā)送我的學(xué)生們}
      2. 網(wǎng)關(guān)接收到消息,獲取集群所有ip地址,逐個(gè)調(diào)用教師的請(qǐng)求
      3. 集群中的每臺(tái)服務(wù)器獲取請(qǐng)求,根據(jù)教師A的信息查找本地有沒(méi)有與學(xué)生關(guān)聯(lián)的session,有則調(diào)用sendMessage方法,沒(méi)有則忽略請(qǐng)求

      clipboard.png

      session廣播實(shí)現(xiàn)很簡(jiǎn)單,但是有一個(gè)致命缺陷:計(jì)算力浪費(fèi)現(xiàn)象,當(dāng)服務(wù)器沒(méi)有消息接收者session的時(shí)候,相當(dāng)于浪費(fèi)了一次循環(huán)遍歷的計(jì)算力,該方案在并發(fā)需求不高的情況下可以?xún)?yōu)先考慮,實(shí)現(xiàn)很容易。

      spring cloud中獲取服務(wù)集群中每臺(tái)服務(wù)器信息的方法如下
      @Resource
      private EurekaClient eurekaClient;
      
      Application app = eurekaClient.getApplication("service-name");
      //instanceInfo包括了一臺(tái)服務(wù)器ip,port等消息
      InstanceInfo instanceInfo = app.getInstances().get(0);
      System.out.println("ip address: " + instanceInfo.getIPAddr());
      服務(wù)器需要維護(hù)關(guān)系映射表,將用戶(hù)的id與session做映射,session建立時(shí)在映射表中添加映射關(guān)系,session斷開(kāi)后要?jiǎng)h除映射表內(nèi)關(guān)聯(lián)關(guān)系

      一致性哈希算法實(shí)現(xiàn)(本文的要點(diǎn))

      這種方法是本人認(rèn)為最優(yōu)雅的實(shí)現(xiàn)方案,理解這種方案需要一定的時(shí)間,如果你耐心看下去,相信你一定會(huì)有所收獲。再?gòu)?qiáng)調(diào)一次,不了解一致性哈希算法的同學(xué)請(qǐng)先看這里,現(xiàn)先假設(shè)哈希環(huán)是順時(shí)針查找的。

      首先,想要將一致性哈希算法的思想應(yīng)用到我們的websocket集群,我們需要解決以下新問(wèn)題:

      1. 集群節(jié)點(diǎn)DOWN,會(huì)影響到哈希環(huán)映射到狀態(tài)是DOWN的節(jié)點(diǎn)。
      2. 集群節(jié)點(diǎn)UP,會(huì)影響到舊key映射不到對(duì)應(yīng)的節(jié)點(diǎn)。
      3. 哈希環(huán)讀寫(xiě)共享。
      在集群中,總會(huì)出現(xiàn)服務(wù)UP/DOWN的問(wèn)題。

      針對(duì)節(jié)點(diǎn)DOWN的問(wèn)題分析如下:

      一個(gè)服務(wù)器DOWN的時(shí)候,其擁有的websocket session會(huì)自動(dòng)關(guān)閉連接,并且前端會(huì)收到通知。此時(shí)會(huì)影響到哈希環(huán)的映射錯(cuò)誤。我們只需要當(dāng)監(jiān)聽(tīng)到服務(wù)器DOWN的時(shí)候,刪除哈希環(huán)上面對(duì)應(yīng)的實(shí)際結(jié)點(diǎn)和虛結(jié)點(diǎn),避免讓網(wǎng)關(guān)轉(zhuǎn)發(fā)到狀態(tài)是DOWN的服務(wù)器上。
      實(shí)現(xiàn)方法:在eureka治理中心監(jiān)聽(tīng)集群服務(wù)DOWN事件,并及時(shí)更新哈希環(huán)。

      針對(duì)節(jié)點(diǎn)UP的問(wèn)題分析如下:

      現(xiàn)假設(shè)集群中有服務(wù)CacheB上線(xiàn)了,該服務(wù)器的ip地址剛好被映射到key1和cacheA之間。那么key1對(duì)應(yīng)的用戶(hù)每次要發(fā)消息時(shí)都跑去CacheB發(fā)送消息,結(jié)果明顯是發(fā)送不了消息,因?yàn)?code>CacheB沒(méi)有key1對(duì)應(yīng)的session。

      clipboard.png

      此時(shí)我們有兩種解決方案。
      方案A簡(jiǎn)單,動(dòng)作大:
      eureka監(jiān)聽(tīng)到節(jié)點(diǎn)UP事件之后,根據(jù)現(xiàn)有集群信息,更新哈希環(huán)。并且斷開(kāi)所有session連接,讓客戶(hù)端重新連接,此時(shí)客戶(hù)端會(huì)連接到更新后的哈希環(huán)節(jié)點(diǎn),以此避免消息無(wú)法送達(dá)的情況。
      方案B復(fù)雜,動(dòng)作?。?/blockquote>
      我們先看看沒(méi)有虛擬節(jié)點(diǎn)的情況,假設(shè)CacheCCacheA之間上線(xiàn)了服務(wù)器CacheB。所有映射在CacheCCacheB的用戶(hù)發(fā)消息時(shí)都會(huì)去CacheB里面找session發(fā)消息。也就是說(shuō)CacheB一但上線(xiàn),便會(huì)影響到CacheCCacheB之間的用戶(hù)發(fā)送消息。所以我們只需要將CacheA斷開(kāi)CacheCCacheB的用戶(hù)所對(duì)應(yīng)的session,讓客戶(hù)端重連。

      clipboard.png

      接下來(lái)是有虛擬節(jié)點(diǎn)的情況,假設(shè)淺色的節(jié)點(diǎn)是虛擬節(jié)點(diǎn)。我們用長(zhǎng)括號(hào)來(lái)代表某段區(qū)域映射的結(jié)果屬于某個(gè)Cache。首先是C節(jié)點(diǎn)未上線(xiàn)的情況。圖大家應(yīng)該都懂吧,所有B的虛擬節(jié)點(diǎn)都會(huì)指向真實(shí)的B節(jié)點(diǎn),所以所有B節(jié)點(diǎn)逆時(shí)針那一部分都會(huì)映射到B(因?yàn)槲覀円?guī)定哈希環(huán)順時(shí)針查找)。

      clipboard.png

      接下來(lái)是C節(jié)點(diǎn)上線(xiàn)的情況,可以看到某些區(qū)域被C占領(lǐng)了。

      clipboard.png

      由以上情況我們可以知道:節(jié)點(diǎn)上線(xiàn),會(huì)有許多對(duì)應(yīng)虛擬節(jié)點(diǎn)也同時(shí)上線(xiàn),因此我們需要將多段范圍key對(duì)應(yīng)的session斷開(kāi)連接(上圖紅色的部分)。具體算法有點(diǎn)復(fù)雜,實(shí)現(xiàn)的方式因人而異,大家可以嘗試一下自己實(shí)現(xiàn)算法。

      哈希環(huán)應(yīng)該放在哪里?

      1. gateway本地創(chuàng)建并維護(hù)哈希環(huán)。當(dāng)ws請(qǐng)求進(jìn)來(lái)的時(shí)候,本地獲取哈希環(huán)并獲取映射服務(wù)器信息,轉(zhuǎn)發(fā)ws請(qǐng)求。這種方法看上去不錯(cuò),但實(shí)際上是不太可取的,回想一下上面服務(wù)器DOWN的時(shí)候只能通過(guò)eureka監(jiān)聽(tīng),那么eureka監(jiān)聽(tīng)到DOWN事件之后,需要通過(guò)io來(lái)通知gateway刪除對(duì)應(yīng)節(jié)點(diǎn)嗎?顯然太麻煩了,將eureka的職責(zé)分散到gateway,不建議這么做。
      2. eureka創(chuàng)建,并放到redis共享讀寫(xiě)。這個(gè)方案可行,當(dāng)eureka監(jiān)聽(tīng)到服務(wù)DOWN的時(shí)候,修改哈希環(huán)并推送到redis上。為了請(qǐng)求響應(yīng)時(shí)間盡量地短,我們不可以讓gateway每次轉(zhuǎn)發(fā)ws請(qǐng)求的時(shí)候都去redis取一次哈希環(huán)。哈希環(huán)修改的概率的確很低,gateway只需要應(yīng)用redis的消息訂閱模式,訂閱哈希環(huán)修改事件便可以解決此問(wèn)題。
      至此我們的spring websocket集群已經(jīng)搭建的差不多了,最重要的地方還是一致性哈希算法。現(xiàn)在有最后一個(gè)技術(shù)瓶頸,網(wǎng)關(guān)如何根據(jù)ws請(qǐng)求轉(zhuǎn)發(fā)到指定的集群服務(wù)器上?答案在負(fù)載均衡。spring cloud gateway或zuul都默認(rèn)集成了ribbon作為負(fù)載均衡,我們只需要根據(jù)建立ws請(qǐng)求時(shí)客戶(hù)端發(fā)來(lái)的user id,重寫(xiě)ribbon負(fù)載均衡算法,根據(jù)user id進(jìn)行hash,并在哈希環(huán)上尋找ip,并將ws請(qǐng)求轉(zhuǎn)發(fā)到該ip便完事了。流程如下圖所示:

      clipboard.png

      接下來(lái)用戶(hù)溝通的時(shí)候,只需要根據(jù)id進(jìn)行hash,在哈希環(huán)上獲取對(duì)應(yīng)ip,便可以知道與該用戶(hù)建立ws連接時(shí)的session存在哪臺(tái)服務(wù)器上了!

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

      題主在實(shí)際操作的時(shí)候發(fā)現(xiàn)了ribbon兩個(gè)不完善的地方......

      1. 根據(jù)網(wǎng)上找的方法,繼承AbstractLoadBalancerRule重寫(xiě)負(fù)載均衡策略之后,多個(gè)不同應(yīng)用的請(qǐng)求變得混亂。假如eureka上有兩個(gè)service A和B,重寫(xiě)負(fù)載均衡策略之后,請(qǐng)求A或B的服務(wù),最終只會(huì)映射到其中一個(gè)服務(wù)上。非常奇怪!可能spring cloud gateway官網(wǎng)需要給出一個(gè)正確的重寫(xiě)負(fù)載均衡策略的demo。
      2. 一致性哈希算法需要一個(gè)key,類(lèi)似user id,根據(jù)key進(jìn)行hash之后在哈希環(huán)上搜索并返回ip。但是ribbon沒(méi)有完善choose函數(shù)的key參數(shù),直接寫(xiě)死了default!

      clipboard.png

      難道這樣子我們就沒(méi)有辦法了嗎?其實(shí)還有一個(gè)可行并且暫時(shí)可替代的辦法!
      如下圖所示,客戶(hù)端發(fā)送一個(gè)普通的http請(qǐng)求(包含id參數(shù))給網(wǎng)關(guān),網(wǎng)關(guān)根據(jù)id進(jìn)行hash,在哈希環(huán)中尋找ip地址,將ip地址返回給客戶(hù)端,客戶(hù)端再根據(jù)該ip地址進(jìn)行ws請(qǐng)求。

      clipboard.png

      由于ribbon未完善key的處理,我們暫時(shí)無(wú)法在ribbon上實(shí)現(xiàn)一致性哈希算法。只能間接地通過(guò)客戶(hù)端發(fā)起兩次請(qǐng)求(一次http,一次ws)的方式來(lái)實(shí)現(xiàn)一致性哈希。希望不久之后ribbon能更新這個(gè)缺陷!讓我們的websocket集群實(shí)現(xiàn)得更優(yōu)雅一點(diǎn)。

      后記

      以上便是我這幾天探索的結(jié)果。期間遇到了許多問(wèn)題,并逐一解決難題,列出兩個(gè)websocket集群解決方案。第一個(gè)是session廣播,第二個(gè)是一致性哈希。這兩種方案針對(duì)不同場(chǎng)景各有優(yōu)缺點(diǎn),本文并未用到ActiveMQ,Karfa等消息隊(duì)列實(shí)現(xiàn)消息推送,只是想通過(guò)自己的想法,不依靠消息隊(duì)列來(lái)簡(jiǎn)單地實(shí)現(xiàn)多用戶(hù)之間的長(zhǎng)連接通訊。希望能為大家提供一條不同于尋常的思路。

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多