[C++][第三方库][RabbitMq]详细讲解
目录
- 1.介绍
- 2.安装
- 1.RabbitMq
- 2.客户端库
- 3.AMQP-CPP 简单使用
- 1.介绍
- 2.使用
- 4.类与接口
- 1.Channel
- 2.ev
- 5.使用
- 1.publish.cc
- 2.consume.cc
- 3.makefile
1.介绍
RabbitMQ
:消息队列组件,实现两个客户端主机之间消息传输的功能(发布&订阅)- 核心概念:交换机、队列、绑定、消息
- 交换机类型:
- 广播交换:当交换机收到消息,则将消息发布到所有绑定的队列中
- 直接交换:根据消息中的
bkey
与绑定的rkey
对比,一致则放入队列 - 主题交换:使用
bkey
与绑定的rkey
进行规则匹配,成功则放入队列
2.安装
1.RabbitMq
- 安装:
sudo apt install rabbitmq-server
- 简单使用:
# 安装完成的时候默认有个用户guest,但是权限不够,要创建一个administrator用户,才可以做为远程登录和发表订阅消息#添加用户 sudo rabbitmqctl add_user root <PASSWORD>#设置用户tag sudo rabbitmqctl set_user_tags root administrator #设置用户权限 sudo rabbitmqctl set_permissions -p / root "." "." ".*" # RabbitMQ自带了web管理界面, 执行下面命令开启, 默认端口15672 sudo rabbitmq-plugins enable rabbitmq_management
2.客户端库
- C语言库
- C++库
sudo apt install libev-dev #libev 网络库组件 git clone https://github.com/CopernicaMarketingSoftware/AMQP-CPP.git cd AMQP-CPP/ make make install
- 如果安装时出现以下报错,则表示
ssl
版本出现问题/usr/include/openssl/macros.h:147:4: error: #error "OPENSSL_API_COMPAT expresses an impossible API compatibility level" 147 | # error "OPENSSL_API_COMPAT expresses an impossible API compatibility level" | ^~~~~ In file included from /usr/include/openssl/ssl.h:18, from linux_tcp/openssl.h:20, from linux_tcp/openssl.cpp:12: /usr/include/openssl/bio.h:687:1: error: expected constructor, destructor, or type conversion before ‘DEPRECATEDIN_1_1_0’ 687 | DEPRECATEDIN_1_1_0(int BIO_get_port(const char *str, unsigned short *port_ptr))
- 解决方案:卸载当前的
ssl
库,重新进行修复安装dpkg -l | grep ssl sudo dpkg -P --force-all libevent-openssl-2.1-7 sudo dpkg -P --force-all openssl sudo dpkg -P --force-all libssl-dev sudo apt --fix-broken install
3.AMQP-CPP 简单使用
1.介绍
AMQP-CPP
是用于与RabbitMq
消息中间件通信的C++库- 它能解析从
RabbitMq
服务发送来的数据,也可以生成发向RabbitMq
的数据包 AMQP-CPP
库不会向RabbitMq
建立网络连接,所有的网络IO由用户完成
- 它能解析从
AMQP-CPP
提供了可选的网络层接口,它预定义了TCP
模块,用户就不用自己实现网络IO,- 也可以选择
libevent、libev、libuv、asio
等异步通信组件, 需要手动安装对应的组件
- 也可以选择
AMQP-CPP
完全异步,没有阻塞式的系统调用,不使用线程就能够应用在高性能应用中- 注意:它需要C++17的支持
2.使用
AMQP-CPP
的使用有两种模式:- 使用默认的
TCP
模块进行网络通信 - 使用扩展的
libevent、libev、libuv、asio
异步通信组件进行通信
- 使用默认的
- 此处以
libev
为例,不需要自己实现monitor
函数,可以直接使用AMQP::LibEvHandler
4.类与接口
1.Channel
channel
是一个虚拟连接,一个连接上可以建立多个通道- 并且所有的
RabbitMq
指令都是通过channel
传输- 所以连接建立后的第一步,就是建立
channel
- 所以连接建立后的第一步,就是建立
- 因为所有操作是异步的,所以在
channel
上执行指令的返回值并不能作为操作执行结果- 实际上它返回的是
Deferred
类,可以使用它安装处理函数
- 实际上它返回的是
- 并且所有的
namespace AMQP
{ /** * Generic callbacks that are used by many deferred objects */ using SuccessCallback = std::function<void()>; using ErrorCallback = std::function<void(const char *message)>; using FinalizeCallback = std::function<void()>;/** * Declaring and deleting a queue */ using QueueCallback = std::function<void(const std::string &name, uint32_t messagecount, uint32_t consumercount)>;using DeleteCallback = std::function<void(uint32_t deletedmessages)>; using MessageCallback = std::function<void(const Message &message, uint64_t deliveryTag, bool redelivered)>; // 当使用发布者确认时,当服务器确认消息已被接收和处理时,将调用AckCallback using AckCallback = std::function<void(uint64_t deliveryTag, bool multiple)>;// 使用确认包裹通道时,当消息被ack/nacked时,会调用这些回调 using PublishAckCallback = std::function<void()>; using PublishNackCallback = std::function<void()>; using PublishLostCallback = std::function<void()>; // 信道类class Channel { Channel(Connection *connection); bool connected();/** *声明交换机 *如果提供了一个空名称,则服务器将分配一个名称。 *以下flags可用于交换机: * *-durable 持久化,重启后交换机依然有效 *-autodelete 删除所有连接的队列后,自动删除交换 *-passive 仅被动检查交换机是否存在 *-internal 创建内部交换 * *@param name 交换机的名称 *@param-type 交换类型 enum ExchangeType { fanout, 广播交换,绑定的队列都能拿到消息 direct, 直接交换,只将消息交给routingkey一致的队列 topic, 主题交换,将消息交给符合bindingkey规则的队列 headers, consistent_hash, message_deduplication }; *@param flags 交换机标志 *@param arguments其他参数 * *此函数返回一个延迟处理程序。可以安装回调 using onSuccess(), onError() and onFinalize() methods. */ Deferred &declareExchange(const std::string_view &name,ExchangeType type,int flags,const Table &arguments);/** *声明队列 *如果不提供名称,服务器将分配一个名称。 *flags可以是以下值的组合: * *-durable 持久队列在代理重新启动后仍然有效 *-autodelete 当所有连接的使用者都离开时,自动删除队列 *-passive 仅被动检查队列是否存在 *-exclusive 队列仅存在于此连接,并且在连接断开时自动删除 * *@param name 队列的名称 *@param flags 标志组合 *@param arguments 可选参数 * *此函数返回一个延迟处理程序。可以安装回调 *使用onSuccess()、onError()和onFinalize()方法。 * Deferred &onError(const char *message) * *可以安装的onSuccess()回调应该具有以下签名: void myCallback(const std::string &name, uint32_t messageCount, uint32_t consumerCount); 例如: channel.declareQueue("myqueue").onSuccess( [](const std::string &name, uint32_t messageCount, uint32_t consumerCount) { std::cout << "Queue '" << name << "' "; std::cout << "has been declared with "; std::cout << messageCount; std::cout << " messages and "; std::cout << consumerCount; std::cout << " consumers" << std::endl; * }); */ DeferredQueue &declareQueue(const std::string_view &name,int flags,const Table &arguments);/** *将队列绑定到交换机 * *@param exchange 源交换机 *@param queue 目标队列 *@param routingkey 路由密钥 *@param arguments 其他绑定参数 * *此函数返回一个延迟处理程序。可以安装回调 *使用onSuccess()、onError()和onFinalize()方法。 */ Deferred &bindQueue(const std::string_view &exchange,const std::string_view &queue,const std::string_view &routingkey,const Table &arguments);/** *将消息发布到exchange*您必须提供交换机的名称和路由密钥。 然后,RabbitMQ将尝试将消息发送到一个或多个队列。 使用可选的flags参数,可以指定如果消息无法路由到队列时应该发生的情况。默认情况下,不可更改的消息将被静默地丢弃。 * *如果设置了'mandatory'或'immediate'标志, 则无法处理的消息将返回到应用程序。 在开始发布之前,请确保您已经调用了recall()-方法, 并设置了所有适当的处理程序来处理这些返回的消息。 * *可以提供以下flags: * *-mandatory 如果设置,服务器将返回未发送到队列的消息 *-immediate 如果设置,服务器将返回无法立即转发给使用者的消息。 *@param exchange要发布到的交易所 *@param routingkey路由密钥 *@param envelope要发送的完整信封 *@param message要发送的消息 *@param size消息的大小 *@param flags可选标志 */ bool publish(const std::string_view &exchange,const std::string_view &routingKey,const std::string &message,int flags = 0);/** *告诉RabbitMQ服务器已准备好使用消息-也就是 订阅队列消息 * *调用此方法后,RabbitMQ开始向客户端应用程序传递消息。 consumer tag是一个字符串标识符, 如果您以后想通过channel::cancel()调用停止它, 可以使用它来标识使用者。 *如果您没有指定使用者tag,服务器将为您分配一个。 * *支持以下flags: * *-nolocal 如果设置了,则不会同时消耗在此通道上发布的消息 *-noack 如果设置了,则不必对已消费的消息进行确认 *-exclusive 请求独占访问,只有此使用者可以访问队列 * *@param queue 您要使用的队列 *@param tag 将与此消费操作关联的消费者标记 *@param flags 其他标记 *@param arguments其他参数 * *此函数返回一个延迟处理程序。 可以使用onSuccess()、onError()和onFinalize()方法安装回调可以安装的onSuccess()回调应该具有以下格式: void myCallback(const std::string_view&tag); 样例: channel.consume("myqueue").onSuccess( [](const std::string_view& tag) { std::cout << "Started consuming under tag "; std::cout << tag << std::endl; }); */ DeferredConsumer &consume(const std::string_view &queue,const std::string_view &tag,int flags,const Table &arguments);/** *确认接收到的消息 **消费者客户端对收到的消息进行确认应答**当在DeferredConsumer::onReceived()方法中接收到消息时, 必须确认该消息, 以便RabbitMQ将其从队列中删除(除非使用noack选项消费)* *支持以下标志: * *-多条确认多条消息:之前传递的所有未确认消息也会得到确认 * *@param deliveryTag 消息的唯一delivery标签 *@param flags 可选标志 *@return bool */ bool ack(uint64_t deliveryTag, int flags=0);};class DeferredConsumer { /* 注册一个回调函数,该函数在消费者启动时被调用void onSuccess(const std::string &consumertag) */ DeferredConsumer &onSuccess(const ConsumeCallback& callback);/* 注册回调函数,用于接收到一个完整消息的时候被调用 void MessageCallback(const AMQP::Message &message, uint64_t deliveryTag, bool redelivered) */ DeferredConsumer &onReceived(const MessageCallback& callback);/* Alias for onReceived() */ DeferredConsumer &onMessage(const MessageCallback& callback);/* 注册要在服务器取消消费者时调用的函数 void CancelCallback(const std::string &tag) */ DeferredConsumer &onCancelled(const CancelCallback& callback);};class Message : public Envelope{ const std::string &exchange();const std::string &routingkey();};class Envelope : public MetaData{ const char *body(); // 获取消息正文uint64_t bodySize(); // 获取消息正文大小};
}
2.ev
typedef struct ev_async
{ EV_WATCHER (ev_async);EV_ATOMIC_T sent; /* private */
}ev_async; //break type
enum
{ EVBREAK_CANCEL = 0, /* undo unloop */ EVBREAK_ONE = 1, /* unloop once */ EVBREAK_ALL = 2 /* unloop all loops */
}; // 实例化并获取IO事件监控接口句柄
struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0));
# define EV_DEFAULT ev_default_loop (0) // 开始运行IO事件监控, 这是一个阻塞接口
int ev_run (struct ev_loop *loop);/* break out of the loop */
// 结束IO监控
// 如果在主线程进行ev_run(), 则可以直接调用,
// 如果在其他线程中进行ev_run(), 需要通过异步通知进行
void ev_break (struct ev_loop *loop, int32_t break_type) ; void (*callback)(struct ev_loop *loop, ev_async *watcher, int32_t revents);// 初始化异步事件结构, 并设置回调函数
void ev_async_init(ev_async *w, callback cb);
// 启动事件监控循环中的异步任务处理
void ev_async_start(struct ev_loop *loop, ev_async *w);
// 发送当前异步事件到异步线程中执行
void ev_async_send(struct ev_loop *loop, ev_async *w);
5.使用
1.publish.cc
#include <ev.h>
#include <amqpcpp.h>
#include <amqpcpp/libev.h>
#include <openssl/ssl.h>
#include <openssl/opensslv.h>int main()
{// 1.实例化底层网络通信框架的IO事件监控句柄auto *loop = EV_DEFAULT;// 2.实例化libEvHandler句柄 -> 将AMQP框架与事件监控关联起来AMQP::LibEvHandler handler(loop);// 3.实例化连接对象AMQP::Address address("amqp://root:SnowK8989@127.0.0.1:5672/");AMQP::TcpConnection connection(&handler, address);// 4.实例化信道对象AMQP::TcpChannel channel(&connection);// 5.声明交换机channel.declareExchange("test-exchange", AMQP::ExchangeType::direct).onError([](const char *message){ std::cout << "声明交换机失败: " << message << std::endl; }).onSuccess([](){ std::cout << "test-exchange 交换机创建成功" << std::endl; });// 6.声明队列channel.declareQueue("test-queue").onError([](const char *message){ std::cout << "声明队列失败: " << message << std::endl; }).onSuccess([](){ std::cout << "test-queue 队列创建成功" << std::endl; });// 7.针对交换机和队列进行绑定channel.bindQueue("test-exchange", "test-queue", "test-queue-key").onError([](const char *message){ std::cout << "test-exchange - test-queue 绑定失败: " \<< message << std::endl; }).onSuccess([](){ std::cout << "test-exchange - test-queue 绑定成功" << std::endl; });// 8.向交换机发布消息for (int i = 0; i < 5; ++i){std::string msg = "Hello SnowK-" + std::to_string(i);if(channel.publish("test-exchange", "test-queue-key", msg) == false){std::cout << "publish 失败" << std::endl;}}// 9.启动底层网络通信框架 -> 开启IOev_run(loop, 0);return 0;
}
2.consume.cc
#include <ev.h>
#include <amqpcpp.h>
#include <amqpcpp/libev.h>
#include <openssl/ssl.h>
#include <openssl/opensslv.h>void MessageCB(AMQP::TcpChannel* channel, const AMQP::Message& message, uint64_t deliveryTag, bool redelivered)
{std::string msg;msg.assign(message.body(), message.bodySize());// 不能这样使用, AMQP::Message后面没有存'\0'// std::cout << message << std::endl std::cout << msg << std::endl;channel->ack(deliveryTag);
}int main()
{// 1.实例化底层网络通信框架的IO事件监控句柄auto *loop = EV_DEFAULT;// 2.实例化libEvHandler句柄 -> 将AMQP框架与事件监控关联起来AMQP::LibEvHandler handler(loop);// 3.实例化连接对象AMQP::Address address("amqp://root:SnowK8989@127.0.0.1:5672/");AMQP::TcpConnection connection(&handler, address);// 4.实例化信道对象AMQP::TcpChannel channel(&connection);// 5.声明交换机channel.declareExchange("test-exchange", AMQP::ExchangeType::direct).onError([](const char *message){ std::cout << "声明交换机失败: " << message << std::endl; }).onSuccess([](){ std::cout << "test-exchange 交换机创建成功" << std::endl; });// 6.声明队列channel.declareQueue("test-queue").onError([](const char *message){ std::cout << "声明队列失败: " << message << std::endl; }).onSuccess([](){ std::cout << "test-queue 队列创建成功" << std::endl; });// 7.针对交换机和队列进行绑定channel.bindQueue("test-exchange", "test-queue", "test-queue-key").onError([](const char *message){ std::cout << "test-exchange - test-queue 绑定失败: " \<< message << std::endl; }).onSuccess([](){ std::cout << "test-exchange - test-queue 绑定成功"; });// 8.订阅消息对垒 -> 设置消息处理回调函数auto callback = std::bind(MessageCB, &channel, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);channel.consume("test-queue", "consume-tag").onReceived(callback).onError([](const char *message){ std::cout << "订阅 test-queue 队列消息失败: " << message << std::endl;exit(0); });// 9.启动底层网络通信框架 -> 开启IOev_run(loop, 0);return 0;
}
3.makefile
all: publish consume
publish: publish.ccg++ -o $@ $^ -lamqpcpp -lev -std=c++17
consume: consume.ccg++ -o $@ $^ -lamqpcpp -lev -std=c++17.PHONY:clean
clean:rm publish consume
相关文章:
[C++][第三方库][RabbitMq]详细讲解
目录 1.介绍2.安装1.RabbitMq2.客户端库 3.AMQP-CPP 简单使用1.介绍2.使用 4.类与接口1.Channel2.ev 5.使用1.publish.cc2.consume.cc3.makefile 1.介绍 RabbitMQ:消息队列组件,实现两个客户端主机之间消息传输的功能(发布&订阅)核心概念࿱…...
Next.js 详解
Next.js是一个基于React的开源JavaScript框架,由Vercel(原Zeit)公司开发。它旨在简化React应用的构建过程,并提供了一系列强大的功能来优化性能和开发体验。以下是对Next.js的详细解析: 一、核心特性 服务器端渲染&…...

