SpringCloud系列(五)| 集成OpenFeign实现服务间调用

前面的案例中我们已经搭建好了SpringCloud的基本架构。目前主要就是三个服务。一个Nacos服务,目前作为我们的注册中心,一个用户服务,一个订单服务。上个章节中,我们已经成功的将用户服务和订单服务注册到了Nacos中,并且模拟了多节点部署的情况下,Nacos对于服务实例的管理。

一、注册中心作用

服务注册中心是服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来存储服务信息,譬如提供者 url 串、路由信息等。服务注册中心是微服务架构中最基础的设施之一。

注册中心可以说是微服务架构中的“通讯录”,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。

简单理解就是:在没有注册中心时候,服务间调用需要知道被当服务调方的具体地址(写死的 ip:port)。更换部署地址,就不得不修改调用当中指定的地址。而有了注册中心之后,每个服务在调用别人的时候只需要知道服务名称(软编码)就好,地址都会通过注册中心根据服务名称获取到具体的服务地址进行调用。

注册中心的主要功能

  • 服务注册表:注册中心的核心功能,记录各个服务提供者的信息,比如服务名、IP、端口等。维护服务订阅者信息。
  • 服务注册、注销接口:供服务提供方进行服务注册和服务注销。
  • 健康检查和服务摘除:提供健康信息上报接口,供注册服务上传健康信息进行服务保活。同时对于失效节点能够及时摘除并同步给服务订阅者。
  • 服务订阅和变更通知:供服务消费者使用

一般情况下我们在部署微服务的时候,每个节点都会部署多个,在服务之间相互调用的时候,都是通过注册中心上的服务名称进行调用,当有服务宕机时,注册中心可以通知消费者,选择可用的节点进行调用,这样就最大程度的保证了服务的可用性。

二、OpenFeign简介

在单体应用时代,我们各个服务之间要想完成调用,一般会使用Http的相关工具类来完成,但是随着微服务的兴起,服务数量的不断增多,继续使用Http工具类无疑会极大的增加我们的工作量,而且也不够优雅。而SpringCloud OpenFeign就是给我们提供了一种声明式的服务调用客户端,使用OpenFeign可以让服务之间的调用变得简单。

OpenFeign到底能干啥:

  • OpenFeign的设计宗旨式简化Java Http客户端的开发。Feign在restTemplate的基础上做了进一步的封装,由其来帮助我们定义和实现依赖服务接口的定义。在OpenFeign的协助下,我们只需创建一个接口并使用注解的方式进行配置(类似于Dao接口上面的Mapper注解)即可完成对服务提供方的接口绑定,大大简化了Spring cloud Ribbon的开发,自动封装服务调用客户端的开发量。
  • OpenFeign集成了Ribbon,利用ribbon维护了服务列表,并且通过ribbon实现了客户端的负载均衡。与ribbon不同的是,通过OpenFeign只需要定义服务绑定接口且以申明式的方法,优雅而简单的实现了服务调用。

还有一点要注意一下, openFeign 和 feign其实是有区别的,简单来说feign一般是老版本的springcloud在用,后来不维护了,就又出了openfeign,也优化了一些用法。 详细的区别大家也可以去网上看看。

三、接口开发

我们先在userservice服务和 orderservice 服务下都添加几个接口。方便观察调用的结果。

我们先把之前讲过的统一结果封装的几个类集成到common模块下,这块如果大家不太清楚,可以去找找我之前的文章: 《SpringBoot统一结果封装》。核心的几个类如下:

然后我们在userservice下创建一个UserController。 先简单开发一个接口。

package com.lsqingfeng.springcloud.user.controller;import com.lsqingfeng.springcloud.common.base.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @className: UserController* @description:* @author: sh.Liu* @date: 2022-03-29 17:25*/
@RestController
@RequestMapping("/user")
public class UserController {@GetMapping("hello")public Result hello(String name){return Result.success(name);}}

同样的,我们在orderservice模块下也创建一个Controller,开发一个接口。

