一、简介
Feign是Netflix公司开发的一个声明式的REST调用客户端;
Ribbon负载均衡、Hystrix服务熔断是我们Spring Cloud开发中非常基础的组件,在使用过程中他们一般是同时出现的,配置也非常相似,每次开发都有许多相同的代码。因此,Spring Cloud基于Netflix Feign整合了Ribbon和Hystrix两个组件,让我们的开发变的更加简单。
Spring Cloud Feign :对Ribbon负载均衡、Hystrix服务熔断进行了简化,在其基础上进行了进一步的封装,不仅在配置上大大简化了开发工作,同时还提供了一种声明式的Web服务客户端定义方式。
二、使用Feign实现服务消费者
- 创建SpringBoot工程
- 添加依赖
spring-cloud-starter-netflix-eureka-client
spring-cloud-starter-openfeign
spring-cloud-starter-netflix-hystrix
- 在入口函数上激活功能
@EnableEurekaClient
@EnableFeignClients
- 配置文件
# 服务端口号
server.port=80
#指定该服务的名字,该名称将在服务被调用时使用
spring.application.name=feign-eureka-client-consumer
#Eureka配置:服务注册到哪里
eureka.client.service-url.defaultZone=http://eureka7001:7001/eureka
- service接口
/**
* 用于标记当前借口是一个Feign声明式服务接口。Spring会为这个接口生成动态代理对象
* 属性name:指定注册中心某个服务的名字
*/
@FeignClient(name="feign-eureka-client-provider")
public interface TestService {
/**
* 定义抽象方法
* RequestMapping标记这个方法用于访问服务提供者,参数对应服务提供者提供的方法
* @return 服务提供者返回的具体内容
*/
@RequestMapping("/test")
String test();
/**
* @RequestParam 指定基本数据类型参数
*/
@RequestMapping("/testParam01")
String testParam01(@RequestParam String name, @RequestParam Integer age);
/**
* @RequestBody 指定对象类型参数
*/
@RequestMapping("/testParam02")
String testParam02(@RequestBody User user);
/**
* 如果服务提供者返回的Json数据符合Json对象格式
* 那么我们可以使用一个实体类或Map集合来接收响应数据
*/
@RequestMapping("/testReturnUser")
User testReturnUser();
/**
* 如果服务提供者返回的Json数据符合Json对象数组格式
* 那么我们可以使用一个List集合来接收响应数据
*/
@RequestMapping("/testReturnList")
List<User> testReturnList();
}
- controller调用远程服务
/**
* 作为服务消费者,可以不使用 RestController 返回json数据,这里为了方便而使用。
*/
@RestController
public class TestController {
@Resource
private TestService testService;
@RequestMapping("/test")
public String test(){
String result = testService.test();
return "使用了Feign的服务消费者..."+result;
}
@RequestMapping("/testParam01")
public String testParam01(){
String result = testService.testParam01("lily",24);
return "使用了Feign的服务消费者..."+result;
}
@RequestMapping("/testParam02")
public String testParam02(){
User user = new User("xiaoming",23);
String result = testService.testParam02(user);
return "使用了Feign的服务消费者..."+result;
}
@RequestMapping("/testReturnUser")
public String testReturnUser(){
User user = testService.testReturnUser();
return "使用了Feign的服务消费者..."+user.toString();
}
@RequestMapping("/testReturnList")
public String testReturnList(){
List<User> list = testService.testReturnList();
return "使用了Feign的服务消费者..."+list.toString();
}
}
三、实现普通的服务提供者
- 创建SpringBoot工程
- 添加依赖
spring-cloud-starter-netflix-eureka-client
- 在入口函数上激活功能
@EnableEurekaClient
- 配置文件
server.port=8001
#指定该服务的名字,该名称将在服务被调用时使用
spring.application.name=feign-eureka-client-provider
#Eureka配置:服务注册到哪里
eureka.client.service-url.defaultZone=http://eureka7001:7001/eureka
- controller提供服务
/**
* 作为服务提供者,要是用 RestController 返回json数据
*/
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "【服务提供者返回内容】";
}
@GetMapping("/testParam01")
public String testParam01(String name,Integer age){
return "【服务提供者返回内容 name:"+name+" age:"+age+"】";
}
@RequestMapping("/testParam02")
public String testParam02(@RequestBody User user){
return "【服务提供者返回内容 name:"+user.getName()+" age:"+user.getAge()+"】";
}
@RequestMapping("/testReturnUser")
public Object testReturnUser(){
User user = new User("小黄",23);
return user;
}
@RequestMapping("/testReturnList")
public Object testReturnList(){
List<User> list = new ArrayList<>();
User user1 = new User("小黄",23);
User user2 = new User("小李",24);
User user3 = new User("小明",25);
list.add(user1);
list.add(user2);
list.add(user3);
return list;
}
}
四、Feign服务调用测试
- 访问:http://localhost/test
- http://localhost/testParam01
- 访问:http://localhost/testParam02
- 访问:http://localhost/testReturnUser
- 访问:http://localhost/testReturnList
五、Feign消费者测试
负载均衡
Spring Cloud提供了Ribbon来实现负载均衡,使用Ribbon直接注入一个RestTemplate对象即可,RestTemplate已经做好了负载均衡的配置;在Spring Cloud下,使用Feign也是直接实现负载均衡的,定义一个有@FeignClient注解的接口,然后使用@RequestMapping注解到方法上映射远程的REST服务,此方法也是做好负载均衡配置的。
服务熔断
- 在application.properties文件开启feign对hystrix功能支持(支持熔断)
feign.hystrix.enabled=true
- 指定熔断回调逻辑
/**
* @FeignClient:标记当前接口是一个Feign声明式服务接口,Spring会为这个接口生成动态代理对象
* 属性name:指定注册中心某个服务的名字
* 属性fallback:指定一个自定义异常熔断器类
* 属性fallbackFactory:指定一个自定义异常熔断器类,可以获得异常信息
*/
//方式一
@FeignClient(name="feign-eureka-client-provider",fallback = MyFallback.class)
//方式二
@FeignClient(name="feign-eureka-client-provider",fallbackFactory = MyFallbackFactory.class)
public interface TestService {...}
- 自定义异常熔断器
//方式一
/**
* 自定义异常熔断器类,并实现自定义声明式Feign的接口
* 为这个接口中的所有抽象方法提供熔断处理
*/
@Component
public class MyFallback implements TestService {
@Override
public String test() {
return "test请求熔断";
}
@Override
public String testParam01(String name, Integer age) {
return "testParam01请求熔断";
}
@Override
public String testParam02(User user) {
return "testParam02请求熔断";
}
@Override
public User testReturnUser() {
return null;
}
@Override
public List<User> testReturnList() {
return null;
}
}
//方式二
/**
* 自定义异常熔断器类,并实现Hystrix的降级回调的父接口
* 注意:泛型决定当前类要为哪个声明式接口提供异常熔断处理
*/
@Component
public class MyFallbackFactory implements FallbackFactory<TestService> {
/**
* @param throwable
* @return 返回一个接口中泛型的对象:当这个泛型对象出现异常后将用create返回的这个对象进行降级
*/
@Override
public TestService create(Throwable throwable) {
//使用匿名内部类来创建TestService声明式服务接口的熔断对象
return new TestService() {
@Override
public String test() {
System.out.println(throwable.getClass());
System.out.println(throwable.getMessage());
return "test请求熔断";
}
@Override
public String testParam01(String name, Integer age) {
return "testParam01请求熔断";
}
@Override
public String testParam02(User user) {
return "testParam02请求熔断";
}
@Override
public User testReturnUser() {
return null;
}
@Override
public List<User> testReturnList() {
return null;
}
};
}
}
- 服务异常
@GetMapping("/test")
public String test(){
System.out.println(1/0);
return "【服务提供者返回内容】";
}
- 调用服务进行熔断测试 http://localhost/test