pygame--超级马里奥(万字详细版)
超级马里奥点我下载https://github.com/marblexu/PythonSuperMario 1.游戏介绍 小时候的经典游戏,代码参考了github上的项目Mario-Level-1,使用pygame来实现,从中学习到了横版过关游戏实现中的一些处理方法。原项目实现了超级玛丽的第一个小…...
【运维】nginx静态代理资源403权限问题
如题,遇到静态代理资源访问403,可以尝试检查其文件权限,父目录权限,需要确保 "目录使用标准的 755,对文件使用 644(umask:022)" 参考资料: 1. nginx “403 …...

java家政预约上门系统源码,家政服务平台源码,基于SpringBoot框架,数据库使用MySQL,界面渲染采用Thymeleaf技术开发
自主知识产权的家政预约上门系统源码,java版本,支持二次开发,适合商用上项目。 在这个快节奏的现代生活中,越来越多的家庭开始寻求高效、便捷的家政服务解决方案。传统的家政服务模式已经很难满足人们日益增长的个性化与即时性需求…...

算法知识点————贪心
贪心:只考虑局部最优解,不考虑全部最优解。有时候得不到最优解。 DP:考虑全局最优解。DP的特点:无后效性(正在求解的时候不关心前面的解是怎么求的); 二者都是在求最优解的,都有最优…...
python数据分析
Python是一种非常流行的编程语言,尤其在数据分析领域。Python拥有丰富的库和框架,可以帮助你执行各种数据分析任务。Python常用的数据分析工具之一:NumPy。 Numpy用于进行大规模数值和矩阵运算,提供了多维数组对象和一系列操作这…...

