【SpringCloud】OpenFeign-远程调用

本文基于上一篇http://t.csdnimg.cn/0qm2R 的基础上添加OpenFeign的使用。

微服务通信

在微服务架构中,微服务之间的通信通常有两种方式:RPC 和 HTTP。在 Spring Cloud 中,默认使用 HTTP 进行微服务的通信,最常用的实现形式有两种:RestTemplate  OpenFeign

HTTP 通信

RestTemplate
RestTemplate是 Spring 提供的用于同步 HTTP 请求的客户端工具,可以方便地与其他微服务进行 HTTP 调用。

OpenFeign
OpenFeign是一个声明式的 HTTP 客户端,使得编写 HTTP 客户端变得非常简单。通过注解来定义接口,Spring Cloud Feign 会自动生成实现该接口的 HTTP 客户端对象。

Netflix于2013年6月发布了Feign的第一个版本1.0.0,并于2016年7月发布了最后一个版本8.18.0。在2016年,Netflix将Feign捐献给社区,并于同年7月发布了OpenFeign的首个版本9.0.0,随后持续发布至今。因此,可以简单理解为Netflix Feign是OpenFeign的祖先,或者说OpenFeign是Netflix Feign的升级版。OpenFeign是Feign的一个更强大、更灵活的实现。(后续提到的Feign都是 OpenFeign)

Spring Cloud Feign 是 Spring 对 Feign 的封装,将 Feign 项目集成到 Spring Cloud 生态系统中。受 Feign 更名影响,Spring Cloud Feign 也有两个 starter:

spring-cloud-starter-feign
spring-cloud-starter-openfeign
由于 Feign 停更维护,因此我们使用的依赖是 spring-cloud-starter-openfeign。


RPC(Remote Procedure Call)

RPC(远程过程调用)是一种通过网络从远程计算机上请求服务,而不需要了解底层网络通信细节的机制。RPC 可以使用多种网络协议进行通信,如 HTTP、TCP、UDP 等,并且在 TCP/IP 网络四层模型中跨越了传输层和应用层。简而言之,RPC 就是像调用本地方法一样调用远程方法。

常见的 RPC 框架有:

1. Dubbo:是一个高性能的 Java RPC 框架,提供透明化的远程方法调用,主要用于构建分布式服务架构。
2. Thrift:是一个由 Facebook 开发的跨语言的 RPC 框架,支持多种编程语言,适用于服务之间高效的通信。
3. gRPC: gRPC 是 Google 开发的高性能、开源的 RPC 框架,使用 Protocol Buffers 作为接口描述语言,支持多种编程语言,适用于不同平台的服务通信。

通过上述工具和框架,可以实现微服务之间的高效通信,无论是通过 HTTP 还是 RPC,选择何种方式取决于具体的业务需求和技术选型。


OpenFeign的使用

引入依赖

在order-service的pom中引入依赖

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

添加注解

在启动类中添加注解,开启feign功能。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients # 开启openFeign功能
@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

编写客户端

import com.demo.order.model.ProductInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

// name:根据注册中心的服务名来调用
// path:调用product-service中的controller中的所有url都有path前缀
@FeignClient(name = "product-service", path = "/product")

// 根据product-service中的controller中的接口写方法
public interface ProductApi {

    // 与product-service中的controller中的接口相对应
    @RequestMapping("/{productId}")
    ProductInfo getProductById(@PathVariable("productId") Integer productId);
}

远程调用

import com.demo.order.api.ProductApi;
import com.demo.order.mapper.OrderMapper;
import com.demo.order.model.OrderInfo;
import com.demo.order.model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private ProductApi productApi;

    public OrderInfo selectOrderById(Integer orderId){
        OrderInfo orderInfo = orderMapper.selectOrderById(orderId);

        ProductInfo productInfo = productApi.getProductById(orderInfo.getProductId());
        orderInfo.setProductInfo(productInfo);
        return orderInfo;
    }
}

