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. 导入依赖:

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

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

    @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 注解配置

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 配置类

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 配置文件

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 请求透传

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 异常处理

@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

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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值