Feign 采用了声明式 API 接口风格,将 Java HTTP 客户端绑定到它的内部,Feign 的首要目标是将 Java HTTP 客户端调用过程变得简单。
本例采用 Maven 多模块结构,其中服务注册中心、服务提供者的代码和使用 RestTemplate 和 Ribbon 消费服务类似,这里只展示服务消费者的代码。
新建一个 Maven 工程,命名为 service-consumer,作为服务消费者,其 pom 文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http:///POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http:///POM/4.0.0 http:///xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>service-consumer</artifactId>
<parent>
<artifactId>spring-cloud-feign</artifactId>
<groupId>com.wuychn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml 文件如下:
spring:
application:
name: service-consumer
server:
port: 9004
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9001/eureka/
程序启动类如下,@EnableEurekaClient 注解用于开启 Eureka Client 的功能,@EnableFeignClients 注解用于开启 Feign Client 的功能:
package com.wuychn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
经过以上配置,service-consumer 就具备了 Feign 的功能,下面看看如何使用 Feign Client 调用 service-provider 的 REST API 接口。
新建一个 HiFeignClient 接口,在该接口上使用 @FeignClient 注解来声明一个 Feign Client,其中 value 表示远程调用的服务名,也就是本例中的 service-provider,configuration 指定 Feign Client 的配置类,该接口中有一个 hi() 方法,这个方法通过 Feign 来调用 service-provider 的 /hi 接口:
package com.wuychn.client;
import com.wuychn.client.config.FeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "service-provider", configuration = FeignConfig.class)
public interface HiFeignClient {
// 如果不添加@RequestParam注解,会被转化为POST请求,从而报405
@GetMapping("/hi")
String hi(@RequestParam(value = "name") String name);
}
需要注意的是,在 hi() 方法的参数中,需要通过 @RequestParam 注解指定参数,否则即使我们使用的是 @GetMapping,Feign 也会将其转换为 POST 请求,从而引发 405 错误。
FeignConfig 是一个配置类,其代码如下:
package com.wuychn.client.config;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static java.util.concurrent.TimeUnit.SECONDS;
@Configuration
public class FeignConfig {
@Bean
public Retryer reignRetryer() {
return new Retryer.Default(100, SECONDS.toMillis(1), 5);
}
}
在 FeignConfig 中,配置了一个 Retryer 类型的 Bean,这样 Feign 在远程调用失败后会进行重试,重试间隔为 100 毫秒,最大重试时间为 1 秒,重试次数为 5 次。
Feign Client 的默认配置类是 FeignClientsConfiguration,其中配置了很多 Feign 相关的 Bean,包括 Retryer、FeignLoggerFactory 等,具体可以参考其源码。如果我们没有自定义这些配置,那么默认的配置将会生效。在我们的这个例子中,FeignConfig 中配置了 Retryer,它会覆盖掉 FeignClientsConfiguration 中的默认 Retryer 配置。
在 HiService 中注入 HiFeignClient,通过 HiFeignClient 去调用服务提供者的 REST API 接口:
package com.wuychn.service;
import com.wuychn.client.HiFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class HiService {
@Autowired
private HiFeignClient hiFeignClient;
public String hi(String name) {
return hiFeignClient.hi(name);
}
}
HiController 如下:
package com.wuychn.controller;
import com.wuychn.service.HiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HiController {
@Autowired
private HiService hiService;
@GetMapping("/hi")
public String hi(@RequestParam(required = false, defaultValue = "wuychn") String name) {
return hiService.hi(name);
}
}
到这里,代码就写完了,项目结构如下图:
依次启动 eureka-server、service-provider 和 service-consumer,其中 service-provider 在 9002 和 9003 两个端口分别启动两个实例,然后在浏览器中多次访问 http://localhost:9004/hi,浏览器中会依次显示如下内容:
hi wuychn, I am from port:9002
hi wuychn, I am from port:9003
可见,service-consumer 使用 Feign Client 远程调用了 service-provider 服务的 /hi 接口,并且有负载均衡的能力。