优雅的实现服务调用 -- OpenFeign

news2024/11/27 17:37:26

文章目录

  • 1. RestTemplate存在问题
  • 2. OpenFeign介绍
  • 3. 快速上手
    • 引入依赖
    • 添加注解
    • 编写OpenFeign的客户端
    • 远程调用
  • 4. OpenFeign参数传递
    • 从URL中获取参数
    • 传递单个参数
    • 传递多个参数
    • 传递对象
    • 传递JSON
  • 5. 最佳实践
    • Feign继承方式
      • 创建一个新的模块
      • 引入依赖
      • 编写接口
      • 打jar包
      • 服务实现方实现ProductApi接口
      • 服务消费方继承接口
    • Feign抽取方式
      • 创建一个module
      • 引入依赖
      • 编写API
      • 打jar包
      • 服务消费方使用接口
      • 测试
  • 6. 服务部署到Linux注意事项
    • 修改pow.xml文件

1. RestTemplate存在问题

之前我们写过的远程调用的代码:

@Autowired
private RestTemplate restTemplate;
public OrderInfo getOrderById(int id) {
    OrderInfo orderInfo = orderMapper.getOrderById(id);
    String url = "http://product-service/product/getProductById?id=" + orderInfo.getProductId();
    ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
    orderInfo.setProductInfo(productInfo);
    return orderInfo;
}
  • URL需要拼接,灵活性高,但是封装臃肿,容易出错
  • 代码可读性差,风格不统一

微服务之间的通信,通常有两种:RCP和HTTP

在Spring Cloud中,默认是使用HTTP来进行微服务的通信,最常用的实现形式有两种:

  • RestTemplate
  • OpenFeign

2. OpenFeign介绍

OpenFeign是一个声明式的Web Service 客户端.它让微服务之间的调用变得更简单,类似于controller调用service,只需要创建一个接口,然后添加注解即可使用OpenFeign

Feign是Netflix公司的一个组件,16年Netflix将Feign捐献给社区,2016年7月OpenFeign的首个版本9.0.0发布

可以理解为 Netflix Feign是Open Feign的祖先,或者说OpenFeign是NetFlix Feign的升级版

Spring Cloud Feign是Spring 对 Feign的封装,将Feign项目集成到Spring Cloud生态系统中

3. 快速上手

引入依赖

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

添加注解

在order-service的启动类添加注解@EnableFeignClients,开启OpenFeign的功能

@EnableFeignClients
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

编写OpenFeign的客户端

基于SpringMVC的注解来声明远程调用的信息

@FeignClient(value = "product-service",path = "/product/getProductBuId")
public interface ProductApi {
    @RequestMapping("/{id}")
    ProductInfo getProductBuId(@PathVariable("id") Integer id);
}

参数说明:

  • name/value:指定FeignClient的名称,也就是微服务的名称,用于服务发现.Feign底层会使用Spring Cloud LoadBalance进行负载均衡,也可以使用url属性指定一个具体的url
  • path:定义当前FeignClient的统一前缀

远程调用

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ProductApi productApi;
    @Autowired
    private RestTemplate restTemplate;
    public OrderInfo getOrderById(int id) {
        OrderInfo orderInfo = orderMapper.getOrderById(id);
//        String url = "http://product-service/product/getProductById?id=" + orderInfo.getProductId();
        ProductInfo productInfo = productApi.getProductById(orderInfo.getProductId());
        orderInfo.setProductInfo(productInfo);
        return orderInfo;
    }
}

Feign 简化了与HTTP服务交互的过程, 把REST客户端的定义转换为Java接⼝, 并通过注解的⽅式来声

明请求参数,请求⽅式等信息, 使远程调⽤更加方便和间接.

4. OpenFeign参数传递

从URL中获取参数

服务提供方product-service

@RequestMapping("product")
@RestController
public class ProductController {
    @RequestMapping("/p1/{id}")
    public String p1(@PathVariable("id") Integer id) {
        return "p1接收到参数" + id;
    }
}

OpenFeign客户端

@FeignClient(value = "product-service",path = "/product")
public interface ProductApi {
    @RequestMapping("/p1/{id}")
    String p1 (@PathVariable("id") Integer id);
}

服务消费方order-service

@RestController
@RequestMapping("testOpenFeign")
public class TestFeignController {
    @Autowired
    private ProductApi productApi;
    @RequestMapping("/o1")
    public String o1(Integer id) {
        return productApi.p1(id);
    }
}

测试:

传递单个参数

服务提供方product-service

@RequestMapping("/p2")
public String p2(@RequestParam("id") int id) {
    return "p2接受到参数" + id;
}

OpenFeign客户端

@RequestMapping("/p2")
String p2 (@RequestParam("id") Integer id);// 这里的@RequestParam做参数绑定,不能省略