package com.lsqingfeng.springcloud.order.controller;import com.lsqingfeng.springcloud.common.base.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @className: OrderController* @description:* @author: sh.Liu* @date: 2022-03-29 17:54*/
@RestController
@RequestMapping("/order")
public class OrderController {@GetMapping("getOrder")public Result getOrder(String orderNo){return Result.success(orderNo);}}

启动两个程序,我们来测试一下这两个接口。

user接口:

order接口:

四、集成OpenFeign

首先来说明一下,OpenFeign是不属于SpringCloud Alibaba这套体系中的,它是一套独立的技术架构,从SpringCloud的目录中我们也可以看出,他其实是和SpringCloud Alibaba是平级的。但是这并不影响我们在SpringCloud Alibaba的架构中使用它,并且在使用上也是完全兼容的。

OpenFein本身的作用其实就是服务之间的调用,这种调用当然也可以选择其他的方式,比如SpringCloud Alibaba体系中的 dubbo做RPC调用, Dubbo本身在SpringCloud出现之前就已经存在了,并且活跃了很长时间,所以在服务调用这一领域我们可以使用OpenFeign,也可以使用dubbo,但是据我观察周围还是使用OpenFeign的比较多。而Dubbo往往还是和Zookeeper一起做分布式RPC调用来用。后面如果有时间我们也会集成dubbo来实现服务之间的调用。

好了大致的背景介绍完了,接下来我们就来实现一个小需求: orderservice通过OpenFeign去调用userservice中的接口。

4.1 引入依赖

由于我们的依赖都是在parent这个模块中统一管理的。现在我们需要使用SpringCloud的相关依赖了,所以在里边添加:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring.cloud-version}</version><type>pom</type><scope>import</scope>
</dependency>

版本信息: <spring.cloud-version>2021.0.1</spring.cloud-version>

完整pom如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.lsqingfeng.springcloud</groupId><artifactId>spring-cloud-learning</artifactId><version>1.0.0</version></parent><artifactId>spring-cloud-learning-parent</artifactId><version>1.0.0</version><packaging>pom</packaging><name>spring-cloud-learning-parent</name><description>项目父pom,用于被继承</description><properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><spring.boot.version>2.6.5</spring.boot.version><spring.cloud.alibaba.version> 2021.0.1.0</spring.cloud.alibaba.version><spring.cloud-version>2021.0.1</spring.cloud-version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><lombok.version>1.18.20</lombok.version><nacos.version>0.2.1</nacos.version><modelmapper.version>2.3.0</modelmapper.version><springfox-swagger2.version>2.9.2</springfox-swagger2.version><xiaoymin.swagger.version>1.9.6</xiaoymin.swagger.version></properties><repositories><!-- maven仓库采用阿里云--><repository><id>maven-ali</id><url>http://maven.aliyun.com/nexus/content/groups/public//</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled><updatePolicy>always</updatePolicy><checksumPolicy>fail</checksumPolicy></snapshots></repository></repositories><dependencyManagement><dependencies><!-- springBoot 版本依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring.boot.version}</version><scope>import</scope><type>pom</type></dependency><!-- springCloud-alibaba版本依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring.cloud.alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- springCloud相关依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring.cloud-version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.lsqingfeng.springcloud</groupId><artifactId>spring-cloud-learning-common</artifactId><version>${project.parent.version}</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><!-- 实体拷贝工具 --><dependency><groupId>org.modelmapper</groupId><artifactId>modelmapper</artifactId><version>${modelmapper.version}</version></dependency><!-- swagger --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>${springfox-swagger2.version}</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>swagger-bootstrap-ui</artifactId><version>${xiaoymin.swagger.version}</version></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

然后在我们的消费者端配置依赖。我们的消费者也就是服务的调用者,就是orderservice.

<!--openfeign -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

4.2 开发OpenFeign客户端接口

依赖准备好了以后,我们现在orderservice的启动类上添加一个注解: @EnableFeignClients

然后我们创建一个包,专门来放feign客户端接口。就叫client.然后在里边创建一个接口。UserFeignClient。接口里的内容要和我们想调用的接口的Controller一模一样,只不过只写接口,不用写实现。如果有pojo类型的参数或返回值,也直接把对应的类拷贝过来。如果太多,把他们放到单独的一个模块中,然后依赖进来。

