当前位置: 首页 > news >正文

从零手搓一个【消息队列】实现数据的硬盘管理和内存管理(线程安全)

文章目录

  • 一、硬盘管理
    • 1, 创建 DiskDataCenter 类
    • 2, init() 初始化
    • 3, 封装交换机
    • 4, 封装队列
    • 5, 关于绑定
    • 6, 关于消息
  • 二、内存管理
    • 1, 数据结构的设计
    • 2, 创建 MemoryDataCenter 类
    • 3, 关于交换机
    • 4, 关于队列
    • 5, 关于绑定
    • 6, 关于消息
    • 7, 恢复数据
  • 三、小结


创建 Spring Boot 项目, Spring Boot 2 系列版本, Java 8 , 引入 MyBatis, Lombok 依赖

提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎批评指点~ 废话不多说,直接上干货!

整体目录结构 :
在这里插入图片描述

本文主要实现 server 包


一、硬盘管理

硬盘管理包括第二篇文章中实现的 DataBaseManager 类, 实现了数据库存储(交换机, 队列, 绑定)和第三篇文章中实现的 MessageFileManager 类, 实现了文件存储(消息)

接下来使用 DiskDataCenter 这个类, 对上述两个类进一步的整合, 封装, 统一对上层提供数据库和文件操作的 API

在这里插入图片描述

整合的好处
上层代码(服务器中的 VirtualHost 在操作硬盘数据的时候, 直接调用 DiskDataCenter 类的 API 即可, 不需要关心硬盘上的数据来自数据库还是文件, 也不需要知道具体是怎么实现的)


1, 创建 DiskDataCenter 类

要有两个成员属性, 分别是 dataBaseManager 对象和 messageFileManager 对象, 我们要持有这两个类的 API, 才能进一步封装

public class DiskDataCenter {private DataBaseManager dataBaseManager = new DataBaseManager();private MessageFileManager messageFileManager = new MessageFileManager();
}

2, init() 初始化

dataBaseManager 中需要对数据库进行初始化, 所以在当前类提供一个初始化 API

    public void init() {dataBaseManager.init();}

3, 封装交换机

    public void insertExchange(Exchange exchange) {dataBaseManager.insertExchange(exchange);}public void deleteExchange(String exchangeName) {dataBaseManager.deleteExchange(exchangeName);}public List<Exchange> selectAllExchanges() {return dataBaseManager.selectAllExchanges();}

4, 封装队列

创建队列的时候要创建对应的目录( queue 目录)和文件( data 文件和 stat 文件), 同理, 删除的时候和也要删除对应的目录和文件

	public void insertQueue(MessageQueue queue) throws IOException {dataBaseManager.insertQueue(queue);messageFileManager.createQueueFiles(queue.getName());}public void deleteQueue(String queueName) throws IOException {dataBaseManager.deleteQueue(queueName);messageFileManager.deleteFiles(queueName);}public List<MessageQueue> selectAllQueue() {return dataBaseManager.selectAllQueues();}

5, 关于绑定

	public void insertBinding(Binding binding) {dataBaseManager.insertBinding(binding);}public void deleteBinding(Binding binding) {dataBaseManager.deleteBinding(binding);}public List<Binding> selectAllBindings() {return dataBaseManager.selectAllBindings();}

6, 关于消息

删除消息的时候顺便查一下 stat 文件, 如果需要GC, 就调用实现GC的方法(读一下 stat 文件并不是很耗时)