运行观察

OpenFeign的参数传递

上面的代码只演示了从url中获取参数,接下来将演示使用单个/多个参数、对象、JSON的方式来接收参数。

调用过程:order-service中的一个controller 使用 feign的客户端,通过远程调用 调用 product-service中的controller方法。

对于product-service中的一个controller(服务方)

import com.demo.product.model.ProductInfo;
import com.demo.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequestMapping("/product")
@RestController
public class ProductController {
    @Autowired
    ProductService productService;

    @RequestMapping("/{productId}")
    public ProductInfo getProductById(@PathVariable("productId") Integer productId) {
        log.info("接收到参数: productId" + productId);
        return productService.selectProductById(productId);
    }

    @RequestMapping("/p1")
    public String p1(Integer id) {
        return "product-service 接收到参数, id:" + id;
    }

    @RequestMapping("/p2")
    public String p2(Integer id, String name) {
        return "product-service 接收到参数, id:" + id + ",name:" + name;
    }

    @RequestMapping("/p3")
    public String p3(ProductInfo productInfo) {
        return "product-service 接收到参数: productInfo" + productInfo.toString();
    }

    @RequestMapping("/p4")
    public String p4(@RequestBody ProductInfo productInfo) {
        return "product-service 接收到参数: productInfo" + productInfo.toString();
    }
}

feign的客户端代码(远程调用桥梁)

import com.demo.order.model.ProductInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

// name:根据注册中心的服务名来调用
// path:调用product-service中的controller中的所有url都有path前缀
@FeignClient(name = "product-service", path = "/product")

// 根据product-service中的controller中的接口写方法
public interface ProductApi {

    // 与product-service中的controller中的接口相对应
    @RequestMapping("/{productId}")
    ProductInfo getProductById(@PathVariable("productId") Integer productId);

    @RequestMapping("/p1")
    String p1(@RequestParam("id") Integer id);

    @RequestMapping("/p2")
    String p2(@RequestParam("id") Integer id, @RequestParam("name") String name);

    @RequestMapping("/p3")
    // @SpringQueryMap:feign客户端接收对象的时候需要这个注解,springmvc的controller不需要
    String p3(@SpringQueryMap ProductInfo productInfo);

    @RequestMapping("/p4")
    String p4(@RequestBody ProductInfo productInfo);
}

对于order-service中的一个controller(消费方)

import com.demo.order.api.ProductApi;
import com.demo.order.model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试feign的一个order-server的controller
 * 通过feign的客户端,远程调用product-server接口
 */
@RequestMapping("/feign")
@RestController
public class TestFeignController {
    @Autowired
    private ProductApi productApi;

    @RequestMapping("/o1")
    public String o1(Integer id){
        return productApi.p1(id);
    }
    @RequestMapping("/o2")
    public String o2(Integer id, String name){
        return productApi.p2(id,name);
    }
    @RequestMapping("/o3")
    public String o3(ProductInfo productInfo){
        return productApi.p3(productInfo);
    }
    @RequestMapping("/o4")
    public String o4(@RequestBody ProductInfo productInfo){
        return productApi.p4(productInfo);
    }
}

此时来调用order-service中的controller方法。


OpenFeign最佳使用

上面使用feign的方式非常冗余,可以看到feign客户端代码和服务方提供的controller代码非常的相似。所以一般不使用上面的方式。一般有继承和抽取两种方式来使用feign。

继承

官方文档:Spring Cloud OpenFeign Features :: Spring Cloud Openfeign

下面将简单说明如何使用。

创建模块

这里是存放公共代码的模块。

引入依赖

把需要的依赖放到product-api的pom文件中

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

提取方法和实体类

把之前写到ProductApi接口中的方法放到ProductInterface中。

把ProductInfo也放到这里。product-service中的实体类可以删除了。

打jar包

删除后,product-service中引用ProductInfo类的地方都会报错。解决方案:

