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

Feign調(diào)用服務(wù)時丟失Cookie和Header信息的解決方案

 更新時間:2022年03月14日 14:58:50   作者:迷霧總會解  
這篇文章主要介紹了Feign調(diào)用服務(wù)時丟失Cookie和Header信息的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

Feign調(diào)用服務(wù)丟失Cookie和Header信息

今天在使用Feign調(diào)用其他微服務(wù)的接口時,發(fā)現(xiàn)了一個問題:因為我的項目采用了無狀態(tài)登錄,token信息是存放在cookie中的,所以調(diào)用接口時,因為cookie中沒有token信息,我的請求被攔截器攔截了。 

參考幾篇文章,靠譜的解決方法是:將cookie信息放到請求頭中,再進(jìn)行調(diào)用接口時,攔截器中可以對請求頭進(jìn)行解析,獲取cookie信息

服務(wù)調(diào)用方

package top.codekiller.manager.upload.config;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
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.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/**
 * @author codekiller
 * @date 2020/5/26 14:22
 * 
 *   自定義的請求頭處理類,處理服務(wù)發(fā)送時的請求頭;
 *   將服務(wù)接收到的請求頭中的uniqueId和token字段取出來,并設(shè)置到新的請求頭里面去轉(zhuǎn)發(fā)給下游服務(wù)
 *   比如A服務(wù)收到一個請求,請求頭里面包含uniqueId和token字段,A處理時會使用Feign客戶端調(diào)用B服務(wù)
 *   那么uniqueId和token這兩個字段就會添加到請求頭中一并發(fā)給B服務(wù);
 */
@Configuration
@Slf4j
public class FeignHeaderConfiguration {
    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attrs != null) {
                HttpServletRequest request = attrs.getRequest();
                // 如果在Cookie內(nèi)通過如下方式取
                Cookie[] cookies = request.getCookies();
                if (cookies != null && cookies.length > 0) {
                    for (Cookie cookie : cookies) {
                        requestTemplate.header(cookie.getName(), cookie.getValue());
                        System.out.println("信息"+cookie.getName()+cookie.getValue());
                    }
                } else {
                    log.warn("FeignHeadConfiguration", "獲取Cookie失?。?);
                }
                
                // 如果放在header內(nèi)通過如下方式取
                Enumeration<String> headerNames = request.getHeaderNames();
                if (headerNames != null) {
                    while (headerNames.hasMoreElements()) {
                        String name = headerNames.nextElement();
                        String value = request.getHeader(name);
                        /**
                         * 遍歷請求頭里面的屬性字段,將jsessionid添加到新的請求頭中轉(zhuǎn)發(fā)到下游服務(wù)
                         * */
                        if ("jsessionid".equalsIgnoreCase(name)) {
                            log.debug("添加自定義請求頭key:" + name + ",value:" + value);
                            requestTemplate.header(name, value);
                        } else {
                            log.debug("FeignHeadConfiguration", "非自定義請求頭key:" + name + ",value:" + value + "不需要添加!");
                        }
                    }
                } else {
                    log.warn("FeignHeadConfiguration", "獲取請求頭失?。?);
                }
            }
        };
    }
}

服務(wù)接受方

//有些請求時從通過feign進(jìn)行請求的,這一部分請求時不包含cookie信息的,因此我們要從請求頭中獲取
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    String value = request.getHeader(name);
                    System.out.println("header的信息"+name+"::::"+value);
                    if(name.equalsIgnoreCase("MC_TOKEN")){  //注意這里變成了小寫
                        token=value;
                    }
                }
            }

運行的時候,我發(fā)現(xiàn)請求還是被攔截了,看了下打印信息,發(fā)現(xiàn)我的MC_TOKEN變成了小寫,所以在字符串進(jìn)行比較的時候要忽略大小寫。

以下為擴展,僅僅記錄一下

這樣仍然有個問題:

在開啟熔斷器之后,方法里的attrs是null,因為熔斷器默認(rèn)的隔離策略是thread,也就是線程隔離,實際上接收到的對象和這個在發(fā)送給B不是一個線程,怎么辦?

有一個辦法,修改隔離策略hystrix.command.default.execution.isolation.strategy=SEMAPHORE,改為信號量的隔離模式,但是不推薦,因為thread是默認(rèn)的,而且要命的是信號量模式,熔斷器不生效,比如設(shè)置了熔斷時間。

另一個辦法:重寫Feign的隔離策略

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的隔離策略;
 * 在轉(zhuǎn)發(fā)Feign的請求頭的時候,如果開啟了Hystrix,Hystrix的默認(rèn)隔離策略是Thread(線程隔離策略),因此轉(zhuǎn)發(fā)攔截器內(nèi)是無法獲取到請求的請求頭信息的,可以修改默認(rèn)隔離策略為信號量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE,這樣的話轉(zhuǎn)發(fā)線程和請求線程實際上是一個線程,這并不是最好的解決方法,信號量模式也不是官方最為推薦的隔離策略;另一個解決方法就是自定義Hystrix的隔離策略,思路是將現(xiàn)有的并發(fā)策略作為新并發(fā)策略的成員變量,在新并發(fā)策略中,返回現(xiàn)有并發(fā)策略的線程池、Queue;將策略加到Spring容器即可;
 *
 */
@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();
            }
        }
    }
}

然后使用默認(rèn)的熔斷器隔離策略,也可以在攔截器內(nèi)獲取到上游服務(wù)的請求頭信息了;

Feign調(diào)用存在的問題

① feign遠(yuǎn)程調(diào)用丟失請求頭

