ElasticSearch - 在 微服务项目 中基于 RabbitMQ 实现 ES 和 MySQL 数据异步同步(考点)

news2024/11/25 4:55:18

目录

一、数据同步

1.1、什么是数据同步

1.2、解决数据同步面临的问题

1.3、解决办法

1.3.1、同步调用

1.3.2、异步通知(推荐)

1.3.3、监听 binlog

1.3、基于 RabbitMQ 实现数据同步

1.3.1、需求

1.3.2、在“酒店搜索服务”中 声明 exchange、queue、routingKey,同时开启监听

1.3.3、在“酒店管理服务”中发布消息

1.3.4、启动微服务并测试


一、数据同步


1.1、什么是数据同步

我们知道 elasticsearch 的数据是来源于 数据库(比如 mysql).  当我们在写了代码将 mysql 中的数据导入 es 中,那么这次导入之后 mysql 的数据并不会一成不变,将来我们的业务中会有 crud,数据库中的数据就和有新增、修改、删除,那么 mysql 数据一定那发生变化, es 如果不跟着变化,就会出现问题.

比如在一个商城系统中,到了 双11 ,数据库中的商品的价格下降,而 es 还是老价格,那么用户搜索时看到的商品的价格还是没有变化,用户可能就得考虑换软件了~  

因此,我们要保证 mysql 数据变化的时候 es 也能跟着同步变化,这就是数据同步.

Ps:实际上不光是 es 存在数据同步问题,凡是涉及到数据库双写的情况,比如 redis 和 mysql,都会存在数据同步问题.

1.2、解决数据同步面临的问题

如果你现在是一个单体式的项目,所有业务都写在一个项目中,那就比较好办,无非就是在及逆行新增、修改、删除业务的时候,同时把 es 也一起更新就  ok.

但是如果我们是一个 微服务 架构的项目上,不同的业务往往会在不同的微服务上,比如 “商品数据管理业务” 和 “商品数据搜索业务” 肯定会在两个不同的微服务上,那么跨微服务的项目就没办法直接操作了.

1.3、解决办法

1.3.1、同步调用

假设我们现在有两个微服务,一个是 酒店数据管理服务,另一个是酒店数据搜索服务. 假设这两个服务之间互相不能访问对方的数据库,也就是说,酒店管理服务只能访问 mysql,而 酒店搜索服务 只能访问 es,这也符合 微服务 里的标准和规范.

这里有一种办法是同步调用,步骤如下:

1.比如用户做新增操作时,首先把数据写到数据库里.

2. 数据库写完了以后紧接着调用 酒店搜索服务中的 “更新索引库” 的接口.

3. 更新 es.

最后更新完了 es 就把响应反馈给搜索服务,然后搜索服务才会把把响应反馈给  管理服务,然后再反馈给用户.  这整个过程依次执行,因此也叫同步调用.

缺陷:

1. 数据耦合,业务耦合:原本只是写数据库,写完就结束了,然后现在还需要再写完数据库的代码后面再加上 调用 “更新索引库” 接口的代码,而且这调用 这个接口的业务跟我新增业务显然没有关系啊,现在业务耦合在一起,将来必然也会影响性能.

2. 影响性能(耦合带来的问题):原本数据库写完了,比如耗时 50ms,但是现在写完数据库,你还得等待后台调用这个接口返回的响应,而这个接口又要等待 es 这里的响应,假如这里也耗时 50ms,那么总的耗时不就是这个三个步骤相加的,达到 100 ms.

3. 牵一发而动全身(耦合带来的问题):如果 步骤 2 和 步骤 3 任意一个位置出现了异常,就会导致整个业务也出现崩溃.

同步调用这么多问题,那么就需要考虑别的方案了.

1.3.2、异步通知(推荐)

这里就需要使用到 mq 来实现了,步骤如下:

1. 当有人做新增操作时,先去写数据库.

2. 写完之后,不调用任何服务的接口,而是向 mq 发送一个消息,通知一下 其他服务:“我这里数据新增了啊~”,整个步骤到这里结束.

