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