Feign-声明式REST调用

简介

本文主要介绍如何使用spring-cloud-feign,在项目中使用Feign进行REST调用。

通常我们进行微服务间的REST调用时,一般会使用restTemplate,写起来也比较方便,例如:

ResponseEntity result = restTemplate.getForEntity(baseurl + "/users?serialNumber=18612341234", UserDTO.class);

ResponseEntity result = restTemplate.exchange(baseurl +  "/users/1715043034165359", HttpMethod.PUT, new HttpEntity(user), String.class);

但RestTemplate这种方式的缺点是代码量略大,且不太直观。

微服务强调跨语言解耦,不提倡以前那种将API部分打包并分发的方式。不同微服务间的业务代码的冗余不可避免。使用Feign,就可以简化Rest客户端这一部分的代码。

引入pom.xml依赖

Spring Boot工程中,直接引入spring-cloud-starter-feign依赖即可。


  
    
      org.springframework.cloud
      spring-cloud-dependencies
      Dalston.SR2
      pom
      import
    
  



  
    org.springframework.cloud
    spring-cloud-starter-feign
  

application.yml配置

新版本的Feign默认是禁用Hystrix的,需要手工配置打开。

feign.hystrix.enabled: true

Feign本身可以与Eureka/Ribbon比较好的配合使用,不需要其它配置,直接使用"应用名"进行调用。

当微服务没有使用Eureka做服务发现时,就需要手工配置Ribbon。例如,当使用marathon-lb时,可以这样配置:

marathon-lb-internal.ribbon.listOfServers: marathon-lb-internal.marathon.mesos

这里指定了一个DNS,当然也可以写死一个或多个IP。

启用EnableFeignClients注解

最简单的,可以在Application.java上,加上这个注解:

@EnableFeignClients

或者,新建一个配置类,指定profile:

@Profile("enable-feign")
@Configuration
@EnableFeignClients(basePackages = {"com.sitech.sdtools"})
public class FeignConfiguration {}

使用配置类+profile的好处是,可以根据不同的环境,非常方便的启用/禁用Feign。
特别是在单元测试时,由于mockito无法对Feign生成的Bean进行Mock,这时就可以在profile中禁用Feign,直接执行Fallback。

另外,需要注意一下,如果配置类的路径不是在根路径,而是在com.foo.bar.config这样的包下,需要加上"basePackages"参数。

解决Bean冲突

我目前的Spring Clound的版本是Dalston.SR2,如果集成了Hystrix,编写Fallback类后会有Bean冲突的问题,貌似是自动生成的@Primary注解无效,具体原因没有深究,可以通过配置解决,示例如下:

@Configuration
@ConditionalOnClass({Feign.class})
public class FeignConfiguration {
    @Bean
    public WebMvcRegistrations feignWebRegistrations() {
        return new WebMvcRegistrationsAdapter() {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                return new FeignFilterRequestMappingHandlerMapping();
            }
        };
    }

    private static class FeignFilterRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
        @Override
        protected boolean isHandler(Class beanType) {
            return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, FeignClient.class) == null);
        }
    }
}

开启日志

Feign的日志是DEBUG级别,在LogBack中有时需要特别配置一下:


  

定义client接口

下面开始,正式进入正题,定义一个接口,并加上@FeignClient注解。

@FeignClient(name = "marathon-lb-internal", fallback = StaticInfoClientFallback.class)
@RequestMapping(value = "/sd/staticinfo")
public interface StaticInfoClient {
    @RequestMapping(value = "/products/{productId}", method = RequestMethod.GET)
    public ProductDTO getProductInfo(@PathVariable("productId") Long productId);
}

可以直接在服务端的Controller层中拷贝代码,稍做修改即可,非常方便。

但请注意:

  1. @PathVariable("productId"),需要显示的指定对应的参数名,不能像SpringMVC一样自动对应。
  2. 目前只能使用@RequestMapping注解,而不能使用@GetMapping

编写fallback类

当调用失败时,会执行fallback类中的逻辑。

@Component
class StaticInfoClientFallback implements StaticInfoClient {
    private static final Logger logger = LoggerFactory.getLogger(TradeInfoClientFallback.class);

    @Override
    public ProductDTO getProductInfo(Long productId) {
        logger.error("StaticInfoClient.getProductInfo 执行失败");

        ProductDTO dto = new ProductDTO();
        dto.setProductId(productId);
        dto.setProductName("产品名称暂时无法获取");
        return dto;
    }
}

注意不要忘记加上@Component

发起REST调用

REST调用也非常简单,一行代码搞定:

ProductDTO productInfo = staticInfoClient.getProductInfo(entity.getProductId());

END

Feign的配置略微复杂,坑也比较多,有一定的学习成本的。但带来的好处理,在使用上即优雅又方便。
与RestTemplate相比,只能说是各有利弊吧,可以酌情选择。

你可能感兴趣的:(Feign-声明式REST调用)