最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Cloud Feign組件實(shí)例解析

 更新時(shí)間:2019年11月15日 16:56:16   作者:技術(shù)與人生  
這篇文章主要介紹了Spring Cloud Feign組件實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

這篇文章主要介紹了Spring Cloud Feign組件實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

采用Spring Cloud微服務(wù)框架后,經(jīng)常會(huì)涉及到服務(wù)間調(diào)用,服務(wù)間調(diào)用采用了Feign組件。

由于之前有使用dubbo經(jīng)驗(yàn)。dubbo的負(fù)載均衡策略(輪訓(xùn)、最小連接數(shù)、隨機(jī)輪訓(xùn)、加權(quán)輪訓(xùn)),dubbo失敗策略(快速失敗、失敗重試等等),

所以Feign負(fù)載均衡策略的是什么? 失敗后是否會(huì)重試,重試策略又是什么?帶這個(gè)疑問(wèn),查了一些資料,最后還是看了下代碼。畢竟代碼就是一切

Spring boot集成Feign的大概流程:

1、利用FeignAutoConfiguration自動(dòng)配置。并根據(jù)EnableFeignClients 自動(dòng)注冊(cè)產(chǎn)生Feign的代理類(lèi)。

2、注冊(cè)方式利用FeignClientFactoryBean,熟悉Spring知道FactoryBean 產(chǎn)生bean的工廠,有個(gè)重要方法getObject產(chǎn)生FeignClient容器bean

3、同時(shí)代理類(lèi)中使用hystrix做資源隔離,F(xiàn)eign代理類(lèi)中 構(gòu)造 RequestTemplate ,RequestTemlate要做的向負(fù)載均衡選中的server發(fā)送http請(qǐng)求,并進(jìn)行編碼和解碼一系列操作。

下面只是粗略的看了下整體流程,先有整體再有細(xì)節(jié)吧,下面利用IDEA看下細(xì)節(jié):

一、Feign失敗重試

SynchronousMethodHandler的方法中的處理邏輯:

@Override
 public Object invoke(Object[] argv) throws Throwable {
  RequestTemplate template = buildTemplateFromArgs.create(argv);
  Retryer retryer = this.retryer.clone();
  while (true) {
   try {
    return executeAndDecode(template);
   } catch (RetryableException e) {
    retryer.continueOrPropagate(e);
    if (logLevel != Logger.Level.NONE) {
     logger.logRetry(metadata.configKey(), logLevel);
    }
    continue;
   }
  }
 }
  • 上面的邏輯很簡(jiǎn)單。構(gòu)造 template 并去進(jìn)行服務(wù)間的http調(diào)用,然后對(duì)返回結(jié)果進(jìn)行解碼
  • 當(dāng)拋出 RetryableException 后,異常邏輯是否重試? 重試多少次? 帶這個(gè)問(wèn)題,看了retryer.continueOrPropagate(e);

具體邏輯如下:

public void continueOrPropagate(RetryableException e) {
   if (attempt++ >= maxAttempts) {
    throw e;
   }
 
   long interval;
   if (e.retryAfter() != null) {
    interval = e.retryAfter().getTime() - currentTimeMillis();
    if (interval > maxPeriod) {
     interval = maxPeriod;
    }
    if (interval < 0) {
     return;
    }
   } else {
    interval = nextMaxInterval();
   }
   try {
    Thread.sleep(interval);
   } catch (InterruptedException ignored) {
    Thread.currentThread().interrupt();
   }
   sleptForMillis += interval;
  }
  • 當(dāng)重試次數(shù)大于默認(rèn)次數(shù)5時(shí)候,直接拋出異常,不在重試
  • 否則每隔一段時(shí)間 默認(rèn)值最大1ms 后重試一次。

這就Feign這塊的重試這塊的粗略邏輯,由于之前工作中一直使用dubbo。同樣是否需要將生產(chǎn)環(huán)境中重試操作關(guān)閉?

思考:之前dubbo生產(chǎn)環(huán)境的重試操作都會(huì)關(guān)閉。原因有幾個(gè):

  • 一般第一次失敗,重試也會(huì)失敗,極端情況下不斷的重試,會(huì)占用大量dubbo連接池,造成連接池被打滿(mǎn),影響核心功能
  • 也是比較重要的一點(diǎn)原因,重試帶來(lái)的業(yè)務(wù)邏輯的影響,即如果接口不是冪等的,重試會(huì)帶來(lái)業(yè)務(wù)邏輯的錯(cuò)誤,引發(fā)問(wèn)題

