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

简介: 🍅程序员小王的博客:程序员小王的博客🍅 欢迎点赞 👍 收藏 ⭐留言 📝🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕🍅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

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
3月前
|
负载均衡 算法 Java
【SpringCloud(4)】OpenFeign客户端:OpenFeign服务绑定;调用服务接口;Feign和OpenFeign
Feign是一个WebService客户端。使用Feign能让编写WebService客户端更加简单。 它的使用方法是定义一个服务接口然后再上面添加注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,十七支持了SpringMVC标准注解和HttpMessageConverters。 Feign可用于Eureka和Ribbon组合使用以支持负载均衡
749 138
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
13504 60
|
5月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1001 3
|
3月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
10月前
|
负载均衡 Java Nacos
Spring Cloud五大组件
Spring Cloud五大组件
|
6月前
|
Prometheus 监控 Cloud Native
Docker 部署 Prometheus 和 Grafana 监控 Spring Boot 服务
Docker 部署 Prometheus 和 Grafana 监控 Spring Boot 服务实现步骤
665 0
|
9月前
|
Java 开发者 微服务
Spring Cloud OpenFeign详解与实践
总结起来说,Spring Cloud OpenFeign提供了一种简单易懂且高效的方式去实现微服务之间通信.它隐藏了许多复杂性,并且允许开发者以声明式方式编写HTTP客户端代码.如果你正在开发基于Spring Cloud 的微服务架构系统,Spring Cloud Open Feign是一个非常好用且强大工具.
691 33
|
10月前
|
人工智能 自然语言处理 Java
对话即服务:Spring Boot整合MCP让你的CRUD系统秒变AI助手
本文介绍了如何通过Model Context Protocol (MCP) 协议将传统Spring Boot服务改造为支持AI交互的智能系统。MCP作为“万能适配器”,让AI以统一方式与多种服务和数据源交互,降低开发复杂度。文章以图书管理服务为例,详细说明了引入依赖、配置MCP服务器、改造服务方法(注解方式或函数Bean方式)及接口测试的全流程。最终实现用户通过自然语言查询数据库的功能,展示了MCP在简化AI集成、提升系统易用性方面的价值。未来,“对话即服务”有望成为主流开发范式。
7623 7