暂时先把product-api这个jar包下载到本地,然后添加模块即可。

同理把order-service中的ProductInfo也删除,并进行上述操作,并重新导入ProductInfo。

服务方实现接口

import com.demo.product.api.ProductInterface;
import com.demo.product.model.ProductInfo;
import com.demo.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequestMapping("/product")
@RestController
public class ProductController implements ProductInterface {
    @Autowired
    ProductService productService;

    @RequestMapping("/{productId}")
    public ProductInfo getProductById(@PathVariable("productId") Integer productId) {
        log.info("接收到参数: productId" + productId);
        return productService.selectProductById(productId);
    }

    @RequestMapping("/p1")
    public String p1(Integer id) {
        return "product-service 接收到参数, id:" + id;
    }

    @RequestMapping("/p2")
    public String p2(Integer id, String name) {
        return "product-service 接收到参数, id:" + id + ",name:" + name;
    }

    @RequestMapping("/p3")
    public String p3(ProductInfo productInfo) {
        return "product-service 接收到参数: productInfo" + productInfo.toString();
    }

    @RequestMapping("/p4")
    public String p4(@RequestBody ProductInfo productInfo) {
        return "product-service 接收到参数: productInfo" + productInfo.toString();
    }
}

消费方继承接口

import com.demo.product.api.ProductInterface;
import org.springframework.cloud.openfeign.FeignClient;

// name:根据注册中心的服务名来调用
// path:调用product-service中的controller中的所有url都有path前缀
@FeignClient(name = "product-service", path = "/product")
// 根据product-service中的controller中的接口写方法
public interface ProductApi extends ProductInterface {

}

测试

可以正常使用。

抽取

官方推荐使用继承的方法来使用OpenFeign,但在企业开发中,更多的是把feign客户端接口抽取为一个独立的模块,并把涉及到的实体类等都放到这个模块,打成一个jar包。

创建模块

引入依赖

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

提取api接口等

把ProductApi复制粘贴到新模块中,并删除之前的。

把ProductInfo按照上面的操作同样的移动到新模块中。

服务端基本不用改,消费端需要从自身的拿取改到从公共模块拿取。

打jar包

对公共模块打jar包到本地。

打完jar包后,在order-service中修改公共代码的使用路径。

添加扫描包

order-service启动的时候,只能扫描当前路径下的,无法扫描到公共模块的api

需要在启动类中添加扫描路径。

import com.demo.product.api.ProductApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

// clients 添加启动类无法扫描到的ProductApi
@EnableFeignClients(clients = {ProductApi.class})
@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

测试


部署

这里部署的时候只有打包的地方和之前有区别。因为这里有个product-api的包是本地的,不是从maven仓库下载的。

解决方案:

  • 把product-api上传到maven仓库(不推荐)如何发布Jar包到Maven中央仓库 – 过往记忆 (iteblog.com)
  • 搭建maven私服,上传jar包到私服(企业做法)
  • 从本地读取(个人使用)

这里使用从本地读取的方案。

把product-api打包

复制路径

修改pom
在order-service中修改一下pom,让它读本地的product-api的jar包。

        <dependency>
            <groupId>org.example</groupId>
            <artifactId>product-api</artifactId>
            <version>1.0-SNAPSHOT</version>
<!--            <scope>compile</scope>-->
            <!-- 从本地仓库引入 -->
            <scope>system</scope>
            <systemPath>C:/Users/pxf/.m2/repository/org/example/product-api/1.0-SNAPSHOT/product-api-1.0-SNAPSHOT.jar</systemPath>
        </dependency>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!-- 让它可以引入本地jar包 -->
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
        </plugins>
    </build>

后面的正常打包部署即可。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#使用Scoket实现服务器和客户端互发信息

