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

相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
13天前
|
Java API 微服务
【Spring Boot系列】通过OpenAPI规范构建微服务服务接口
【4月更文挑战第5天】通过OpenAPI接口构建Spring Boot服务RestAPI接口
|
2月前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
171 0
|
2月前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
108 0
|
7天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
8天前
|
负载均衡 前端开发 Java
第六章 Spring Cloud 之 OpenFeign
第六章 Spring Cloud 之 OpenFeign
11 0
|
8天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
13天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
25 0
|
1月前
|
开发框架 负载均衡 Java
Spring boot与Spring cloud之间的关系
总之,Spring Boot和Spring Cloud之间的关系是一种构建和扩展的关系,Spring Boot提供了基础,而Spring Cloud在此基础上提供了分布式系统和微服务架构所需的扩展和工具。
19 4
Spring boot与Spring cloud之间的关系
|
2月前
|
SpringCloudAlibaba 负载均衡 Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
68 1
|
2月前
|
Java Nacos Sentinel
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
215 0