服务消费方order-service

@RequestMapping("/o2")
    public String o2(Integer id) {
    return productApi.p2(id);
}

测试:

传递多个参数

需要使用多个@RequestParam进行参数绑定

服务提供方product-service

    @RequestMapping("/p3")
    public String p3(Integer id,String name) {
        return "p3接受到参数" + id + ": " + name;
    }

OpenFeign客户端

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

服务消费方order-service


    @RequestMapping("/o3")
    public String o3(Integer id,String name) {
        return productApi.o3(id,name);
    }

测试:

传递对象

服务提供方product-service

    @RequestMapping("/p4")
    public String p4(ProductInfo productInfo) {
        return "接收到参数" + productInfo;
    }

OpenFeign客户端

   @RequestMapping("/p4")
    String p4 (@SpringQueryMap ProductInfo productInfo);

服务消费方order-service

    @RequestMapping("/o4")
    public String o4(ProductInfo productInfo) {
        return productApi.p4(productInfo);
    }

测试:

传递JSON

服务提供方product-service

     @RequestMapping("/p5")
    public String p5(@RequestBody ProductInfo productInfo) {
        return "接收到参数" + productInfo;
    }

OpenFeign客户端

     @RequestMapping("/p5")
    String p5 (@RequestBody ProductInfo productInfo);

服务消费方order-service


    @RequestMapping("/o5")
    public String o5(@RequestBody ProductInfo productInfo) {
        return productApi.p5(productInfo);
    }

测试:

在这里插入图片描述

5. 最佳实践

不难发现,Feign的客户端代码和服务提供者的代码有很大的相似性

提出两种解决方法

Feign继承方式

我们可以将一些常见的操作封装到接口里,服务提供方实现这个接口,服务消费方编写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>

编写接口

public interface ProductInterface {
    @RequestMapping("/getProductById")
    ProductInfo getProductById(@RequestParam("id") Integer id);

    @RequestMapping("/p1/{id}")
    String p1 (@PathVariable("id") Integer id);


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

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

    @RequestMapping("/p4")
    String p4 (@SpringQueryMap ProductInfo productInfo);

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

注意:这里的ProductInfo需要在product-api抽取出来,因此可以将order-service以及product-service里面的ProductInfo删除

打jar包

此时就会将product-api的jar包放到本地的maven仓库,供product-service以及order-service使用

服务实现方实现ProductApi接口

package org.JWCB.controller;

import org.JWCB.api.ProductInterface;
import org.JWCB.model.ProductInfo;
import org.JWCB.service.ProductService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RequestMapping("product")
@RestController
public class ProductController implements ProductInterface {
    private static final Logger log = LoggerFactory.getLogger(ProductController.class);
    @Autowired
    private ProductService productService;
    @RequestMapping("getProductById")
    public ProductInfo getProductById(@RequestParam Integer id) {
        log.info("getProductById");
        return productService.getProductById(id);
    }


    @RequestMapping("/p1/{id}")
    public String p1(@PathVariable("id") Integer id) {
        return "p1接收到参数" + id;
    }

    @RequestMapping("/p2")
    public String p2(@RequestParam("id") Integer id) {
        return "p2接受到参数" + id;
    }

    @RequestMapping("/p3")
    public String p3(Integer id,String name) {
        return "p3接受到参数" + id + ": " + name;
    }

    @RequestMapping("/p4")
    public String p4(ProductInfo productInfo) {
        return "接收到参数" + productInfo;
    }

    @RequestMapping("/p5")
    public String p5(@RequestBody ProductInfo productInfo) {
        return "接收到参数" + productInfo;
    }
}

此时需要将本地Maven仓库的ProductApi导入,可以先将接口名完整打出来,由idea自己导入

<dependency>
  <groupId>org.JWCB</groupId>
  <artifactId>product-api</artifactId>
  <version>1.0-SNAPSHOT</version>
  <scope>compile</scope>
</dependency>

注意:这里的ProductInfo是来自于product-api这个包

服务消费方继承接口

package org.JWCB.api;
import org.JWCB.model.ProductInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.*;

@FeignClient(value = "product-service",path = "/product")
public interface ProductApi extends ProductInterface{
    @RequestMapping("/getProductById")
    ProductInfo getProductById(@RequestParam("id") Integer id);

    @RequestMapping("/p1/{id}")
    String p1 (@PathVariable("id") Integer id);


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

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

