【SpringCloud】OpenFeign-远程调用

news2024/11/24 17:40:12

本文基于上一篇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.coloradmin.cn/o/1852850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea或vscode支持vue语法,ts可解析*.vue

一、ide不能解析vue文件 刚开始导入时&#xff0c;在vscode中的vue文件中内容都是灰色的 ide不能解析vue解决方法&#xff1a; 1.idea或webstorm安装vue.js插件 2.在vscode中 vue2.0的项目安装vetur插件vue3.0及以上的项目安装Vue-official插件&#xff08;之前是Volar&…

WordPress项目教程:自动采集并发布,让你轻松实现网站内容更新

随着互联网的发展&#xff0c;越来越多的人开始关注自己的个人网站&#xff0c;通过网站展示自己的才华、分享知识、推广产品等。然而&#xff0c;个人网站的运营并非易事&#xff0c;尤其是内容更新方面。为了解决这个问题&#xff0c;今天我们将为大家推荐一款WordPress插件主…

使用C语言实现植物大战僵尸教程

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

数据资产与云计算深度融合:借助云计算技术,优化数据存储、高效处理并创新应用,驱动企业数字化转型

目录 一、引言 二、数据资产与云计算深度融合的必要性 1、数据资产的重要性 2、云计算技术的优势 三、云计算技术在数据资产管理中的应用 1、数据存储的优化 2、数据处理的高效性 3、数据应用的创新 四、云计算驱动企业数字化转型的实践案例 案例一&#xff1a;金融行…

STM32玩转物联网07-WIFI实验

前言 上一节我们学习了串口的简单使用&#xff0c;本节我们增加难度&#xff0c;做一个demo通过AT指令控制ESP8266&#xff0c;使用DMA方式接收ESP8266发来的数据&#xff0c;后续我们便开始通过ESP8266连接物联网云平台&#xff0c;敬请关注。 一、准备 1. ESP8266硬件准备 准…

[图解]建模相关的基础知识-16

1 00:00:00,350 --> 00:00:04,130 刚才那个&#xff0c;就相当于&#xff0c;12这个我们可以认为是什么 2 00:00:05,020 --> 00:00:11,360 我们用类图来表达就是&#xff0c;员工、电话 3 00:00:13,320 --> 00:00:15,080 多个 4 00:00:15,090 --> 00:00:16,440 …

stm32使用time模块输出pwm波,stm32-matlab开发电机控制

simulink: stm32cubemx : 注意在stm32配置了两路的一个互补输出&#xff0c;但实际上在matlab里只需要给定占空比就行了&#xff0c;他会自动输出互补&#xff0c;驱动电机&#xff0c;这是因为有点的电机输出需要6路&#xff0c;有的只需要1路&#xff0c;我们看下图就知道了…

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

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

能正常执行但是 cion 标红/没有字段提示

ctrl q 退出 clion 找到工程根目录&#xff0c;删除隐藏文件 .idea 再重新打开 clion 标红消失&#xff0c;同时再次输入函数/类属性&#xff0c;出现字段提示 clion 的智能提示方案存储在 .idea 文件中&#xff0c;如果工程能够正常编译执行&#xff0c;那么说明是智能提示…

ARM32开发--WDGT看门狗

知不足而奋进 望远山而前行 目录 文章目录 前言 目标 内容 什么是看门狗 ARM中的看门狗 独立看门狗定时器 窗口看门狗定时器 独立看门狗FWDGT 初始化配置 喂狗 完整代码 窗口看门狗WWDGT 初始化配置 喂狗 完整代码 注意 总结 前言 嵌入式系统在如今的科技发…

程序猿大战Python——面向对象——私有权限

私有属性 目标&#xff1a;掌握私有属性的使用。 为了更好的限制属性的访问和包含隐私&#xff0c;可以给属性设置私有权限。 当把属性设置为私有属性后&#xff0c;则该属性只能被本类直接访问。 定义私有属性语法&#xff1a; self.__属性名 设置和获取私有属性值语法&am…

云计算期末综合测试题

云计算综合测试题 单选题填空题判断题简答题 单选题 这里选择题&#xff0c;直接以填空题展示&#xff0c;并给出解析 Bigtable是&#xff08;Google&#xff09;开发的分布式存储系统 解析&#xff1a;分布式结构化数据表Bigtable是Google基于GFS和Chubby开发的分布式存储系统…

Redis 学习笔记(2)

目录 1 Redis的持久化1.1 RDB持久化方案1.2 AOF持久化方案 2 Redis架构2.1 主从复制架构2.2 哨兵集群设计2.3 哨兵集群设计 3 Redis事务机制4 Redis过期策略与内存淘汰机制4.1 过期策略4.2 内存淘汰机制 5 Redis高频面试题4.1 缓存穿透4.2 缓存击穿4.3 缓存雪崩 1 Redis的持久化…

2. 数据结构分析即索引库的crud

1. 数据库脚本 DROP TABLE IF EXISTS tb_hotel; CREATE TABLE tb_hotel (id bigint(0) NOT NULL,name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT COMMENT 酒店名称,address varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_090…

FlowUs2024重磅革新预告:RAG技术赋能『问问AI』,笔记变现新纪元等你开启!

&#x1f389; 在FlowUs的广阔天地间&#xff0c;知识的边界被无限拓展&#xff0c;引领着一场场创新与收获的庆典&#xff01;&#x1f680; 随着一年间不断的精进与革新&#xff0c;FlowUs与众多用户并肩前行&#xff0c;在追求极致体验的道路上迈出坚实步伐。步入2024年&am…

Javase.图书管理系统基本框架

图书管理系统基本框架 1.核心类介绍2. book包详解2.1 Book 类2.1.2 代码展示2.1.2 代码解析 2.2 BookList 类2.2.2 代码展示2.2.2 代码解析 2.3Book类和BookList类的联系 3. 用户角色与管理3.1 User 类3.1.1 代码展示3.1.2 代码解析 3.2 adminUser 类3.2.1 代码展示3.2.2代码解…

学生选课管理系统(JAVA课设)PS:有前端界面

1.课设要求描述 实现系统的所有功能&#xff0c;包括但不限于&#xff1a; 学生信息管理&#xff08;增加、删除、修改、查询&#xff09;课程信息管理选课操作成绩管理 2.制作思路及基础讲解 此项目主要是用于完成大二下半学期的JAVA大作业&#xff0c;也可当作课设&…

Ubuntu:解决github出现 Permission denied (publickey)的问题

因为使用的Ubuntu 长久没有使用&#xff0c;使用下载的时候突然报错&#xff0c;使用ssh key这种方式进行clone &#xff0c;pull github上面的项目&#xff0c;使用 git clone或者git pull origin master出现permission denied (publickey)&#xff0c;原因是因为ssh key过期失…

leetcode 二分查找·系统掌握 x的平方根

题目&#xff1a; 题解 这题可以使用~01~泛型查找在0~x/2的范围内查找答案。 int mySqrt(int x) {long l0,rx,mid;while(l<r){mid(lr1)>>1;if(mid*mid>x)rmid-1;else lmid;}//因为一定有答案所以不用判定是否查找失败return l;}

计算机系统基础实训六-ShellLab实验

实验目的与要求 1、让学生更加理解进程控制的概念和具体操作方法&#xff1b; 2、让学生更加理解信号的概念和具体使用方法&#xff1b; 3、让学生更加理解Unix shell程序的原理和实现方法&#xff1b; 实验原理与内容 shell是一种交互式的命令行解释器&#xff0c;能代表…