网上很少有人把 Feign 单独作为一章来写,但我认为 Feign 也很重要,使用好 Feign 能更好的理解 RESTFUL 接口设计规范。如果想要用一篇文章来写好 Feign 我认为是很难的,我尽量用一篇文章来描述清楚。
Feign 是一个声明式 Web Service 客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud 对 Feign 进行了封装,使其支持了 Spring MVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。
在 Spring Cloud 中 Feign 是用作服务调用和客户端负载均衡的。Feign 可以配合 Spring Cloud 使用,也可以单独使用。
在不使用 Feign 之前,我们调用其它服务需要用到 httpclient 或者 okhttp 这样相对较重的框架,而且需要编写大量的代码。虽然 spring 中 RestTemplate 也可以灵活的使用,但是它不具备负载均衡的功能,需要我们自己实现;另外 RestTemplate 对配置化,支持的还不够友好,所以 Feign 应运而生。
我们先来看看 Feign 的单独使用。首先,需要导入 Maven 的相关配置:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.sourceEncoding>UTF-8</project.reporting.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-gson</artifactId>
<version>8.18.0</version>
</dependency>
</dependencies>
然后自定义一个远程调用接口,代码如下:
public interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
上面的 Contributor 类代码如下:
public class Contributor {
String login;
int contributions;
}
然后对 Feign 的远程调用功能进行测试,代码如下:
public class FeignTest {
public static void main(String... args) {
//www.xttblog.com
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
}
运行后,输出的结果我就不写了,大家可以自行的去测试。相关代码我已上传到 https://github.com/xmt1139057136/xttblog-cloud/xttblog-feign-github。
上面的案例,我们主要是对 Feign 有了一个简单的认识,在 Spring Cloud 中并不会这样的去使用。下面我们来看看在 Spring Cloud 中如何使用 Feign。
首先创建一个 xttblog-cloud-producer 项目。pom.xml 配置如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
application.yml 配置文件内容如下:
server:
port: 8888
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8761/eureka/
spring:
application:
name: xttblog-cloud-producer
然后编写一个服务提供者的 Controller,代码如下:
@RestController
@RequestMapping("/cal")
public class CalController {
@RequestMapping("/add")
public Integer add(@RequestParam Integer a, @RequestParam Integer b){
return a + b;
}
}
启动类的代码没有大的改动,具体如下:
@SpringBootApplication
@EnableDiscoveryClient
public class XttblogProducerApplication {
public static void main(String[] args) {
SpringApplication.run(XttblogProducerApplication.class, args);
}
}
至此,服务提供者我们已经写完了,下面我们来编写服务调用着代码。同样的先新建一个 xttblog-cloud-consumer 工程。pom.xml 代码如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
接着是 application.yml 文件的代码:
server:
port: 7777
eureka:
instance:
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8761/eureka/
spring:
application:
name: xttblog-cloud-consumer
再接着是消费者的 Controller 代码:
@RestController
@RequestMapping("/test")
public class ConsumerController {
@Autowired
private CalService calService;
@Autowired
private CalRemoteService calRemoteService;
@RequestMapping("/add")
public String add(@RequestParam Integer a, @RequestParam Integer b){
return a + "+" + b + "的RestTemplate调用结果为:" + calService.add(a, b);
}
@RequestMapping("/radd")
public String rAdd(@RequestParam Integer a, @RequestParam Integer b){
return a + "+" + b + "的Cloud调用结果为:" + calRemoteService.add(a, b);
}
}
然后是 Feign 的远程调用的代码:
@FeignClient(name= "xttblog-cloud-producer")
public interface CalRemoteService {
@RequestMapping(value = "/cal/add")
public String add(@RequestParam(value="a") Integer a, @RequestParam("b") Integer b);
}
public interface CalService {
public String add(Integer a, Integer b);
}
@Service
public class CalServiceImpl implements CalService{
@Autowired
private RestTemplate restTemplate;
@Bean
//@LoadBalanced//启用ribbon负载均衡调用服务
public RestTemplate cteateRestTemplate(){
return new RestTemplate();
}
@Override
public String add(Integer a, Integer b) {
String url = "http://localhost:8888/cal/add?a=" + a + "&b=" + b;
ResponseEntity responseEntity = restTemplate.getForEntity(url,String.class);
return responseEntity.getBody().toString();
}
}
我这里有两个 Service 类,一个是使用 RestTemplate 调用服务,一个是 Feign 来调用;让大家比较一下两者的调用方式。
在使用 @FeignClient 注解后,必须在主启动类上加上 @EnableFeignClients 注解,否则调用不会成功。
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class XttblogConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(XttblogConsumerApplication.class, args);
}
}
另外需要注意的是,在服务消费方必须引入 Feign 的依赖,否则启动时会报错。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
那有人会问了,为什么我引入注解时不报错,而是在运行时才报错呢?那是因为我们都是面向接口编程的。引入注解,并没有引入 Feign 的实现,所以会报错。
上面两个服务,我们都注册到了 Eureka,用的是 Spring Cloud 教程系列第二章的服务注册工程《Spring Cloud 教程第二章 服务的注册、发现、治理之 Eureka 实战》,具体可以到我的 github 上查看源码:https://github.com/xmt1139057136/xttblog-cloud/tree/master/xttblog-eureka-server。
写完上面的代码后,我们依次启动,xttblog-eureka-server、xttblog-cloud-producer、xttblog-cloud-consumer。然后浏览器地址栏里输入 http://localhost:7777/test/add?a=1&b=2 和 http://localhost:7777/test/radd?a=1&b=3 即可看到运行效果。
最后本文涉及的相关源码都已上传到:https://github.com/xmt1139057136/xttblog-cloud,喜欢的可以给个 star。

: » Spring Cloud 教程第五章 Feign 的声明式服务调用与负载均衡
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/251824.html