UGUI(现成组合控件)
Drop Down Scroll View Scroll Bar size是滚动条的填充程度 Slider 如果设置为静态,那么传入的值始终为自己设置的那个值 Input Field content type为standard时 可以设置line type, 只读不改,就是可以复制,但是你已经不能输入了…...

软件交付体系文件(Word源资料)
软件文档交付清单是指在软件开发项目完成后,开发团队需要准备的一份详细清单,用于确保交付的软件产品符合客户需求并达到预期的质量标准。以下是软件文档交付清单中可能包含的一些关键要素 软件全套资料部分文档清单: 工作安排任务书…...

【视频目标分割-2024CVPR】Putting the Object Back into Video Object Segmentation
Cutie 系列文章目录1 摘要2 引言2.1背景和难点2.2 解决方案2.3 成果 3 相关方法3.1 基于记忆的VOS3.2对象级推理3.3 自动视频分割 4 工作方法4.1 overview4.2 对象变换器4.2.1 overview4.2.2 Foreground-Background Masked Attention4.2.3 Positional Embeddings 4.3 Object Me…...
掌握 C# 文件和输入输出操作
在任何编程语言中,文件和输入输出操作(I/O)都是非常重要的组成部分。C# 提供了一系列工具和类来帮助开发者处理文件的读取、写入、二进制文件的处理以及数据的序列化与反序列化。本文将介绍 C# 中的文件操作,包括 File 类、Stream…...

