<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      網關升級

      背景

      這是去年做的事情了,去年九月我們將一個系統的網關zuul平滑升級為spring cloud gateway,在此記錄一下升級方案,有相同需求的朋友可以做個參考。

      升級原因:
      1、之前我們升級了spring boot/cloud版本,網關模塊沒有升級,一直使用舊版本,不統一,公共包的管理和代碼不好維護。

      2、低版本的spring cloud 使用zuul 1.x作為網關,zuul 1.x使用的是同步阻塞的serlvet線程模型,處理請求能力薄弱,容易出現線程膨脹問題。
      例如我們配置了ribbon.MaxHttpConnectionsPerHost = 600,即每個host會開600個線程處理請求,當請求越多時,就需要開更多的線程支持,而線程是占用資源的,在并發高的時候會造成機器負載高,線程切換頻繁,gc頻繁等問題。
      盡管zuul 2.x開始支持異步請求,但spring cloud并沒有集成計劃,而是推出了自家的網關spring cloud gateway。

      3、低版本的網關Netflix不再維護,有bug無法解決,同時在一些組件上支持不好,例如不能很好的整合websocket,resilience4j,redis ratelimit等組件。

      4、網關作為流量入口,高性能是基本要求,zuul已經不適用,現有使用spring cloud框架,spring cloud gateway是首選。

      因此我們決定對網關模塊進行升級。

      原理分析

      簡介

      spring cloud gateway是基于webflux框架構建的,功能豐富,高性能的,響應式網關。
      webflux是spring5推出的響應式web服務,與之前的spring mvc對比,傳統的servlet是阻塞的。官網介紹如下:

      z-g-1

      https://spring.io/reactive/

      可見spring并沒有打算用reactive替換傳統的servlet框架,而是兩個分支發展,但毫無疑問,未來的重心發展在reactive stack。
      當然,現在看起來,reactive很可能遭受的挑戰是虛擬線程,它比reactive更輕量,性能、可讀性、調試都更優秀。

      兩者也公用了一些基礎組件,對于開發者來說,@Controller,@RequestMapping等使用和spring mvc是一樣的。
      需要注意的是,響應式web服務并不能降低請求處理時間,例如一個請求本應該就要消耗1s,在webflux框架下,時間不會減少。
      響應式服務的重點是:用較少的線程,通常是cpu的核數,處理更多的請求,提升吞吐量。

      上面提到的reactive,有一個標準,叫做reactive stream:Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure。參考:https://en.wikipedia.org/wiki/Reactive_Streams
      這個標準由Netflix, Pivotal and Lightbend發起,其中Pivotal就是開發spring的公司。
      project reactor是基于這個標準實現的類庫,webflux是構建在reactor上的web服務。jdk9中對也對這個標準進行實現,提供了Flow接口。

      生產者-消費者模型

      project reactor是基于生產者-消費者模型,生產者負責生產數據,消費者通過訂閱,可以處理生產者生產的數據,并可以在完成和出錯時做出響應。

      頂層Publisher接口:

      z-g-2

      頂層Subscriber接口:

      z-g-3

      Mono和Flux是兩個最常用的生產者,我們平時使用的幾乎都是它們,Mono表示生產0或1個元素的生產者,Flux表示生產0至N個元素的生產者,可以簡單理解為Object和List。

      如下示例:定義一個包含1,2,3,4的Flux,然后map定義一個方法,將每個元素*2,然后定義一個消費者,打印結果。

      Flux.just(1, 2, 3, 4)
            .map(s -> s * 2)
            .subscribe(s -> System.out.println(s));
      

      看起來和java8里的stream集合操作和相似,它們都是在生產數據,然后定義處理數據的流程(過濾,加工),最后進行消費,同樣是在消費時才會觸發前面的一系列操作。

      傳統servlet vs webflux

      傳統servlet采用的是一個servlet一個線程的處理方式,這種模式在線程數少的cpu密集型服務下不會有多少問題,但一旦遇到IO,線程就會掛起,等待IO返回,此時線程什么事情都做不了,只能干等待。
      遇到這種問題,一般我們的做法就是增加處理線程,但沒有免費的午餐,增加線程會增加資源消耗,每個線程都可以申請占用1M的棧空間,和少量的內核空間,同時更多的線程會帶來線程切換,也會有性能損耗。
      而一旦servlet容器的線程被使用完了,請求就不得不排隊,進入隊列,盡管cpu此時是空閑的,但得不到任何利用。

      z-g-4

      servlet 3.0后開始支持非阻塞,tomcat等常用容器都支持servlet3.0。
      與之相比基于響應式的webflux框架是非阻塞的,這樣線程可以立馬返回,處理其它任務,而當IO返回,如讀取數據庫完成時,響應式框架會通知我們,線程接著處理返回的數據。

      z-g-5

      從圖可以看到,通過事件的方式將同步變成異步,請求只需要將阻塞操作提交給Event Loop就可以返回處理其它請求,當操作返回時,EventLoop會通知線程繼續處理,這樣一個線程就可以處理很多個請求。

      熟悉Linux IO多路復用模型的同學對這種方式肯定很熟悉,思想上是一樣的。
      webflux 需要使用非阻塞的容器,如:netty,tomcat等都可以,默認使用的是netty,服務啟動后可以看到:o.s.b.web.embedded.netty.NettyWebServer : Netty started on port 18001
      netty是一個高性能、易擴展、社區活躍的網絡開發框架,已經過大量的生產驗證,ElasticSearch、Dubbo、Rocketmq、HBase、spring webflux,gRPC都使用了netty作為底層網絡開發框架。

      注意,既然使用了響應式框架,意味著只有少量處理請求的線程,請求從頭到尾就不能有阻塞操作,否則請求線程很快會消耗完。

      正例:web 請求 → 查接口(非阻塞)→ 處理返回數據 → 查數據庫(非阻塞)→ 處理數據
      反例:web 請求 → 查接口(非阻塞)→ 處理返回數據 → 查數據庫(阻塞)→ 處理數據

      幸運的是現在基本所有的阻塞IO操作都有相應的reactive實現,如Feign → ReactiveFeign,Redis → ReactiveRedis,jdbc → r2dbc。

      springcloud gateway處理請求流程

      z-g-6

      • global filter,實現GlobalFilter接口,攔截所有請求
      • gateway filter,實現GatewayFilter接口,攔截指定的路由請求

      功能調整

      3.1 配置調整

      3.1.1

      server:
        port: 8001
        tomcat:
          max-threads: 5000
      
      ->
      
      server:
        port: 8002
      

      說明:使用8002端口,與8001會有一段時間并行運行,等驗證切換正常,下掉8001服務,詳細見下面上線方案。

      3.1.2

      spring:
        application:
          name: gateway
        servlet:
          multipart:
            max-file-size: 100MB
            max-request-size: 100MB
            enabled: true
      
      ->
      
      spring:
        application:
          name: gateway
      

      說明:servlet配置對webflux不再適用,gateway下,網關不再需要配置請求大小,由ng和后端服務決定。

      3.1.3

      management:
        endpoints:
          web:
            exposure:
              include: health, prometheus
      

      說明:不需要調整,端點測試正常。

      3.1.4

      ribbon:
        MaxAutoRetries: 0
        MaxAutoRetriesNextServer: 1
        OkToRetryOnAllOperations: true
        retryableStatusCodes: 500,503
        ReadTimeout: 30000
        ConnectTimeout: 1000
        MaxHttpConnectionsPerHost: 200
        MaxTotalHttpConnections: 5000
        ServerListRefreshInterval: 12000
        restclient:
          enabled: true
      
      ->
      
      spring: 
        cloud:
          loadbalancer:
            retry:
              retryableStatusCodes: 500,503
              retryOnAllOperations: true
            cache:
              enabled: false
      	gateway:
      	  httpclient:
      	    connect-timeout: 1000
          	response-timeout: 30000
      

      說明:gateway下ribbon已經廢棄,使用loadbalancer。
      cache.enabled: false 禁止loadbalancer緩存,避免雙緩存,使用eureka client緩存即可。

      3.1.5

      hystrix:
        command:
          default:
            fallback:
              isolation:
                semaphore:
                  maxConcurrentRequests: 500
      
      ->
      

      說明:刪掉,gateway下hystrix已經廢棄,改用resilience4j。

      3.1.6

      zuul:
        semaphore:
          max-semaphores: 5000
        retryable: true
        routes:
          data:
            stripPrefix: false
            path: /data/service/**
            serviceId: data-server
      
      ->
      
      routes:
        - id: data-server
          uri: lb://data-server
          predicates:
            - Path=/data/service/**
          filters:
            - name: CircuitBreaker
      

      3.1.7

      eureka:
        client:
          registry-fetch-interval-seconds: 13
      
      ->
      
      eureka:
        client:
          registry-fetch-interval-seconds: 25
      

      3.2 阻塞代碼改寫

      3.2.1 feign
      openfeign并沒有提供reactive的實現,而是推薦使用第三方的:https://github.com/PlaytikaOSS/feign-reactive, 這是一家游戲公司開源的。
      相關issues:https://github.com/spring-cloud/spring-cloud-openfeign/issues/668#issuecomment-1607854972。
      使用方式如下,ReactiveFeignClient標記Feign接口,接口方法返回值必須是Mono或者Flux。

      <dependency>
          <groupId>com.playtika.reactivefeign</groupId>
          <artifactId>feign-reactor-spring-cloud-starter</artifactId>
          <version>3.2.11</version>
      </dependency>
      
      @EnableReactiveFeignClients
      public class SpringCloudGatewayApplication{}
      
      //定義feigin
      @ReactiveFeignClient(name = "data-server")
      public interface DataClient {
      
         @GetMapping("/user")
         Mono<List<String>> user(@RequestParam("uid") Long uid);
      }
      

      3.2.2 redis
      使用非阻塞的ReactiveRedisTemplate。

      @Autowired
      ReactiveRedisTemplate reactiveRedisTemplate;
      

      3.3 其它

      3.3.1 session問題
      webflux使用的是WebSession,redis session使用的是EnableRedisWebSession。
      sessionId問題需要重寫一下解析sessionId的方法,保證傳到下游服務的sessionId一致,參考:https://juejin.cn/post/7181636384979943481。

      3.3.2 國際化問題
      現有i18n工具類,獲取國際化信息,LocaleContextHolder內部使用了ThreadLocal,ThreadLocal在webflux中不適用,需要改寫。
      可以通過exchange.getRequest().getHeaders().getAcceptLanguageAsLocales()拿到當前語言的Locales對象。

      public static String get(String key, String defaultMessage) {
          return messageSource.getMessage(key, new Object[]{defaultMessage}, defaultMessage, LocaleContextHolder.getLocale());
      }
      

      3.3.3 全局異常處理
      原有的GlobalFallbackProvider和ErrorFilter已經不適用,使用ErrorWebExceptionHandler。

      @Slf4j
      @Component
      @Order(-1)
      public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
      
         @Override
         public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
      	  //handle exception
            return null;
         }
      }
      

      3.3.4 日志
      必須使用AsyncAppender異步寫入方式,在響應式的世界里,所有涉及到io的都必須非阻塞。
      3.3.5 不支持
      spring redis SessionCreateEvent等事件在WebSession不被支持,無法使用。

      四、壓測

      部署環境:4C32G
      服務部署:單機
      jvm參數:-Xms1g -Xmx1g
      壓測時間:3min,壓測線程30s內啟動完成
      壓測超時時間:2s
      壓測接口:接口20%的時間為100ms,80%的時間為50ms

      4.1 zuul

      線程數:200
      執行情況:失敗率:0,P99:388,吞吐量:1403
      z-g-7

      gc情況:2秒左右一次young gc,無full gc(超過3分鐘沒觀測到)
      z-g-8

      線程情況:大量線程
      z-g-9

      cpu情況:cpu負載14,使用率60%
      z-g-10

      線程數:600
      執行情況:失敗率:0.01,P99:810,吞吐量:1952
      z-g-11

      gc情況:每秒一次young gc,每分鐘一次full gc
      z-g-12

      線程情況:大量線程
      z-g-13

      cpu情況:cpu負載77,使用率80%
      z-g-14

      4.2 springcloud gateway

      線程數:200
      執行情況:失敗率:0,P99:403,吞吐量:1388
      z-g-15

      gc情況:3秒左右一次young gc,無full gc
      z-g-16

      線程情況:線程數穩定
      z-g-17

      cpu情況:cpu負載14,使用率50%
      z-g-18

      線程數:600
      執行情況:失敗率:0.02%,P99:787,吞吐量:2202
      z-g-19

      gc情況:2秒左右一次young gc,無full gc
      z-g-20

      線程情況:線程數穩定
      z-g-21

      cpu情況:cpu負載28,使用率70%
      z-g-22

      官網的benchmark:https://github.com/spencergibb/spring-cloud-gateway-bench
      z-g-23

      總結

      使用springcloud gateway在并發增加時,線程數始終穩定,與cpu核數一致,圖中的http-reactor線程,而zuul會創建大量線程。
      在并發高時,springcloud gateway對cpu的使用顯要優于zuul,吞吐量也更好。springcloud gateway gc表現稍好。
      springcloud gateway請求時間并沒有比zuul好,這也符合前面的原理分析,webflux接口相應時間并沒有減少。

      上線方案

      網關是所有流量的入口,為了避免新網關出現問題影響業務,需要平滑過度,新網關驗證正常后,再將流量完全切換,下掉舊網關服務。
      發版時不能直接使用滾動發布,可以使用灰度發布或藍綠發布,本次采用灰度發布的方式,切換過程要運維配合,需提前通知。

      方案一:灰度發布

      逐個節點切換為新代碼,部署時先部署1個節點,放少量流量,驗證沒問題,再部署其它節點。
      優點:不需要部署新服務,切換過程簡單,不需要下線舊服務。
      缺點:切換驗證過程,老網關節點壓力會有較大壓力。
      整體過程如下:
      z-g-24

      方案二:藍綠發布

      部署一套新網關服務,放少量流量到新網關服務,驗證沒問題,直接下線老網關服務。
      優點:對老網關完全沒有影響,不會增加節點壓力。
      缺點:需要部署一套新服務,切換過程比較復雜,需要下線舊服務。
      整體過程如下:
      z-g-25

      回滾方案

      驗證過程發現有問題,通過ng切量回老網關。
      老網關代碼master checkout一個分支保留,有問題可以隨時回退到老代碼。

      其它問題

      1.熔斷,https://cloud.spring.io/spring-cloud-gateway/reference/html/#spring-cloud-circuitbreaker-filter-factory
      熔斷后默認拋出的異常不友好,無法看出是被熔斷了,可以重寫其邏輯。

      2.reactive feign超時設置,沒有application直接配置方式,代碼配置。https://github.com/PlaytikaOSS/feign-reactive/tree/develop/feign-reactor-spring-configuration

      3.注意Mono/Flux寫法,避免嵌套太深。1.抽取方法 2.流式寫法。

      4.url編碼問題,解密接口前端進行了兩次編碼,在zuul沒有問題,zuul會decode一次,進入后端服務,spring mvc會decode一次,gateway則沒有decode,導致到后端服務只decod一次,報錯。

      5.參數調優
      目前的機器配置,connect-timeout設置為2s時,在請求量大時會出現connection timeout exception,原因是處理連接的線程能力不足,將其設置到5s。這會導致量大時請求時間增加,可以通過增加機器解決,不過只有一瞬間出現,暫時忽略。或可以嘗試設置netty IO_SELECT_COUNT為比較大的值,這個會增加線程成本。
      上線后出現一些連接提前關閉的錯誤,例如connection reset by peer, Connection prematurely closed BEFORE response,原因是gateway對連接緩存,但下游服務在一定時間后會關閉連接,導致用了一個已關閉的鏈接。設置spring.cloud.gateway.http-client.pool.max-idle-time為15s,連接空閑15s后關閉。

      更多分享,歡迎關注我的github:https://github.com/jmilktea/jtea

      posted @ 2025-08-05 10:54  jtea  閱讀(568)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产成人综合精品| 国产情侣激情在线对白| 国产av午夜精品福利| 日韩有码中文字幕av| 国产久免费热视频在线观看 | 亚洲日本欧美日韩中文字幕| 无码专区 人妻系列 在线| 97精品伊人久久大香线蕉APP| 亚洲国产成人av毛片大全| 欧美牲交a欧美在线| 国产精品久久久久久久9999 | 亚洲一区成人av在线| 九九在线精品国产| 亚洲精品无码日韩国产不卡av| 日韩乱码人妻无码中文字幕| 崇明县| 亚洲码与欧洲码区别入口| 国产精品美女免费无遮挡| 涪陵区| 人妻精品动漫H无码中字| 天堂V亚洲国产V第一次| 国产福利酱国产一区二区| 国产91色综合久久免费| 亚洲精品成a人在线观看| 成年女人片免费视频播放A| 亚洲成人av在线系列| 好吊视频一区二区三区在线| 精品少妇后入一区二区三区| 天天摸夜夜摸夜夜狠狠添| 亚洲精品国产男人的天堂| 一区二区国产高清视频在线| V一区无码内射国产| 国产福利酱国产一区二区| 97人人添人人澡人人澡人人澡 | 亚洲sm另类一区二区三区| 成人综合婷婷国产精品久久蜜臀| 久热这里只精品视频99| 无码中文字幕av免费放| 午夜精品久久久久久99热| 亚洲成人一区| 成人永久免费A∨一级在线播放|