那么至于谁来监听这个消息,监听了以后做什么,跟我有关系吗?没关系,这样一来,业务的耦合就解除了;  至于其他服务耗时多少秒,跟我也没关系,我写完数据库,发完消息就结束了,因此性能也提升了;再者,就算其他服务出现异常了,跟我这里也没关系.

缺陷:

1. 这样一来,比较依赖 mq 消息的可靠性.

2. 引入新的中间件,实现的复杂度也会有一定的上升.

不过这些缺陷,跟同步调用比起来,算不了什么,因此这也是比较推荐的方案.

1.3.3、监听 binlog

mysql 默认情况下 binlog 时关闭的,一旦开启,那么每次 mysql 在做增删改的时候,都会记录相应的操作到 binlog 中.

那么可以使用类似于 canal 这样的中间件来监听 binlog,一旦发现变化,立马通知对应的微服务,这个时候就知道数据发生变更,就可以进行更新了.

优势:

这种方案他既不给任何中间件发消息,也不去调用任何接口,因此耦合度是最低的.

劣势:

1.开启 binlog,对 mysql 的压力就增加了.

2.引入新的中间件.

1.3、基于 RabbitMQ 实现数据同步

1.3.1、需求

现在有两个微服务:“酒店管理服务” 和 "酒店搜索服务"

用户操作 “酒店管理服务” 进行增删改数据、要求对 "酒店搜索服务" 中的 es 的数据也要完成相同的数据更改操作.

这里使用 MQ 的异步方式实现数据同步.

1.3.2、在“酒店搜索服务”中 声明 exchange、queue、routingKey,同时开启监听

在 “酒店搜索服务” 中去声明一个 exchange,用来接收 增删改 的消息,接着声明两个队列即可,一个队列用来增改(这两用一个队列是因为在 es 中,增改可以使用同一个 DSL 语句实现),另一个用来删除(这里我是以 Bean 的方式注入到容器中了).

public class MqConstants {

    //主题交换机
    public static final String EXCHANGE_TOPIC = "hotel.topic";
    //增加 or 修改酒店 队列
    public static final String INSERT_QUEUE = "hotel.insert.queue";
    //删除酒店 队列
    public static final String DELETE_QUEUE = "hotel.delete.queue";
    //增加 or 修改酒店 routingKey
    public static final String INSERT_KEY = "hotel.insert.key";
    //删除酒店 routingKey
    public static final String DELETE_KEY = "hotel.delete.key";

}
@Configuration
public class MqConfig {

    @Bean
    public TopicExchange hotelTopicExchange() {
        return new TopicExchange(MqConstants.EXCHANGE_TOPIC, true, false);
    }

    @Bean
    public Queue hotelInsertQueue() {
        return new Queue(MqConstants.INSERT_QUEUE, true);
    }

    @Bean
    public Queue hotelDeleteQueue() {
        return new Queue(MqConstants.DELETE_QUEUE, true);
    }

    @Bean
    public Binding hotelInsertBinding() {
        return BindingBuilder.bind(hotelInsertQueue()).to(hotelTopicExchange()).with(MqConstants.INSERT_KEY);
    }

    @Bean
    public Binding hotelDeleteBinding() {
        return BindingBuilder.bind(hotelDeleteQueue()).to(hotelTopicExchange()).with(MqConstants.DELETE_KEY);
    }

}

Ps:此类(MqConfig)的包必须与启动类同级,否则声明交换机和队列失败.

最后就可以使用 @RabbitListener 监听 队列 了.

@Component
public class MqListener {

    @Autowired
    private IHotelService hotelService;

    @RabbitListener(queues = MqConstants.INSERT_QUEUE)
    public void HotelInsertOrUpdateListener(Long id) {
        hotelService.insertHotelById(id);
    }

    @RabbitListener(queues = MqConstants.DELETE_QUEUE)
    public void HotelDeleteListener(Long id) {
        hotelService.deleteHotelById(id);
    }

}

Ps:此类(MyListener,监听者类)上必须要有 @Component 注解(交由给 Spring 来管理),否则声明的交换机和队列无效.

1.3.3、在“酒店管理服务”中发布消息

