SpringCloud Hello Feign

原文来自《重新定义Spring Cloud实战》

Feign 简介

Feign是一个声明式的WebService客户端。它的出现使开发WebService客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解,比如:FeignClient注解。Feign有可插拔的注解,包括Feign注解和JAXRS注解。Feign也支持编码器和解码器,SpringCloudOpenFeign对Feign进行增强支持SpringMVC注解,可以像SpringWeb一样使用HttpMessageConverters等。

Feign工作原理

  • 当程序启动时,会进行包扫描,扫描所有@FeignClients的注解的类,并将这些信息注入SpringIOC容器中。当定义的Feign接口中的方法被调用时,通过JDK的代理的方式,来生成具体的RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequetTemplate对象,该对象封装了HTTP请求需要的全部信息,如请求参数名、请求方法等信息都是在这个过程中确定的。

  • 然后由RequestTemplate生成Request,然后把Request交给Client去处理,这里指的Client可以是JDK原生的URLConnection、Apache的HttpClient,也可以是Okhttp。最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发起服务之间的调用。

Hello World Demo

环境配置

  1. JDK8
  2. Maven

在Demo服务中,调用 github openAPI 的接口

  • maven依赖

    org.springframework.cloud
    spring-cloud-starter-openfeign

  • Feign配置 + 启用Feign
@Configuration
@EnableFeignClients
public class HelloFeignServiceConfig {

    /**
     *
     * Logger.Level 的具体级别如下:
         NONE:不记录任何信息
         BASIC:仅记录请求方法、URL以及响应状态码和执行时间
         HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息
         FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据
     * @return
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

}
  • OpenAPI的调用客户端编写
@FeignClient(name = "github-client", url = "https://api.github.com", configuration = HelloFeignServiceConfig.class)
public interface HelloFeignService {

    @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)
    String searchRepo(@RequestParam("q") String queryStr);

}
  • HelloFeignService 的使用
@RestController
public class HelloFeignController {

    @Autowired
    private HelloFeignService helloFeignService;

    @GetMapping(value = "/search/github")
    public String searchGithubRepoByStr(@RequestParam("str") String queryStr) {
        return helloFeignService.searchRepo(queryStr);
    }

}

实战运用

Feign默认Client的替换

Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistenceconnection。我们可以用Apache的HTTPClient替换Feign原始的HTTPClient,通过设置连接池、超时时间等对服务之间的调用调优。SpringCloud从Brixtion.SR5版本开始支持这种替换,接下来介绍一下如何用HTTPClient和okhttp去替换Feign默认的Client。

使用HTTPClient替换Feign默认Client

  • 引入 httpclient 依赖

    org.apache.httpcomponents
    httpclient



    com.netflix.feign
    feign-httpclient
    8.17.0

  • 开启httpclient为Feign默认的Client
feign:
  httpclient:
      enabled: true

使用okhttp替换Feign默认的Client

  • 引入 okhttp 依赖

  io.github.openfeign
  feign-okhttp

  • 开启okhttp为Feign默认的Client
feign:
    httpclient:
         enabled: false
    okhttp:
         enabled: true
  • 初始化OkHttpClient
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
    @Bean
    public okhttp3.OkHttpClient okHttpClient(){
        return new okhttp3.OkHttpClient.Builder()
                 //设置连接超时
                .connectTimeout(60, TimeUnit.SECONDS)
                //设置读超时
                .readTimeout(60, TimeUnit.SECONDS)
                //设置写超时
                .writeTimeout(60,TimeUnit.SECONDS)
                //是否自动重连
                .retryOnConnectionFailure(true)
                .connectionPool(new ConnectionPool())
                //构建OkHttpClient对象
                .build();
    }

}

Feign调用传递Token

在进行认证鉴权的时候,不管是jwt,还是security,当使用Feign时就会发现外部请求到A服务的时候,A服务是可以拿到Token的,然而当服务使用Feign调用B服务时,Token就会丢失,从而认证失败。解决方法相对比较简单,需要做的就是在Feign调用的时候,向请求头里面添加需要传递的Token。

  • 实现 RequestInterceptor 接口,在发起请求前给 RequestTemplate 设置请求头
@Component
public class FeignTokenInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("oauthToken", "请求token");
    }

}

Feign 使用 FastJson 进行序列化

Feign默认使用 Jackson 进行请求参数和响应数据的JSON序列化。

  • 指定使用FastJson进行序列化
@Configuration
public class FeignConfig {

    @Bean
    public Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    private ObjectFactory feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters =
                new HttpMessageConverters(getFastJsonConverter());
        return () -> httpMessageConverters;
    }

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

        List 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;
    }

}

解决Feign首次请求失败问题

当Feign和Ribbon整合了Hystrix之后,可能会出现首次调用失败的问题,造成该问题出现的原因分析如下:Hystrix默认的超时时间是1秒,如果超过这个时间尚未做出响应,将会进入fallback代码。由于Bean的装配以及懒加载机制等,Feign首次请求都会比较慢。如果这个响应时间大于1秒,就会出现请求失败的问题。

  • 将Hystrix的超时时间改为5秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
  • 禁用Hystrix的超时时间
hystrix.command.default.execution.timeout.enabled=false
  • 使用Feign的时候直接关闭Hystrix,该方式不推荐使用
feign.hystrix.enabled=false 

你可能感兴趣的:(SpringCloud Hello Feign)