k8s 中的金丝雀发布(灰度发布)
目录 1 什么是金丝雀发布 2 Canary 发布方式 3 Canary 两种发布方式实操 3.1 准备工作 3.1.1 将 nginx 命名两个版本 v1 与 v2 3.1.2 暴露端口并指定微服务类型 3.1.3 进入 pod 修改默认发布文件 3.1.4 测试 service 是否正常 3.2 基于权重的灰度发布 3.2.1 创建 Igress 资源类…...
《IDEA:让编程效率翻倍的强大工具》
哪个编程工具让你的工作效率翻倍? 在众多编程工具中,IntelliJ IDEA 无疑是一款让我的工作效率得到显著提升的利器。 一、功能特点 智能代码补全 IDEA 的代码补全功能极其智能。它不仅能根据你输入的前缀快速列出可能的代码选项,还会根据上…...
Docker 部署 Prometheus+Grafana 监控系统快速指南
Docker 部署 PrometheusGrafana 监控系统快速指南 文章目录 Docker 部署 PrometheusGrafana 监控系统快速指南一 创建网络二 监控部署三 配置 prometheus.yml四 测试部署是否成功五 Grafana表盘下载 本文详细介绍了通过 Docker 和 Docker Compose 快速部署 Prometheus 和 Grafa…...

