openfeign应用汇总(二)

发布时间 2023-08-07 21:43:51作者: VincentYew

openfeign应用汇总(二)

1、开启feign日志

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.ErrorDecoder;
@Configuration
public class FeignClientConfiguration{
	@Bean
	public Logger.Level feignLogLevel(){
		// 配置日志级别
		return Logger.Level.FULL;
	}
	@Bean
	public Decoder feignDefDecoder() {
		return new FeignResultDecoder();
	}
	@Bean
	public ErrorDecoder feignErrorDecoder() {
		return new FeignErrorDecoder();
	}
}
配置文件形式配置
feign: client: config: default: #此处写的是服务名称,针对我们feign微服务的配置,如果是default就是全局配置 loggerLevel: FULL #配置Feign的日志级别,相当于代码配置方式中的Logger
日志级别 打印内容
NONE(默认 不记录任何日志
BASIC 仅记录请求方法、URL、响应状态码以及执行时间(适合生产)
HEADERS 记录BASIC基础上,记录请求和响应的header
FULL   记录请求和响应header、body和元数据

2、openfeign设置header的5种方式

  • 在@RequestMapping注解里添加headers属性
  • 在方法参数前面添加@RequestHeader注解
  • 在方法或者类上添加@Headers的注解
  • 在方法参数前面添加@HeaderMap注解
  • 实现RequestInterceptor接口
@PostMapping(value = "/weixin/api", headers = {"Content-Type=application/json;charset=UTF-8", "App-Secret=${app.secret}"})
void saveUser(@RequestBody User user);
@GetMapping(value = "/getUser")
public User getUser(@RequestBody User user, @RequestHeader("Authorization") String token);
 ###设置多个headers
1@PostMapping(value = "/getUser") 2 public User getUser(@RequestBody User user, @RequestHeader MultiValueMap<String, String> headers);
@FeignClient(url = "${user.api.url}", name = "user", configuration = FeignClientConfiguration.class)
public interface UserFeignClient {
    @RequestLine("GET /{id}")
    @Headers({"Content-Type: application/json;charset=UTF-8", "Authorization: {token}"})
    public User findById(@Param("id") String id, @Param("token") String token);
}

###使用feign自带契约 ####@Headers不起作用,其实@Headers注解没有生效的原因是:官方的Contract没有生效 #####
@Configuration
public class Configuration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
}
###使用feign自带契约 
@FeignClient(url = "${user.api.url}", name = "user", configuration = FeignClientConfiguration.class)
public interface UserFeignClient {
    @RequestLine("GET /{id}")
    public User findById(@Param("id") String id, @HeaderMap HttpHeaders headers);
}
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        template.header("Authorization", token);
    }
}

3、修改header参数值(实现requestInterceptor)

package com.kayak.integration.client.dto.weixin;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import feign.RequestInterceptor;
import feign.RequestTemplate;
public class WxRequestInceptor implements RequestInterceptor{
	final Logger logger = LoggerFactory.getLogger(this.getClass());
	private static final String header_key = "Authorization";
	private Iterator<String> iterator;
	@Override
	public void apply(RequestTemplate paramRequestTemplate) {
		Map<String, Collection<String>> headersMap = paramRequestTemplate.headers();#获取报文头中的参数信息
		if(headersMap != null) {
			Collection<String> value = headersMap.get(header_key);
			if(value != null) {
				iterator = value.iterator();
				while(iterator.hasNext()){
					String auth = iterator.next();
					if(auth.startsWith("WECHATPAY2-SHA256-RSA2048") && auth.contains(", ")) {
						paramRequestTemplate.removeHeader(header_key);
						paramRequestTemplate.header(header_key, auth.replaceAll(", ", ","));
					}
				}	
			}
		}else {
			logger.info("headersMap is null");
			return ;
		}
####修改上下文中的header
		//		ServletRequestAttributes reqAttr = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
		//		logger.info("reqAttr >>>"+reqAttr);
		//		if(Objects.isNull(reqAttr)) {
		//			return ;
		//		}
		//		HttpServletRequest request = reqAttr.getRequest();
		//		Enumeration<String> headerNames = request.getHeaderNames();
		//		if(headerNames != null) {
		//			while(headerNames.hasMoreElements()) {
		//				String name = headerNames.nextElement();
		//				String value = request.getHeader(name);
		//				logger.info("name "+name+";value"+value);
		//			}
		//		}
		//		String auth = request.getHeader(header_key);
		//		logger.info("auth "+auth);
		//		if(auth != null) {
		//			paramRequestTemplate.header(header_key, auth.replaceAll(", ", ","));
		//		}
	}
}

3、Feign接口的熔断机制为:线程模式

自定义了一个RequestInterceptor实现类,就会导致hystrix熔断机制失效,接口调用异常(404、null)

原因:

  • 在feign调用之前,会走RequestInterceptor拦截器,拦截器中使用了ServletRequestAttributes获取请求数据;
  • 默认feign使用的是线程池模式,当开启熔断的时候,负责熔断的线程和执行Feign接口的线程不是同一个线程,ServletRequestAttributes取到的将会是空值。

解决方案:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 30000
          strategy: SEMAPHORE  ####线程隔离

 4、开启RequestInterceptor的4种方式

  • @Bean方法将RequestInterceptor实现类注入到Spring容器
  • 配置文件形式全局生效
  • 在某个接口针对类生效
  • 配置方式针对某个服务生效
方式一:
@Configuration
public class MyConfiguration {
    @Bean
    public RequestInterceptor requestInterceptor() {
        return new MyFeignRequestInterceptor();
    }
}

方式二:
feign:
  client:
    config:
      default:  ####全局生效
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        # 拦截器配置(和@Bean的方式二选一)
        requestInterceptors:
          - com.yew.feign.config.MyFeignRequestInterceptor

方式三:
在@FeignClient注解中指定configuration属性为RequestInterceptor实现类
@FeignClient(Configuration=MyRequestInterceptor.class) 方式四: feign: client: config: service
-A: ####指定服务生效 connectTimeout: 5000 readTimeout: 5000 loggerLevel: full # 拦截器配置(和@Bean的方式二选一) requestInterceptors: - com.yew.feign.config.MyFeignRequestInterceptor