SpringCloud OpenFeign组件实现服务间的调用

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
日志服务 SLS,月写入数据量 50GB 1个月
简介: 🍅程序员小王的博客:程序员小王的博客🍅 欢迎点赞 👍 收藏 ⭐留言 📝🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕🍅java自学的学习路线:java自学的学习路线

简述:OpenFeign组件代替RestTemplate实现服务间的调用,并解决负载均衡和后期维护问题

0.png


一、什么是Feign组件——>OpenFeign

Feign组件(NetFlix)——>维护状态(NetFlix)——>SpringCloud提供OpenFeign


1、Feign 概述

在使用 Spring Cloud 开发微服务应用时,各个服务提供者都是以 HTTP 接口的形式对外提供服务,因此在服务消费者调用服务提供者时,底层通过 HTTP Client 的方式访问。此时可以使用 JDK 原生的 URLConnection、Apache 的 HTTP Client、Netty 的异步 HTTP Client 或者 Spring 的 RestTemplate 去实现服务间的调用。但是最方便、最优雅的方式是通过 Feign 进行服务间的调用。Feign 是由 Netflix 开发的一个声明式的 Web Service 客户端,它的出现使开发 Web Service 客户端变得很简单;Feign 同时也是一款声明式、模板化的 HTTP 客户端。更多介绍可参考:Feign 项目、Spring Cloud Feign 官方中文教程


2、Spring Cloud OpenFeign 概述

Spring Cloud OpenFeign 对 Feign 进行了二次封装,使得在 Spring Cloud 中使用 Feign 的时候,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程访问,更感知不到在访问 HTTP 请求。Spring Cloud OpenFeign 增强了 Feign 的功能,使 Feign 有限支持 Spring MVC 的注解,如 @RequestMapping 等。OpenFeign 的 @FeignClient 注解可以解析 Spring MVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,在实现类中做负载均衡并调用其他服务,默认集成了 Ribbon 与 Hystrix。更多介绍可参考:Spring Cloud OpenFeign 项目,OpenFeign入门教程


3、OpenFeign官网介绍

官网: Spring Cloud OpenFeign


Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持。


1.png


4、OpenFeign和RestTemplate区别

RestTemplate:Spring框架封装HttpClient对象


OpenFeign:伪HttpClient客户端对象,让服务间的通信更简单,实现了Ribbon组件,实现负载均衡


简单: 使用时写一个接口加一个注解 ,自动完成数据传递过程中对象转换


5、为什么使用OpenFeign

RestTemplate 使用问题 1.路径写死 2.不能自动转化响应结果为对应对象 3.必须集成Ribbon实现负载均衡


OpenFeign组件 解决RestTemplate实现服务间通信所有问题


2.png


二、OpenFeign组件的使用

思考: 使用RestTemplate+ribbon已经可以完成对端的调用,为什么还要使用feign?


String restTemplateForObject = restTemplate.getForObject("http://服务名/url?参数" + name, String.class);
#存在问题:
- 1.每次调用服务都需要写这些代码,存在大量的代码冗余
- 2.服务地址如果修改,维护成本增高
- 3.使用时不够灵活


1、OpenFeign服务调用

(1)服务调用方法引入依赖OpenFeign依赖

<!--Open Feign依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>


(2)入口类加入注解开启OpenFeign支持

@SpringBootApplication
@EnableFeignClients  //入口类加入注解开启OpenFeign支持
@EnableDiscoveryClient  //都是能够让注册中心能够发现,扫描到该服务。
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}


(3)创建一个客户端调用接口


@FeignClient("products")
public interface ProductClient {
    @GetMapping("/product/findAll") //书写服务调用路径
    String findAll();
}


(4)使用feignClient客户端对象调用服务


 

   @Autowired
    private ProductClient productClient;
    @GetMapping("/user/findAllFeignClient")
    public String findAllFeignClient(){
        log.info("通过使用openFeign组件调用商品服务");
        String msg = productClient.findAll();
        return msg;
    }


