欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

SpringCloud請求重試機(jī)制的示例分析

這篇文章主要為大家展示了“Spring Cloud請求重試機(jī)制的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Spring Cloud請求重試機(jī)制的示例分析”這篇文章吧。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長期合作伙伴,公司提供的服務(wù)項(xiàng)目有:空間域名、虛擬空間、營銷軟件、網(wǎng)站建設(shè)、靖邊網(wǎng)站維護(hù)、網(wǎng)站推廣。

場景

發(fā)布微服務(wù)的操作一般都是打完新代碼的包,kill掉在跑的應(yīng)用,替換新的包,啟動。

spring cloud 中使用eureka為注冊中心,它是允許服務(wù)列表數(shù)據(jù)的延遲性的,就是說即使應(yīng)用已經(jīng)不在服務(wù)列表了,客戶端在一段時(shí)間內(nèi)依然會請求這個(gè)地址。那么就會出現(xiàn)請求正在發(fā)布的地址,而導(dǎo)致失敗。

我們會優(yōu)化服務(wù)列表的刷新時(shí)間,以提高服務(wù)列表信息的時(shí)效性。但是無論怎樣,都無法避免有那么一段時(shí)間是數(shù)據(jù)不一致的。

所以我們想到一個(gè)辦法就是重試機(jī)制,當(dāng)a機(jī)子在重啟時(shí),同個(gè)集群的b是可以正常提供服務(wù)的,如果有重試機(jī)制就可以在上面這個(gè)場景里進(jìn)行重試到b而不影響正確響應(yīng)。

操作

需要進(jìn)行如下的操作:

ribbon:
 ReadTimeout: 10000
 ConnectTimeout: 10000
 MaxAutoRetries: 0
 MaxAutoRetriesNextServer: 1
 OkToRetryOnAllOperations: false

引入spring-retry包

<dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
 </dependency>

以zuul為例子還需要配置開啟重試:

zuul.retryable=true

遇到了問題

然而萬事總沒那么一帆風(fēng)順,通過測試重試機(jī)制生效了,但是并沒有我想象的去請求另一臺健康的機(jī)子,于是被迫去吧開源碼看一看,最終發(fā)現(xiàn)是源碼的bug,不過已經(jīng)修復(fù),升級版本即可。

代碼分析

使用的版本是

spring-cloud-netflix-core:1.3.6.RELEASE

spring-retry:1.2.1.RELEASE

spring cloud 依賴版本:

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

因?yàn)閱⒂昧酥卦?,所以請求?yīng)用時(shí)會執(zhí)行RetryableRibbonLoadBalancingHttpClient.execute方法:

public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
    final RequestConfig.Builder builder = RequestConfig.custom();
    IClientConfig config = configOverride != null ? configOverride : this.config;
    builder.setConnectTimeout(config.get(
        CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
    builder.setSocketTimeout(config.get(
        CommonClientConfigKey.ReadTimeout, this.readTimeout));
    builder.setRedirectsEnabled(config.get(
        CommonClientConfigKey.FollowRedirects, this.followRedirects));

    final RequestConfig requestConfig = builder.build();
    final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
    RetryCallback retryCallback = new RetryCallback() {
      @Override
      public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception {
        //on retries the policy will choose the server and set it in the context
        //extract the server and update the request being made
        RibbonApacheHttpRequest newRequest = request;
        if(context instanceof LoadBalancedRetryContext) {
          ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
          if(service != null) {
            //Reconstruct the request URI using the host and port set in the retry context
            newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(),
                newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(),
                newRequest.getURI().getPath(), newRequest.getURI().getQuery(),
                newRequest.getURI().getFragment()));
          }
        }
        newRequest = getSecureRequest(request, configOverride);
        HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
        final HttpResponse httpResponse = RetryableRibbonLoadBalancingHttpClient.this.delegate.execute(httpUriRequest);
        if(retryPolicy.retryableStatusCode(httpResponse.getStatusLine().getStatusCode())) {
          if(CloseableHttpResponse.class.isInstance(httpResponse)) {
            ((CloseableHttpResponse)httpResponse).close();
          }
          throw new RetryableStatusCodeException(RetryableRibbonLoadBalancingHttpClient.this.clientName,
              httpResponse.getStatusLine().getStatusCode());
        }
        return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
      }
    };
    return this.executeWithRetry(request, retryPolicy, retryCallback);
  }