20240616 By wdhuag 目录 前言&#xff1a; 参考&#xff1a; 一、服务器端&#xff1a; 1、服务器端口绑定&#xff1a; 2、服务器关闭&#xff1a; 二、客户端&#xff1a; 1、客户端连接&#xff1a; 2、客户端断开&#xff1a; 三、通讯&#xff1a; 1、接收信…

【动态规划】简单多状态dp问题

一、经验总结 在分析dp问题的状态表示时&#xff0c;发现当前阶段的状态可以继续细分为多个状态&#xff0c;且多个状态之间可以通过某种方式进行转换&#xff0c;这就是动态规划的多状态问题。 多状态问题的关键有以下几点&#xff1a; 找出dp问题的多个状态表示&#xff1a…

MATLAB-SSA-CNN-SVM,基于SSA麻雀优化算法优化卷积神经网络CNN结合支持向量机SVM数据分类(多特征输入多分类)

MATLAB-SSA-CNN-SVM,基于SSA麻雀优化算法优化卷积神经网络CNN结合支持向量机SVM数据分类(多特征输入多分类) 1.数据均为Excel数据&#xff0c;直接替换数据就可以运行程序。 2.所有程序都经过验证&#xff0c;保证程序可以运行。 3.具有良好的编程习惯&#xff0c;程序均包含…

Vue.JS中如何监听生命周期事件?

目录 一、Vue.JS框架介绍二、Vue.JS的监听事件三、Vue.JS的生命周期事件四、Vue.JS中如何监听生命周期事件 一、Vue.JS框架介绍 Vue.js是一个用于构建用户界面的渐进式JavaScript框架。它设计得非常灵活&#xff0c;可以轻松地被集成到现有的项目中&#xff0c;也可以作为一个…

52、U-boot2023的移植教程

uboot&#xff1a;https://ftp.denx.de/pub/u-boot/ nxp-uboot&#xff1a;https://github.com/nxp-imx/uboot-imx 1、顶层Makefile 文件加入编译的两种方式&#xff1a;以xxx/xxx.c文件为例 1、使用menuconfig: 先编辑.c所在目录下的Kconfig&#xff0…

CCS提示No XDCtools,equivalent...怎么办

摘要&#xff1a;本文介绍CCS( Version: 12.7.0.00007 )编译TI毫米波雷达遇到的No XDCtools&#xff0c;equivalent to the specified version 3.50.8.24_core,are available - defaulting to 3.62.1.16_core.问题的解决方法。 解决这个问题的方法是下载所需要的版本。上图所示…

38 - 换座位(高频 SQL 50 题基础版)

38 - 换座位 -- 方法一 select(casewhen id%21 and id(select max(id) from seat) then idwhen id%20 then id-1else id1end) as id, student fromseat order byid;-- 方法二selectif(id%20,id-1,if(id(select max(id) from Seat),id,id1)) as id,student fromSeat order by id…

1996年-2023年 全国298个地级市-外商直接投资FDI(数据收集)

外商直接投资&#xff08;FDI&#xff09;是一种跨国界的经济活动&#xff0c;它涉及外国投资者在中国境内进行的直接投资行为。这种投资行为不仅包括以货币、实物、技术等形式的资本投入&#xff0c;还可能包括开办独资企业、合资企业、合作企业&#xff0c;以及参与资源开发等…

【网络安全常用术语解读 :什么是0day、1day、nday漏洞】

脆弱性攻击的时间窗被称作脆弱性窗口。通常情况下&#xff0c;一个安全漏洞的时间越久&#xff0c;攻击者就会有更多的机会去攻击它。 2. 0day 漏洞 0天漏洞&#xff0c;也被称作"零日漏洞"&#xff0c;是指尚未由供应商公布的缺陷&#xff0c;表示攻击者已知晓该缺…

CentOS 7、Debian、Ubuntu,这些是什么意思