由于我们想调用的UserController中只有一个方法,就直接拿过来就行了。同时在接口上添加feinClient注解。

package com.lsqingfeng.springcloud.order.client;import com.lsqingfeng.springcloud.common.base.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;/*** @interface: UserFeignClient* @description:* @author: sh.Liu* @date: 2022-03-29 19:10*/
@Component
@FeignClient(name="userservice")
public interface UserFeignClient {/*** userController中的hello* @param name* @return*/@GetMapping("hello")Result hello(String name);
}

这里注意,@FeignClient中的参数name就是我们要调用的服务的名称。也就是对应服务的spring.application.name值。这个值也注册到nacos中的名称也是一样的。

4.3 在消费者中使用

客户端准备好了,就可以和我们正常依赖注入的方式一样使用了。我们在OrderController中依赖对应的客户端完成对用户模块接口的调用。

package com.lsqingfeng.springcloud.order.controller;import com.lsqingfeng.springcloud.common.base.Result;
import com.lsqingfeng.springcloud.order.client.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @className: OrderController* @description:* @author: sh.Liu* @date: 2022-03-29 17:54*/
@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate UserFeignClient userFeignClient;@GetMapping("getOrder")public Result getOrder(String orderNo){Result result = userFeignClient.hello(orderNo);return result;}}

然后我们重新编译运行项目。运行orderservice的时候发现报了一个错。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderController': Unsatisfied dependency expressed through field 'userFeignClient'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.lsqingfeng.springcloud.order.client.UserFeignClient': Unexpected exception during bean creation; nested exception is java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?

这个问题是由于:SpringCloud Feign在Hoxton.M2 RELEASED版本之后不再使用Ribbon而是使用spring-cloud-loadbalancer,所以不引入spring-cloud-loadbalancer会报错。

解决方法:

加入spring-cloud-loadbalancer依赖

<!--loadbalancer -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

再次启动项目成功,我们调用订单的接口看看效果:

再次出现错误,这次是405, 心态要炸了,这个主要是由于OpenFein在调用的时候对于参数缺少注解导致的。我们在openFein接口的参数里加上 @RequestParam

package com.lsqingfeng.springcloud.order.client;import com.lsqingfeng.springcloud.common.base.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** @interface: UserFeignClient* @description:* @author: sh.Liu* @date: 2022-03-29 19:10*/
@Component
@FeignClient(name="userservice")
public interface UserFeignClient {/*** userController中的hello* @param name* @return*/@GetMapping("/user/hello")Result hello(@RequestParam("name") String name);
}

这个只能说这是属于OpenFeign不够完美的地方,很多注解不能省略。再次调用。有结果了:

这样我们就实现了就像调用自己项目内部的接口一样的方式去调用其他服务的接口,这完全依赖于注册中心的帮助。我们加个断点,看的更加清晰一下。

好了这样就实现了OpenFeign的调用。大家赶快好好理解理解吧。代码已经上传到了git上,有不清楚的地方可以把代码下载下来研究。

代码地址: 一缕82年的清风 / spring-cloud-learning · GitCode

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/284865.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux权限(下)

目录 文件访问权限的设置和更改 粘滞位 权限掩码 本期我们接着进行Linux权限的学习。 文件访问权限的设置和更改 在上期我们学习了文件的访问权限&#xff0c;这些访问权限其实是可以更改的。 chmod指令 chmod指令&#xff1a;用于设置访问权限和更改权限。 :向权限范围…

产品调研——AI平台

本文主要记录了对腾讯云-TIONE平台、华为云-ModelArt等主流AI平台的产品调研。 交互式建模 简单点说就是提供了带训练资源的云IDE&#xff0c;使用形态包括Notebook、VsCode等。 腾讯云-TI平台 TI平台将tensorflow、pytorch、spark环境等均集成到一个Notebook容器中&#xf…

【Java 集合】ArrayBlockingQueue

