SPRING CLOUD微服务实战笔记--声明式服务调用:Spring Cloud Feign

快速入门

通过Spring Cloud Feign提供的声明式服务绑定功能来实现对该服务接口的调用

  • 创建Spring Boot工程,在pom.xml中引入依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-feign</artifactId>
	<version>1.4.6.RELEASE</version>
</dependency>
  • 创建应用主类,并通过@EnableFeignClients注解开启Spring Cloud Feign的支持功能
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class HelloApplication {
	public static void main(String[] args) {
		SpringApplication.run(HelloApplication.class, args);
	}
}
  • 定义HelloService接口,通过@FeignClient注解指定服务名来绑定服务,然后再使用SpringMVC的注解绑定具体该服务提供的REST接口
@FeignClient("hello-service")
public interface HelloService {
    @RequestMapping("/hello")
    String hello();
}
  • 创建一个ConsumerController来实现Feign客户端的调用
@RestController
public class ConsumerController {
    @Autowired
    HelloService helloService;
    @RequestMapping(value = "/feign-consumer" , method = RequestMethod.GET)
    public String helloConsumer(){
        return helloService.hello();
    }
}
  • 在application.properties中指定服务注册中心
spring.application.name=feign-consumer
server.port=9003
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

测试验证
访问http://localhost:9003/feign-consumer,页面如下:
SPRING CLOUD微服务实战笔记--声明式服务调用:Spring Cloud Feign

参数绑定

第一步:改造服务提供方的服务
服务提供方修改服务,增加接口定义,分别带有Request参数的请求,带有Header信息的请求,带有RequestBody的请求

@RequestMapping(value = "/hello1",method = RequestMethod.GET)
    public String hello(@RequestParam String name){
        return "Hello " + name;
    }

    @RequestMapping(value = "/hello2",method = RequestMethod.GET)
    public User hello(@RequestHeader String name,@RequestHeader Integer age){
        return new User(name,age);
    }

    @RequestMapping(value = "/hello3" , method = RequestMethod.POST)
    public String hello(@RequestBody User user){
        return "Hello " + user.getName() + ", " + user.getAge();
    }

User对象定义如下:

public class User {
    private String name;
    private Integer age;
    public User(){}
    public User(String name,Integer age){
        this.name = name;
        this.age = age;
    }
//省略getter和setter方法
    @Override
    public String toString(){
        return "name=" + name + ",age=" + age;
    }
}

第二步:改造服务消费端的请求绑定

  • 在feign-consumer中添加和上面一样的User类
  • 在HelloService中增加三个新增接口的绑定声明
@FeignClient("hello-service")
public interface HelloService {
        @RequestMapping("/hello")
        String hello();
        @RequestMapping(value="/hello1",method = RequestMethod.GET)
        String hello(@RequestParam("name") String name);
        @RequestMapping(value = "/hello2",method = RequestMethod.GET)
        User hello(@RequestHeader("name") String name,@RequestHeader("age") Integer age);
        @RequestMapping(value = "/hello3",method = RequestMethod.POST)
        String hello(@RequestBody User user);
}
  • 在ConsumerController中新增一个/feign-consumer2接口,对新增接口进行调用
@RequestMapping(value = "/feign-consumer2",method = RequestMethod.GET)
    public String helloConsumer2(){
        StringBuilder sb = new StringBuilder();
        sb.append(helloService.hello()).append("\n");
        sb.append(helloService.hello("DIDI")).append("\n");
        sb.append(helloService.hello("DIDI",30)).append("\n");
        sb.append(helloService.hello(new User("DIDI",30))).append("\n");
        return sb.toString();
    }

测试验证
访问http://localhost:9003/feign-consumer2,返回如下:

Hello World! Hello DIDI name=DIDI,age=30 Hello DIDI, 30

继承特性

通过Spring Cloud Feign的继承特性来实现REST接口定义的复用
先新建一个Maven工程:

  • 创建一个Maven工程,命名为hello-service-api
  • 修改工程的pom,引入依赖
<modelVersion>4.0.0</modelVersion>
	<groupId>com.didispace</groupId>
	<artifactId>hello-service-api</artifactId>
	<packaging>jar</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>hello-service-api</name>
	<description>Demo project for Spring Boot</description>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.7.RELEASE</version>
		<relativePath></relativePath>
	</parent>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>
  • 将上一节实现的User对象复制到hello-service-api工程中
  • 在Maven工程中添加HelloService
@RequestMapping("/refactor")
public interface HelloService {
    @RequestMapping(value = "/hello4",method = RequestMethod.GET)
    public String hello(@RequestParam String name);
    @RequestMapping(value = "/hello5",method = RequestMethod.GET)
    public User hello(@RequestHeader String name, @RequestHeader Integer age);
    @RequestMapping(value = "/hello6" , method = RequestMethod.POST)
    public String hello(@RequestBody User user);
}

再修改服务提供方,对其进行重构

  • 修改pom.xml,新增对hello-service-api的依赖