CentOS 7、Debian、Ubuntu 都是基于 Linux 内核的操作系统&#xff0c;它们各自有不同的特性和用途。以下是对它们的详细解释&#xff1a; CentOS 7 CentOS&#xff08;Community ENTerprise Operating System&#xff09; 是一个基于开源的 Linux 发行版。CentOS 7 是 CentOS …

摄像头画面显示于unity场景

&#x1f43e; 个人主页 &#x1f43e; &#x1faa7;阿松爱睡觉&#xff0c;横竖醒不来 &#x1f3c5;你可以不屠龙&#xff0c;但不能不磨剑&#x1f5e1; 目录 一、前言二、UI画面三、显示于场景四、结语 一、前言 由于标题限制&#xff0c;这篇文章主要是讲在unity中调用摄…

基于JSP的教学质量评价系统

开头语&#xff1a; 你好&#xff0c;我是计算机学长猫哥。如果您对教学质量评价系统感兴趣或有相关需求&#xff0c;欢迎随时联系我。 开发语言&#xff1a; Java 数据库&#xff1a; MySQL 技术&#xff1a; JSP技术 Java语言 工具&#xff1a; MyEclipse、Tomcat服…

华为Mate 70系列,将首发搭载纯血鸿蒙正式版,第四季度登场

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 6月22日消息&#xff0c;华为在HDC 2024上已经宣布&#xff0c;HarmonyOS NEXT开启开发者先锋用户Beta测试。 首批覆盖Mate 60系列、Mate X5系列、MatePad Pro 13.2英寸。 根据官方公布的时间表&…

oracle发送https请求

参照 https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/u_http.htm#i1025869 https://docs.oracle.com/cd/E11882_01/network.112/e40393/asowalet.htm#ASOAG160 https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_networkacl_adm.htm#ARPLS148 https://d…

江协科技51单片机学习- p11 Proteus安装模拟51单片机

前言&#xff1a; 本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记&#xff0c;在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。 引用&#xff1a; Proteus快速入门&…

OneNote for Windows 10 下载

OneNote for Windows 10 安装 1.在浏览器中输入地址&#xff1a;https://apps.microsoft.com/detail/9wzdncrfhvjl?hlzh-cn&glUS2OneNote for Windows 10 - 在 Windows 上免费下载并安装 |Microsoft StoreOneNote 是用于在设备上捕获和组织你的一切内容的数字笔记本。快速…

打破数据分析壁垒:SPSS复习必备(六)

一、数据的报表呈现 1.报表概述 (1).SPSS中的报表功能 1&#xff09;Base 模块 2&#xff09;Custom Tables 模块 3) Original Tables 模块 (2).报表的基本绘制步骤 步骤一:确定基本结构 步骤二:使用对话框绘制表格的基本结构 步骤三:完善细节 步骤四:添加其余变…

java课程设计GUI学生信息管理系统

目录 系统内容.. 3 用户界面模块... 4 数据存储模块... 4 信息管理模块... 4 管理模块.. 4 主要模块的算法描述... 4 –简要的语言描述... 4 运行及调试分析&#xff08;测试数据及测试结果&#xff09;.. 5 课程设计总结... 7 参考文献&#xff08;至少三个&#xf…

基于CSDN的Markdown文本编辑器的博客界面优化 | HTML | 文本标签 | 图像标签 | 个人主页引导

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 今天毛毛张分享的内容是如何在CSDN的Markdown编辑器中实现上图的效果&#xff0c;如果觉得能帮助到你的话就点击个人主页点点关注吧❗ 文章目录 1.前言2.基础知识3.字…

Sword and Shield Animations(劈砍防御剑盾带动画动作)

这是一个动画资产包,为剑和盾牌用户提供手工制作的成对动画和空闲。包括8向步行和跑步动画、攻击、跳跃、冲刺、向下状态移动和过渡、躲避、阻挡、蹒跚、各种配对终结者动画等。 一切你需要把剑和盾牌战士带到生活中。 动画总数:115 攻击19 区块5 关闭状态14 Evade 5 怠速9 跳…