ArrayBlockingQueue, 顾名思义: 基于数组的阻塞队列, 位于 JUC (java.util.concurrent) 下, 是一个线程安全的集合, 其本身具备了 不支持 null 元素: 存入 null 元素会抛出异常固定容量: 在初始化时需要指定一个固定的容量大小。这意味着一旦队列达到最大容量&#xff0c;将不再…

人工智能原理课后习题(考试相关的)

文章目录 问答题知识表示一阶谓词逻辑表示法语义网络表示法 确定推理谓词公式永真和可满足性内容归结演绎推理 不确定推理主观贝叶斯可信度方法证据理论 搜索策略机器学习 问答题 什么是人工智能&#xff1f; 人工智能就是让机器看起来像人类表现出的智能水平一样 人工智能就是…

磁盘及文件系统(上)

这次博客我们将重点理解Ext2文件系统。 首先我们要理解什么是文件系统。 在之前我们一直理解的文件都是一个被打开的文件&#xff0c;而os为了能够管理这样的文件创建了struct_file这样的结构体对象在内核中描述被打开的文件&#xff0c;这个结构体对象中包含了被打开文件的基…

9、ble_mesh基础

node节点&#xff0c;不属于网络的设备称为未配置设备。未配置的设备无法发送或接收网格消息&#xff1b;但是&#xff0c;它会向 Provisioners 宣传其存在。 Provisioner供应&#xff0c;验证&#xff0c;邀请&#xff0c;加入网络成为节点。 一个节点有多个控制或开关&#x…

音视频学习(二十一)——rtmp收流(tcp方式)

前言 本文主要介绍rtmp协议收流流程&#xff0c;在linux上搭建rtmp服务器&#xff0c;通过自研的rtmp收流库发起取流请求&#xff0c;使用ffmpegqt实现视频流的解码与播放。 关于rtmp协议基础介绍可查看&#xff1a;https://blog.csdn.net/www_dong/article/details/13102607…

webpack学习-6.缓存

webpack学习-6.缓存 1.前言2.输出文件的文件名3. 提取引导模板4.模块标识符5.总结 1.前言 webpack 会在打包后生成可部署的 /dist 目录&#xff0c;并将打包后的内容放在此目录。一旦 /dist 目录中的内容部署到服务器上&#xff0c;客户端&#xff08;通常是浏览器&#xff09…

一文搞懂系列——DBC数据库信号解析规则及案例

背景 最近在项目中&#xff0c;同事遇到了一个dbc数据库解析错误的问题&#xff1a;基于ekuiper 对can报文解析&#xff0c;发现实际输出结果与预期差距较大。当时他第一反应是ekuiper的解析规则有误&#xff0c;因此就没有跟踪下去了。因为之前我用过ekuiper的CAN报文解析功能…

采购oled屏幕,应注意什么

在采购OLED屏幕时&#xff0c;应注意以下几点&#xff1a; 规格和参数&#xff1a;了解OLED屏幕的规格和参数&#xff0c;包括尺寸、分辨率、亮度、对比度、响应时间等。确保所采购的屏幕符合项目的需求和预期效果。 品质和可靠性&#xff1a;选择具有可靠品质和稳定性的OLED屏…

Shell三剑客:sed(命令)二

一、插入命令&#xff1a;i&#xff08;之前&#xff09; [rootlocalhost ~]# sed -r 2i aaaaaaa passwd.txt root:x:0:0:root:/root:/bin/bash aaaaaaa bin:x:1:1:bin:/bin:/sbin/nologin[rootlocalhost ~]# sed -r 2i aaaaaaa\ > bbb\ > ccc passwd.txt root:x:0:0:r…

Stable-Diffusion|从图片反推prompt的工具:Tagger(五)

stable-diffusion-webui-wd14-tagger 前面几篇&#xff1a; Stable-Diffusion|window10安装GPU版本的 Stable-Diffusion-WebUI遇到的一些问题&#xff08;一&#xff09; 【Stable-Diffusion|入门怎么下载与使用civitai网站的模型&#xff08;二&#xff09;】 Stable-Diffusi…