public void sendMessage(MessageQueue queue, Message message) throws IOException, MQException {messageFileManager.sendMessage(queue, message);}public void deleteMessage(MessageQueue queue, Message message) throws IOException, MQException {messageFileManager.deleteMessage(queue, message);// 这是逻辑删除, 而不是真正从文件中删掉这个数据, 并且需要判断是否需要进行垃圾回收if (messageFileManager.isNeedGC(queue.getName())) {messageFileManager.gc(queue);}}public LinkedList<Message> selectAllMessages(String queueName) throws MQException, IOException {return messageFileManager.loadAllMessageFromQueue(queueName);}

二、内存管理

硬盘上存储数据, 只是为了实现 “持久化” 这样的效果. 但是实际的消息存储/转发, 还是主要靠内存的结构

对于 MQ 来说, 内存部分是更关键的, 内存速度更快, 可以达成更⾼的并发

对于交换机, 队列, 绑定, 消息的查找, 直接在内存中找, 增加, 删除需要在管理内存的同时也管理硬盘
在这里插入图片描述


1, 数据结构的设计

需要在内存中存储交换机, 队列, 绑定, 交换机, 就需要使用合适的数据结构来辅助我们更方便的存储和管理数据, 并且还需要保证线程安全

  • 交换机
    使用 ConcurrentHashMap<String, Exchange>, key: exchangeName, value: exchange
  • 队列
    使用 ConcurrentHashMap<String, MessageQueue>, key: queueName, value: queue
  • 绑定
    使用(嵌套的) ConcurrentHashMap<String, ConcurrentHashMap<String, Binding>>, key: exchangeName, value: [key: queueName, value: binding]

生产者发布消息的时候, 要指定一个 exchangeName, 服务器需要拿着这个 exchangeName, 在绑定表里查有没有已经绑定过的队列, 如果有队列, 并且符合 bindingKey 和 routingKey 的匹配规则, 才能转发消息
绑定是从交换机和队列这两个维度建立的, 所以使用嵌套的 Map 存储绑定表

  • 消息
    使用 ConcurrentHashMap<String, Message>, key: messageId, value: message

  • 在队列里的 N 条消息
    使用 ConcurrentHashMap<String, LinkedList< Message>>, key: queueName, value: 该队列中存储消息的链表

  • 在队列里的 N 条未确认的消息( “待确认队列” )
    使用(嵌套的) ConcurrentHashMap<String, ConcurrentHashMap<String, Message>>, key: queueName, value: [key: messageId, value: message]

  1. 之前的文章说明过, 消费者消费消息采用"推"的方式, 即: 队列中有消息之后, 服务器主动推送给订阅了该队列的消费者
  2. 推送之后, 如果消费者是手动应答
  3. 在消费者还没应答之前, 服务器视为消费者还没成功消费消息, 需要备份这条消息, 所以这个嵌套的Map 相当于一个 “待确认队列”
  4. 消费者确认应答之后, 服务器再从这个 “待确认队列” 中删除该消息

在这里插入图片描述

2, 创建 MemoryDataCenter 类

public class MemoryDataCenter {private ConcurrentHashMap<String, Exchange> exchangeMap = new ConcurrentHashMap<>();private ConcurrentHashMap<String, MessageQueue> queueMap = new ConcurrentHashMap<>();private ConcurrentHashMap<String, ConcurrentHashMap<String, Binding>> bindingsMap = new ConcurrentHashMap<>();private ConcurrentHashMap<String, Message> messageMap = new ConcurrentHashMap<>();private ConcurrentHashMap<String, LinkedList<Message>> messageInQueueMap = new ConcurrentHashMap<>();private ConcurrentHashMap<String, ConcurrentHashMap<String, Message>> messageInQueueNotAckMap = new ConcurrentHashMap<>();
}

3, 关于交换机

    public void addExchange(Exchange exchange) {exchangeMap.put(exchange.getName(), exchange);System.out.println("[MemoryDataCenter.addExchange()] " + exchange.getName() + "交换机添加成功");}public Exchange getExchange(String exchangeName) {return exchangeMap.get(exchangeName);}public void removeExchange(String exchangeName) {exchangeMap.remove(exchangeName);System.out.println("[MemoryDataCenter.removeExchange()] " + exchangeName + "交换机删除成功");}

4, 关于队列

	public void addQueue(MessageQueue queue) {queueMap.put(queue.getName(), queue);System.out.println("[MemoryDataCenter.addQueue()] " + queue.getName() + "队列添加成功");}public MessageQueue getQueue(String queueName) {return queueMap.get(queueName);}public void removeQueue(String queueName) {queueMap.remove(queueName);System.out.println("[MemoryDataCenter.removeQueue()] " + queueName + "队列删除成功");}

5, 关于绑定

  • 1, 添加绑定
    注意 bindingsMap 和 bindingMap 不同, bindingMap 表示 bindingsMap 的 value 值
    使用 bindingsMap.computeIfAbsent(key) 可以创建 bindingsMap 的 value 值( bindingMap )
    public void addBinding(Binding binding) throws MQException {String exchangeName = binding.getExchangeName();String queueName = binding.getQueueName();
//        ConcurrentHashMap<String, Binding> bindingMap = bindingsMap.get(binding.getExchangeName());
//        if (bindingMap == null) {
//            bindingMap = new ConcurrentHashMap<>();
//            bindingsMap.put(binding.getExchangeName(), bindingMap);
//        }// 1, 先查是否存在exchange绑定的queue, 没有则创建ConcurrentHashMap<String, Binding> bindingMap =bindingsMap.computeIfAbsent(exchangeName, V -> new ConcurrentHashMap<>());// 2, 再查exchange和queue是否已经存在绑定, 没有创建synchronized (bindingMap) {if (bindingMap.get(exchangeName) != null) {// 如果 exchange-queue 这个绑定已经存在, 不能再插入throw new MQException("[MemoryDataCenter.addBinding()] exchangeName = " + queueName+ "-queueName = " + queueName + "的绑定已经存在, 添加失败");}bindingMap.put(queueName, binding);}System.out.println("[MemoryDataCenter.addBinding()] exchangeName = " + exchangeName+ "-queueName = " + queueName + "的绑定已经添加成功");}
  • 2, 获取绑定(根据交换机和队列获取绑定)
    public Binding getBinding(String exchangeName, String queueName) {ConcurrentHashMap<String, Binding> bindingMap = bindingsMap.get(exchangeName);if (bindingMap == null) {return null;}return bindingMap.get(queueName);}
  • 3, 获取绑定(根据交换机, 获取该交换机的所有绑定关系)
    public ConcurrentHashMap<String, Binding> getBindings(String exchangeName){return bindingsMap.get(exchangeName);}
  • 4, 删除绑定
public void removeBinding(Binding binding) throws MQException {// 1, 先判断该交换机是否存在绑定String exchangeName = binding.getExchangeName();ConcurrentHashMap<String, Binding> bindingMap = bindingsMap.get(exchangeName);if (bindingMap == null) {throw new MQException("[MemoryDataCenter.removeBinding()] exchangeName = " + exchangeName + "该交换机的绑定不存在, 删除失败");}// 2, 交换机不存在绑定才能删除bindingsMap.remove(binding.getExchangeName());System.out.println("[MemoryDataCenter.removeBinding()] exchangeName = " + exchangeName + "该交换机的绑定已删除成功");}

6, 关于消息

  • 关于 messageMap
    public void addMessage(Message message) {messageMap.put(message.getMessageId(), message);System.out.println("[MemoryDataCenter.addMessage()] 消息添加成功");}public Message getMessage(String messageId) {return messageMap.get(messageId);}public void removeMessage(String messageId){messageMap.remove(messageId);System.out.println("MemoryDataCenter.removeMessage()] 消息删除成功");}
  • 关于 messageInQueueMap
	public void sendMessage(MessageQueue queue, Message message) {LinkedList<Message> messageList = messageInQueueMap.computeIfAbsent(queue.getName(), V -> new LinkedList<>());synchronized (messageList) {messageList.add(message); // 尾插}addMessage(message);System.out.println("[MemoryDataCenter.sendMessage()] 消息发送成功");}/*** 从队列中取走消息, 但不代表消息在内存中没了, 在 messageMap 中还保留*/public Message pollMessage(MessageQueue queue) {LinkedList<Message> messageList = messageInQueueMap.get(queue.getName());if (messageList == null || messageList.size() == 0) {return null;}synchronized (messageList) {Message result = messageList.removeFirst(); // 头删System.out.println("[MemoryDataCenter.pollMessage()] 消息取出成功");return result;}}/*** 获取队列中的消息总数*/public int getMessageCount(MessageQueue queue) {LinkedList<Message> messageList = messageInQueueMap.get(queue.getName());if (messageList == null) {return 0;}return messageList.size();}
  • 关于 messageInQueueNotAckMap
	public void addMessageNotAck(String queueName, Message message) {ConcurrentHashMap<String, Message> messageNotAckMap = messageInQueueNotAckMap.computeIfAbsent(queueName,V -> new ConcurrentHashMap<>());messageNotAckMap.put(message.getMessageId(), message);System.out.println("[MemoryDataCenter.addMessageNotAck()] 未确认消息添加成功");}public void removeMessageNotAck(String queueName, String messageId) {ConcurrentHashMap<String, Message> messageNotAckMap = messageInQueueNotAckMap.get(queueName);if (messageNotAckMap == null) {return;}messageNotAckMap.remove(messageId);System.out.println("[MemoryDataCenter.removeMessageNotAck()] 未确认消息删除成功");}public Message getMessageNotAck(String queueName, String messageId) {ConcurrentHashMap<String, Message> messageNotAckMap = messageInQueueNotAckMap.get(queueName);if (messageNotAckMap == null) {return null;}return messageNotAckMap.get(messageId);}

7, 恢复数据

当服务器重启时, 需要把硬盘上的数据恢复到内存中

	public void recover(DiskDataCenter diskDataCenter) throws MQException, IOException {// 1, 清楚所有内存数据(防止残留)exchangeMap.clear();queueMap.clear();bindingsMap.clear();messageMap.clear();messageInQueueMap.clear();messageInQueueNotAckMap.clear();// 2. 从硬盘中获取数据并存储在内存// 2.1, 获取交换机数据List<Exchange> exchangeList =  diskDataCenter.selectAllExchanges();for (Exchange exchange : exchangeList) {exchangeMap.put(exchange.getName(), exchange);}// 2.2, 获取队列数据List<MessageQueue> queueList = diskDataCenter.selectAllQueue();for (MessageQueue queue : queueList) {queueMap.put(queue.getName(), queue);}// 2.3, 获取绑定数据List<Binding> bindingList = diskDataCenter.selectAllBindings();for (Binding binding : bindingList) {ConcurrentHashMap<String, Binding> bindingMap =bindingsMap.computeIfAbsent(binding.getExchangeName(), V -> new ConcurrentHashMap<>());bindingMap.put(binding.getQueueName(), binding);}// 2.4, 获取消息数据for (MessageQueue queue : queueList) {LinkedList<Message> messageList = diskDataCenter.selectAllMessages(queue.getName());messageInQueueMap.put(queue.getName(), messageList);for (Message message : messageList) {messageMap.put(message.getMessageId(), message);}}}

恢复消息数据, 只需要恢复 messageInQueueMap 和 messageMap , 不需要关心 messageInQueueNotAckMap 因为 messageInQueueNotAckMap 只是在内存中存储, 如果服务器重启, 这块数据丢失了就丢失了, 因为 “未确认的消息” 不会被服务器真正的删除, 重启之后, “未确认的消息” 会被重新加载回内存


三、小结

本文主实现了两点:

  • 1, 实现了对数据库+文件这两个模块的进一步整合, 封装, 为上层提供了硬盘数据管理的 API
  • 2, 实现了内存数据管理, 并考虑了线程安全
    • 2.1, 设计了消息在内存中存储使用的数据结构
    • 2.2, 实现了内存中的交换机, 队列, 绑定, 消息, 以及消息和队列的关联, 这些数据的增删查

至此, 服务器对于硬盘数据和内存数据都做好了管理, 下篇文章就可以设计 VirtualHost 了, 需要对硬盘数据和内存数据再做进一步的整合, 封装, 统一管理

相关文章:

从零手搓一个【消息队列】实现数据的硬盘管理和内存管理(线程安全)

文章目录 一、硬盘管理1, 创建 DiskDataCenter 类2, init() 初始化3, 封装交换机4, 封装队列5, 关于绑定6, 关于消息 二、内存管理1, 数据结构的设计2, 创建 MemoryDataCenter 类3, 关于交换机4, 关于队列5, 关于绑定6, 关于消息7, 恢复数据 三、小结 创建 Spring Boot 项目, S…...

自动驾驶中的感知模型:实现安全与智能驾驶的关键

自动驾驶中的感知模型&#xff1a;实现安全与智能驾驶的关键 文章目录 引言感知模型的作用感知模型的技术安全与挑战结论 2023星火培训【专项营】Apollo开发者社区布道师倾力打造&#xff0c;包含PnC、新感知等的全新专项课程上线了。理论与实践相结合&#xff0c;全新的PnC培训…...

【CVPR 2023】DSVT: Dynamic Sparse Voxel Transformer with Rotated Sets

文章目录 开场白效果意图 重点VoxelNet: End-to-End Learning for Point Cloud Based 3D Object DetectionX-Axis DSVT LayerY-Axis DSVT Layer Dynamic Sparse Window AttentionDynamic set partitionRotated set attention for intra-window feature propagation.Hybrid wind…...

MySQL超入门(1)__迅速上手掌握MySQL

# 1.选择语句 # 注意事项&#xff1a;MySQL不区分大小写&#xff0c;SELECT * 代表选择全部 // 测试一 USE sql_store; -- 使用 sql_store库 SELECT * FROM customers -- 查询customers表 WHERE customer_id 1 OR customer_id 4 -- 条件判断为customer_id 1或customer_id …...

四、浏览器渲染过程,DOM,CSSDOM,渲染,布局,绘制详细介绍

知识点&#xff1a; 1、为什么不能先执行 js文件&#xff1f;&#xff1f; 我们不能先执行JS文件&#xff0c;必须等到CSSOM构建完成了才能执行JS文件&#xff0c;因为前面已经说过渲染树是需要DOM和CSSOM构建完成了以后才能构建&#xff0c;而且JS是可以操控CSS样式的&#…...

2021-06-10 51单片机设计一个蜂鸣器报警电路每秒

缘由求助一下谢谢啦51单片机_嵌入式-CSDN问答设计一个蜂鸣器报警电路&#xff0c;按下K1&#xff0c;蜂鸣器响一声&#xff0c;按下K2&#xff0c;蜂鸣器响三声&#xff0c;按下K3,蜂鸣器长鸣。要求响声和间隔的时间均为1秒&#xff0c;长鸣不限时&#xff0c;但是此时应设置一…...

D‘Agostino-Pearson正态检验|偏度skewness和峰度kurtosis

DAgostino-Pearson检验&#xff08;也称为DAgostino和Pearson正态性检验&#xff09;是一种用于检验数据是否符合正态分布的统计检验方法。它基于数据的样本统计量&#xff0c;主要包括偏度&#xff08;skewness&#xff09;和峰度&#xff08;kurtosis&#xff09;&#xff0c…...

基于树莓派CM4制作img系统镜像批量制作TF卡

文章目录 前言1. 环境与工具2. 制作镜像3. 烧录镜像4. 总结 前言 树莓派烧录完系统做定制化配置比较费时间。在面对大批量的树莓派要配置&#xff0c;那时间成本是非常巨大的。第一次配置完可以说是摸着石头过河&#xff0c;但是会弄了以后再配置&#xff0c;都是一些重复性操…...

【中秋国庆不断更】OpenHarmony组件内状态变量使用:@State装饰器

State装饰的变量&#xff0c;或称为状态变量&#xff0c;一旦变量拥有了状态属性&#xff0c;就和自定义组件的渲染绑定起来。当状态改变时&#xff0c;UI会发生对应的渲染改变。 在状态变量相关装饰器中&#xff0c;State是最基础的&#xff0c;使变量拥有状态属性的装饰器&am…...

【Java 进阶篇】MySQL多表关系详解

MySQL是一种常用的关系型数据库管理系统&#xff0c;它允许我们创建多个表格&#xff0c;并通过各种方式将这些表格联系在一起。在实际的数据库设计和应用中&#xff0c;多表关系是非常常见的&#xff0c;它能够更好地组织和管理数据&#xff0c;实现数据的复杂查询和分析。本文…...

【开发篇】十、Spring缓存:手机验证码的生成与校验

文章目录 1、缓存2、用HashMap模拟自定义缓存3、SpringBoot提供缓存的使用4、手机验证码案例完善 1、缓存 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质使用缓存可以有效的减少低速数据读取过程的次数&#xff08;例如磁盘IO&#xff09;&#xff0c;提高…...

【Aurora 8B/10B IP(1)--初步了解】

Aurora 8B/10B IP(1)–初步了解 1 Aurora 8b/10b IP的基本状态: •通用数据通道吞吐量范围从480 Mb/s到84.48 Gb/s •支持多达16个连续粘合7GTX/GTH系列、UltraScale™ GTH或UltraScale+™ GTH收发器和4绑定GTP收发器 •Aurora 8B/10B协议规范v2.3顺从的 •资源成本低(请参…...

C++ vector容器的介绍与使用

一、vector简介 std::vector 是 C 标准模板库 (STL) 中的一个动态数组容器。允许存储元素&#xff08;可以使用任何数据类型作为其元素类型&#xff09;集合&#xff0c;并能够动态调整其大小。 特点&#xff1a; 动态大小&#xff1a;与常规数组不同&#xff0c;vector 可以…...

openstack的组成

OpenStack 是一个开源的云计算平台&#xff0c;由一系列组件构成&#xff0c;各组件之间相互协作&#xff0c;提供了完整的基础设施即服务&#xff08;IaaS&#xff09;解决方案。下面详细解释了 OpenStack 的主要组件及其相互关系&#xff1a; Nova&#xff08;计算服务&…...

[React] React高阶组件(HOC)

文章目录 1.Hoc介绍2.几种包装强化组件的方式2.1 mixin模式2.2 extends继承模式2.3 HOC模式2.4 自定义hooks模式 3.高阶组件产生初衷4.高阶组件使用和编写结构4.1 装饰器模式和函数包裹模式4.2 嵌套HOC 5.两种不同的高阶组件5.1 正向的属性代理5.2 反向的继承 6.如何编写高阶组…...

【逐步剖C++】-第二章-C++类和对象(中)

前言&#xff1a;本章继【逐步剖C】-第二章-C类和对象&#xff08;上&#xff09;介绍有关类和对象更深层次的知识点&#xff0c;这里是文章导图&#xff1a; 本文较长&#xff0c;内容较多&#xff0c;大家可以根据需求跳转到自己感兴趣的部分&#xff0c;希望能对读者有一些帮…...

PL/SQL动态SQL

目录 1. 动态 sql 2. 带参数的动态 sql -- 不使用 USING 传参 1. 动态 sql -- 在 PL/SQL 程序开发中,可以使用 DML 语句,但是很多语句(如 DDL),不能直接在 PL/SQL中执行,这些语句可以使用动态 sql 来实现. 语法格式: EXECUTE IMMEDIATE --动态语句的字符串 [into 变量…...

Python绘图系统24:添加辅助坐标轴

文章目录 辅助坐标增减坐标轴时间轴**代码优化源代码 Python绘图系统&#xff1a; 前置源码&#xff1a; Python打造动态绘图系统&#x1f4c8;一 三维绘图系统 &#x1f4c8;二 多图绘制系统&#x1f4c8;三 坐 标 轴 定 制&#x1f4c8;四 定制绘图风格 &#x1f4c8;五 数据…...

Java自学网站--十几个网站的分析与评测

原文网址&#xff1a;Java自学网站--十几个网站的分析与评测_IT利刃出鞘的博客-CSDN博客 简介 很多想学Java的人不知道怎样选教程&#xff0c;本文对Java自学网站进行评测。 本文不带主观倾向&#xff0c;只客观分析各个网站的区别。 第1类&#xff1a;大型培训机构(黑马等…...

java接口怎么写

Java接口是一种定义规范的抽象类型&#xff0c;可以包含常量和方法的声明。接口在Java编程中具有重要的作用&#xff0c;可以实现代码的重用和灵活性。本文将详细介绍Java接口的编写方式和使用方法。 一、什么是Java接口 在Java中&#xff0c;接口&#xff08;Interface&…...

第8章 Spring(二)

8.11 Spring 中哪些情况下,不能解决循环依赖问题 难度:★★ 重点:★★ 白话解析 有一下几种情况,循环依赖是不能解决的: 1、原型模式下的循环依赖没办法解决; 假设Girl中依赖了Boy,Boy中依赖了Girl;在实例化Girl的时候要注入Boy,此时没有Boy,因为是原型模式,每次都…...

从0开始python学习-24.selenium 浏览器常见的操作

1. 浏览器的最大化/最小化&#xff1a;maximize_window () / minimize_window() 2. 设置浏览器的宽高&#xff1a;set_window_size() 3. 设置浏览器的位置&#xff1a;set_window_position(0,0) —》左上角为原点 4. 刷新&#xff1a;refresh() 5. 前进&#xff1a;forward() 6…...

Canal实现数据同步

1、Canal实现数据同步 canal可以用来监控数据库数据的变化&#xff0c;从而获得新增数据&#xff0c;或者修改的数据。 1.1 Canal工作原理 原理相对比较简单&#xff1a; 1、canal模拟mysql slave的交互协议&#xff0c;伪装自己为mysql slave&#xff0c;向mysql master发送…...

数据库学习笔记——DDL

数据库学习笔记——DDL 建立EMPLOYEE数据库&#xff1a; CREATE TABLE employee(employee_ID int not null,employee_name varchar(20) not null,street varchar(20) not null,city varchar(20) not null,PRIMARY KEY(employee_ID) );CREATE TABLE company(company_name varc…...

MATLAB算法实战应用案例精讲-【人工智能】边缘计算(附python代码实现)

目录 前言 几个高频面试题目 边缘计算和云计算的关系 云计算(cloud computing) 边缘计算...

精彩回顾 | 迪捷软件亮相2023世界智能网联汽车大会

2023年9月24日&#xff0c;2023世界智能网联汽车大会&#xff08;以下简称大会&#xff09;在北京市圆满落幕。迪捷软件北京参展之行圆满收官。 本次大会由工业和信息化部、公安部、交通运输部、中国科学技术协会、北京市人民政府联合主办&#xff0c;是我国首个经国务院批准的…...

【ShaderLab PBR 嗜血边缘角色_美式朋克风格_“Niohoggr“_角色渲染(第一篇)】

嗜血边缘角色Cyberpunk style Unity 渲染 《嗜血边缘》截取其中的片段如下:资源分析其中Guitar贴图4张模型:人物细节图:人物模型 Inspector面板这里做一个区域区分:Body贴图1_BC贴图1_BC属性:Body贴图2_NBody贴图3_CMBody贴图4_SRMBody贴图4_RGBReflection Probe第一版Sha…...

python经典百题之围圈报数

题目:有n个人围成一圈&#xff0c;顺序排号。从第一个人开始报数&#xff08;从1到3报数&#xff09;&#xff0c;凡报到3的人退出圈子&#xff0c;问最后留下的是原来第几号的那位。 程序分析 思路1&#xff1a;模拟游戏过程 使用一个循环队列模拟游戏过程&#xff0c;每次循…...

Google Earth Engine(GEE)案例——如何去除和过滤Landsat和sentinel等系列影像集合中的空影像(三种方法)

简介 本文的主要解决的问题是如何去除和过滤Landsat和sentinel等系列影像集合中的空影像?这个主要源于一下的问题: “从图像集中删除空图像”是什么意思?您的脚本将图像集合过滤到没有图像的日期,这会产生包含 0 个图像的图像集合:https: https://code.earthengine.goog…...

Leetcode 69.x的平方根

给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 注意&#xff1a;不允许使用任何内置指数函数和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0.5 。 示例 1&#xff1…...

网页制作公司/做seo排名

var bg document.getElementById(xxb);var ctx bg.getContext(2d);//第一个圆圈ctx.beginPath();ctx.lineWidth10; //圆圈的边线的大小ctx.strokeStyleRGB(43,0,155); //圆圈的颜色ctx.arc(60,60,50,0*Math.PI,2.25*Math.PI,false); // 圆圈的 水平位置 、 垂直位置 、 圆圈的…...

成品网站1688特色/温州seo网站建设

爽爽子他妈的烦死了&#xff0c;这代码到底谁写的&#xff0c;我怎么都看不懂&#xff1f;他妈的烦死了&#xff0c;我怎么知道看Git提交日志&#xff1f;他妈的烦死了&#xff0c;Git日志显示是我提交的就是我写的代码吗&#xff1f;我今天写了208行代码&#xff0c;好辛苦&am…...

郑州市做网站公司a汉狮/b站推广入口2023mmm

文章目录路由搭建品牌管理静态组件列表展示添加品牌与修改品牌静态页面添加品牌动态路由搭建 首先把多余的路由组件删除 接下来修改router文件夹中的路由配置。 删除多余的路由配置&#xff0c;添加新的路由配置&#xff0c;如下所示。 品牌管理 静态组件 页面布局利用…...

app的后台和网站的后台差别/iis搭建网站

如何在MainActivity中使用ReactApplicationContext,让android 可以在原生任何一处向RN上层发送通知 例如我们app启动的Activity为MainActivity&#xff0c;而MainActivity这里需要继承ReactActivity&#xff0c;然后我们来点进去看下ReactActivity具有的方法&#xff0c;除…...

做网站需要多少兆空间/b2b电子商务网站

目录 在 select 语句中查询得到的是一张二维表&#xff0c; 水平方向上看是一个个字段&#xff0c; 垂直方向上看是一条条记录。 作为面向对象的语言&#xff0c; Java 中的的对象是根据类定义创建的。类之间的引用关系可以认为是嵌套的关系。 在 mybatis 中&#xff0c; resul…...

用笔记本做网站/网店网络营销策划方案

项目管理到底需不需要懂产品&#xff1f;   项目管理对产品肯定是要有基本的了解的&#xff0c;知道有哪些功能点&#xff0c;熟悉产品的用法。这样才能对产品研发有个宏观把握&#xff0c;对计划有预估&#xff0c;执行过程中也能更好的控制风险&#xff0c;当出现变化时也可…...