1. 程式人生 > 其它 >feign服務中呼叫,傳遞token

feign服務中呼叫,傳遞token

預設spring-boot 微服務中 用feign來做服務間呼叫,是不會攜帶token傳遞的。為了能讓服務間呼叫的時候帶上token,需要進行配置,增強resTemplate  

1、先實現請求攔截器

/**
 * feign配置token
 */
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 這裡可以新增feign請求的全域性引數
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes == null || attributes.getRequest() == null) { return; } HttpServletRequest request = attributes.getRequest(); requestTemplate.header(
"token", request.getHeader("token")); requestTemplate.header("feignClient", "ifaas-hotel-robot-platform"); } }
 

2.在@FeignClient接口裡新增configuration = {FeignConfig.class}

@FeignClient(name="被呼叫的服務名", configuration={FeignRequestInterceptor .class})

 

由於feign的熔斷器hystrix的隔離策略的原因,feign呼叫執行緒和主執行緒隔離了,請求上下文不共用,導致feign攔截器中 RequestContextHolder.getRequestAttributes()為空   原因: 此屬性指示HystrixCommand.run()執行哪種隔離策略,是以下兩種選擇之一:
  • THREAD —它在單獨的執行緒上執行,併發請求受執行緒池中執行緒數的限制
  • SEMAPHORE —它在呼叫執行緒上執行,併發請求受訊號量限制
 

3、解決feign中RequestContextHolder.getRequestAttributes()為null方案有兩種:

3.1、修改隔離策略:預設是 採用THREAD ,修改成SEMAPHORE 即可,但是不推薦這種做法,因為併發請求收到限制。

 

3.2、自定義feign的併發策略 繼承HystrixConcurrencyStrategy,然後重寫wrapCallable方法。如下:

import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


import java.util.concurrent.Callable;

/**
 * Created by YangGuanRong
 * date: 2022/3/8
 */
@Slf4j
@Primary
@Component
public class CustomFeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

    public CustomFeignHystrixConcurrencyStrategy() {
        try {

            HystrixPlugins.getInstance().registerConcurrencyStrategy(this);

        } catch (Exception e) {
            log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
        }
    }


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


    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;
        }

        /**
         * feign opens the fuse (hystrix): feign.hystrix.enabled=ture, and uses the default signal isolation level,
         * The HttpServletRequest object is independent of each other in the parent thread and the child thread and is not shared.
         * So the HttpServletRequest data of the parent thread used in the child thread is null,
         * naturally it is impossible to obtain the token information of the request header In a multithreaded environment, call before the request, set the context before the call
         *
         * feign啟用了hystrix,並且feign.hystrix.enabled=ture。採用了執行緒隔離策略。
         * HttpServletRequest 請求在物件在父執行緒和子執行緒中相互獨立,且不共享
         * 所以父執行緒的 HttpServletRequest 在子執行緒中為空,
         * 所以通常 在多執行緒環境中,在請求呼叫之前設定上下文
         * @return T
         * @throws Exception Exception
         */
        @Override
        public T call() throws Exception {
            try {
                // Set true to share the parent thread's HttpServletRequest object setting
                RequestContextHolder.setRequestAttributes(requestAttributes, true);
                return target.call();
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        }
    }
}