問題描述:

當(dāng)遠(yuǎn)程調(diào)用其他服務(wù)時,設(shè)置了攔截器判斷用戶是否登錄,但是結(jié)果是即使用戶登錄了,也會顯示用戶沒登錄,原因在于遠(yuǎn)程調(diào)用時,發(fā)送的請求是一個新的情求,請求中并不存在cookie,而原始請求中是攜帶cookie的。

解決方案如下:

@Configuration
public class MallFeignConfig {
    @Bean("requestInterceptor")
    public RequestInterceptor requestInterceptor() {
        RequestInterceptor requestInterceptor = template -> {
            //1、使用RequestContextHolder拿到剛進(jìn)來的請求數(shù)據(jù)
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (requestAttributes != null) {
                //老請求
                HttpServletRequest request = requestAttributes.getRequest();
                if (request != null) {
                    //2、同步請求頭的數(shù)據(jù)(主要是cookie)
                    //把老請求的cookie值放到新請求上來,進(jìn)行一個同步
                    String cookie = request.getHeader("Cookie");
                    template.header("Cookie", cookie);
                }
            }
        };
        return requestInterceptor;
    }
}

② 異步調(diào)用Feign丟失上下文問題

問題描述:

由于feign請求攔截器為新的request設(shè)置請求頭底層是使用ThreadLocal保存剛進(jìn)來的請求,所以在異步情況下,其他線程并不能獲取到主線程的ThreadLocal,所以也拿不到請求。

解決:

先獲取主線程的requestAttributes,再分別向其他線程中設(shè)置

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture.runAsync(() ->{
   RequestContextHolder.setRequestAttributes(requestAttributes);
});

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java多線程下載文件實例詳解

    Java多線程下載文件實例詳解

    這篇文章主要為大家詳細(xì)介紹了Java多線程下載文件的實例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • SpringBoot淺析依賴管理與自動配置概念與使用

    SpringBoot淺析依賴管理與自動配置概念與使用

    一般來講SpringBoot項目是不需要指定版本,而SSM項目是需要指定版本,SpringBoot的核心依賴就是spring-boot-starter-parent和spring-boot-starter-web兩個依賴,這篇文章主要介紹了SpringBoot依賴管理與自動配置概念與使用
    2022-10-10
  • Java FileWriter輸出換行操作

    Java FileWriter輸出換行操作

    這篇文章主要介紹了Java FileWriter輸出換行操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • RandomAccessFile簡介_動力節(jié)點Java學(xué)院整理

    RandomAccessFile簡介_動力節(jié)點Java學(xué)院整理

    RandomAccessFile 是隨機訪問文件(包括讀/寫)的類。它支持對文件隨機訪問的讀取和寫入,即我們可以從指定的位置讀取/寫入文件數(shù)據(jù)。這篇文章主要介紹了RandomAccessFile簡介,需要的朋友可以參考下
    2017-05-05
  • Java Spring開發(fā)環(huán)境搭建及簡單入門示例教程

    Java Spring開發(fā)環(huán)境搭建及簡單入門示例教程

    這篇文章主要介紹了Java Spring開發(fā)環(huán)境搭建及簡單入門示例,結(jié)合實例形式分析了spring環(huán)境搭建、配置、使用方法及相關(guān)注意事項,需要的朋友可以參考下
    2017-11-11
  • Spring中WebClient的創(chuàng)建和使用詳解

    Spring中WebClient的創(chuàng)建和使用詳解

    這篇文章主要介紹了Spring中WebClient的創(chuàng)建和使用詳解,在Spring5中,出現(xiàn)了Reactive響應(yīng)式編程思想,并且為網(wǎng)絡(luò)編程提供相關(guān)響應(yīng)式編程的支持,如提供了WebFlux,它是Spring提供的異步非阻塞的響應(yīng)式的網(wǎng)絡(luò)框架,需要的朋友可以參考下
    2023-11-11
  • Spring MVC Controller返回值及異常的統(tǒng)一處理方法

    Spring MVC Controller返回值及異常的統(tǒng)一處理方法

    這篇文章主要給大家介紹了關(guān)于Spring MVC Controller返回值及異常的統(tǒng)一處理方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Spring MVC具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Java中的zookeeper常用命令詳解

    Java中的zookeeper常用命令詳解

    這篇文章主要介紹了Java中的zookeeper常用命令,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • 詳解Java中的八種單例創(chuàng)建方式

    詳解Java中的八種單例創(chuàng)建方式

    單例設(shè)計模式,就是采取一定的方法保證在整個的軟件系統(tǒng)中,對某個類只能存在一個對象實例,并且該類只提供一個取得其對象實例的方法。本文將詳細(xì)介紹Java中單例的八種創(chuàng)建方式,需要的可以參考一下
    2022-02-02
  • java web返回中文亂碼問題及解決

    java web返回中文亂碼問題及解決

    這篇文章主要介紹了java web返回中文亂碼問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05

最新評論

博湖县| 阿合奇县| 苏尼特左旗| 峡江县| 雅江县| 台州市| 深水埗区| 大石桥市| 修水县| 胶州市| 濉溪县| 桐乡市| 海阳市| 惠安县| 塔河县| 台中县| 台中县| 平罗县| 通辽市| 梨树县| 印江| 兴山县| 和林格尔县| 交口县| 白山市| 嘉禾县| 邳州市| 桃园市| 荣昌县| 视频| 渑池县| 吴堡县| 都昌县| 曲水县| 临邑县| 牟定县| 津市市| 永新县| 博爱县| 玛沁县| 呼和浩特市|