参考文章:
[SpringCloud-Feign] Feign转发请求头,(防止session失效)
feign调用session丢失解决方案
目录
1. 自定义feign隔离策略
2.设置feign拦截器,进行相应header的转发
2.1 示例一
2.2 示例一
package com.cloud.config;
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的隔离策略:
* 在转发Feign的请求头的时候, 如果开启了Hystrix,
* Hystrix的默认隔离策略是Thread(线程隔离策略), 因此转发拦截器内是无法获取到请求的请求头信息的,
* 可以修改默认隔离策略为信号量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE,
* 这样的话转发线程和请求线程实际上是一个线程, 这并不是最好的解决方法, 信号量模式也不是官方最为推荐的隔离策略;
* 另一个解决方法就是自定义Hystrix的隔离策略:
* 思路是将现有的并发策略作为新并发策略的成员变量,在新并发策略中,
* 返回现有并发策略的线程池、Queue;将策略加到Spring容器即可;
*/
@Component
public class FeignHystrixConcurrencyStrategyIntellif extends HystrixConcurrencyStrategy {
Logger log = LoggerFactory.getLogger(this.getClass());
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 instance = HystrixPlugins.getInstance();
instance.registerConcurrencyStrategy(this);
instance.registerCommandExecutionHook(commandExecutionHook);
instance.registerEventNotifier(eventNotifier);
instance.registerMetricsPublisher(metricsPublisher);
instance.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 Callable wrapCallable(Callable callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty corePoolSize,
HystrixProperty maximumPoolSize,
HystrixProperty keepAliveTime,
TimeUnit unit, BlockingQueue 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 getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public HystrixRequestVariable getRequestVariable(HystrixRequestVariableLifecycle rv) {
return this.delegate.getRequestVariable(rv);
}
static class WrappedCallable implements Callable {
private final Callable target;
private final RequestAttributes requestAttributes;
public WrappedCallable(Callable 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();
}
}
}
}
2.设置feign拦截器,进行相应header的转发
2.1 示例一
package com.cloud.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import static com.cloud.config.Constants.CLOUD_SESSION_ID;
/**
* feign转发header参数
* @author langpf 2018/12/19
*/
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
@Autowired
HttpServletRequest httpServletRequest;
public FeignRequestInterceptor() {
}
public void apply(RequestTemplate requestTemplate) {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
String sessionId2 = httpServletRequest.getSession().getId();
String appkey0 = httpServletRequest.getHeader("appKey");
String appkey = request.getHeader("appKey");
// String traceId = request.getHeader("traceId");
// String requestIp = MyRequestUtil.getIpAddress(request);
// String userAgent = request.getHeader("User-agent");
// String contentType = request.getHeader("Content-Type");
//
// requestTemplate.header("Content-Type", new String[]{contentType});
// requestTemplate.header("User-agent", new String[]{userAgent});
// requestTemplate.header("User-IP", new String[]{requestIp});
// requestTemplate.header("traceId", new String[]{traceId});
// requestTemplate.header("appKey", new String[]{appkey});
requestTemplate.header(CLOUD_SESSION_ID, sessionId);
}
}
2.2 示例一
转发所有header
package com.cloud.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/**
* 转发所有header
* @author langpf 2019/2/1
*/
// @Configuration // 注释掉, 不启用该拦截器; 如果启用, 直接打开注释即可
public class FeignRequestInterceptor3 implements RequestInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void apply(RequestTemplate template) {
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);
template.header(name, values);
}
logger.info("feign interceptor header:{}",template);
}
// 转发参数
Enumeration bodyNames = request.getParameterNames();
StringBuilder body = new StringBuilder();
if (bodyNames != null) {
while (bodyNames.hasMoreElements()) {
String name = bodyNames.nextElement();
String values = request.getParameter(name);
body.append(name).append("=").append(values).append("&");
}
}
if (body.length() != 0) {
body.deleteCharAt(body.length() - 1);
template.body(body.toString());
//logger.info("feign interceptor body:{}",body.toString());
}
}
}