<dependency>
	<groupId>com.didispace</groupId>
	<artifactId>hello-service-api</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>
  • 创建RefactorHelloController类继承hello-service-api中定义HelloService接口,并实现这个接口中的方法
@RestController
public class RefactorHelloController implements HelloService {
    @Override
    public String hello(@RequestParam("name") String name) {
        return "Hello " + name;
    }
    @Override
    public User hello(@RequestHeader("name") String name,@RequestHeader("age") Integer age) {
        return new User(name,age);
    }
    @Override
    public String hello(@RequestBody User user) {
        return "Hello " + user.getName() + ", " + user.getAge();
    }
}

最后修改服务消费者

  • 修改pom.xml,新增对hello-service-api的依赖
<dependency>
	<groupId>com.didispace</groupId>
	<artifactId>hello-service-api</artifactId>
	<version>0.0.1-SNAPSHOT</version>
</dependency>
  • 创建RefactorHelloService接口,并继承hello-service-api包中的HelloService接口,然后添加@FeignClient注解来绑定服务
@FeignClient(value = "HELLO-SERVICE")
public interface RefactorHelloService extends com.didispace.service.HelloService{
}
  • 在ConsumerController中,注入RefactorHelloService的实例,并新增一个请求/feign-consumer3来触发RefactorHelloService的实例
@RequestMapping(value = "/feign-consumer3",method = RequestMethod.GET)
    public String helloConsumer3(){
        StringBuilder sb = new StringBuilder();
        sb.append(refactorHelloService.hello("MIMI")).append("\n");
        sb.append(refactorHelloService.hello("MIMI",30)).append("\n");
        sb.append(refactorHelloService.hello(new com.didispace.User.User("MIMI",20))).append("\n");
        return sb.toString();
    }

遇到的问题:
将项目引入到maven私服库的时候,项目中调用接口不能识别,原来之前为了打包将pom中的编译插件换了,导致执行mvn install命令时编译有问题

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<version>2.0.3.RELEASE</version>
			<executions>
				<execution>
					<goals>
						<goal>repackage</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

将上面这段从pom中删除,重新执行mvn install后成功

Ribbon配置

全局配置

通过使用ribbon.=方式来设置ribbon的各项默认参数,比如修改默认的客户端调用超时时间:

ribbon.ConnectTimeout = 500
ribbon.ReadTimeout=5000

指定服务配置

有时需要个性化配置,采用.ribbon.key=value的格式进行设置
使用@FeignClient(value = “HELLO-SERVICE”)来创建Feign客户端的时候,同时也创建了一个名为HELLO-SERVICE的Ribbon客户端

HELLO-SERVICE.ribbon.ConnectTimeout=500
HELLO-SERVICE.ribbon.ReadTimeout=2000

Hystrix配置

全局配置

Hystrix的全局配置采用默认配置前缀hystrix.command.default就可以设置,比如设置全局的超时时间:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000

通过feign.hystrix.enabled=false来全局关闭Hystrix功能
通过feign.command.default.execution.timeout.enabled=false来关闭熔断功能

禁用Hystrix

@Scope(“prototype”)注解为指定的客户端配置Feign.Builder实例

  • 构建一个Hystrix配置类
@Configuration
public class DisableHystrixConfiguration{
	@Bean
	@Scope("prototype")
	public Feign.Builder feignBuilder(){
		return Feign.builder();
	}
}
  • 在HelloService的@FeignClient注解中,通过configuration参数引入上面实现的配置
@FeignClient(name="HELLO-SERVICE",configuration =DisableHystrixConfiguration.class)
public interface HelloService{
}

其他配置

请求压缩

SpringCloudFeign支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗

feign.compression.request.enabled=true
feign.compression.response.enabled=true

同时还可以进行更加细化的配置

feign.compression.request.enabled=true;
//指定压缩请求数据类型
feign.comresstion.request.mime-types=text/xml,application/xml,application/json
//请求压缩的大小下限,只有查过这个大小的请求才会对其进行压缩
feign.compression.request.min-request-size=2048

日志配置

可以在application.properties文件中使用logging.level.<FeignClient>的参数配置格式来开启指定Feign客户端的DEBUG日志,其中<FeignClient>为Feign客户端定义接口的完整路径,比如:

logging.level.com.didispace.web.HelloService=DEBUG

同时调整全局的日志级别,可以在应用主类中直接加入Logger.Level的Bean创建,具体如下:

@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class HelloApplication {
	@Bean
	Logger.Level feignLoggerLevel(){
		return Logger.Level.FULL;
	}
	public static void main(String[] args) {
		SpringApplication.run(HelloApplication.class, args);
	}
}

对于Feign的Logger级别主要有如下4类,可以根据实际需要进行调整使用

  • NONE:不记录任何信息
  • BASIC:仅记录请求方法,URL以及响应状态码和执行时间
  • HEADERS:除了记录BASIC级别的信息之外,还会记录请求和响应的头信息
  • FULL:记录所有请求与响应的明细,包括头信息,请求体和元数据等