Spring Cloud(Dalston.SR5)--Feign 声明式REST客户端
Spring Cloud 对 Feign 进行了封装,集成了 Ribbon 并结合 Eureka 可以实现客户端的负载均衡,Spring Cloud 实现的 Feign 客户端类名为 LoadBalancerFeignClient,在该类中,维护着与 SpringClientFactory 相关的实例,通过SpringClientFactory 可以获取负载均衡器,负载均衡器会根据一定的规则来选取处理请求的服务器,最终实现负载均衡功能。
集成了 Hystrix 增加了服务容错处理,并为 Feign 的使用提供了各种默认属性,例如编码器、解码器、日志、注解翻译器、实例创建者和客户端,我们可以简单的使用 Feign,也可以增加自定义的配置,下面我们在 Spring Cloud 中使用 Feign 示例如下:
- 创建项目
创建名称为 spring-cloud-feign-client 的 Spring Cloud 项目,修改 POM.xml 中增加以下依赖项:
<?xmlversion="1.0"encoding="UTF-8"?>
<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.lixue</groupId>
<artifactId>spring-cloud-feign-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-feign-client</name>
<description>DemoprojectforSpringBoot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/><!--lookupparentfromrepository-->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.SR5</spring-cloud.version>
</properties>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 创建 Feign 接口
在 Spring Cloud 中使用 Feign ,需要使用的是 Spring Cloud 的相关注解,例如 @RequestMapping、@RequestParam 等,而不是 Feign 的注解,创建 HelloWorldClient 接口,使用 @FeignClient 注解来标注服务名称(调用服务的 spring.application.name 属性),使用 @RequestMapping 来标注 REST 服务的相关属性;@RequestParam 和 @PathVariable 注解标注请求参数
package org.lixue;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient("helloworld-provider")
public interface HelloWorldClient{
@RequestMapping(path="/speak?body={body}",method=RequestMethod.GET)
String speak(@RequestParam(value="body",required=false) String body);
@RequestMapping(path="/person/{personId}",method=RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
Person findById(@PathVariable("personId")Integer personId);
@RequestMapping(path="/person/create",method=RequestMethod.POST,
consumes=MediaType.APPLICATION_JSON_UTF8_VALUE,
produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
ReturnValue create(Person person);
}
- 启动类启用 Feign
Feign 集成了 Ribbon 并结合 Eureka 可以实现客户端的负载均衡,因此在启动类我们需要启动 Eureka客户端和 Feign,使用 @EnableEurekaClient 启用 Eureka 客户端;使用 @EnableFeignClients 启用 Feign
package org.lixue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SpringCloudFeignClientApplication{
public static void main(String[]args){
SpringApplication.run(SpringCloudFeignClientApplication.class,args);
}
}
- 增加配置
Feign 集成了 Ribbon 并结合 Eureka 可以实现客户端的负载均衡,因此需要增加 Eureka 相关配置,指定 eureka 服务注册中心的地址
##配置应用名称
spring:
application:
name:spring-cloud-feign-client
#配置服务端口
server:
port:8070
#设置eureka服务注册中心的地址,如果多个以逗号分割
eureka:
client:
service-url:
defaultZone:http://eurekaserver01:9000/eureka/,http://eurekaserver02:9000/eureka/
- 创建 REST 服务
在调用服务时,只需要使用 @Autowired 注解标注 Feign 接口 HelloWorldClient , Spring Cloud 会帮我们创建和维护客户端实例
package org.lixue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
@RestController
public class InvokerController{
@Autowired
private HelloWorldClient helloWorldClient;
@RequestMapping(path="/invokerSpeak",method=RequestMethod.GET)
public String invokerSpeak(@RequestParam(name="body",required=false)String body){
return helloWorldClient.speak(body);
}
@RequestMapping(path="/invokerFindByID/{personID}",method=RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public Person invokerFindByID(@PathVariable("personID")Integer personID){
return helloWorldClient.findById(personID);
}
@RequestMapping(path="/invokerCreate",method=RequestMethod.GET,
produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
ReturnValue invokerCreate(){
Person newPerson=new Person();
newPerson.setId(3434);
newPerson.setAge(34);
newPerson.setName("343434");
newPerson.setMessage("33333333333333333");
return helloWorldClient.create(newPerson);
}
}
- 测试验证
启动 eureka-server 和 service-provider 项目,启动该项目后,访问地址 http://localhost:8070/invokerSpeak 可以查看到访问,如果启动了多个 service-provider ,多次刷新可以看到,具体的服务调用被轮询调用。
日志配置
启用服务的 Feign 日志,增加配置 Bean 如下:
package org.lixue;
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class MyFeignConfiguration{
@Bean
publicLogger.Level level(){
returnLogger.Level.FULL;
}
}
在 Feign 接口的 @FeignClient 注解中,增加 configuration 指向到创建的配置类:
@FeignClient(value="helloworld-provider",configuration=MyFeignConfiguration.class)
public interface HelloWorldClient{
//其他代码如上
}
修改 src/main/resources 目录下的 application.yml 增加日志级别配置:
logging:
level:
org.lixue.HelloWorldClient: debug
此时就可以看到请求日志的输出,如下:
2018-04-06 12:43:24.578 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] ---> GET http://helloworld-provider/speak HTTP/1.1
2018-04-06 12:43:24.581 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] ---> END HTTP (0-byte body)
2018-04-06 12:43:25.112 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] <--- HTTP/1.1 200 (529ms)
2018-04-06 12:43:25.112 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] content-length: 21
2018-04-06 12:43:25.113 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] content-type: text/plain;charset=UTF-8
2018-04-06 12:43:25.113 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] date: Fri, 06 Apr 2018 04:43:25 GMT
2018-04-06 12:43:25.113 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] x-application-context: helloworld-provider:8001
2018-04-06 12:43:25.113 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak]
2018-04-06 12:43:25.118 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] hello world port:8001
2018-04-06 12:43:25.118 DEBUG 3968 --- [nio-8070-exec-4] org.lixue.HelloWorldClient : [HelloWorldClient#speak] <--- END HTTP (21-byte body)
默认配置
Spring Cloud 为 Feign 的使用提供了各种默认属性,默认情况下,Spring 将会为 Feign 的属性提供以下 Bean 来实现 Feign 的调用
Bean 类型 | Bean 名称 | 实现类 | 说明 |
Decoder | feignDecoder | ResponseEntityDecoder | 解码器 |
Encoder | feignEncoder | SpringEncoder | 编码器 |
Logger | feignLogger | Slf4jLogger | 日志 |
Contract | feignContract | SpringMvcContract | 注解翻译器 |
Feign.Builder | feignBuilder | HystrixFeign.Builder | Feign 实例创建者 |
Client | feignClient |
| Feign 客户端,启用 Ribbon 实现类为LoadBalancerFeignClient 否则为 Feign 默认实现类 |
Logger.Level | 自命名 | 无 | 没有默认提供,只需要提供该类型的 Bean 即可 |
Retryer | 自命名 | 无 | 没有默认提供,只需要提供该类型的 Bean 即可 |
ErrorDecoder | 自命名 | 无 | 没有默认提供,只需要提供该类型的 Bean 即可 |
Request.Options | 自命名 | 无 | 没有默认提供,只需要提供该类型的 Bean 即可 |
RequestInterceptor | 自命名 | 无 | 没有默认提供,只需要提供该类型的 Bean 即可 |