OpenFeign 个性化_注解配置_日志_请求拦截_请求透传_FastJson解析

相关组件概念

  1. Ribbon:
    • Ribbon 是 Netflix开源的基于 HTTP 和 TCP 等协议负载均衡组件;
    • Ribbon 可以用来做客户端负载均衡,调用注册中心的服务;
  2. Feign:
    • Feign 是 Spring Cloud 组件中的一个轻量级 RESTful 的 HTTP 服务客户端;
    • Feign 内置了 Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务;
    • Feign 的使用方式是:使用 Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心的服务;
    • Feign 本身不支持 Spring MVC 的注解,它有一套自己的注解;
  3. OpenFeign:
    • OpenFeign 是 Spring Cloud 在 Feign 的基础上支持了 Spring MVC 的注解,如 @RequesMapping 等等。
    • OpenFeign 的 @FeignClient 可以解析 SpringMVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

使用 OpenFeign

  1. 导入依赖:

    xml 复制代码
     <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-openfeign</artifactId>
         <version>${feign.version}</version>
    </dependency>
  2. 启动类配置:

    java 复制代码
    @SpringBootApplication
    @EnableFeignClients
    public class FeignDemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
  3. 配置 FeignClient 接口:

    java 复制代码
    @FeignClient("stores")
    public interface StoreClient {
        @RequestMapping(method = RequestMethod.GET, value = "/stores")
        List<Store> getStores();
    
        @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
        Store update(@PathVariable("storeId") Long storeId, Store store);
    }

个性化配置 Feign

1. @FeignClient 注解配置

java 复制代码
public @interface FeignClient {
	/**
	 * FeignContext 中 Bean 名称,若使用 Ribbon 则作为服务提供方名称,用于服务发现
	 */
    @AliasFor("name")
    String value() default "";				

	/**
	 * 替代 value 成为 Client Bean 名称
	 */
    String contextId() default "";

    @AliasFor("value")
    String name() default "";

    @Deprecated
    String qualifier() default "";

    /**
	 * Client Bean 别名
	 */
    String[] qualifiers() default {};

	/**
	 * 配置绝对 URL 或可解析的主机名(协议是可选的)
	 */
    String url() default "";

    /**
	 * 404是否应该解码而不是抛出虚假异常
	 */
    boolean decode404() default false;

    /**
	 * 自定义配置类
	 */
    Class<?>[] configuration() default {};

    /**
	 * 定义容错的处理类,也就是回退逻辑
	 * fallback 的类必须实现 Feign Client 的接口,无法知道熔断的异常信息
	 */
    Class<?> fallback() default void.class;

    /**
	 * 定义容错的处理,可以知道熔断的异常信息。可以自定义fallbackFactory
	 */
    Class<?> fallbackFactory() default void.class;

    /**
	 * 所有方法级映射使用的路径前缀
	 */
    String path() default "";

    /**
	 * 对应的是 @Primary 注解,默认为 true
	 */
    boolean primary() default true;
}

2. 定义 Feign 配置类

Java 复制代码
public class FeignConfig {
 /**
     * 配置 FeignClient 合约类型
     * 1. SpringMvcContract,默认;
     * 2. Default;
     */
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    } 

    /**
     * 配置 Feign Client 类型:
     * 1. Client.Default:默认,内部使用 HttpURLConnnection 完成URL请求处理;
     * 2. ApacheHttpClient:内部使用 Apache httpclient 完成请求处理;
     * 3. OkHttpClient:内部使用 OkHttp3 完成请求处理;
     * 4. FeignBlockingLoadBalancerClient:在其他 client 基础上封装 ribbon 技术完成请求处理;
     * 若引入 Spring Cloud LoadBalancer,则使用 FeignBlockingLoadBalancerClient。
     * 如果无,则使用默认 Feign 客户端。
     */
    @Bean
    public Client feignClient() {
        return new Client.Default(null, null);
        // return new Client.Default(getSSLSocketFactory(), null);
    }

    /**
     * 停用 http ssl 证书检查
     */
    private SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(
                null, new TrustSelfSignedStrategy()
            ).build();
            return sslContext.getSocketFactory();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
	 * 配置 Feign 日志级别:
	 * NONE:默认,不显示任何日志
	 * BASIC: 仅记录请求方法、URL、响应状态码及执行时间
	 * HEADERS:除了BASIC中定义的信息之外,还有请求头和响应头信息
	 * FULL:除了HEADERS中定义的信息之外,还有请求的正文和响应数据
     */
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    /**
     * 配置 Feign 的超时时间 (毫秒):
     * connectTimeoutMillis 连接超时时间
     * readTimeoutMillis 请求处理时间
     */
    @Bean
    public Request.Options options() {
        return new Request.Options(5000,10000);
    }


    /**
     * 注入自定义的拦截器
     */
    @Bean
    public RequestInterceptor requestInterceptor() {
        return new RequestInterceptor(){
            @Override
    		public void apply(RequestTemplate template) {
        		System.out.println("执行拦截器....");

            }
    	}
    }
}

3. 定义 Feign 配置文件

