7-1 使用Feign实现远程HTTP调用

1)什么是Feign

  • Feign是Netflix开源的声明式HTTP客户端
  • Feign的Github地址:Feign

image.png

2)使用Feign重构前面的代码

7-声明式HTTP客户端Feign - 图3之前代码架构存在的问题
image.png
三板斧

7-声明式HTTP客户端Feign - 图5重构代码的流程(以 /shares/{id}接口为例):

  1. 服务提供者user-center编写相应接口UserController,实现根据id获取UserDTO的方法findUserById()
  2. 服务消费者content-center声明一个UserFeignClient,去调用UserController的方法
  3. ShareService注入UserFeignClient的实例,获取到其调用其他微服务的能力,从而将ShareMapper和UserFeignClient提供的数据进行拼装,得到ShareDTO
  4. ShareController注入ShareService实例,调用即可

老报数据库连接实例过多的问题,解决方法

实现过程视频 Feign.mp4 (25.99MB)

7-声明式HTTP客户端Feign - 图7根据调用结果可以看出随机负载均衡的规则
image.png

7-2 Feign的组成

Feign使用很简单,但它的源码中大量充满各种设计模式,比如建造者模式、代理模式等等
以下表格整理了Feign的组成
image.png
通过以下方法查看Feign的底层代码,比如默认的Feign.Default
image.png
采用的是HttpUrlConnetion,它没有连接池,也没有资源管理的概念,性能比较差
image.png

再来看下LoadBalancerFeignClient
image.png
可以看到它使用了代理模式,我们可以传入任何的Client,默认传的就是上面那种Default
我们可以认为LoadBalancerFeignClient是一个带有负载均衡的Client.Default,它也用Url去请求,到性能优化的部分,我们可以改为其他方式如HttpClient来请求,引入连接池等 。

image.png

Contract:Feign默认是不支持SpringMVC注解的,它默认的注解如下图:
image.png
Spring Cloud对Feign进行了一些扩展,使之支持了SpringMVC的注解。

7-3 细粒度配置自定义

两种方式:

  • Java代码方式
  • 配置属性方式

以Feign的日志打印为例来学习以上两点

以下是Feign的日志级别
image.png

1)Java代码方式

  • 先写一个自定义的类,返回feign的日志级别,头部不要加@Configuration注解,否则会父子上下文重叠,必须挪到外面的包

image.png

  • 在UserCenterFeignClient中,通过@FeignClient注解的configuration属性使用它

image.png

  • application.yml中配置feign的日志

image.png

  • 访问内容中心接口,可以看到详细日志信息

image.png

2)配置属性方式

超级简单,只需如下一行配置
image.png
feign.client.config..loggerLevel:full
先去掉配置属性
image.png
然后在yml文件中加入以下配置即可
image.png
重新运行,一样可以看到请求的详细日志结果,这种方式更简单
image.png

7-4 全局配置

1)Java代码方式

image.png
方式一:让父子上下文omponentSean重叠(强烈不建
议使用于
image.png
方式二(唯一正确的途径
@EnableFeignclients(defaultConfigurationxxX.class)

  • 注释掉细粒度配置

image.png

  • 启动主类注解加上defaultConfiguration属性,指向配置类即可(基于语义,类名使用GlobalFeignConfiguration,代码同UserCenterFeignConfiguration)

重新运行,效果一致
_image.png

2)配置属性方式

  • 注释掉启动类的defaultConfiguration属性

image.png

  • yml中加入如下配置,用default代替之前的user-center即可实现全局配置

image.png
so easy,效果一致,不再截图

7-5 支持的配置项

1)代码方式支持的配置项

每一项配置对应了Feign的组成那一节
image.png

2)属性方式支持的配置项

留一些印象即可,需要用的时候知道在哪
image.png

7-6 配置总结

1)Ribbon配置 VS Feign配置

image.png

2)Feign代码方式 VS 属性方式

image.png
优先级关系
image.png
全局代码<全局属性<细粒度代码
细粒度属性

3)最佳实践总结

  1. 尽量使用属性配置,属性方式实现不了的情况再考虑用代码配置
  2. 在同一个微服务内尽量保持单一性,比如统一使用属性配置,不要两种方式混用,增加定位代码的复杂性

7-7 Feign的继承

按照官方建议, 不要用Feign的继承,会造成紧耦合。

7-8 多参数请求的构造

1)GET请求

  • 用户中心Controller增加方法,多个参数

image.png

  • 启动用户中心,没有错误

浏览器测试,通过url参数的构造,使得传入的参数封装到请求体,最后返回该实体
image.png

  • 内容中心相关代码

image.png
image.png

  • 重启运行,应用启动报错

image.png

  • 按照建议,在yml中加入这个配置

image.png

  • 再次启动,可以成功

image.png

WHY???

因为出现了两个 “user-center”同名的FeignClient,加上上述配置就可以允许同名的存在**
image.png

  • 现在,通过如下url访问测试接口,发现报错

image.png
控制台报了405异常
image.png

如何解决呢?
方法一(推荐)
很简单,在FeignClient方法参数前加一个注解就可以了
image.png
再次启动后验证结果
image.png
方法二(推荐)
使用@RequestParam注解在接口中构造参数

  1. @FeignClient(name = "microservice-provider-user")
  2. public interface UserFeignClient {
  3. @RequestMapping(value = "/get", method = RequestMethod.GET)
  4. public User get1(@RequestParam("id") Integer id, @RequestParam("username") String username);
  5. }

方法三(不推荐)

多参数的URL也可使用Map来构建。当目标URL参数非常多的时候,可使用这种方式简化Feign接口的编写。

  1. @FeignClient(name = "microservice-provider-user")
  2. public interface UserFeignClient {
  3. @RequestMapping(value = "/get", method = RequestMethod.GET)
  4. public User get2(@RequestParam Map<String, Object> map);
  5. }

在调用时,可使用类似以下的代码

  1. public User get(String username, String password) {
  2. HashMap<String, Object> map = Maps.newHashMap();
  3. map.put("id", "1");
  4. map.put("username", "张三");
  5. return this.userFeignClient.get2(map);
  6. }

注意:这种方式不建议使用。主要是因为可读性不好,而且如果参数为空的时候会有一些问题,例如map.put("username", null); 会导致microservice-provider-user 服务接收到的username是"" ,而不是null。

2)POST请求

假设服务提供者的Controller是这样编写的

  1. @RestController
  2. public class UserController {
  3. @PostMapping("/post")
  4. public User post(@RequestBody User user) {
  5. ...
  6. }
  7. }

我们要如何使用Feign去请求呢?答案非常简单,示例如下:

  1. @FeignClient(name = "microservice-provider-user")
  2. public interface UserFeignClient {
  3. @RequestMapping(value = "/post", method = RequestMethod.POST)
  4. public User post(@RequestBody User user);
  5. }


7-9 Feign脱离Ribbon使用

现在用Feign调用的微服务都是注册到Nacos上的,如何使用Feign调用没有注册到Nacos的服务呢,比如调用百度,如何实现?

写BaiduFeignClient
注意:@FeignClient注解,要么指定value,要么指定name,否则会报错!
image.png
image.png
测试结果
image.png

7-10 RestTemplate VS Feign

image.png
如何选择?

  • 尽量使用Feign,代码优雅
  • 合理选择

    7-11 Feign性能优化

    7-12 常见问题总结

    看这篇

7-13 现有架构总结

微服务架构初具雏形
image.png