No.8 笔记 | SQL 查询语句:数据探索的钥匙
2024/10/7 心记 - 致在路上默默奋斗的你 在当今数字化的时代,网络安全已成为我们生活中不可或缺的一部分。它如同守护数字世界的隐形盾牌,保护着我们的隐私、数据和整个社会的稳定运行。 学习网络安全,是踏上一段充满挑战与机遇的征程。 每一…...

全局数据在Python包中模块间管理方法探讨
在开发大型 Python 应用程序时,有时需要多个模块共享和管理全局数据。如何优雅地在 Python 包内的不同模块间共享全局数据是一个常见的设计问题。我们希望避免全局变量的混乱和难以维护的代码,但同时能够安全、高效地管理这些共享数据。 下面我们将探讨…...

无人机在矿业领域的应用!
矿区测绘与建模 无人机可以快速、全面地获取矿区的地形地貌数据,生成高精度的二维或三维模型。 这些模型可用于矿区的规划、设计、监测和管理,提高矿山的生产效率。 库存量量化监测 无人机能够捕捉厘米级的地形数据,通过计算得出准确的库…...

基于JavaWeb开发的java springmvc+mybatis学生考试系统设计和实现
基于JavaWeb开发的java springmvcmybatis学生考试系统设计和实现 🍅 作者主页 网顺技术团队 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 文末获取源码联系方式 📝 🍅 查看下方微信号获取联系方式 承接各…...

【CKA】四、etcd的备份与恢复
4、etcd的备份与恢复 1. 考题内容: 2. 答题思路: 1、ssh到有etcdctl、etcdutl命令的节点 2、备份时注意添加证书并保证路径正确 3、备份完可以验证下 4、恢复备份时要停服务,恢复备份后重启kubelet 题型是一样的,我考的证书的路…...

基于Arduino的SG90舵机驱动
一.SG90舵机引脚说明 SG90舵机三根线的连接方法: 1.红色线:电源线(VCC),接入5v电源 2.棕色线:地线(GND),接地 3.黄色线:信号线(SIG)…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...