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;
    }
}
相关推荐
工业甲酰苯胺1 小时前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
bjzhang753 小时前
SpringBoot开发——集成Tess4j实现OCR图像文字识别
spring boot·ocr·tess4j
flying jiang3 小时前
Spring Boot 入门面试五道题
spring boot
小菜yh3 小时前
关于Redis
java·数据库·spring boot·redis·spring·缓存
爱上语文5 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
荆州克莱5 小时前
springcloud整合nacos、sentinal、springcloud-gateway,springboot security、oauth2总结
spring boot·spring·spring cloud·css3·技术
serve the people5 小时前
springboot 单独新建一个文件实时写数据,当文件大于100M时按照日期时间做文件名进行归档
java·spring boot·后端
罗政10 小时前
[附源码]超简洁个人博客网站搭建+SpringBoot+Vue前后端分离
vue.js·spring boot·后端
Java小白笔记13 小时前
关于使用Mybatis-Plus 自动填充功能失效问题
spring boot·后端·mybatis
小哇66613 小时前
Spring Boot,在应用程序启动后执行某些 SQL 语句
数据库·spring boot·sql