中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringCloud Feign轉發請求頭并防止session失效的解決方法

發布時間:2020-10-27 14:13:26 來源:億速云 閱讀:272 作者:Leah 欄目:開發技術

SpringCloud Feign轉發請求頭并防止session失效的解決方法?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

微服務開發中經常有這樣的需求,公司自定義了通用的請求頭,需要在微服務的調用鏈中轉發,比如在請求頭中加入了token,或者某個自定義的信息uniqueId,總之就是自定義的一個鍵值對的東東,A服務調用B服務,B服務調用C服務,這樣通用的東西如何讓他在一個調用鏈中不斷地傳遞下去呢?以A服務為例:

方案1

最傻的辦法,在程序中獲取,調用B的時候再轉發,怎么獲取在Controller中國通過注解獲取,或者通過request對象獲取,這個不難,在請求B服務的時候,通過注解將值放進去即可;簡代碼如下:
獲取:
@RequestMapping(value = "/api/test", method = RequestMethod.GET)
public String testFun(@RequestParam String name, @RequestHeader("uniqueId") String uniqueId) {
  if(uniqueId == null ){
     return "Must defined the uniqueId , it can not be null";
  }
  log.info(uniqueId, "begin testFun... ");
 return uniqueId;
}

然后A使用Feign調用B服務的時候,傳過去:

@FeignClient(value = "DEMO-SERVICE")
public interface CallClient {

  /**
 * 訪問DEMO-SERVICE服務的/api/test接口,通過注解將logId傳遞給下游服務
 */
 @RequestMapping(value = "/api/test", method = RequestMethod.GET)
  String callApiTest(@RequestParam(value = "name") String name, @RequestHeader(value = "uniqueId") String uniqueId);

}

方案弊端:毫無疑問,這方案不好,因為對代碼有侵入,需要開發人員沒次手動的獲取和添加,因此舍棄

方案2

服務通過請求攔截器,在請求從A發送到B之后,在攔截器內將自己需要的東東加到請求頭:
import com.intellif.log.LoggerUtilI;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

/**
 * 自定義的請求頭處理類,處理服務發送時的請求頭;
 * 將服務接收到的請求頭中的uniqueId和token字段取出來,并設置到新的請求頭里面去轉發給下游服務
 * 比如A服務收到一個請求,請求頭里面包含uniqueId和token字段,A處理時會使用Feign客戶端調用B服務
 * 那么uniqueId和token這兩個字段就會添加到請求頭中一并發給B服務;
 *
 * @author mozping
 * @version 1.0
 * @date 2018/6/27 14:13
 * @see FeignHeadConfiguration
 * @since JDK1.8
 */
@Configuration
public class FeignHeadConfiguration {
  private final LoggerUtilI logger = LoggerUtilI.getLogger(this.getClass().getName());

  @Bean
  public RequestInterceptor requestInterceptor() {
    return requestTemplate -> {
      ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      if (attrs != null) {
        HttpServletRequest request = attrs.getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
          while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            String value = request.getHeader(name);
            /**
             * 遍歷請求頭里面的屬性字段,將logId和token添加到新的請求頭中轉發到下游服務
             * */
            if ("uniqueId".equalsIgnoreCase(name) || "token".equalsIgnoreCase(name)) {
              logger.debug("添加自定義請求頭key:" + name + ",value:" + value);
              requestTemplate.header(name, value);
            } else {
              logger.debug("FeignHeadConfiguration", "非自定義請求頭key:" + name + ",value:" + value + "不需要添加!");
            }
          }
        } else {
          logger.warn("FeignHeadConfiguration", "獲取請求頭失敗!");
        }
      }
    };
  }

}

