マイクロサービスにおけるFeign呼び出しおよびゲートウェイZuulのリクエストヘッダに関する問題

37373 ワード

マイクロサービスにおけるFeign呼び出しおよびゲートウェイZuulの要求ヘッダとフィールドに関する問題
文書ディレクトリ
  • マイクロサービスにおけるFeign呼び出しおよびゲートウェイZuulの要求ヘッダとフィールドに関する問題
  • 前言
  • 1.Feignリモートコール要求ヘッダ損失
  • 1.1原因分析
  • 1.2ソリューション1
  • 1.3ソリューション2
  • 2.Zuul対要求ヘッドの加工
  • 前言
    Spring CloudでFeignを使用してリモートコールを行うと、要求ヘッダ情報が失われ、下流サービスが上流サービス要求ヘッダを取得できないという問題が発生します.Zuulゲートウェイを組み合わせると、フロントフィルタを使用して上流サービスのリクエストヘッダ情報を変更、追加、またはフィルタリングして下流サービスに移動する必要がある場合があります.
    1.Feignリモートコール要求ヘッダの紛失
    構成されていない要求の下で、Feignはリモートコールを行うと、下流サービスが上流サービスから送信された要求ヘッダを取得できないことに気づきますが、多くの場合、要求ヘッダには関連ID、認証などの重要な情報が含まれています.
    1.1原因分析
    Feignはリモートコールを開始し、実際には@HystrixCommandコマンドによって開始されるが、Hystrixのコール分離ポリシーを理解すると、Hsyrixには2つの分離ポリシーがあり、1つのSEMAPHORE(信号量)は軽量レベルの分離ポリシーであり、上流サービスでリモートコールが発生すると、元のコールスレッドで直接開始される.もう1つのThread(スレッド)これはデフォルトの分離ポリシーであり、Hystrix公式に強力に推奨されているポリシーでもあり、THREADポリシーを使用すると別のサブスレッドがリモートコールされ、リモートコールで発生する可能性のあるエラーや異常が元のコールスレッドにも考えられない.
    下流サービスがリクエストヘッダ情報を受信できない原因は2つある.1つは、上流サービスがリクエストヘッダの新しい情報を受信した後、リクエストヘッダ情報をリモートコールのrequestにカプセル化していないことであり、ここではブロックFeignInterceptorを追加することによって追加することができる.二つ目は、FeignInterceptorが追加されても下流サービスではリクエストヘッダ情報を取得できないことがわかる.これは、FeignInterceptorでは、リクエストヘッダ情報をrequestにカプセル化する際にすでに失われているためであり、リクエストヘッダ情報を取得する際には、RequestContextHolderによって取得するのが一般的である.このクラスはRequestContext要求コンテキストをThreadLocalにカプセル化して使用しますが、Hystrixのデフォルトの分離ポリシーのため、サブスレッドと親スレッドの間でThreadLocalオブジェクトを渡すことはできません.
    1.2ソリューション1
    理由が分かれば2つの解決策があり、最も簡単で乱暴だが推奨しないのはHystrixの隔離戦略を変更し、デフォルトのThread戦略をSEMAPHORE戦略に変更することだが、政府が極力推奨しない以上、理解するしかないだろう.
    ①FeignInterceptorブロッカー、転送が必要なリクエストヘッダ情報を追加
    /**
     * @Author: Jam
     * @Date: 2020/5/30 13:12
     */
    @Component
    public class FeignInterceptor implements RequestInterceptor {
        @Override
        public void apply(RequestTemplate template) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                //header                   
                template.header("tmx-correlation-id",request.getHeader("tmx-correlation-id"));
                template.header("tmx-auth-token",request.getHeader("tmx-auth-token"));
                template.header"tmx-user-id",request.getHeader("tmx-user-id"));
                template.header("tmx-org-id",request.getHeader("tmx-org-id"));
            }
        }
    }
    

    ②@HystrixCommandの実行ポリシーの変更
    @HyscrixCommand(commandProperties={@HysrixProperty(name="execution.isolation.strategy",value="SEMAPHORE")})
    public void RemoteCall(){
        return feignClient.get();
    }
    

    以上,Hystrixの分離ポリシーをSEMAHOREに変更することでFeign呼び出しで要求ヘッダを渡すことができないことを解決した.でもお勧めしない、お勧めしない、お勧めしない!
    1.3ソリューション2
    Threadポリシーを使用してHystrix呼び出しを実行するには、親スレッド(元の呼び出しスレッド)のRequestContextをサブスレッドに渡す必要があります.Hystrixはコンテキストの伝播を完了する同時ポリシーメカニズムを提供しているので、カスタム同時ポリシーを使用するだけでサブスレッドに要求ヘッダ情報を取得できます.tips:Hystrixでは1つの同時ポリシーしか許可されていないため、同時ポリシーをカスタマイズすると、セキュリティを処理するための同時ポリシーが存在するため、統合します.
    ①コンカレントポリシーFeignConfigをカスタマイズし、HystrixConcurrencyStrategyクラスを継承
    @Component
    public class FeignConfig extends HystrixConcurrencyStrategy {
        private static final Logger log = LoggerFactory.getLogger(FeignConfig.class);
        private HystrixConcurrencyStrategy delegate; 
    
        public FeignConfig() {
            try {
                this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
                if (this.delegate instanceof FeignConfig) {
                    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 BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
            return this.delegate.getBlockingQueue(maxQueueSize);
        }
    
        @Override
        public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
            return this.delegate.getRequestVariable(rv);
        }
    
        //    Callable    ,              
        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();
                }
            }
        }
    }
    

    FeignConfigには2つのことが必要です.新しい同時ポリシーを登録するため、すべてのHystrixコンポーネントを取得し、リセットしてから再設定します.詳細はpublic Feign()メソッド、キーメソッドWrappedCallableメソッドのcall()メソッドを参照してください.このメソッドは@HystrixCommandの前に呼び出されます.このHystrixで管理されているサブスレッドは、親スレッドのRequestAttributesを取得し、要求ヘッダ情報を取得します.
    ②FeignInterceptorブロッカー、転送が必要なリクエストヘッダ情報を追加し、コードは1.2のコードと一致し、これ以上説明しないで、このブロッカークラスを漏らさないでください!カスタムコンカレントポリシーは、親子スレッドRequestContextの伝播の問題を完了しただけです.
    2.Zuul対要求ヘッドの加工
    本明細書で採用した@EnableZuulProxyは、他の起動クラス構成であれば適用されない場合があり、深く検討していないため、@EnableZuulProxyの構成のみをテストした.
    デフォルトでは、Zuulは上流からのリクエスト(リクエストヘッダ情報もすべて携帯)を直接転送しますが、ZuulはリクエストのHTTPリクエストヘッダを直接追加または変更することは許されません.追加し、後でフィルタで再アクセスできるようにするには、RequestContext.getZuulRequestHeadersおよび.addZuulRequestHeaders()を使用して取得および追加する必要があります.この方法では、個別のHTTPヘッダマッピングが維持され、Zuulサーバがターゲットサービスを呼び出すと、ZuulRequestHeaderマッピングに含まれるデータがマージされます.
    例:要求ヘッダ情報の追加、要求ヘッダ情報の変更、要求ヘッダ情報のフィルタ(削除)
    @Component
    public class TrackingFilter extends ZuulFilter {
        private static final int FILTER_ORDER=0;
        private static final boolean SHOULD_FILTER=true;
        private static final Logger logger= LoggerFactory.getLogger(TrackingFilter.class);
    
        @Override
        public boolean shouldFilter() {
            return SHOULD_FILTER;
        }
    
        private boolean isCorrelationIdPresent(){
            HttpServletRequest request=RequestContext.getCurrentContext().getRequest();
            return request.getHeader("tmx-correlation-id") != null;
        }
    
        @Override
        public Object run() throws ZuulException {
            //run()                  
            if(isCorrelationIdPresent()){
                logger.info("tmx-correlation-id found in tracking filter{}.",RequestContext.getCurrentContext().getRequest().getHeader("tmx-correlation-id"));
                //  Zuul      tmx-correlation-id   
                RequestContext.getCurrentContext().addZuulRequestHeader("tmx-correlation-id","123");
                //   tmx-correlation-id       123
                RequestContext.getCurrentContext().addZuulRequestHeader("tmx-zuul","123");
                //   tmx-zuul        123
            }else{
                logger.info("    ");
            }
            RequestContext ctx=RequestContext.getCurrentContext();
            logger.info("Processing incoming request for{}.",ctx.getRequest().getRequestURI());
            return null;
        }
    
        @Override
        public String filterType() {
            return PRE_TYPE; 
     //   String ERROR_TYPE = "error"; 
     //   String POST_TYPE = "post";
     //  String PRE_TYPE = "pre";          
     //   String ROUTE_TYPE = "route";
        }
    
        @Override
        public int filterOrder() {
            return FILTER_ORDER;
        }
    }
    
    

    上記のコードではaddZuulRequestHeaderによってリクエストにリクエストヘッダ情報が追加され修正されている(Zuulがリクエストヘッダに対する操作が有効でないことが判明した場合、取得コンテキストの位置をチェックし、クラス属性ではなくrunメソッドで行われた取得であることを確認する)、リクエストヘッダをフィルタリングする必要がある情報の一部が下流に転送されない場合、アプリケーション.ymlファイルを変更できます.
    zuul:
      ignored-headers: tmx-user-id #   , “,”   
    

    参考書:『Springマイクロウェア実務戦』参考ブログ:feign呼び出しsessionロスソリューション