RabbitMQ 消息队列
1. 消息队列是什么?
当用户注册成功后,就发送邮件。当邮件发送成功了,接口才会提示注册成功信息。但由于发送邮件,依赖于其他厂商的服务,有可能他们的接口会非常耗时。那么用户就一直要等着邮件发送成功了,才提示注册成功。因为耗时很长,这样就会造成很糟糕的体验。
然后大家想一想,我们到底有没有必要等着邮件发送完成呢?就好比大家去邮局寄信,当你把信丢入邮筒里,你需要一直等着信送到别人手里,然后你再离开吗?
当然不需要,只要将信丢入邮筒,我们人就可以离开了。因为你知道,在将来的某个时候,你的信就会被寄到收件人手里。
消息队列
,就好比是这里寄信的过程!消息队列
由三部分构成:
- 生产者(Producer):创建并发送消息,相当于去邮局寄信的人。
- 队列(Queue):用来存放消息的,将消息从生产者传递到消费者,就相当于邮局了。邮局收到你的任务后,它去处理是需要排队的。按照先进先出的原则,需要等其他先来的任务处理完了,才会处理你的任务。
- 消费者(Consumer):接收并处理消息的人。当邮局把信送到他手里了,收到消息了,最终处理任务的人。
还是以我们注册功能为例。当用户注册成功了,我们就通知RabbitMQ,你去发个邮件,然后就直接提示用户注册成功。我们完全没有必要等待邮件发送完成。
接着再写一个程序去接收消息,当它收到消息了,就去执行发邮件任务了。
所以,生产者、消费者,都是我们要写的程序。一个负责提交任务,另一个负责处理任务。互相之间通信,就靠中间的RabbitMQ。
2. 安装
2.1. 使用 Docker
当然,我最推荐的还是用Docker
,因为太方便了,大家打开docker-compose.yml
。增加RabbitMQ
的配置,注意缩进非常重要,一定要和之前的MySQL和Redis的配置对齐
services:mysql:# ....redis:# ....rabbitmq:image: rabbitmq:4.0-managementports:- "5672:5672"- "15672:15672"volumes:- ./data/rabbitmq:/var/lib/rabbitmq # 持久化数据environment:RABBITMQ_DEFAULT_USER: adminRABBITMQ_DEFAULT_PASS: xw
- 我们这里使用的
RabbitMQ
版本号是:4.0
。 - 将RabbitMQ运行的默认端口
5672
和15672
,映射到本机上。 - 其中
15672
是RabbitMQ自带的管理后台端口号
。 - 接着将
RabbitMQ
的用来持久化数据的备份数据
,映射
到项目根目录的data/rabbitmq
目录中。 - 最下面的是
RabbitMQ
用来连接的账号密码,大家可以根据需要自己设置。
完成后,命令行,再次运行
docker-compose up -d
这样Docker,就会自动下载,并启动好RabbitMQ。
2.2. 直接安装
对于部分Windows
上用不了Docker
的同学,那就有点麻烦了。因为既要安装Erlang
,又要安装RabbitMQ
。这里附上下载地址:
-
Erlang下载地址:https://www.erlang.org/downloads
- -
RabbitMQ下载地址:https://www.rabbitmq.com/docs/install-windows#downloads
-
对于这种安装方式,如果在安装过程出现了各种问题,请来讨论群中和我讨论。总而言之,我还是推荐大家尽可能,想办法将Docker
跑通。
2.3.1 安装完Erlang
和RabbitMQ
后,添加环境变量
打开环境变量设置窗口: 右键点击 “
此电脑
”,选择 “属性
”。 在弹出的窗口中,点击 “高级系统设置
”。 在 “系统属性
” 窗口中,点击 “环境变量
” 按钮。 添加RabbitMQ
命令路径到系统的 Path 变量
: 在 “系统变量
” 列表中找到 “Path
” 变量,选中它
并点击 “编辑
” 按钮。 在弹出的 “编辑环境变量
” 窗口中,点击 “新建
”,然后将RabbitMQ 的 sbin目录路径
添加进去,
例如C:\Program Files\RabbitMQServer\rabbitmq_server-<version>\sbin
。注意要将 替换为你实际安装的 RabbitMQ 版本号。 点击 “确定
” 保存设置,依次关闭所有窗口具体选择你自己安装的路径
>。
2.3.2 通过命令行(管理员)安装rabbitmq_management 插件
RabbitMQ 的管理界面依赖于rabbitmq_management 插件
rabbitmq-plugins enable rabbitmq_management
2.3.3 通过命令行(管理员)运行
rabbitmq-service start
2.3. RabbitMQ 管理后台
启动好后,稍等个几秒钟,用浏览器访问:http://127.0.0.1:15672/
,就能进入RabbitMQ
的管理界面了
以游客模式登录 账号:guest 密码:guest
2.3.2.1 新增账号密码
输入刚才docker-compose.yml
里的设置账号密码,就能进去了。里面的东西很多,先不用管它,一会儿再来看。
3. 官方案例解析
学习任何东西之前,最好的办法,无疑就是看官方文档了。
直接看JavaScript
章节的Hello World
部分。很明显,用之前先要装包
npm i amqplib
amqplib官方文档
3.1. 生产者:send.js
在public
目录里,新建一个send.js
,代码大家从讲义文档直接复制过来。
amqplib.connect(‘amqp://admin:xw@localhost
’) 参数详解
字段 | 注解 |
---|---|
amqp:// | 表示使用 AMQP 协议进行连接 |
admin | 连接到 RabbitMQ 服务器时使用的用户名,用于身份验证 |
xw | 是与用户名对应的密码,用于验证用户身份。用户名和密码通过冒号 : 分隔 |
localhost | 指定了 RabbitMQ 服务器所在的主机地址,这里表示服务器运行在本地机器上。如果 RabbitMQ 服务器部署在其他机器上,需要将 localhost 替换为实际的 IP 地址或域名 |
const amqplib = require('amqplib');(async () => {try {const connection = await amqplib.connect('amqp://admin:xw@localhost');// 创建一个通道。通道是进行通信的基本单位,通过通道可以发送和接收消息const channel = await connection.createChannel();// 队列的名字是:helloconst queue = 'hello';// 要发送的消息内容是:你好,xw!const msg = '你好,xw!';// 创建一个队列。如果队列不存在,则创建一个队列。如果队列已经存在,则不会创建新的队列// durable: 表示队列是否持久化。如果设置为true,即重启后队列不会消失await channel.assertQueue(queue, { durable: true });// 发送消息到队列// queue: 要发送消息的队列的名字// content: 要发送的消息内容// persistent: true,消息持久化,确保消息在 RabbitMQ 重启后仍然存在。channel.sendToQueue(queue, Buffer.from(msg), { persistent: true });// 打印发送提示信息console.log('[x] 发送了:%s', msg);// 500ms 后关闭连接setTimeout(() => {connection.close();process.exit(0);}, 500);} catch (error) {console.log(error);}
})();
我们一点点分析下,它这里都写了些什么东西。
1、连接 const connection = await amqplib.connect(‘amqp://admin:clwy1234@localhost’)
2、创建一个通道。通道是进行通信的基本单位,只有通过通道,才可以发送和接收消息
3、我们定义了队列的名字是:hello。并定义了要发送的消息内容是:你好,xw!
4、创建了一个队列。但如果队列已经存在,那就直接使用,不会创建新的了。这里要把队列的名字放进去,也就是hello
这里还有个参数:durable: false
,它表示队列是否持久化
false:不持久化,true:队列的信息会被写入磁盘,也就是docker-compose里设置的./data/rabbitmq,当RabbitMQ服务重启后,队列会被自动恢复
。
5、channel.sendToQueue
,很明显,这就是将消息发送到队列了。
接着做了个console.log提示,可以显示在命令行里,便于我们观察。
最后面,等500ms后,将连接断开
看完后,我们就先将它跑起来,看看到底有个什么效果。用cd命令,进入public目录中。然后直接用node运行这个文件
cd public
node send.js
打开RabbitMQ的管理后台:http://127.0.0.1:15672/
里面的菜单,依次是:
- 概览:Overview
- 连接:Connections
- 通道:Channels
- 交换机:Exchanges
- 队列和流:Queues and Streams
- 管理用户:Admin
由于代码里,在500ms之后,就将连接关闭了。所以连接和通道里,都看不到东西。大家点击队列这里,里面可以看到hello队列了。这正是我们刚才创建的队列名字。
点进去看,里面可以看到队列的一些详细内容。
-
上面有一些统计信息。一共收到了一条信息,准备好的有几条,在内存中的又几条,占有了多少空间、多少内存什么的。
-
再往下看,注意这里有个
发送消息(Publish message)
和获取消息(Get message)
。 -
我们先点
获取消息
,这里就能看到刚才发送的:你好,xw
4.2. receive.js
刚才是生产者的代码
,我们接着继续看消费者的代码
。在public目录中新建receive.js
,
const amqplib = require('amqplib');(async () => {try {// 连接到 RabbitMQconst connection = await amqplib.connect('amqp://admin:clwy1234@localhost');// 创建一个通道。通道是进行通信的基本单位,通过通道可以发送和接收消息const channel = await connection.createChannel();// 队列的名字是:helloconst queue = 'hello';// 创建一个队列。如果队列不存在,则创建一个队列。如果队列已经存在,则不会创建新的队列// durable: 表示队列是否持久化。如果设置为true,即重启后队列不会消失await channel.assertQueue(queue, { durable: true });// 打印等待接收消息的提示信息console.log(' [*] 等待接收消息在 %s 队列中. 按 CTRL+C 退出', queue);// 当接收到消息channel.consume(queue, (msg) => {// 打印接收到的消息内容console.log('[x] 接收到了:%s', msg.content.toString());// 如果不是自动确认,需要手动确认消息// channel.ack(msg);}, {// noAck: 表示是否自动确认消息,设置为true表示自动确认,设置为false表示手动确认// 如果设置为false,需要手动确认消息,否则消息会被重复消费。例如:channel.ack(msg)noAck: true});} catch (error) {console.log(error);}
})();
我们来运行一下,
node receive.js
再回去刷新页面,也多刷新个几次。因为刚才的两条消息都已经处理完成了,所以现在的准备好的(Ready)
和在内存中的(In memory)
都是0
了。
再点击连接(Connections)
,因为我们这次的消费者代码
,并没有关闭连接
,所以一直连在RabbitMQ上
。
点击通道(Channels)
,里面也能看到创建的通道。
4. noAck 自动、手动确认消息
关于自动确认消息,大家可以改为
noAck: false
ack(确认) | 当消费者成功处理完一条消息后,会向 RabbitMQ 服务器发送一个确认信号(ack),告知服务器该消息已经被成功消费,服务器可以将该消息从队列中移除 |
---|---|
nack(否定确认) | 当消费者处理消息失败或者遇到某些异常情况时,会向 RabbitMQ 服务器发送一个否定确认信号(nack),表示消息处理未成功。根据不同的配置,服务器会对该消息进行不同的处理,比如重新入队、丢弃等。 |
channel.nack(message, multiple, requeue); 参数
参数 | 注释 |
---|---|
message | 该参数代表需要进行否定确认的消息对象,也就是消费者从队列中接收到的消息。这个消息对象包含了消息的内容、属性等信息 |
multiple | 这是一个布尔类型的参数,用于指定是否批量否定确认消息。当 multiple 为 true 时,表示会将当前消费者已经接收到但还未确认(包括 ack 和 nack)的所有消息进行否定确认。这在需要一次性处理多个未确认消息时很有用,可以提高处理效率。当 multiple 为 false 时,仅对当前传入的这一条消息进行否定确认。 |
requeue | 同样是布尔类型的参数,用于控制消息在被否定确认后的去向。当 requeue 为 true 时,消息会被重新放回原队列中,通常会被放置在队列的尾部,之后可能会再次被分发给其他消费者或者当前消费者(取决于 RabbitMQ 的分发策略)。这种方式适用于消息处理失败是由于临时原因导致的情况,例如网络抖动、数据库短暂不可用等,让消息有机会被重新处理。当 requeue 为 false 时,消息不会被重新放回原队列,而是会被丢弃或者根据死信队列(Dead Letter Queue,DLQ)的配置进行处理。死信队列用于存储那些无法被正常消费的消息,方便后续分析和处理 |
这样就需要手动确认消息了,我们先不确认。将接收消息的终端重启一下,重新发送一次,可以看到收到信息了。
按CTRL+C
停止后,再次接收消息,发现还能接收到这个消息。这就错了,按道理消息被处理完成后,就不应该重复收到了。
这就是因为,由于我们没有手动确认,所以RabbitMQ
,认为消息一直没有被处理成功,就会不断的发过来,让你处理。
加上
// 当接收到消息
channel.consume(queue, function (msg) {console.log('[x] 接收到了:%s', msg.content.toString());// 如果不是自动确认,需要手动确认消息channel.ack(msg);}, {noAck: false
});
CTRL+C
停止后,重新连接,再接收一次。现在的代码里,已经将消息确认了。再次按CTRL+C
停止,再连接上去,就不会收到重复的消息
了。
那么ack
设计的目的,是让你有更高的灵活度。因为有些任务的执行,有可能失败,需要重试。我这里有个代码示例,大家可以参考一下。
// 模拟处理逻辑
channel.consume(queue, function (msg) {try {// 假设处理成功,手动确认消息channel.ack(msg);} catch (error) {// 处理失败,可以选择拒绝消息,将消息从队列中删除// nack:否定确认):当消费者处理消息失败或者遇到某些异常情况时,会向 RabbitMQ 服务器发送一个否定确认信号(nack),表示消息处理未成功。根据不同的配置,服务器会对该消息进行不同的处理,比如重新入队、丢弃等。channel.nack(msg, false, false);console.error(error);}
}, {noAck: false
});
总结
1、消息队列的组成
基础的消息队列,由三部分组成:生产者、队列和消费者。
名称 | 说明 |
---|---|
生产者 | 将消息发送到队列 |
队列 | 临时存储消息,按照先进先出的原则,在生产者和消费者之间传递消息 |
消费者 | 监听队列,收到消息后进行处理。 |
2、RabbitMQ 的方法
方法 | 说明 |
---|---|
const connection = amqplib.connect | 连接到 RabbitMQ |
connection.close | 关闭连接 |
const channel = connection.createChannel | 创建通道 |
channel.assertQueue | 创建队列 |
channel.sendToQueue | 发送消息 |
channel.consume | 接收消息 |
channel.ack | 确认消息,消息被成功处理。 |
channel.nack | 拒绝消息,消息处理失败。 |
3、nack的参数
参数 | 注释 |
---|---|
message | 该参数代表需要进行否定确认的消息对象,也就是消费者从队列中接收到的消息。这个消息对象包含了消息的内容、属性等信息 |
multiple | 这是一个布尔类型的参数,用于指定是否批量否定确认消息。当 multiple 为 true 时,表示会将当前消费者已经接收到但还未确认(包括 ack 和 nack)的所有消息进行否定确认。这在需要一次性处理多个未确认消息时很有用,可以提高处理效率。当 multiple 为 false 时,仅对当前传入的这一条消息进行否定确认。 |
requeue | 同样是布尔类型的参数,用于控制消息在被否定确认后的去向。当 requeue 为 true 时,消息会被重新放回原队列中,通常会被放置在队列的尾部,之后可能会再次被分发给其他消费者或者当前消费者(取决于 RabbitMQ 的分发策略)。这种方式适用于消息处理失败是由于临时原因导致的情况,例如网络抖动、数据库短暂不可用等,让消息有机会被重新处理。当 requeue 为 false 时,消息不会被重新放回原队列,而是会被丢弃或者根据死信队列(Dead Letter Queue,DLQ)的配置进行处理。死信队列用于存储那些无法被正常消费的消息,方便后续分析和处理 |
4、 RabbitMQ 的参数
字段 | 说明 |
---|---|
durable: true | 队列持久化,队列信息将被写入磁盘,在RabbitMQ服务重启后仍然存在。但是队列已经创建过后,这个参数就没法改了,可以删掉队列,重新建一个新的。 |
persistent: true | 消息持久化,消息将会被写入磁盘,在RabbitMQ服务重启后仍然存在。 |
noAck: true | 自动确认消息,只要收到了就会自动确认。如果设置成false,则需要手动确认。 |
使用RabbitMQ发送邮件
相关文章:
RabbitMQ 消息队列
1. 消息队列是什么? 当用户注册成功后,就发送邮件。当邮件发送成功了,接口才会提示注册成功信息。但由于发送邮件,依赖于其他厂商的服务,有可能他们的接口会非常耗时。那么用户就一直要等着邮件发送成功了,…...
idea从远程gitee拉取项目
文章目录 从gitee上面拿到项目地址填写远程地址,并且设置项目保存位置拉取成功 从gitee上面拿到项目地址 填写远程地址,并且设置项目保存位置 拉取成功...
PHP集成软件用哪个比较好?
在Windows环境下,使用PHP时,通常需要一个集成开发环境(IDE)或者集成软件来简化开发和调试过程。以下是几款常用且推荐的PHP集成软件,每款都有其特点,可以根据需求进行选择: 1. XAMPP 特点&…...
Es的text和keyword类型以及如何修改类型
昨天同事触发定时任务发现es相关服务报了一个序列化问题, 今天早上捕获异常将异常堆栈全部打出来看,才发现是聚合的字段不是keyword类型的问题。 到kibbna命令行执行也是一样的错误 使用 /_mapping查看索引的字段类型,才发现userUniqueid是te…...
【找工作】C++和算法复习(自用)
文章目录 C头文件自定义排序函数stl 算法数据结构树状数组 数学 自用随便记录 C 排序 stl 头文件 全能头文件: #include<bits/stdc.h>自定义排序函数 bool compare(const int &odd1,const int &odd2) {return odd1>odd2; }stl 枚举map map&…...
Python VsCode DeepSeek接入
Python VsCode DeepSeek接入 创建API key 首先进入DeepSeek官网,https://www.deepseek.com/ 点击左侧“API Keys”,创建API key,输出名称为“AI” 点击“创建",将API key保存,复制在其它地方。 在VsCode中下载…...
开放表格式和对象存储架构指南
比较 Apache Iceberg、Delta Lake 和 Apache Hudi,并了解如何为您的数据湖仓一体选择合适的开放表格式。开放表格式和对象存储正在重新定义组织构建其数据系统的方式,为可扩展、高效且面向未来的数据湖仓一体奠定了基础。通过利用对象存储的独特优势&…...
Netty入门详解
引言 Netty 是一个基于 Java 的高性能、异步事件驱动的网络应用框架,用于快速开发可维护的高性能网络服务器和客户端。它提供了一组丰富的 API,使得开发人员能够轻松地处理各种网络协议,如 TCP、UDP 等,并且支持多种编解码方式&a…...
我国首条大型无人机城际低空物流航线成功首航
首航震撼开场:羊肉 “飞” 越 540 公里 在夜色的笼罩下,榆阳马合通用机场的跑道上,一架大型固定翼无人机蓄势待发,机身被灯光照亮,宛如一只即将展翅翱翔的钢铁巨鸟。它的货舱里,满满装载着新鲜的榆林羊肉&a…...
【数据挖掘】--算法
【数据挖掘】--算法 目录:1. 缺失值和数值属性处理1缺失值处理: 2. 用于文档分类的朴素贝叶斯3. 分治法:建立决策树4. 覆盖算法建立规则5. 挖掘关联规则6. 线性模型有效寻找最近邻暴力搜索(Brute-Force Search)kd树&am…...
C++初阶——简单实现vector
目录 1、前言 2、Vector.h 3、Test.cpp 1、前言 简单实现std::vector类模板。 相较于前面的string,vector要注意: 深拷贝,因为vector的元素可能是类类型,类类型元素可以通过赋值重载,自己实现深拷贝。 迭代器失效…...
三、Three.js模型对象、材质
一、三维向量Vector3与模型位置 点模型Points、线模型Line、网格网格模型Mesh等模型对象的父类都是Object3D,如果想对这些模型进行旋转、缩放、平移等操作,如何实现,可以查询Threejs文档Object3D对相关属性和方法的介绍 1、三维向量Vector3 …...
C# 背景 透明 抗锯齿 (效果完美)
主要是通过 P/Invoke 技术调用 Windows API 函数 gdi32.dll/user32.dll,同时定义了一些结构体来配合这些 API 函数的使用,常用于处理图形绘制、窗口显示等操作。 运行查看效果 局部放大,抗锯齿效果很不错,尾巴毛毛清晰可见。 using System; u…...
Ubuntu 22.04 一键部署MinerU1.1.0
MinerU MinerU是一款将PDF转化为机器可读格式的工具(如markdown、json),可以很方便地抽取为任意格式。 MinerU诞生于书生-浦语的预训练过程中,我们将会集中精力解决科技文献中的符号转化问题,希望在大模型时代为科技发…...
10、k8s对外服务之ingress
service和ingress的作用 service的作用 NodePort:会在每个节点开放一个端口,端口号30000-32767。 也是只能用于内网访问,四层转发。实现负载均衡。不能基于域名进行访问。 clusterip:service的默认类型,只能在集群…...
mysql面试题
一、基础概念 什么是主键(Primary Key)? 答案: 唯一标识表中每行数据的字段或字段组合,不允许 NULL 值,确保数据唯一性。 外键(Foreign Key)的作用是什么? 答案…...
什么是关系型数据库?什么是非关系型数据库?
关系型数据库:关系型数据库是基于关系模型的数据库,它将数据组织成二维表格的形式,每个表格称为一个表(Table),表中的每一行称为一条记录(Record)或元组(Tuple࿰…...
科技云报到:科技普惠潮流渐起,“开源”将带我们走向何方?
科技云报到原创。 开源决定软件未来,已成为全球技术和产业创新的主导模式之一。“开源”思想的诞生,可以说是计算机发展史中极具理想主义和浪漫主义色彩的一页,是科技自由与技术极客思想的延伸。 数字化浪潮奔涌,从软件开发的底…...
校园网架构设计与部署实战
一、学习目标 掌握校园网分层架构设计原则 理解多业务VLAN规划方法 学会部署认证计费系统 实现基础网络安全防护 二、典型校园网场景 需求分析:某中学需建设新型校园网络 覆盖教学楼/宿舍/图书馆三区域 区分教师/学生/访客网络权限 满足2000终端并发接入 …...
【含开题报告+文档+PPT+源码】基于Springboot的乡村老龄居民信息管理系统
开题报告 本文介绍了一个基于Spring Boot框架的乡村老龄居民信息管理系统。该系统旨在通过信息化手段,提高乡村老龄居民的生活质量,并为相关部门提供便捷的数据管理和服务支持。系统主要实现了用户注册登录、个人信息查看、健康数据录入、健康建议查询、…...
前端插件使用xlsx-populate,花样配置excel内容,根据坐添加标替换excel内容,修改颜色,合并单元格...。
需求要求:业务人员有个非常复杂得excel表格,各种表头等,但是模板是固定得。当然也可以实现在excel上搞出各种表格,但是不如直接用已有模板替换其中要动态得内容方便,这里我们用到CSDN得 xlsx-populate 插件。 实列中我…...
Mac m1 连接公司内网
1、创建VPN 1、在系统偏好设置 2、选择网络 3、进行添加 2、添加设置 1、选择VPN 2、类型选择L2TP/IPSec 3、填写服务器IP和账号 4、点击认证设置-填写密码 。然后应用 3、进行特殊配置 网上说苹果系统的问题。 1、创建命令 sudo vim /etc/ppp/options 2、添加内容-主要别…...
Jenkins 部署在 Mac 并在局域网内通过 ip 访问
Jenkins 部署在 Mac 并在局域网内通过 ip 访问 一、修改配置文件 打开文件 ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist 打开文件 /usr/local/opt/jenkins/homebrew.mxcl.jenkins.plist 两个文件目录不同,内容一样 <?xml version"1.0" e…...
臻识相机,华夏相机,芊熠车牌识别相机加密解密
臻识,华夏,芊熠这三种车牌识别相机解密我都试过了,可以正常解密成功,其它品牌我暂时没有测试。超级简单,免费的,白嫖无敌! 流程: ①:先导出配置文件,例如我以…...
【Python 专题】数据结构 树
LeetCode 题目104. 二叉树的最大深度(gif 图解)方法一:后序遍历(DFS)方法二:层序遍历(BFS)872. 叶子相似的树(DFS 遍历)1448. 统计二叉树中好节点的数目(DFS 遍历)437. 路径总和 III(前缀和 + DFS 回溯)1372. 二叉树中的最长交错路径(DFS)236. 二叉树的最近公共…...
【飞行器原理学习】——1. 机翼及机翼参数
飞行器原理学习——1.机翼 一、 概述 飞机的各种机翼是飞机的控制面 通过铰链、钢索、液压等方式连接在机身上 操纵面运动时,会改变机翼的弧度和形状,使流经的空气发生偏转,从而影响空气动力的大小。使飞机围绕着3轴运动 二、机翼的操纵面…...
css之display:grid布局改块级元素布局
1.问题: div是块级元素,一个div元素占一行,但是,今天测试样式时,总是会有两个div并占一行,很困惑,结果发现是app这个样式 在main.css里 #app样式布局在main.ts里被应用 2.原因以及样式分析 im…...
如何组织和管理JavaScript文件:最佳实践与策略
在现代Web开发中,JavaScript已经成为不可或缺的一部分。随着项目规模的扩大,JavaScript代码的复杂性也随之增加。如何有效地组织和管理这些文件,不仅影响开发效率,还直接关系到项目的可维护性和可扩展性。本文将深入探讨如何组织和…...
Vue 中组件通信的方式有哪些,如何实现父子组件和非父子组件之间的通信?
一、父子组件通信(垂直通信) 1. Props 传值(父 → 子) 实现方案: <!-- Parent.vue --> <template><Child :user"userData" /> </template><script setup> import { ref } …...
伯克利 CS61A 课堂笔记 11 —— Mutability
本系列为加州伯克利大学著名 Python 基础课程 CS61A 的课堂笔记整理,全英文内容,文末附词汇解释。 目录 01 Objects 02 Example: Strings Ⅰ Representing Strings: the ASCII Standard Ⅱ Representing Strings: the Unicode Standard 03 Mutatio…...
MacOS安装Emacs
个人博客地址:MacOS安装Emacs | 一张假钞的真实世界 在MacOS X上可以使用Homebrew 安装Emacs: $ brew install emacs --with-cocoa 或者用MacPorts: $ sudo port install emacs-app OSX Emacs 网站提供了通用的二进制包。 前两种方法安装…...
基于海思soc的智能产品开发(图像处理的几种需求)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 对于一个嵌入式设备来说,如果上面有一个camera,那么就可以有很多的用途。简单的用途就是拍照,比拍照更多一点的…...
百度智能云AI收入增3倍,2025开源引流打赢生态战
免费、开源。在DeepSeek重塑产业价值观念后,百度、OpenAI等AI时代的“领航员”纷纷采取行动。 随着开源以雷霆万钧之势袭来,百度下定决心求变。而其底气,就藏在这份财报中。根据财报,2024年,百度集团总收入为1331亿元…...
[数据结构]顺序表详解
目录 一.线性表 二.顺序表 2.1概念及结构 1. 静态顺序表:使用定长数组存储元素。 2. 动态顺序表:使用动态开辟的数组存储。 2.1按需申请 2.2 接口实现:增删查改 SeqList.h: SeqList.c: test.c 一.线性表 线性表 ( line…...
力扣hot100 ——搜索二维矩阵 || m+n复杂度优化解法
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 解题思路: 借助行和列有序特性,不断按行或者列缩小范围;途中数字表示每…...
VirtualBox 中使用 桥接网卡 并设置 MAC 地址
在 VirtualBox 中使用 桥接网卡 并设置 MAC 地址,可以按照以下步骤操作: 步骤 1:设置桥接网卡 打开 VirtualBox,选择你的虚拟机,点击 “设置” (Settings)。进入 “网络” (Network) 选项卡。在 “适配器 1” (Adapt…...
ETL工具: Kettle入门(示例从oracle到oracle的数据导入)
kettle介绍 ETL工具,用于对数据的抽取(Extract), 转换(Transform),加载 (Load) Kettle 是一种ETL工具, 现称为 Pentaho Data Integration (PDI) 特点:纯JAVA语言编写 官方学习文档 网站: https://docs.hitachivantara.com/r/en-us/pentaho-data-int…...
未来AI方向落地场景:小语言模型,super_private_agent
未来AI方向落地场景:小语言模型,super_private_agent 目录 未来AI方向落地场景:小语言模型,super_private_agent小语言模型super - private - agent(注重隐私的智能代理)碳基生命和硅基生命交互界面面向agent的专用交互协议和数据接口从web平台经济到网络平台举例说明社交…...
Oracle RAC数据库单节点轮流重启
0、sqlplus / as sysdba 备份参数文件 create pfile/home/oracle/pfile.ora from spfile; 备份控制文件 Alter database backup controlfile to trace; 1、关闭两节点的监听; 2、操作系统层面kill掉所有LOCALNO的所有进程,即:连接会话。 p…...
渲染101对Blender的支持与硬件配置详解
一、Blender版本与渲染器支持 渲染101全面支持 Blender全版本(包括Blender 4.0及早期版本),并适配其内置的 Eevee、Cycles、Workbench 三大渲染器 。此外,平台还兼容 Aces、Realflow、Anima 等40插件,覆盖建模、动画…...
安卓burp抓包,bypass ssl pinning
好久好久没有发东西了。主要是懒。。。 这几天在搞apk渗透,遇到了burp无法抓包问题,觉得可以写下来。 问题描述 1. 一台安卓手机,装了面具,可以拿到root 2. 电脑上有burp,设置代理 3.手机和电脑连同一个网段&…...
植物大战僵尸杂交版v3.2.1最新版本(附下载链接)
B站游戏作者潜艇伟伟迷于12月21日更新了植物大战僵尸杂交版3.2.1版本!!!,有b站账户的记得要给作者三连关注一下呀! 不多废话下载链接放上: 夸克网盘链接::https://pan.quark.cn/s/e5…...
【SQL】多表查询案例
📢本章节主要学习使用SQL多表查询的案例,多表查询基础概念 请点击此处。 🎄数据准备 首先我们创建一个新的表也就是薪资等级表,其余两个表(员工表和薪资表)在多表查询章节中已经创建。然后我么根据这三个表完成下面的12个需求。 create tab…...
使用Python结合`stable-baselines3`库(包含PPO和TD3算法)以及`gym`库来实现分层强化学习的示例代码
以下是一个使用Python结合stable-baselines3库(包含PPO和TD3算法)以及gym库来实现分层强化学习的示例代码。该代码将环境中的动作元组分别提供给高层处理器PPO和低层处理器TD3进行训练,并实现单独训练和共同训练的功能。 代码实现 import g…...
解锁机器学习核心算法|神经网络:AI 领域的 “超级引擎”
一、神经网络:AI 领域的 “超级引擎” 在机器学习的庞大算法体系中,有十种算法被广泛认为是最具代表性和实用性的,它们犹如机器学习领域的 “十大神器”,各自发挥着独特的作用。这十大算法包括线性回归、逻辑回归、决策树、随机森…...
一周学会Flask3 Python Web开发-redirect重定向
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 前面我们学过渲染到模板页面,这个其实是一种内部的转发,浏览器地址栏地址没有变化。如果我们想重定向…...
FFmpeg 源码编译安装
参考: https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu Linux (Ubuntu) 下载 FFmpeg 源码,并将其解压,这里我将它放在 ~/ffmpeg_source 目录下; cd ~/ffmpeg_sources wget -O ffmpeg-snapshot.tar.bz2 https://ffmpeg.org…...
3.Docker常用命令
1.Docker启动类命令 1.启动Docker systemctl start docker 2.停止Docker systemctl stop docker 3.重启Docker systemctl restart docker 4.查看Docker状态 systemctl status docker 5.设置开机自启(执行此命令后每次Linux重启后将自启动Docker) systemctl enable do…...
vue3面试题进阶版
覆盖 Vue3 的核心知识点、高频考点及实战场景 一、基础与核心概念 MVVM 与 MVC 的区别 MVC:Model(数据)、View(视图)、Controller(控制器),视图更新需手动操作 DOM。MVVM࿱…...
深入理解指针
例题1 int main() {int a[5][5];int(*p)[4];p a;printf("%p, %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0; }由图知第二个打印的是-4 因为%p是直接将内存中的值当作地址打印 而-4在内存中存放的是其二进制的补码,打印以16进制形式…...