網上很多關于這種方法的博文或者資料,大同小異,但是有一個問題,在開啟熔斷器之后,這里的attrs就是null,因為熔斷器默認的隔離策略是thread,也就是線程隔離,實際上接收到的對象和這個在發送給B不是一個線程,怎么辦?有一個辦法,修改隔離策略hystrix.command.default.execution.isolation.strategy=SEMAPHORE,改為信號量的隔離模式,但是不推薦,因為thread是默認的,而且要命的是信號量模式,熔斷器不生效,比如設置了熔斷時間hystrix.command.default.execution.isolation.semaphore.timeoutInMilliseconds=5000,五秒,如果B服務里面sleep了10秒,非得等到B執行完畢再返回,因此這個方案也不可取;但是有什么辦法可以在默認的Thread模式下讓攔截器拿到上游服務的請求頭?自定義策略:代碼如下:

import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 自定義Feign的隔離策略;
 * 在轉發Feign的請求頭的時候,如果開啟了Hystrix,Hystrix的默認隔離策略是Thread(線程隔離策略),因此轉發攔截器內是無法獲取到請求的請求頭信息的,可以修改默認隔離策略為信號量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE,這樣的話轉發線程和請求線程實際上是一個線程,這并不是最好的解決方法,信號量模式也不是官方最為推薦的隔離策略;另一個解決方法就是自定義Hystrix的隔離策略,思路是將現有的并發策略作為新并發策略的成員變量,在新并發策略中,返回現有并發策略的線程池、Queue;將策略加到Spring容器即可;
 *
 * @author mozping
 * @version 1.0
 * @date 2018/7/5 9:08
 * @see FeignHystrixConcurrencyStrategyIntellif
 * @since JDK1.8
 */
@Component
public class FeignHystrixConcurrencyStrategyIntellif extends HystrixConcurrencyStrategy {

  private static final Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategyIntellif.class);
  private HystrixConcurrencyStrategy delegate;

  public FeignHystrixConcurrencyStrategyIntellif() {
    try {
      this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
      if (this.delegate instanceof FeignHystrixConcurrencyStrategyIntellif) {
        // Welcome to singleton hell...
        return;
      }
      HystrixCommandExecutionHook commandExecutionHook =
          HystrixPlugins.getInstance().getCommandExecutionHook();
      HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
      HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
      HystrixPropertiesStrategy propertiesStrategy =
          HystrixPlugins.getInstance().getPropertiesStrategy();
      this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);
      HystrixPlugins.reset();
      HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
      HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
      HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
      HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
      HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
    } catch (Exception e) {
      log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
    }
  }

  private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
                         HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) {
    if (log.isDebugEnabled()) {
      log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
          + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
          + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
      log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
    }
  }

  @Override
  public <T> Callable<T> wrapCallable(Callable<T> callable) {
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    return new WrappedCallable<>(callable, requestAttributes);
  }

  @Override
  public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                      HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize,
                      HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
        unit, workQueue);
  }

  @Override
  public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                      HystrixThreadPoolProperties threadPoolProperties) {
    return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
  }

  @Override
  public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
    return this.delegate.getBlockingQueue(maxQueueSize);
  }

  @Override
  public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
    return this.delegate.getRequestVariable(rv);
  }

  static class WrappedCallable<T> implements Callable<T> {
    private final Callable<T> target;
    private final RequestAttributes requestAttributes;

    public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
      this.target = target;
      this.requestAttributes = requestAttributes;
    }

    @Override
    public T call() throws Exception {
      try {
        RequestContextHolder.setRequestAttributes(requestAttributes);
        return target.call();
      } finally {
        RequestContextHolder.resetRequestAttributes();
      }
    }
  }
}

然后使用默認的熔斷器隔離策略,也可以在攔截器內獲取到上游服務的請求頭信息了

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

正定县| 澄江县| 长春市| 通化市| 宣化县| 抚远县| 青海省| 新兴县| 杭州市| 威宁| 定日县| 常熟市| 河西区| 苍山县| 万全县| 通辽市| 浪卡子县| 武城县| 沈丘县| 皮山县| 门头沟区| 安阳市| 祁阳县| 峨眉山市| 特克斯县| 休宁县| 汤阴县| 镇江市| 寿阳县| 甘南县| 高邑县| 贡觉县| 新乡市| 社会| 乌鲁木齐县| 扎赉特旗| 当涂县| 彭阳县| 安仁县| 静宁县| 灌云县|