我們發(fā)現(xiàn)先new 一個(gè)RetryCallback,然后執(zhí)行this.executeWithRetry(request, retryPolicy, retryCallback);

而這個(gè)RetryCallback.doWithRetry的代碼我們清楚看到是實(shí)際請求的代碼,也就是說this.executeWithRetry方法最終還是會調(diào)用RetryCallback.doWithRetry

protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,
      RecoveryCallback<T> recoveryCallback, RetryState state)
      throws E, ExhaustedRetryException {

    RetryPolicy retryPolicy = this.retryPolicy;
    BackOffPolicy backOffPolicy = this.backOffPolicy;

    // Allow the retry policy to initialise itself...
    RetryContext context = open(retryPolicy, state);
    if (this.logger.isTraceEnabled()) {
      this.logger.trace("RetryContext retrieved: " + context);
    }

    // Make sure the context is available globally for clients who need
    // it...
    RetrySynchronizationManager.register(context);

    Throwable lastException = null;

    boolean exhausted = false;
    try {

      // Give clients a chance to enhance the context...
      boolean running = doOpenInterceptors(retryCallback, context);

      if (!running) {
        throw new TerminatedRetryException(
            "Retry terminated abnormally by interceptor before first attempt");
      }

      // Get or Start the backoff context...
      BackOffContext backOffContext = null;
      Object resource = context.getAttribute("backOffContext");

      if (resource instanceof BackOffContext) {
        backOffContext = (BackOffContext) resource;
      }

      if (backOffContext == null) {
        backOffContext = backOffPolicy.start(context);
        if (backOffContext != null) {
          context.setAttribute("backOffContext", backOffContext);
        }
      }

      /*
       * We allow the whole loop to be skipped if the policy or context already
       * forbid the first try. This is used in the case of external retry to allow a
       * recovery in handleRetryExhausted without the callback processing (which
       * would throw an exception).
       */
      while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {

        try {
          if (this.logger.isDebugEnabled()) {
            this.logger.debug("Retry: count=" + context.getRetryCount());
          }
          // Reset the last exception, so if we are successful
          // the close interceptors will not think we failed...
          lastException = null;
          return retryCallback.doWithRetry(context);
        }
        catch (Throwable e) {

          lastException = e;

          try {
            registerThrowable(retryPolicy, state, context, e);
          }
          catch (Exception ex) {
            throw new TerminatedRetryException("Could not register throwable",
                ex);
          }
          finally {
            doOnErrorInterceptors(retryCallback, context, e);
          }

          if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
            try {
              backOffPolicy.backOff(backOffContext);
            }
            catch (BackOffInterruptedException ex) {
              lastException = e;
              // back off was prevented by another thread - fail the retry
              if (this.logger.isDebugEnabled()) {
                this.logger
                    .debug("Abort retry because interrupted: count="
                        + context.getRetryCount());
              }
              throw ex;
            }
          }

          if (this.logger.isDebugEnabled()) {
            this.logger.debug(
                "Checking for rethrow: count=" + context.getRetryCount());
          }

          if (shouldRethrow(retryPolicy, context, state)) {
            if (this.logger.isDebugEnabled()) {
              this.logger.debug("Rethrow in retry for policy: count="
                  + context.getRetryCount());
            }
            throw RetryTemplate.<E>wrapIfNecessary(e);
          }

        }

        /*
         * A stateful attempt that can retry may rethrow the exception before now,
         * but if we get this far in a stateful retry there's a reason for it,
         * like a circuit breaker or a rollback classifier.
         */
        if (state != null && context.hasAttribute(GLOBAL_STATE)) {
          break;
        }
      }

      if (state == null && this.logger.isDebugEnabled()) {
        this.logger.debug(
            "Retry failed last attempt: count=" + context.getRetryCount());
      }

      exhausted = true;
      return handleRetryExhausted(recoveryCallback, context, state);

    }
    catch (Throwable e) {
      throw RetryTemplate.<E>wrapIfNecessary(e);
    }
    finally {
      close(retryPolicy, context, state, lastException == null || exhausted);
      doCloseInterceptors(retryCallback, context, lastException);
      RetrySynchronizationManager.clear();
    }
  }

