第六章 声明式服务调用: Spring Cloud Feign
我们在使用 Spring Cloud Ribbon 时, 通常都会利用它对 RestTemplate 的请求拦截来实现对依赖服务的接口调用, 而 RestTemplate 已经实现了对 HTTP 请求的封装处理, 形成了一套模板化的调用方法。在之前的例子中,我们只是简单介绍了 RestTemplate 调用的实现,但是在实际开发中,由于对服务依赖的调用可能不止于一处,往往一个接口会被多处调用,所以我们通常都会针对各个微服务自行封装一些客户端类来包装这些依赖服务的调用。 这个时候我们会发现, 由于 RestTemplate 的封装, 几乎每一个调用都是简单的模板化内容。综合上述这些情况, Spring Cloud Feign 在此基础上做了进一步封装, 由它来帮助我们定义和实现依赖服务接口的定义。在 Spring Cloud Feign 的实现下, 我们只需创建一个接口并用注解的方式来配置它, 即可完成对服务提供方的接口绑定, 简化了在使用 Spring Cloud Ribbon 时自行封装服务调用客户端的开发量。 Spring Cloud Feign 具备可插拔的注解支持,包括 Feign 注解和 JAX-RS 注解。 同时, 为了适应 Spring 的广大用户,它在 Netflix Feign的基础上扩展了对 Spring MVC 的注解支待
快速入门
通过 Spring Cloud Feign 提供的声明式服务绑定功能来实现对该服务接口的调用。首先, 创建一个 Spring Boot 基础工程, 取名为 feign-consumer, 并在 pom.xml中引入 spring-cloud-starter-eureka和spring-cloud-starter-feign依赖。 具体内容如下所示:
<parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactId> <version>l.3.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springfrarnework.cloud</groupid> <artifactid>spring-cloud-starter-eureka</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-feign</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-dependencies</artifactid> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency>
</dependencies> </dependencyManagement>
创建应用主类ConsumerApplication, 并通过@EnableFeignClients 注解开启 Spring Cloud Feign 的支待功能
1 @EnableFeignClients
2 @EnableDiscoveryClient
3 @SpringBootApplication
4 public class ConsumerApplication {
5 public static void main(String[] args) {
6 SpringApplication.run(ConsurnerApplication.class, args);
7 }
8 }
定义 HelloService 接口, 通过@FeignClient 注解指定服务名来绑定服务, 然后再使用 Spring MVC 的注解来绑定具体该服务提供的 REST 接口
1 @FeignClient("hello-service")
2 public interface HelloService {
3
4 @RequestMapping("/hello")
5 String hello();
6
7 }
注意:这里服务名不区分大小写, 所以使用 hello-service和HELLO-SERVICE 都是可以的。 另外, 在 Brixton.SR5 版本中, 原有的 serviceld 属性已经被废弃,若要写属性名, 可以使用 name或value
接着, 创建一个 ConsumerController 来实现对 Feign 客户端的调用。 使用@Autowired 直接注入上面定义的HelloService 实例, 并在 helloConsumer函数中调用这个绑定了 hello-service 服 务接口的客户端来向该服务发起/hello 接口的调用
1 @RestController 2 public class ConsumerController { 3 @Autowired 4 HelloService helloService; 5 @RequestMapping(value = "/feign-consumer", method = RequestMethod.GET) 6 7 public String helloConsumer () { 8 return helloService.hello(); 9 } 10 11 }
最后, 同 Ribbon 实现的服务消费者一样, 需要在 application.properties 中指定服务注册中心, 并定义自身的服务名为 feign-consumer, 为了方便本地调试与之前的 Ribbon 消费者区分, 端口使用 9001
spring.application.name=feign-consumer server.port=9001 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
启动服务注册中心以及两个HELLO-SERVICE, 然后启动 FEIGN-CONSUMER,发送几次 GET 请求到 http://localhost:9001/feign-consumer, 可以得到如之前Ribbon 实现时 一样的效果, 正确返回了 "Hello World"。 并且根据控制台的输出, 我们可以看到 Feign 实现的消费者,依然是利用 Ribbon 维护了针对 HELLO-SERVICE 的服务列表信息, 并且通过轮询实现了客户端负载均衡。 而与 Ribbon 不同的是, 通过 Feign我们只需定义服务绑定接口, 以声明式的方法, 优雅而简单地实现了服务调用
参数绑定
在定义各参数绑定时,@RequestParam、@RequestHeader 等可以指定参数名称的注解, 它们的 value 千万不能少。 在SpringMVC 程序中, 这些注解会根据参数名来作为默认值,但是在Feign 中绑定参数必须通过 value 属性来指明具体的参数名,不然会抛出口legalStateException 异常, value 属性不能为空
全局配置
全局的超时时间:
hystrix.command.default.execution.isolation.thread. timeoutinMilliseconds=5OOO
在对Hystrix进行配置之前,我们需要确认 feign.hystrix.enabled参数没有被设置为false, 否则该参数设置会关闭Feign客户端的Hystrix支持