• 微服务外交官-Feign


    引言

    书接上篇 负载均衡组件Ribbon核心-@LoadBalanced-下我们讲完了Ribbon负载均衡原理之后,接下讲一个SpringCloud Alibaba新的组件:Fegin

    前面章节我们使用Ribbon方式实现负载均衡版的远程调用

    1. //方案4:使用Ribbon方式--带负载均衡
    2. String url = "http://product-service/products/" + pid;
    3. Product product = restTemplate.getForObject(url, Product.class);
    4. order.setPid(pid);
    5. order.setProductName(product.getName());
    6. order.setProductPrice(product.getPrice());

    从实现效果上看没有任何问题,但每次都需要手动拼接URL,有点小麻烦,有没有更加优雅的方式呢?答案是yes:Feign

    什么是Feign

    Feign是啥?如果我们将所有为微服务比作国家,那Feign就是各国杰出的外交家

    Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。

    Nacos很好的兼容了Feign, Feign默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负载均衡的效果。

    实现案例

    还是之前的订单服务调用商品服务,此处使用Feign组件发起远程调用。

    步骤1:在shop-order-server项目的pom文件加入Fegin的依赖 

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-openfeignartifactId>
    4. dependency>

    注意:哪一方需要发起远程调用,Feign依赖就加在哪一方

    步骤2:启动类OrderServer.java上添加Fegin的扫描注解,注意扫描路径

    1. @SpringBootApplication
    2. @MapperScan("cn.wolfcode.mapper")
    3. @EnableDiscoveryClient
    4. @EnableFeignClients
    5. public class OrderServer {
    6. public static void main(String[] args) {
    7. SpringApplication.run(OrderServer.class,args);
    8. }
    9. }

    @EnableFeignClients 注解用于扫描后续定义Feign接口,代理生产接口实现类

    步骤3:在shop-order-server项目中新增接口IProductFeignService

    1. @FeignClient(name = "product-service")
    2. public interface IProductFeginService {
    3. @GetMapping("/products/{pid}")
    4. Product get(@PathVariable("pid") Long pid);
    5. }

    注意1:@FeignClient 指向要调用服务名

    注意2:接口方法定义跟被调用服务接口定义一模一样,只是少了实现

    步骤4:修改OrderServiceImpl.java的远程调用方法

    1. @Service
    2. public class OrderServiceImpl extends ServiceImpl implements IOrderService {
    3. @Autowired
    4. private RestTemplate restTemplate;
    5. @Autowired
    6. private DiscoveryClient discoveryClient;
    7. @Autowired
    8. private IProductFeginService productFeginService;
    9. @Override
    10. public Order createOrder(Long pid, Long uid) {
    11. Order order = new Order();
    12. //商品
    13. //方案1:通过restTemplate方式
    14. //String url = "http://localhost:8081/products/" + pid;
    15. //Product product = restTemplate.getForObject(url, Product.class);
    16. //方案2:使用注册中心api方式-discoveryClient
    17. //List instances = discoveryClient.getInstances("product-service");
    18. //ServiceInstance instance = instances.get(0); //当前只有一个
    19. //String host = instance.getHost();
    20. //int port = instance.getPort();
    21. //String url = "http://" + host + ":" + port + "/products/" + pid;
    22. //Product product = restTemplate.getForObject(url, Product.class);
    23. //方案3:使用注册中心api方式-discoveryClient--带负载均衡
    24. //List instances = discoveryClient.getInstances("product-service");
    25. //int index = new Random().nextInt(instances.size());
    26. //ServiceInstance instance = instances.get(index);
    27. //String host = instance.getHost();
    28. //int port = instance.getPort();
    29. //String url = "http://" + host + ":" + port + "/products/" + pid;
    30. //System.out.println("从nacos中获取的url地址:" + url);
    31. //Product product = restTemplate.getForObject(url, Product.class);
    32. //方案4:使用Ribbon方式--带负载均衡
    33. //String url = "http://product-service/products/" + pid;
    34. //Product product = restTemplate.getForObject(url, Product.class);
    35. //方案5:使用fegin接口--带负载均衡
    36. Product product = productFeginService.get(pid);
    37. order.setPid(pid);
    38. order.setProductName(product.getName());
    39. order.setProductPrice(product.getPrice());
    40. //用户
    41. order.setUid(1L);
    42. order.setUsername("dafei");
    43. order.setNumber(1);
    44. System.out.println(order);
    45. super.save(order);
    46. return order;
    47. }
    48. }

    Feign接口定义成功之后,后续调用跟普通的本地服务层接口一样。

    步骤5:重启订单服务,并验证

    Feign接口使用原理

    看到这,肯定有小伙伴觉得好神奇啊,就定义一个接口就能发起远程访问了,牛逼!其实,细心的朋友应该能看出端倪,Feign接口定义与配置内容都涵盖远程调用所有的参数:

    服务名:结合nacos 注册中心可以获取到 ip 与 端口

    接口路径:请求路径

    接口参数:请求参数

    这些参数一组装,不就是完整的服务接口url么~

    Feign接口定义注意要点

    1. @FeignClient(name = "product-service")
    2. public interface IProductFeginService {
    3. @GetMapping("/products/{pid}")
    4. Product get(@PathVariable("pid") Long pid);
    5. }

    1>@FeignClient 中name为服务提供者在nacos上注册的服务名, 否则报错

    Load balancer does not have available server for client:xxxx-service

    2>@GetMapping("/products/{pid}") 指定接口路径,必须跟服务提供者提供接口url一样,否则报错

    feign.FeignException$NotFound: [404] during [GET] to [http://xxx-service/xxx] 

    3> 定义接口参数:如果使用了参数路径方式访问,需要使用@PathVariable("pid") 明确指定路径参数,否则报错

    feign.FeignException$NotFound: [404] during [GET] to [http://xxx-service/xxx] 

    4>定义接口参数:如果使用普通方式访问,参数需要使用@RequestParam标记,否则报错

    1. @FeignClient(name = "product-service")
    2. public interface IProductFeginService {
    3. @GetMapping("/products")
    4. Product get(@RequestParam("pid") Long pid);
    5. }
    feign.FeignException$MethodNotAllowed: [405] during [GET] to [http://xxxx-service/xxxx?xxx=1]

    5>定义接口参数:如果是对象参数,参数需要使用@RequestBody标记(注意fegin接口,controler接口都要),否则报错

    1. @FeignClient(name = "product-service")
    2. public interface IProductFeginService {
    3. @GetMapping("/products")
    4. Product get(@RequestBody Product product);
    5. }
    参数无法获取

    6>定义接口参数:如果是对象,可以使用@SpringQueryMap替换上面的@RequestBody

    1. @FeignClient(name = "product-service")
    2. public interface IProductFeginService {
    3. @GetMapping("/products")
    4. Product get(@SpringQueryMap Product product);
    5. }

    7>定义接口参数:如果需要进行文件上传,需要使用@RequestPart注解标记

    1. @FeignClient(name = "product-service")
    2. public interface IProductFeginService {
    3. @PostMapping("/upload")
    4. void upload(MultipartFile file);
    5. }

    8>Feign接口调用默认连接时间是1s,如果电脑较慢,开发中可以配置长一点时间

    注意:后面学sentinel 时候,不要配置,会影响观测效果

    1. feign:
    2. client:
    3. config:
    4. default:
    5. connectTimeout: 5000 #连接时间,单位毫秒
    6. readTimeout: 5000 #操作时间

    到这,Feign简单介绍就结束啦,如果有小伙伴想了解Feign源码怎么玩的,那请期待下回分解啦~

     看文字不过瘾可以切换视频版:SpringCloud Alibaba 极简入门

  • 相关阅读:
    104.(前端)分类管理增加优化——elementui按钮禁用、清除数据后同时清空查询的内容并处理数据不完整报错
    数据库管理-第四十二期 复盘一下(20221104)
    JavaScript算法45- 字母异位词分组(leetCode:49middle)
    将时间序列转成图像——短时傅里叶方法 Matlab实现
    MQTT X Newsletter 2022-08 | v1.8.2 发布、支持使用 Docker
    PHP对接api时,如何保证其安全性
    最新版仿东郊到家小程序源码 上门服务小程序源码
    【图论】【并集查找】【C++算法】928. 尽量减少恶意软件的传播 II
    高防服务器有用么?
    Delay Penalty for RNN-T and CTC
  • 原文地址:https://blog.csdn.net/langfeiyes/article/details/128143082