yaml 复制代码
feign:
 client:
     config:
         your-feign-ame:
             connectTimeout: 5000
             readTimeout: 5000
             loggerLevel: BASIC
             errorDecoder: com.example.SimpleErrorDecoder
             retryer: com.example.SimpleRetryer
             defaultQueryParameters:
                 query: queryValue
             defaultRequestHeaders:
                 header: headerValue
             requestInterceptors:
                    - com.example.FooRequestInterceptor
                    - com.example.BarRequestInterceptor
                decode404: false
                encoder: com.example.SimpleEncoder
                decoder: com.example.SimpleDecoder
                contract: com.example.SimpleContract
	okhttp:
		enabled: true
logging:
    level:
        lxllyy.top.feign: debug

4. 配置 Feign 请求透传

java 复制代码
public class FeignConfig { 
 @Bean
 public RequestInterceptor requestInterceptor() {
     return new RequestInterceptor() {
         @Override
         public void apply(RequestTemplate template) {
             HttpServletRequest request = getHttpServletRequest();
             if (request != null) {
                 //第一种 把header里所有的值都透传,简单粗暴
                 Map<String, String> headers = getHeaders(request);
                 for (String headerName : headers.keySet()) {
                     template.header(
                             headerName,
                             getHeaders(getHttpServletRequest()).get(headerName)
                     );
                 }

                 //第二种 只针对性的传递想要的header里的值
                 String x_access_token = request.getHeader("x-access-token");
                 if (StringUtils.hasText(x_access_token)) {
                     template.header("x-access-token", x_access_token);
                 }
             }
         }

         private HttpServletRequest getHttpServletRequest() {
             try {
                 return
                         ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                                 .getRequest();
             } catch (Exception e) {
                 e.printStackTrace();
                 return null;
             }
         }

         private Map<String, String> getHeaders(HttpServletRequest request) {
             Map<String, String> map = new LinkedHashMap<>();
             Enumeration<String> enumeration = request.getHeaderNames();
             while (enumeration.hasMoreElements()) {
                 String key = enumeration.nextElement();
                 String value = request.getHeader(key);
                 map.put(key, value);
             }
             return map;
         }
     };
 }
}

5. 配置 Feign 异常处理

java 复制代码
@FeignClient(
 name = "service-provider1",
 fallback = UserFeignClientFallback.class,					// 不推荐
 fallbackFactory = UserFeignClientFallbackFactory.class		// 推荐
)
public interface UserFeignClient {
 @RequestMapping(value = "/getNameById/{id}",method = RequestMethod.GET)
 String getNameById(@PathVariable("id") Integer id);
}

@Component
@Slf4j
public class UserFeignClientFallback implements UserFeignClient{
 @Override
 public String getNameById(Integer str) {
     log.error("UserFeignClient #getNameById failed");
     return null;
 }
}

@Component
@Slf4j
public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient>{
	@Override
	public UserFeignClient create(Throwable throwable) {
	    log.error("异常原因:{}", throwable.getMessage(), throwable);
		return new UserFeignClient(){
			@Override
			public Object getNameById(Integer str) {
				//出现异常,自定义返回内容,保证接口安全
				return null;
			}
		};
	}
}

6. 配置 Feign FastJson

java 复制代码
public class MyFeignConfig {
	@Bean
	public Encoder feignEncoder() {
	    return new SpringEncoder(feignHttpMessageConverter());
	}

	@Bean
	public Decoder feignDecoder() {
	    return new SpringDecoder(feignHttpMessageConverter());
	}

	/**
     * 设置解码器为fastjson
     *
     * @return
     */
    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters = 
            new HttpMessageConverters(this.getFastJsonConverter());
        return () -> httpMessageConverters;
    }

    private FastJsonHttpMessageConverter getFastJsonConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();

        List<MediaType> supportedMediaTypes = new ArrayList<>();
        MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_UTF8_VALUE);
        supportedMediaTypes.add(mediaTypeJson);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        FastJsonConfig config = new FastJsonConfig();
        config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
        config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
        converter.setFastJsonConfig(config);

        return converter;
    }
}
相关推荐
HaiFan.36 分钟前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
大梦百万秋2 小时前
Spring Boot实战:构建一个简单的RESTful API
spring boot·后端·restful
斌斌_____3 小时前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
苹果醋33 小时前
React系列(八)——React进阶知识点拓展
运维·vue.js·spring boot·nginx·课程设计
等一场春雨5 小时前
springboot 3 websocket react 系统提示,选手实时数据更新监控
spring boot·websocket·react.js
荆州克莱6 小时前
Golang的性能监控指标
spring boot·spring·spring cloud·css3·技术
AI人H哥会Java6 小时前
【Spring】控制反转(IoC)与依赖注入(DI)—IoC容器在系统中的位置
java·开发语言·spring boot·后端·spring
赖龙6 小时前
springboot restful mybatis连接mysql返回日期格式不对
spring boot·mybatis·restful
自律的kkk6 小时前
SpringBoot中使用AOP切面编程实现登录拦截
java·spring boot·aop·切面编程·登录拦截
武昌库里写JAVA6 小时前
【MySQL】MySQL 通过127.0.0.1和localhost登录的区别
spring boot·spring·毕业设计·layui·课程设计