istio中的FeignClient Header

1. istio-proxy

istio-proxy 注入时会从istio-sidecar-injector中获取需要被拦截outbound的IP地址范围,定位于configmap中的includeOutboundIPRanges,默认值是*,即拦截所有出口流量。

通过istio VirtualService配置,将请求header中包含某个key:value的转发的其他的接口版本中,需要将header中的这个值传递到feignclient调用的后续服务中。例如将header中包含end-user,值为jason的转到reviews v3,其他的v2。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v3

2. Feignclient Headers

springboot中feignclient可以通过实现RequestInterceptor来设置feignclient的请求头。百度FeignClient传递headers都采用如下实现。

@Configuration
public class FeignClientRequestInterceptor implements RequestInterceptor {
    private static Logger LOGGER = LoggerFactory.getLogger(FeignClientRequestInterceptor.class);
    private AirparkingProperties properties;

    public FeignClientRequestInterceptor(AirparkingProperties properties) {
        this.properties = properties;
    }

    public void apply(RequestTemplate requestTemplate) {
        String traceKey = TraceUtil.getTraceKey(this.properties);
        if (StringUtils.isNotEmpty(MDC.get(traceKey))) {
            requestTemplate.header(traceKey, new String[]{MDC.get(traceKey)});
        }

        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Enumeration headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while(headerNames.hasMoreElements()) {
                String name = (String)headerNames.nextElement();
                String values = request.getHeader(name);
                requestTemplate.header(name, new String[]{values});
            }
        }

    }
}

以上实现存在的问题:

1)由于请求中已经包含content-type,导致feignclient的请求中Content-Type的错误。通过debug发现,feignclient的请求包含content-type和ContentType,从而导致@RequestBody的请求出错。

2)将上一个请求头的host添加到feignclient请求中,导致istio-proxy将请求redirect到本项目,出现feignclient调用错误。

所以,对于feignclient公共请求头的转发,建议只针对特定的请求头做处理,比如转发请求header中的version和username的值。

package com.airparking.cloud.spring;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

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

@Configuration
public class FeignClientRequestInterceptor implements RequestInterceptor {
    private static Logger LOGGER = LoggerFactory.getLogger(FeignClientRequestInterceptor.class);

    private AirparkingProperties properties;

    public FeignClientRequestInterceptor(AirparkingProperties properties) {
        this.properties = properties;
    }

    @Override
    public void apply(RequestTemplate requestTemplate) {
        final String traceKey = TraceUtil.getTraceKey(this.properties);
        if (StringUtils.isNotEmpty(MDC.get(traceKey))) {
            requestTemplate.header(traceKey, MDC.get(traceKey));
        }

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Enumeration headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);

                switch (name) {
                    case "version":
                    case "username":
                        requestTemplate.header(name, values);
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

 

你可能感兴趣的:(istio,feignclient,springboot,kubernetes)