酒店管理服务中,一旦用户进行酒店的 增删改,就会对数据库信息进行修改,然后将增删改的消息发布到 MQ 中.

Ps:这里不要发送 hotel 整体数据,太大可能会导致占满队列(Mq 是基于内存存储的,因此会设定队列上限),因此这里发送 id 即可.  es 这边拿到 id,就可进行相应的增删改.

    @PostMapping
    public void saveHotel(@RequestBody Hotel hotel){
        // 新增酒店
        hotelService.save(hotel);
        rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_TOPIC, MqConstants.INSERT_KEY, hotel.getId());
    }

    @PutMapping()
    public void updateById(@RequestBody Hotel hotel){
        //修改酒店信息
        if (hotel.getId() == null) {
            throw new InvalidParameterException("id不能为空");
        }
        hotelService.updateById(hotel);
        rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_TOPIC, MqConstants.INSERT_KEY, hotel.getId());
    }

    @DeleteMapping("/{id}")
    public void deleteById(@PathVariable("id") Long id) {
        //删除酒店信息
        hotelService.removeById(id);
        rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_TOPIC, MqConstants.DELETE_KEY, id);
    }

1.3.4、启动微服务并测试

a)在酒店管理页面中,修改 “7天连锁酒店(上海莘庄地铁站店)” 为 “7天连锁酒店(此处正在施工,请谨慎前往)”,如下.

b)在酒店搜索页面中,搜索 “施工” 关键词,就可以看到在 “酒店管理服务” 中更新的信息,已经同步到了 “酒店搜索服务” 中.

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

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

相关文章

【C++】vector的介绍 | 常见接口的使用

目录 vector的介绍 常见接口 构造函数 尾插push_back() vector的遍历 1.用方括号下标 遍历: 2.调用at()来访问: 3.用迭代器遍历: 4.范围for遍历: vector空间 vector增删查改 覆盖assign() 查找find() 插入insert() …

css自学框架之幻灯片展示效果

这一节,我自学了焦点图效果(自动播放,圆点控制),首先看一下效果: 下面我们还是老思路,css展示学习三个主要步骤:一是CSS代码,二是Javascript代码,三是Html代码。 一、css代码主要如…

【JavaEE】锁策略

文章目录 前言1. 乐观锁和悲观锁2. 重量级锁和轻量级锁3. 自旋锁和挂起等待锁4. 公平锁和非公平锁5. 可重入锁和非可重入锁6. 读写锁Java synchronized 分别对应哪些锁策略1. 乐观锁和悲观锁2. 重量级锁和轻量级锁3. 自旋锁和挂起等待锁4. 公平锁和非公平锁5. 可重入锁和非可重…

Scala第八章节

Scala第八章节 scala总目录 章节目标 能够使用trait独立完成适配器, 模板方法, 职责链设计模式能够独立叙述trait的构造机制能够了解trait继承class的写法能够独立完成程序员案例 1. 特质入门 1.1 概述 有些时候, 我们会遇到一些特定的需求, 即: 在不影响当前继承体系的情…

基于Java的图书管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

JavaScript系列从入门到精通系列第十篇:JavaScript中的相等运算符与条件运算符

文章目录 一:相等运算符 1: 2:! 3:与! (一): (二):! 二:条件运算符 1:语法 2:使用 3:容易挨打的写法 一:相等运算符 用于比较两个值是…

前端框架介绍

一、node.js 配置淘宝镜像源 npm config set registry https://registry.npm.taobao.org可以使用npm config list命令来确认镜像地址是否已成功更改。 如果需要将配置的镜像恢复为默认的npm官方源,可以执行以下命令: npm config delete registry二、vue 1、创建Vue项目 …

算法的时间复杂度分析习题专题

之前写了一篇重点是讲理论,今天重点在于对于题目的分析 题目难度不分先后,有题目来源会直接给出链接或者位置 第一题:消失的数字 题目来源:LeetCode消失的数字 分析 第一种思路分析: 参考代码: #include …

RTSP协议抓包及讲解

