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;
    }
}
相关推荐
菜鸟‍5 小时前
【后端项目】苍穹外卖day01-开发环境搭建
java·开发语言·spring boot
indexsunny6 小时前
互联网大厂Java求职面试实战:核心技术与业务场景解析
java·spring boot·redis·微服务·kafka·互联网大厂·面试技巧
程序猿大波6 小时前
基于java,SpringBoot和Vue餐饮公司食堂管理系统设计
java·vue.js·spring boot
wuyaolong0077 小时前
Spring Boot 3.4 正式发布,结构化日志!
java·spring boot·后端
a5629916198 小时前
【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目
java·spring boot·spring
编程小风筝10 小时前
如何用redission实现springboot的分布式锁?
spring boot·分布式·后端
码喽7号11 小时前
Springboot学习六:MybatisPlus的多表查询以及分页查询
java·spring boot·学习
不吃香菜学java11 小时前
苍穹外卖-新增菜品需求分析
java·spring boot·spring·tomcat·maven·ssm
智能工业品检测-奇妙智能13 小时前
开源知识库平台有哪些
服务器·人工智能·spring boot·开源·openclaw·奇妙智能
计算机学姐13 小时前
基于SpringBoot的中药材店铺管理系统
java·vue.js·spring boot·后端·spring·tomcat·推荐算法