二、Feign負(fù)載均衡策略

那么負(fù)載均衡的策略又是什么呢?由上圖中可知 executeAndDecode(template)

Object executeAndDecode(RequestTemplate template) throws Throwable {
  Request request = targetRequest(template);

  if (logLevel != Logger.Level.NONE) {
   logger.logRequest(metadata.configKey(), logLevel, request);
  }

  Response response;
  long start = System.nanoTime();
  try {
   response = client.execute(request, options);
   // ensure the request is set. TODO: remove in Feign 10
   response.toBuilder().request(request).build();
  } catch (IOException e) {
   if (logLevel != Logger.Level.NONE) {
    logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
   }
   throw errorExecuting(request, e);
  }
  long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

  boolean shouldClose = true;
  try {
   if (logLevel != Logger.Level.NONE) {
    response =
      logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
    // ensure the request is set. TODO: remove in Feign 10
    response.toBuilder().request(request).build();
   }
   if (Response.class == metadata.returnType()) {
    if (response.body() == null) {
     return response;
    }
    if (response.body().length() == null ||
        response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
     shouldClose = false;
     return response;
    }
    // Ensure the response body is disconnected
    byte[] bodyData = Util.toByteArray(response.body().asInputStream());
    return response.toBuilder().body(bodyData).build();
   }
   if (response.status() >= 200 && response.status() < 300) {
    if (void.class == metadata.returnType()) {
     return null;
    } else {
     return decode(response);
    }
   } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
    return decode(response);
   } else {
    throw errorDecoder.decode(metadata.configKey(), response);
   }
  } catch (IOException e) {
   if (logLevel != Logger.Level.NONE) {
    logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
   }
   throw errorReading(request, response, e);
  } finally {
   if (shouldClose) {
    ensureClosed(response.body());
   }
  }
 }

概括的說(shuō)主要做了兩件事:發(fā)送HTTP請(qǐng)求,解碼響應(yīng)數(shù)據(jù)

想看的負(fù)載均衡應(yīng)該在11行 response = client.execute(request, options); 而client的實(shí)現(xiàn)方式有兩種 Default、LoadBalancerFeignClient

猜的話應(yīng)該是LoadBalancerFeignClient,帶這個(gè)問(wèn)題去看源碼(其實(shí)個(gè)人更喜歡帶著問(wèn)題看源碼,沒(méi)有目的一是看很難將復(fù)雜的源碼關(guān)聯(lián)起來(lái),二是很容易迷失其中)

果然通過(guò)一番查找發(fā)現(xiàn) Client 實(shí)例就是LoadBalancerFeignClient,而設(shè)置這個(gè)Client就是通過(guò)上面說(shuō)的FeignClientFactoryBean的getObject方法中設(shè)置的,具體不說(shuō)了

下面重點(diǎn)看LoadBalancerFeignClient execute(request, options)

@Override
  public Response execute(Request request, Request.Options options) throws IOException {
    try {
      URI asUri = URI.create(request.url());
      String clientName = asUri.getHost();
      URI uriWithoutHost = cleanUrl(request.url(), clientName);
      FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
          this.delegate, request, uriWithoutHost);

      IClientConfig requestConfig = getClientConfig(options, clientName);
      return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
          requestConfig).toResponse();
    }
    catch (ClientException e) {
      IOException io = findIOException(e);
      if (io != null) {
        throw io;
      }
      throw new RuntimeException(e);
    }
  }

通過(guò)幾行代碼比較重要的點(diǎn)RibbonRequest ,原來(lái)Feign負(fù)載均衡還是通過(guò)Ribbon實(shí)現(xiàn)的,那么Ribbo又是如何實(shí)現(xiàn)負(fù)載均衡的呢?