(5)访问


网址:http://localhost:9999/user/findAllFeignClient


3.png


三、调用服务并传参

服务和服务之间通信,不仅仅是调用,往往在调用过程中还伴随着参数传递,接下来重点来看看OpenFeign在调用服务时如何传递参数


1、GET方式调用服务传递参数

4.png


在商品服务中加入需要传递参数的服务方法来进行测试


在用户服务中进行调用商品服务中需要传递参数的服务方法进行测试


(1)传参方式


queryString方式传值(地址栏) 例: ?name=王恒杰


路径传递参数(restFul) 例:/name/王恒杰


总结:当只有一个参数时以问号形式传参,当有多个参数不能自动用

?传参,需要在OpenFeign接口中必须给参数加入注解@RequestParam


如果是路径(RestFul)传参需要在OpenFeign接口中加入@PathVariable注解


(2)商品服务中添加如下方法

@RestController
@Slf4j
public class ProductAction {
    @Value("${server.port}")
    private int port;
    @GetMapping("/product/findOne")
    public Map<String,Object> findAll(String productId) {
        log.info("商品服务调度成功,当前服务端口[{}]", port);
        log.info("当前商品接收信息的ID:[{}]", productId);
        HashMap<String, Object> map = new HashMap<>();
        map.put("msg","服务调度成功,服务端口为:" + port);
        map.put("status",true);
        map.put("productId",productId);
        return map;
    }
}


(3)用户服务中在product客户端中声明方法(OpenFeignClient)


@FeignClient("products")
public interface ProductClient {
    /**
     * 根据Id查询商品
     * @RequestParam 路径传参
     * @param productId
     * @return
     */
    @GetMapping("/product/findOne")
    String findOne(@RequestParam("productId")String productId);
}


(4)用户服务中调用并传递参数


   @Autowired
    private ProductClient productClient;
    @GetMapping("/user/findOneFeignClient")
    public String findOneFeignClient(String productId){
        log.info("通过openFeign组件调用商品服务。。。。。");
        String msg=productClient.findOne(productId);
        return  msg;
    }

(5)测试访问

5.png

6.png




2、post方式调用服务传递零散参数

在商品服务中加入需要传递参数的服务方法来进行测试


在用户服务中进行调用商品服务中需要传递参数的服务方法进行测试


(1)商品服务加入post方式请求并接受name


 

    @PutMapping("/product/saveName")
    public Map<String, Object> save(String name) {
        log.info("商品服务现在调用的端口号[{}]"+port);
        log.info("商品接收到的姓名[{}]"+name);
        HashMap<String, Object> map = new HashMap<>();
        map.put("msg","商品服务查询商品信息调用成功,当前服务端口: "+port);
        map.put("status",true);
        map.put("name",name);
        return  map;
    }


(2)用户服务中在product客户端中声明方法

   /**
     * 存储商品名
     * @RequestParam ?传参
     * @param name
     * @return
     */
    @PutMapping("/product/saveName")
    String saveName(@RequestParam("name") String name);

(3)用户服务中调用并传递参数


    @GetMapping("/user/saveName")
    public String saveName(String name) {
        log.info("调用商品组件的保存姓名功能");
        String s = productClient.saveName(name);
        return s;
    }

(4)测试

7.png


8.png



3、post传递对象类型参数

商品服务定义对象


商品服务定义对象接收方法


用户服务调用商品服务定义对象参数方法进行参数传递


(1)商品服务定义对象


@Data  //等价于@Setter、@Getter、@RequiredArgsConstructor、@ToString、@EqualsAndHashCode
public class Product {
    private String productId;
    private String name;
    private Double Price;
}