在一個(gè)while循環(huán)里實(shí)現(xiàn)重試機(jī)制,當(dāng)執(zhí)行retryCallback.doWithRetry(context)出現(xiàn)異常的時(shí)候,就會catch異常,然后用 retryPolicy判斷是否進(jìn)行重試,特別注意registerThrowable(retryPolicy, state, context, e);方法,不但判斷了是否重試,在重試情況下會新選出一個(gè)機(jī)子放入context,然后再去執(zhí)行retryCallback.doWithRetry(context)時(shí)帶入,如此就實(shí)現(xiàn)了換機(jī)子重試了。

但是我的配置怎么會沒有換機(jī)子呢?調(diào)試代碼發(fā)現(xiàn)registerThrowable(retryPolicy, state, context, e);選出來的機(jī)子沒問題,就是新的健康的機(jī)子,但是在執(zhí)行retryCallback.doWithRetry(context)代碼的時(shí)候依然請求的是那臺掛掉的機(jī)子。

所以我們再仔細(xì)看一下retryCallback.doWithRetry(context)的代碼:

我們發(fā)現(xiàn)了這行代碼:

newRequest = getSecureRequest(request, configOverride);
protected RibbonApacheHttpRequest getSecureRequest(RibbonApacheHttpRequest request, IClientConfig configOverride) {
    if (isSecure(configOverride)) {
      final URI secureUri = UriComponentsBuilder.fromUri(request.getUri())
          .scheme("https").build(true).toUri();
      return request.withNewUri(secureUri);
    }
    return request;
  }

newRequest在前面已經(jīng)使用context構(gòu)建完畢,request是上一次請求的數(shù)據(jù),只要執(zhí)行這個(gè)代碼就會發(fā)現(xiàn)newRequest永遠(yuǎn)都會被request覆蓋??吹竭@里我們才發(fā)現(xiàn)原來是一個(gè)源碼bug。

以上是“Spring Cloud請求重試機(jī)制的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

網(wǎng)站欄目:SpringCloud請求重試機(jī)制的示例分析
分享路徑:http://chinadenli.net/article24/jgjcje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)網(wǎng)站策劃、軟件開發(fā)網(wǎng)站內(nèi)鏈、企業(yè)網(wǎng)站制作、品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作
国产精品涩涩成人一区二区三区 | 高清在线精品一区二区| 欧美日韩国产亚洲三级理论片| 在线观看欧美视频一区| 国产精品香蕉免费手机视频| 五月婷日韩中文字幕四虎| 五月激情综合在线视频| 视频一区日韩经典中文字幕| 老司机精品视频免费入口 | 久久精品中文扫妇内射| 日本理论片午夜在线观看| 日本一区二区三区久久娇喘| 精品人妻精品一区二区三区| 激情图日韩精品中文字幕| 国内精品偷拍视频久久| 真实国产乱子伦对白视频不卡| 日本熟女中文字幕一区| 欧美野外在线刺激在线观看| 亚洲国产av一二三区| 日本黄色高清视频久久| 日韩精品少妇人妻一区二区| 又黄又爽禁片视频在线观看| 欧美在线观看视频免费不卡| 日本午夜免费啪视频在线| 国产精品流白浆无遮挡| 激情五月天深爱丁香婷婷| 日韩欧美综合中文字幕| 成人午夜爽爽爽免费视频| 亚洲国产精品久久精品成人| 日本丰满大奶熟女一区二区| 日韩成人中文字幕在线一区| 初尝人妻少妇中文字幕在线| 国产精品免费视频久久| 色婷婷亚洲精品综合网| 99香蕉精品视频国产版| 国产又粗又猛又爽又黄的文字| 99久久精品国产日本| 久久精品视频就在久久| 成年午夜在线免费视频| 欧美综合色婷婷欧美激情| 日本欧美三级中文字幕|