public Observable<T> submit(final ServerOperation<T> operation) {
    final ExecutionInfoContext context = new ExecutionInfoContext();
    
    if (listenerInvoker != null) {
      try {
        listenerInvoker.onExecutionStart();
      } catch (AbortExecutionException e) {
        return Observable.error(e);
      }
    }

    final int maxRetrysSame = retryHandler.getMaxRetriesOnSameServer();
    final int maxRetrysNext = retryHandler.getMaxRetriesOnNextServer();

    // Use the load balancer
    Observable<T> o = 
        (server == null ? selectServer() : Observable.just(server))
        .concatMap(new Func1<Server, Observable<T>>() {
          @Override
          // Called for each server being selected
          public Observable<T> call(Server server) {
            context.setServer(server);
            final ServerStats stats = loadBalancerContext.getServerStats(server);
            
            // Called for each attempt and retry
            Observable<T> o = Observable
                .just(server)
                .concatMap(new Func1<Server, Observable<T>>() {
                  @Override
                  public Observable<T> call(final Server server) {
                    context.incAttemptCount();
                    loadBalancerContext.noteOpenConnection(stats);
                    
                    if (listenerInvoker != null) {
                      try {
                        listenerInvoker.onStartWithServer(context.toExecutionInfo());
                      } catch (AbortExecutionException e) {
                        return Observable.error(e);
                      }
                    }
                    
                    final Stopwatch tracer = loadBalancerContext.getExecuteTracer().start();
                    
                    return operation.call(server).doOnEach(new Observer<T>() {
                      private T entity;
                      @Override
                      public void onCompleted() {
                        recordStats(tracer, stats, entity, null);
                        // TODO: What to do if onNext or onError are never called?
                      }

                      @Override
                      public void onError(Throwable e) {
                        recordStats(tracer, stats, null, e);
                        logger.debug("Got error {} when executed on server {}", e, server);
                        if (listenerInvoker != null) {
                          listenerInvoker.onExceptionWithServer(e, context.toExecutionInfo());
                        }
                      }

                      @Override
                      public void onNext(T entity) {
                        this.entity = entity;
                        if (listenerInvoker != null) {
                          listenerInvoker.onExecutionSuccess(entity, context.toExecutionInfo());
                        }
                      }              
                      
                      private void recordStats(Stopwatch tracer, ServerStats stats, Object entity, Throwable exception) {
                        tracer.stop();
                        loadBalancerContext.noteRequestCompletion(stats, entity, exception, tracer.getDuration(TimeUnit.MILLISECONDS), retryHandler);
                      }
                    });
                  }
                });
            
            if (maxRetrysSame > 0) 
              o = o.retry(retryPolicy(maxRetrysSame, true));
            return o;
          }
        });
      
    if (maxRetrysNext > 0 && server == null) 
      o = o.retry(retryPolicy(maxRetrysNext, false));
    
    return o.onErrorResumeNext(new Func1<Throwable, Observable<T>>() {
      @Override
      public Observable<T> call(Throwable e) {
        if (context.getAttemptCount() > 0) {
          if (maxRetrysNext > 0 && context.getServerAttemptCount() == (maxRetrysNext + 1)) {
            e = new ClientException(ClientException.ErrorType.NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED,
                "Number of retries on next server exceeded max " + maxRetrysNext
                + " retries, while making a call for: " + context.getServer(), e);
          }
          else if (maxRetrysSame > 0 && context.getAttemptCount() == (maxRetrysSame + 1)) {
            e = new ClientException(ClientException.ErrorType.NUMBEROF_RETRIES_EXEEDED,
                "Number of retries exceeded max " + maxRetrysSame
                + " retries, while making a call for: " + context.getServer(), e);
          }
        }
        if (listenerInvoker != null) {
          listenerInvoker.onExecutionFailed(e, context.toFinalExecutionInfo());
        }
        return Observable.error(e);
      }
    });
  }

通過(guò)上面代碼分析,發(fā)現(xiàn)Ribbon和Hystrix一樣都是利用了rxjava看來(lái)有必要掌握下rxjava了又。這里面 比較重要的就是17行,