(2)商品服务加入post方式请求并接受对象


 

  @PostMapping("/product/saveProduct")
    public Map<String, Object> saveProduct(@RequestBody Product product) {
        log.info("商品服务保存商品信息调用成功,当前服务端口:[{}]", port);
        log.info("当前接收商品名称:[{}]", product);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("msg", "商品服务查询商品信息调用成功,当前服务端口: " + port);
        map.put("status", true);
        map.put("product", product);
        return map;
    }


(3)用户服务中在product客户端中声明方法


 

    @PostMapping("/product/saveProduct")
    String saveProduct(@RequestBody Product product);


(4)用户服务中调用并传递对象参数


 

    @GetMapping("/user/saveProduct")
    public String saveProduct(Product product){
        log.info("接收到的商品信息:[{}]",product);
        String save = productClient.saveProduct(product);
        log.info("调用成功返回结果: "+save);
        return save;
    }

(5)测试


9.png


4、OpenFeign超时设置

默认情况下,openFiegn在进行服务调用时,要求服务提供方处理业务逻辑时间必须在1S内返回,如果超过1S没有返回则OpenFeign会直接报错,不会等待服务执行,但是往往在处理复杂业务逻辑是可能会超过1S,因此需要修改OpenFeign的默认服务调用超时时间。


(1)模拟超时


 

    @PostMapping("/product/saveProduct")
    public Map<String, Object> saveProduct(@RequestBody Product product) throws InterruptedException {
        Thread.sleep(2000);
        log.info("商品服务保存商品信息调用成功,当前服务端口:[{}]", port);
        log.info("当前接收商品名称:[{}]", product);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("msg", "商品服务查询商品信息调用成功,当前服务端口: " + port);
        map.put("status", true);
        map.put("product", product);
        return map;
    }

11.png


(2)进行客户端调用


调用超时会出现如下错误:


10.png


(3)在User中修改OpenFeign默认超时时间


#配置指定服务连接超时
feign.client.config.product.connectTimeout=5000
#配置指定服务等待超时
feign.client.config.product.readTimeout=5000


13.png12.png

5、OpenFeign调用详细日志展示

往往在服务调用时我们需要详细展示feign的日志,默认feign在调用时并不是最详细日志输出,因此在调试程序时应该开启feign的详细日志展示。feign对日志的处理非常灵活可为每个feign客户端指定日志记录策略,每个客户端都会创建一个logger默认情况下logger的名称是feign的全限定名需要注意的是,feign日志的打印只会DEBUG级别做出响应。


我们可以为feign客户端配置各自的logger.lever对象,告诉feign记录那些日志logger.lever有以下的几种值


  `NONE  不记录任何日志
  `BASIC 仅仅记录请求方法,url,响应状态代码及执行时间 
  `HEADERS 记录Basic级别的基础上,记录请求和响应的header
  `FULL 记录请求和响应的header,body和元数据


(1)开启日志展示


feign.client.config.products.loggerLevel=full  #开启指定服务日志展示
#feign.client.config.default.loggerLevel=full  #全局开启服务日志展示
logging.level.com.tjcu.feignclients=debug    #指定feign调用客户端对象所在包,必须是debug级别


(2)测试服务调用查看日志


14.png

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
21天前
|
存储 数据可视化 Java
基于MicrometerTracing门面和Zipkin实现集成springcloud2023的服务追踪
Sleuth将会停止维护,Sleuth最新版本也只支持springboot2。作为替代可以使用MicrometerTracing在微服务中作为服务追踪的工具。
71 1
|
3月前
|
缓存 NoSQL Java
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
|
2月前
|
消息中间件 存储 Java
SpringCloud基础9——服务异步通信-高级篇
消息可靠性、死信交换机、惰性队列、MQ集群
SpringCloud基础9——服务异步通信-高级篇
|
2月前
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
46 1
|
20天前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
30 0
|
3月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
3月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
3月前
|
存储 Java Spring
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
|
3月前
|
SQL Java 数据库连接
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
|
2月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。