    @RequestMapping("/p4")
    String p4 (@SpringQueryMap ProductInfo productInfo);

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

测试:

Feign抽取方式

做法实际上与继承的方式相似,但是理念不同,这种方式是将Feign抽取成一个独立的模块

就是将Feign的Client抽取为一个独立的模块,并把涉及的实体类都放入这个模块里面,打成一个jar包. 服务消费方只需要依赖该jar包即可

创建一个module

引入依赖

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

编写API

@FeignClient(value = "product-service",path = "/product")
public interface ProductInterface {
    @RequestMapping("/getProductById")
    ProductInfo getProductById(@RequestParam("id") Integer id);

    @RequestMapping("/p1/{id}")
    String p1 (@PathVariable("id") Integer id);


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

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

    @RequestMapping("/p4")
    String p4 (@SpringQueryMap ProductInfo productInfo);

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

打jar包

服务消费方使用接口

删除ProductApi以及ProductInfo

引入依赖:

<dependency>
  <groupId>org.JWCB</groupId>
  <artifactId>product-api</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

修改order-service代码

指定扫描类

测试

在这里插入图片描述

6. 服务部署到Linux注意事项

使用Maven进行打包的时候.默认是从远程中央仓库进行下载的,但是product-api这个包在本地,我们可以制定从本地读取jar包

修改pow.xml文件

<dependency>
  <groupId>org.JWCB</groupId>
  <artifactId>product-api</artifactId>
  <version>1.0-SNAPSHOT</version>
  <scope>system</scope>
  <systemPath>C:/Users/31127/.m2/repository/org/JWCB/product-api/1.0-SNAPSHOT/product-api-1.0-SNAPSHOT.jar</systemPath>
</dependency>


<plugins>
  <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
      <includeSystemScope>true</includeSystemScope>
    </configuration>
  </plugin>
</plugins>

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

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

相关文章

锐龙7 7800X3D与i7-14700K到底怎么选!其实很简单

从2022年的锐龙7 5800X3D到后来的锐龙7 7800X3D&#xff0c;笔者使用X3D处理器已有2年多的时间。站在自己的立场&#xff0c;我是非常希望游戏老鸟购买这类处理器的&#xff0c;并且也推荐了不少。 这里说的是老鸟&#xff0c;也就是比较懂电脑的玩家。 但是对于新手玩家而言&a…

Canal 扩展篇(阿里开源用于数据同步备份,监控表和表字段(日志))

1.Canal介绍 Canal把自己伪装成从数据库&#xff0c;获取mysql主数据库的日志&#xff08;binlog&#xff09;信息&#xff0c;所以要想使用canal就得先开启数据库日志 https://github.com/alibaba/canal Canal 主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量…

刷题 链表

面试经典150题 - 链表 141. 环形链表 class Solution { public:bool hasCycle(ListNode *head) {ListNode* slow head, *fast head;while (fast ! nullptr && fast->next ! nullptr) {slow slow->next;fast fast->next->next;if (slow fast) {return…

maven加载依赖成功但是引入import不了包,注解报错

突然就复现不出来了&#xff0c;奇了怪了&#xff0c;简单说一下吧&#xff0c;就是模块里引入了SpringBoot Test那个依赖然后&#xff0c; 这个地方是显示引入成功的&#xff0c;但是 这个包下没有&#xff0c;导致我SpringBootTest一直出不来&#xff0c;就找不到这个包下的注…

【一步步开发AI运动小程序】二十、AI运动小程序如何适配相机全屏模式?

引言 受小程序camera组件预览和抽帧图像不一致的特性影响&#xff0c;一直未全功能支持全屏模式&#xff0c;详见本系列文件第四节[小程序如何抽帧]https://blog.csdn.net/alphaair/article/details/133981787 “小程序如何抽帧”)&#xff1b;随着插件在云上赛事、健身锻炼、A…

一个神奇的 Python 库:flanker-next

文章目录 一个神奇的 Python 库&#xff1a;flanker-next背景介绍库简介安装指南函数使用示例解析邮箱地址验证邮箱地址解析 MIME 消息 应用场景自动化邮件处理邮件内容分析客户支持自动化 常见问题及解决方案问题1&#xff1a;解析无效的电子邮件地址问题2&#xff1a;无法找到…

1992-2022年各省夜间灯光数据(excel+shp格式)

1992-2022年各省夜间灯光数据&#xff08;excelshp格式&#xff09; 1、时间&#xff1a;1992-2022年 2、来源&#xff1a; DMSP-OLS、NPP-VIIRS 3、指标&#xff1a;均值、总和、最小值、最大值、标准差 4、范围&#xff1a;34省市&#xff08;含港澳台&#xff09; 5、说…

(25)QPSK信号在AWGN和Rayleigh衰落信道下的性能仿真

文章目录 前言一、MATLAB仿真代码二、仿真结果 前言 QPSK信号在AWGN和Rayleigh衰落信道下的性能仿真MATLAB代码。 一、MATLAB仿真代码 代码如下&#xff1a; nSamp 8; % 矩形脉冲的采样点数 nSymbol 1000000; % 传输的符号…

GPT-SOVIT模型部署指南

一、模型介绍 强大的小样本语音转换和文本转语音 WebUI。 具有以下特征&#xff1a; 零样本 TTS&#xff1a; 输入 5 秒的声音样本并体验即时文本到语音的转换。少量样本 TTS&#xff1a; 仅使用 1 分钟的训练数据对模型进行微调&#xff0c;以提高语音相似度和真实感。跨语…

QD1-P11 HTML常用标签:图片标签

本节学习 HTML 常用标签&#xff1a;图片标签 img ‍ 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p11 ‍ 知识点 1&#xff1a;img 是行内标签 img 是一个行内标签&#xff0c;不会自动换行。 知识点 2&#xff1a;img 标签使用格式 <img src"图片URL" a…

Leetcode 删除链表倒数第 N 个节点

算法思想&#xff1a; 使用了双指针法。下面是详细的算法思想&#xff1a; 1. 引入虚拟头节点&#xff08;dummy node&#xff09; 为了处理链表的一些边界情况&#xff08;比如删除头节点&#xff09;&#xff0c;我们在链表的头部引入了一个虚拟节点 dummy&#xff0c;并让…

点评项目-5-商户查询缓存,从 2s 优化到 12ms

业务需求&#xff1a;当前端发送 shop/id 的请求时&#xff0c;我们需要向前端响应对应 id 的详细数据给前端 直接查询 mysql 效率比较低&#xff0c;我们可以使用 redis 作为中间件进行数据的缓存&#xff0c;先查询 redis &#xff0c;若redis 中未查询到&#xff0c;则在 m…

24.3 基于文件的服务发现模式

本节重点介绍 : 基于文件的服务发现提供了一种配置静态目标的更通用的方法可以摆脱对特定服务发现源的依赖通常的做法是调用内部CMDB的接口获取target数据&#xff0c;打上标签&#xff0c;生成json文件发给prometheus采集 基于文件的服务发现模式 解决的问题 之前手动配置…

视频声音怎么去除?高效的视频声音去除方法

视频声音怎么去除&#xff1f;在多媒体创作与编辑的过程中&#xff0c;我们时常会遇到这样一个需求&#xff1a;为了使用视频素材&#xff0c;需要去除视频里的声音。这一步骤看似简单&#xff0c;实则对于提升作品的整体质量和专业度至关重要。很多时候&#xff0c;我们收集到…

人工智能风险预警以及区块链解决方案探索

​​发表时间&#xff1a;2024年9月26日 一个专家小组在为世界经济论坛撰写的报告中警示道&#xff0c;人工智能&#xff08;以下均简称为AI&#xff09;技术增加了各类组织遭受攻击的风险&#xff0c;并带来了训练数据污染和提示词注入攻击等新威胁。由于训练和测试数据库的庞…

VmWare中安装CenterOs(内网服务器)

VmWare中安装CenterOs(内网服务器) 文章目录 VmWare中安装CenterOs(内网服务器)[toc] 一 、CentOS 7的下载与安装1、下载2、安装&#xff08;1&#xff09;前期准备&#xff08;2&#xff09;正式安装 开始等待&#xff01;&#xff01;&#xff01; 二、软件仓库更换1、root用…

openmmlab实现图像超分辨率重构

文章目录 前言一、图像超分辨率重构简介二、mmmagic实现图像超分 前言 超分辨率重构技术&#xff0c;作为计算机视觉领域的一项重要研究课题&#xff0c;近年来受到了广泛关注。随着科技的飞速发展&#xff0c;人们对图像质量的要求越来越高&#xff0c;尤其是在智能手机、监控…

使用idea和vecode创建vue项目并启动(超详细)

一、idea创建vue项目 创建项目之前先下载好插件 新建项目找到vue生成器 写好名称&#xff0c;找到自己需要存放的地址&#xff0c;node解释器安装方式可以看我上一个博客&#xff0c;vueCLI是选择vue的版本&#xff0c;我们可以使用idea自带的vue版本默认是vue3&#xff0c;创…

【LeetCode】修炼之路-0004-Median of Two Sorted Arrays【python】

题目 Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. The overall run time complexity should be O(log (mn)). Example 1: Input: nums1 [1,3], nums2 [2] Output: 2.00000 Explanation: merged…

java.lang.NoClassDefFoundError: kotlin/Result解决方案

问题 在控制窗口上虽然报错是找不到对应的class&#xff0c;但是呢在我们导入kotlin的后&#xff0c;还是报相同的异常&#xff0c;在网上查找了各种资料&#xff0c;都没有解决方案。 问题分析 在idea2021之后&#xff0c;kotlin都使用远程仓库&#xff08;kotlinx-coeouti…