文章目录
问题引入
在前面的学习中,我们使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码:
String baseurl = "http://user-service/user/" ;
User user = this.restTemplate.getForobject(baseUrl + id, User.class);
但是可能以后需要编写类似的大量重复代码,格式基本相同,无非参数不一样而已,而且例如这里的user-service服务名称我们可以通过eureka获取,但是user这个路径怎么知道呢?
Feign就能很好的解决这个远程调用的问题。
Feign
- 较为官方的解释:
Feign是一个声明性的Web服务客户端。它使编写Web服务客户端变得更容易。feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问。与当我们访问别的服务端口的时候 大部分使用httpclient等请求进行调用不同,在eureka注册的服务,我们可以使用Feign 声明接口的形式来进行相关服务的调用,并提供了失败回退(其实是Hystrix组件的使用)。Feign只是一个便利的rest框架,简化调用,最后还是通过ribbon在注册服务器中找到服务实例,然后对请求进行分配。 - 我的理解:
Feign可以把例如上面这样的远程请求给隐藏起来,让别人看不到,看起来好像是在访问本地服务一样,有一种伪装功能。Feign替我们解决这件事的时候,我们也应该去告诉他 请求路径、请求参数、请求方式、返回结果,仔细想想这四个东西springmvc的注解就可以搞定,我们的controller方法,有@RequestMapping(method属性)、@PathVariable、return返回值。所以Feign就可以利用springmvc的注解识别这些信息,帮我们远程调用,不用我们写。
Feign的使用
引入依赖并启动
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@EnableFeignClients
@SpringCloudApplication
public class ConsumerApplication {
/*这个也不需要了,注释掉
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}*/
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class);
}
}
编写注解类
这个注解类,其实就是Feign的一个客户端
@FeignClient("user-service")
:这个注解时指定服务名,去eureka拉取服务列表,然后底层利用ribben进行负载均衡挑选任意一个服务。访问@GetMapping(“user/findById/{id}”)路径,参数为id,返回一个User对象的json字符串,全部自动完成。
@FeignClient("user-service")
public interface UserClient {
@GetMapping("user/findById/{id}")
User findByid(@PathVariable("id")Integer id);
}
修改ConsumerController类
ConsumerController是服务调用端的一个controller类。
@RestController
@RequestMapping("/consumer")
@DefaultProperties(defaultFallback = "findByIdFallback")
public class ConsumerController {
@Autowired//注入UserClient
private UserClient userClient;
@GetMapping("{id}")
public User findById(@PathVariable("id")Integer id){
return userClient.findByid(id);
}
//默认fallback方法,不需要再有参数了
public String findByIdFallback(){
return "不好意思!服务器正忙!!!!";
}
}
userClient.findByid(id)
:这样的调用,是不是像在调用本地的一个方法一样,但是本质是进行了远程调用,很优雅。
Feign与负载均衡与服务降级
思考:在上面的代码中,我们只告诉了服务调用者服务名称是user-service,并没有告诉他ip地址,但是他却能实现访问服务并调用功能,说明他内部是有负载均衡处理的。
我们看看feign的包依赖,他内部是有ribbon的实现了负载均衡、hystrix实现了服务熔断和降级的,而且此时我们如果引入了这个Feign,那么这两个依赖就不需要在去单独引入依赖了。
feign与ribbon负载均衡
而且ribbon的负载均衡和以前使用方式是一样的,Feign还内置了一个超时时长默认1秒钟。我们可以在application.yml中进行配置。
ribbon:
ConnectionTimeOut: 1000 # 建立连接的超时时长(1秒未建立就超时)
ReadTimeOut: 1000 # 读取数据超时时长(1秒未读取就超时)
feign与hystrix熔断
注意Hystrix在feign中默认是关闭的,我们应该在application.yml中进行开启。
# 开启feign的hystrix功能
feign:
hystrix:
enabled: true
需要注意的是,Feign底层在实现熔断时,没有走Springcloud提供的方式,之前我们的配置的值是可用的,但是写法变了,我们需要自己去写一个熔断类。
这个类必须实现我们的UserClient接口,并且要注入到容器中。
@Component
public class UserClientFallBack implements UserClient {
@Override
public User findByid(Integer id) {
User user=new User();
user.setId(id);
user.setUsername("用户查询异常!!");
return user;
}
}
UserClient类的@FeignClient(value = "user-service",fallback =UserClientFallBack.class )
指定自定义熔断类。
@FeignClient(value = "user-service",fallback =UserClientFallBack.class )
public interface UserClient {
@GetMapping("user/findById/{id}")
User findByid(@PathVariable("id")Integer id);
}
测试
启动服务器,测试一下
未关闭前,一切正常。可以查出数据。
关闭服务提供者UserApplication服务器,肯定会出现远程连接异常,此时就会得到我们自定义的那个熔断类对应方法返回的信息了。