fegin+hystrix中Request Contect Holder.get Request Attributesを解決します.null値を返す解決方法
ヤンキーの記事を転載: http://www.itmuch.com/spring-cloud-sum/hystrix-threadlocal/カスタムfeignスクリーンショット
途中コードでnullに戻ります.
解決方法:ユーザー定義のヒューズポリシー
feignは他のサービスへの呼び出しを提供します.
元々はスクリーンにログ情報を出力したいですが、だめでした.これは処理します.今は個人的に理解している原因です.
後は公式のfeignログで出力します.
簡単です
ログの設定クラスを作成します.
以下のとおりです
ymlに出力するログレベルと指定feignクラスを設定します.
package com.wm.commonfeign.config;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @class_name: CustomFeignConfig
* @description: authorization ,
* @author: wm_yu
* @create: 2019/07/26
**/
@Configuration
@Slf4j
public class CustomFeignConfig implements RequestInterceptor {
private static final String TOKEN_HEADER = "authorization";
@Override
public void apply(RequestTemplate requestTemplate) {
HttpServletRequest request = getHttpServletRequest();
// TODO RequestInterceptor request , , , feign
String jsonString = this.getRequestJsonString(request);
// feign
log.info("feign ,methodName={},Parameter={}",request.getMethod(),jsonString);
if (ObjectUtils.isEmpty(request)) {
requestTemplate.header(TOKEN_HEADER, getHeaders(request).get(TOKEN_HEADER));
}
}
/**
* request
* @return
*/
private HttpServletRequest getHttpServletRequest() {
try {
// hystrix RequestContextHolder.getRequestAttributes() null
// :http://www.itmuch.com/spring-cloud-sum/hystrix-threadlocal/
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (!ObjectUtils.isEmpty(attributes)){
return attributes.getRequest();
}
return null;
} catch (Exception e) {
log.error("feign HttpServletRequest error...",e.getMessage(),e);
return null;
}
}
/**
* header
* @param request
* @return
*/
private Map getHeaders(HttpServletRequest request) {
Map map = new LinkedHashMap<>();
Enumeration enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
/**
* request json
* @param request
* @return
*/
public String getRequestJsonString(HttpServletRequest request){
try {
int contentLength = request.getContentLength();
if (contentLength < 0) {
return null;
}
byte buffer[] = new byte[contentLength];
for (int i = 0; i < contentLength;) {
int readlen = request.getInputStream().read(buffer, i, contentLength - i);
if (readlen == -1) {
break;
}
i += readlen;
}
String charEncoding = request.getCharacterEncoding();
if (charEncoding == null) {
charEncoding = "UTF-8";
}
return new String(buffer, charEncoding);
} catch (Exception e) {
log.error(" request ....",e.getMessage(),e);
}
return null;
}
}
途中コードでnullに戻ります.
解決方法:ユーザー定義のヒューズポリシー
package com.wm.commonfeign.strategy;
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 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.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @class_name: CustomFeignHystrixConcurrencyStrategy
* @description: RequestContextHolder.getRequestAttributes() null
* @author: wm_yu
* @create: 2019/07/26
**/
@Component
@Primary
@Slf4j
public class CustomFeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy{
private HystrixConcurrencyStrategy hystrixConcurrencyStrategy;
public CustomFeignHystrixConcurrencyStrategy() {
try {
this.hystrixConcurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.hystrixConcurrencyStrategy instanceof CustomFeignHystrixConcurrencyStrategy) {
// 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.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.hystrixConcurrencyStrategy + "]," + "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.hystrixConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
unit, workQueue);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.hystrixConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties);
}
@Override
public BlockingQueue getBlockingQueue(int maxQueueSize) {
return this.hystrixConcurrencyStrategy.getBlockingQueue(maxQueueSize);
}
@Override
public HystrixRequestVariable getRequestVariable(HystrixRequestVariableLifecycle rv) {
return this.hystrixConcurrencyStrategy.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();
}
}
}
}
fegin対応hystrixを設定し、溶断のタイムアウトを指定します.feign:
hystrix:
enabled: true
# hystrix
hystrix:
shareSecurityContext: true
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
feignは他のサービスへの呼び出しを提供します.
package com.wm.examapi.feign;
import com.wm.commoncore.constant.ServiceConstant;
import com.wm.commoncore.model.Response;
import com.wm.commonfeign.config.CustomFeignConfig;
import com.wm.examapi.feign.fallback.ExaminationServiceClientFallbackImpl;
import com.wm.examapi.model.Examination;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @class_name: ExaminationServiceClient
* @description: .... FeignClient configuration CustomFeignConfig, token
* @author: wm_yu
* @create: 2019/07/26
**/
@FeignClient(name = ServiceConstant.EXAM_SERVICE, configuration = CustomFeignConfig.class, fallback = ExaminationServiceClientFallbackImpl.class)
public interface ExaminationServiceClient {
/**
*
*
* @param examination
* @return ResponseBean
*/
/**
* RequestMapping post, @PostMapping
* @param examination
* @return
*/
@RequestMapping(method = RequestMethod.POST,value = "/api/exam/getExaminationCount")
Response findExaminationCount(@RequestBody Examination examination);
}
溶断類の設定:package com.wm.examapi.feign.fallback;
import com.wm.commoncore.constant.BussiConstant;
import com.wm.commoncore.model.Response;
import com.wm.examapi.feign.ExaminationServiceClient;
import com.wm.examapi.model.Examination;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @class_name: ExaminationServiceClientFallbackImpl
* @description:
* @author: wm_yu
* @create: 2019/07/26
**/
@Slf4j
@Component
public class ExaminationServiceClientFallbackImpl implements ExaminationServiceClient {
private Throwable throwable;
@Override
public Response findExaminationCount(@RequestBody Examination examination) {
log.error(" {} , {}, {}", "findExaminationCount", examination.getTenantCode(),throwable);
return Response.createError(BussiConstant.EXAM_SERVICE_FEIGN_ERROR,0);
}
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
}
溶断工場を作成し、溶断例を生成する.package com.wm.examapi.feign.factory;
import com.wm.examapi.feign.ExaminationServiceClient;
import com.wm.examapi.feign.fallback.ExaminationServiceClientFallbackImpl;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* @class_name: UserServiceClientFallbackFactory
* @description:
* @author: wm_yu
* @create: 2019/07/26
**/
@Component
public class ExaminationServiceClientFallbackFactory implements FallbackFactory {
@Override
public ExaminationServiceClient create(Throwable throwable) {
ExaminationServiceClientFallbackImpl examinationServiceClientFallback = new ExaminationServiceClientFallbackImpl();
examinationServiceClientFallback.setThrowable(throwable);
return examinationServiceClientFallback;
}
}
クラススキャンを開始するpackage com.wm.userservice;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@SpringBootApplication
@EnableDiscoveryClient //
@MapperScan("com.wm.userservice.mapper")
@EnableFeignClients(basePackages = {"com.wm.examapi.feign"}) // fegin
@ComponentScan({"com.wm.examapi.feign","com.wm.userservice","com.wm.commonfeign"}) // ( ), :fegin
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
/**
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
}
ここは一つのvueを多く配置して、ドメインを越えて解決します.元々はスクリーンにログ情報を出力したいですが、だめでした.これは処理します.今は個人的に理解している原因です.
後は公式のfeignログで出力します.
簡単です
ログの設定クラスを作成します.
以下のとおりです
package com.wm.commonfeign.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @class_name: FeignLogConfig
* @description: feign
* @author: wm_yu
* @create: 2019/07/27
**/
/**
* Feign logging
* A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the DEBUG level.
*
* application.yml
*
* logging.level.project.user.UserClient: DEBUG
*
*
* The Logger.Level object that you may configure per client, tells Feign how much to log. Choices are:
*
* (Logger.Level), :
*
* NONE, No logging (DEFAULT).
* BASIC, Log only the request method and URL and the response status code and execution time.
* HEADERS, Log the basic information along with request and response headers.
* FULL, Log the headers, body, and metadata for both requests and responses.
* For example, the following would set the Logger.Level to FULL:
*
* (FULL):
*
* @Configuration
* public class FooConfiguration {
* @Bean
* Logger.Level feignLoggerLevel() {
* return Logger.Level.FULL;
* }
* }
*/
@Configuration
public class FeignLogConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
コメントが削除されたのは公式文書の解釈です.ymlに出力するログレベルと指定feignクラスを設定します.
logging:
level:
root: info
####sql
com.wm.examservice.mapper: debug
#### feign
com.wm.examapi.feign: debug