文章目录 前言一、RTSP 亲手搭建直播点播1、数据源为视频文件2、数据源为摄像头①、搭建 RTSP 流媒体服务器②、客户端拉流 二、RTSP 协议简介三、手撕 RTSP 协议1、Wireshark 抓包①、搭建环境②、wireshark 抓包 2、RTSP 交互流程①、OPTIONS②、DESCRIBE③、SETUP④、PLAY⑤…

全面横扫:dlib Python API在Linux和Windows的配置方案

前言 在计算机视觉和人工智能领域,dlib是一个备受推崇的工具库。它为开发者提供了强大的图像处理、机器学习和深度学习功能。在计算机视觉项目中,配置dlib Python API是一个重要的初始步骤。本文将引导读者详细了解在Linux和Windows系统上安装和配置dli…

【算法分析与设计】动态规划(下)

目录 一、最长公共子序列1.1 最长公共子序列的结构1.2 子问题的递归结构1.3 计算最优值1.4 举例说明1.5 算法的改进 二、最大子段和2.1 代码2.2 最大子段和问题的分治算法2.3 代码2.4 分治算法的时间复杂度2.5 最大子段和问题的动态规划算法 三、凸多边形最优三角剖分3.1 三角剖…

Flutter笔记:关于应用程序中提交图片作为头像

Flutter笔记 关于应用程序中提交图片作为头像 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/details/133418554…

raw智能照片处理工具DxO PureRAW mac介绍

DxO PureRAW Mac版是一款raw智能照片处理工具,该软件采用了智能技术,以解决影响所有RAW文件的七个问题:去马赛克,降噪,波纹,变形,色差,不想要的渐晕,以及缺乏清晰度。 Dx…

软件测试之单元测试自动化入门基础

单元测试自动化 所谓的单元测试(Unit Test)是根据特定的输入数据,针对程序代码中的最小实体单元的输入输出的正确性进行验证测试的过程。所谓的最小实体单元就是组织项目代码的最基本代码结构:函数,类,模块等。在Python中比较知名…

picoctf_2018_got_shell

picoctf_2018_got_shell Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)32位,只开了NX int __cdecl __noreturn main(int argc, const char **argv, const char **envp) {_DWOR…

DeepFace【部署 02】轻量级人脸识别和面部属性分析框架(实时分析+API+Docker部署+命令行接口)

轻量级人脸识别和面部属性分析框架 2.10 Real Time Analysis2.11 API2.12 Dockerized Service2.13 Command Line Interface 2.10 Real Time Analysis 你也可以运行deepface实时视频。流功能将访问您的网络摄像头,并应用面部识别和面部属性分析。如果能连续聚焦5帧&…

2023-9-29 LCR 083 全排列

题目链接&#xff1a;全排列 class Solution {int [] nums;List<List<Integer>> res new ArrayList<>();List<Integer> path;boolean[] st;public List<List<Integer>> permute(int[] nums) {this.nums nums;path Arrays.asList(new In…

DAMA-DMBOK2重点知识整理CDGA/CDGP——第14章 大数据与数据科学

目录 一、分值分布 二、重点知识梳理 1、引言 1.1 业务驱动因素 1.2 原则 1.3 基本理念 2、活动 2.1 定义大数据战略和业务需求 2.2 选择数据源 2.3 获得和接收数据源 2.4 制定数据假设和方法 2.5 集成和调整数据进行分析 2.6 使用模型探索数据 2.7 部署和监控 …

09链表-单链表移除元素

目录 链表&#xff08;Linked List&#xff09; 链表的数据结构 单链表 双链表 循环链表 链表的存储方式 删除节点 添加节点 LeetCode之路——203. 移除链表元素 分析&#xff1a; 链表&#xff08;Linked List&#xff09; 链表是一种线性数据结构&#xff0c;用于…

C运算符和控制语句

几乎每一个程序都需要进行运算&#xff0c;对数据进行加工处理&#xff0c;否则程序就没有意义了。要进行运算&#xff0c;就需规定可以使用的运算符。 C语言的运算符范围很宽&#xff0c;把除了控制语句和输人输出以外的几乎所有的基本操作都作为运算符处理。 运算符分类1 除…