selectServer() 方法選擇指定的Server,負(fù)載均衡的策略主要是有ILoadBalancer接口不同實(shí)現(xiàn)方式:

  • BaseLoadBalancer采用的規(guī)則為RoundRobinRule 輪訓(xùn)規(guī)則
  • DynamicServerListLoadBalancer繼承了BaseLoadBalancer,主要運(yùn)行時(shí)改變Server列表
  • NoOpLoadBalancer 什么操作都不做
  • ZoneAwareLoadBalancer 功能主要是根據(jù)區(qū)域Zone分組的實(shí)例列表

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot詳細(xì)探究講解默認(rèn)組件掃描

    SpringBoot詳細(xì)探究講解默認(rèn)組件掃描

    在項(xiàng)目中我們創(chuàng)建了Controller,這個(gè)Controller是如何被spring自動(dòng)加載的呢?為什么Controller必須放在啟動(dòng)類(lèi)的同級(jí)目錄下呢
    2022-06-06
  • Java中關(guān)于控制臺(tái)讀取數(shù)字或字符串的方法

    Java中關(guān)于控制臺(tái)讀取數(shù)字或字符串的方法

    下面小編就為大家?guī)?lái)一篇Java中關(guān)于控制臺(tái)讀取數(shù)字或字符串的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-10-10
  • java反射調(diào)用方法NoSuchMethodException的解決方案

    java反射調(diào)用方法NoSuchMethodException的解決方案

    這篇文章主要介紹了java反射調(diào)用方法NoSuchMethodException的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java ThreadLocal的設(shè)計(jì)理念與作用

    Java ThreadLocal的設(shè)計(jì)理念與作用

    這篇文章主要介紹了Java ThreadLocal的設(shè)計(jì)理念與作用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • java基礎(chǔ)之字符串編碼知識(shí)點(diǎn)總結(jié)

    java基礎(chǔ)之字符串編碼知識(shí)點(diǎn)總結(jié)

    這篇文章主要介紹了java基礎(chǔ)之字符串編碼總結(jié),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,要的朋友可以參考下
    2021-04-04
  • springcloud config配置讀取優(yōu)先級(jí)過(guò)程詳解

    springcloud config配置讀取優(yōu)先級(jí)過(guò)程詳解

    這篇文章主要介紹了springcloud config配置讀取優(yōu)先級(jí)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Maven 錯(cuò)誤找不到符號(hào)的解決方法

    Maven 錯(cuò)誤找不到符號(hào)的解決方法

    這篇文章主要介紹了Maven 錯(cuò)誤找不到符號(hào)的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • idea的easyCode的 MybatisPlus模板的配置詳解

    idea的easyCode的 MybatisPlus模板的配置詳解

    這篇文章主要介紹了idea的easyCode的 MybatisPlus模板的配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 基于SpringBoot實(shí)現(xiàn)IP黑白名單的詳細(xì)步驟

    基于SpringBoot實(shí)現(xiàn)IP黑白名單的詳細(xì)步驟

    IP黑白名單是網(wǎng)絡(luò)安全管理中常見(jiàn)的策略工具,用于控制網(wǎng)絡(luò)訪問(wèn)權(quán)限,根據(jù)業(yè)務(wù)場(chǎng)景的不同,其應(yīng)用范圍廣泛,比如比較容易被盜刷的短信接口、文件接口,都需要添加IP黑白名單加以限制,所以本文給大家介紹了基于SpringBoot實(shí)現(xiàn)IP黑白名單的詳細(xì)步驟,需要的朋友可以參考下
    2024-01-01
  • springboot項(xiàng)目賬戶(hù)注冊(cè)邏輯功能實(shí)現(xiàn)

    springboot項(xiàng)目賬戶(hù)注冊(cè)邏輯功能實(shí)現(xiàn)

    這篇文章主要介紹了springboot項(xiàng)目賬戶(hù)注冊(cè)邏輯功能實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12

最新評(píng)論

武义县| 武邑县| 伊宁县| 晴隆县| 盘山县| 崇州市| 灵川县| 峨眉山市| 伊川县| 宁强县| 军事| 湖口县| 武城县| 纳雍县| 彭山县| 四会市| 肇庆市| 建瓯市| 易门县| 桂阳县| 临泉县| 常山县| 桃园县| 甘谷县| 泾源县| 西吉县| 北流市| 正蓝旗| 镇宁| 平昌县| 美姑县| 吉水县| 洪湖市| 航空| 临武县| 天气| 大冶市| 朔州市| 常宁市| 绥德县| 阿荣旗|