springCloudAlibaba之分布式事务组件---seata
Seata
- Sea学习
- 分布式事务
- Seata
- 二阶段提交协议
- AT模式
- TCC模式
- Seata服务搭建
- Seata Server(事务协调者TC)环境搭建
- seata服务搭建-db数据源
- seata服务搭建-nacos
- 启动seata服务
- 分布式事务代码搭建-client端搭建
- 接入微服务应用
Sea学习
事务:事务是访问数据库并更新数据库中各项数据的一个程序执行单元。在关系数据库中,一个事务由一组或多组SQL语句组成。事务应该具有4个属性:原子性、一致性、隔离性、持久性。例如更新商品的接口,这就是一个事务,事务是对程序而言的。
- 原子性:事务是一个不可分割的工作单元,事务中包括的诸多操作要么都做要么都不做。
- 一致性:保持数据的一致
- 隔离性:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的并发执行的各个事务之间不能互相干扰。隔离性又分为四个级别,来保证事务之间的隔离性:读未提交、读已提交、可重复读、串行化(事务串行执行,不会产生并发问题)
- 持久性:一个事务执行并提交后,就持久到数据库
本地事务:大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务。本地事务的ACID特性由数据库直接支持。
分布式事务
两种分布式事务场景:1、同一服务不同数据库;2、同一数据库不同服务;此时都会产生分布式事务的问题。此时用本地数据库事务是无法支持分布式事务的。此时用Seata处理分布式事务问题
Seata
seata的三大角色
在seata的架构中,一共有三个角色:
- TC:事务协调者
- TM:事务管理器
- RM:资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务的状态,并驱动分支事务提交或回滚。其中,TC为单独部署的server服务端,TM和RM为嵌入到应用中的client客户端。
二阶段提交协议
常见的分布式事务解决方案:
- seata阿里分布式事务框架
- 消息队列
- saga
- XA
他们都有一个共同点,都是遵循二阶段协议(2PC)。两阶段是指完成整个分布式事务,划分成两个步骤完成。
- 1、准备阶段
- 在这个阶段,事务的协调者会向所有涉及的数据库或者资源管理器发送准备请求。
- 每个数据库或者资源管理器收到准备请求后,会执行相应的操作,并且记录准备状态。如果准备成功,就向协调者发送准备就绪的ask。
- 协调者等待所有参与者都发送准备就绪的信号,如果有任何一个参与者未能准备就绪或者出现了错误,协调者将会发送回滚请求给所有参与者。
- 提交阶段
- 如果所有参与者都准备就绪,协调者会向他们发送提交请求。
- 参与者接收到提交请求后,会正式提交事务,并且释放相关资源。
- 如果有任何一个参与者未能接收到请求或者出现了错误,协调者将会发送回滚请求给所有参与者。
- 图例
- 第一阶段
- 第二阶段
就像是军训一样,准备阶段:教官让所有同学都立正站好,所有同学都得对齐站好,谁没有站好,全部同学都得重新立正,直到所有同学都准备好,然后,训练阶段:同学们都站一排齐步走,只要有一个没走齐的就重新开始;(👴已经尽力去解释了,我相信即使没有学过计算机的应该也知道这玩意是什么了,嘿嘿);看着是不是毫无压力,
AT模式
-
一阶段
befrore image:将要更新的数据查询出来。afer image:事务执行后,将执行完的数据查出来
-
二阶段
-
二阶段提交
因为一阶段已经完成了提交动作,所以二阶段如果没有异常,则直接删除before image、after image、行锁
-
二阶段回滚
根据before image拿到逆向sql回滚数据,回滚完成后删除befor image、after image、行锁
TCC模式
Seata服务搭建
Seata分TC、TM、RM三个角色,其中TC为单独服务端部署,TM和RM由业务系统引入依赖来进行集成。
Seata Server(事务协调者TC)环境搭建
seata服务搭建-db数据源
- 下载安装包
2.2.5对应的seata包时1.3.0,下载地址:https://github.com/apache/incubator-seata/releases?page=2
- 修改server端存储模式
- 打开config/file.conf
- 修改mode=“db”
- 修改数据库连接信息
- 创建数据库
- 新建表:可以去seata提供的资源信息中下载
## transaction log store, only used in seata-server
store {## store mode: file、db、redismode = "db"## database store propertydb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.datasource = "druid"## mysql/oracle/postgresql/h2/oceanbase etc.dbType = "mysql"driverClassName = "com.mysql.jdbc.Driver"url = "jdbc:mysql://192.168.184.1:3306/seata"user = "root"password = "123456"minConn = 5maxConn = 30globalTable = "global_table"branchTable = "branch_table"lockTable = "lock_table"queryLimit = 100maxWait = 5000}
}
- 建表sql
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(`xid` VARCHAR(128) NOT NULL,`transaction_id` BIGINT,`status` TINYINT NOT NULL,`application_id` VARCHAR(32),`transaction_service_group` VARCHAR(32),`transaction_name` VARCHAR(128),`timeout` INT,`begin_time` BIGINT,`application_data` VARCHAR(2000),`gmt_create` DATETIME,`gmt_modified` DATETIME,PRIMARY KEY (`xid`),KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(`branch_id` BIGINT NOT NULL,`xid` VARCHAR(128) NOT NULL,`transaction_id` BIGINT,`resource_group_id` VARCHAR(32),`resource_id` VARCHAR(256),`branch_type` VARCHAR(8),`status` TINYINT,`client_id` VARCHAR(64),`application_data` VARCHAR(2000),`gmt_create` DATETIME(6),`gmt_modified` DATETIME(6),PRIMARY KEY (`branch_id`),KEY `idx_xid` (`xid`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(`row_key` VARCHAR(128) NOT NULL,`xid` VARCHAR(96),`transaction_id` BIGINT,`branch_id` BIGINT NOT NULL,`resource_id` VARCHAR(256),`table_name` VARCHAR(32),`pk` VARCHAR(36),`gmt_create` DATETIME,`gmt_modified` DATETIME,PRIMARY KEY (`row_key`),KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8;
seata服务搭建-nacos
db存储模式+nacos(注册&配置中心)部署
步骤5:配置Nacos注册中心,负责事务参与者(微服务)和TC通信
使用DB+Nacos的方式部署高可用集群模式
问题:seata为何需要注册中心和配置中心?
注册中心是事务参与者需要与事务协调者进行通信。配置中心是,如果不配置seata会使用默认的配置:script/config-center/config.txt文件。使用的配置中心的目的是所有TC都使用nacos配置中心中的配置,方便统一管理
- 将Seata server注册到nacos,修改confi目录下的registry.conf配置
registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype = "nacos"nacos {application = "seata-server"serverAddr = "127.0.0.1:8848"group = "SEATA_GROUP"namespace = ""cluster = "default"username = ""password = ""}
}config {# file、nacos 、apollo、zk、consul、etcd3type = "nacos"nacos {serverAddr = "127.0.0.1:8848"namespace = ""group = "SEATA_GROUP"username = ""password = ""}
}
- 将配置注册到nacos
- 修改confi.txt,还需要将config.txt的数据源改为db,然后通过nacos/nacos-config.sh,注册到nacos
store.db.url=jdbc:mysql://192.168.184.1:3306/seata?useUnicode=true store.db.user=root store.db.password=123456
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
service.vgroupMapping.my_test_tx_group=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://192.168.184.1:3306/seata?useUnicode=true
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
store.redis.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
store.redis.queryLimit=100
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
- 注册配置到nacos
打开\script\config-center\nacos,运行nacos-config.sh
linux端运行:如果是本地:sh nacos-config.sh,如果nacos在远程:sh nacos-config.sh -h 192.168.184.15 -p 8848
- 执行后
可以看到配置已经注册到了nacos
启动seata服务
sh seata-server.sh -h 192.168.184.15 -p 8091
运行seata服务:sh seata-server.sh -h 192.168.184.15 -p 8091,默认端口号8091
此时可以看到seata服务已经注册到了nacos
分布式事务代码搭建-client端搭建
声明式事务实现(@GlobalTransactional)
接入微服务应用
业务场景
用户下单,整个业务逻辑由三个微服务构成:
- 订单服务:根据采购需求创建订单。
- 库存服务:对给定的商品扣除库存数量;
- 启动seata server端,seata server使用nacos作为配置中心和注册中心(上一步已完成)
- 配置微服务整合seata
- 前置工作:新建两个数据库用来测试
在两个库创建两个表
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (`id` int NOT NULL AUTO_INCREMENT,`product_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`total_amount` int NULL DEFAULT NULL,`status` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
DROP TABLE IF EXISTS `stock_tpl`;
CREATE TABLE `stock_tpl` (`id` int NOT NULL AUTO_INCREMENT,`product_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`count` int NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
- 测试项目架构图
具体项目见码云:
https://gitee.com/www_zzq_com/springcloud-alibaba_study - 第一步:添加pom依赖
<!--添加seata依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency>
- 第二步:各微服务对应的数据库中添加undo_log表
undo_log表作用:回滚数据使用
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
- 第三步:配置事务的组
config.txt中配置的分组和客户端必须一一对应
server:port: 8083
spring:application:name: order-seata-servicecloud:nacos:server-addr: 192.168.184.15:8848username: nacospassword: nacosdiscovery:namespace: publicalibaba:seata:tx-service-group: shanghai #配置事务分组
- 第四步
配置文件中进行配置与seata服务进行交互
#seata配置
seata:#seata注册中心registry:#配置seata的注册中心,告诉seata client怎么去访问seata servertype: nacosnacos:server-addr: 192.168.184.15:8848 #seata server所在的nacos服务地址application: seata-server #seata server的服务名username: nacospassword: nacos#seata配置中心config:type: nacosnacos:server-addr: 192.168.184.15:8848 #seata server所在的nacos服务地址username: nacospassword: nacos
- 第五步:使用,在业务方法上添加@GlobalTransactional注解
@GlobalTransactionalpublic OrderTbl create(OrderTbl orderTbl) {//插入能否成功?orderMapper.insertOrder(orderTbl);//扣减库存,能否成功?String s = stockService.detectStock(orderTbl.getProductId());int a=1/0;return orderTbl;}
相关文章:

springCloudAlibaba之分布式事务组件---seata
Seata Sea学习分布式事务Seata二阶段提交协议AT模式TCC模式 Seata服务搭建Seata Server(事务协调者TC)环境搭建seata服务搭建-db数据源seata服务搭建-nacos启动seata服务 分布式事务代码搭建-client端搭建接入微服务应用 Sea学习 事务:事务是…...

无公网IP与服务器完成企业微信网页应用开发远程调试详细流程
文章目录 前言1. Windows安装Cpolar2. 创建Cpolar域名3. 创建企业微信应用4. 定义回调本地接口5. 回调和可信域名接口校验6. 设置固定Cpolar域名7. 使用固定域名校验 前言 本文主要介绍如何在企业微信开发者中心通过使用内网穿透工具提供的公网域名成功验证回调本地接口服务! …...

CSS 字体颜色渐变
CSS 字体颜色渐变 css 代码: 注意:background: linear-gradient(属性),属性可以调整方向 例如:to bottom 上下结构,to right 左右结构font-family: DIN, DIN;font-weight: normal;font-size: 22px;color:…...
【机器学习】基于CTC模型的语音转换可编辑文本研究
1.引言 1.1语音识别技术的研究背景 1.1.1.语音识别技术的需求 语音识别技术的研究和发展,对于提升人类与机器的交互方式具有深远的影响。首先,它极大地提高了工作效率和便利性。通过语音指令控制设备,用户可以更快捷地完成任务,…...

数据结构错题答案汇总
王道学习 第一章 绪论 1.1 3.A 数据的逻辑结构是从面向实际问题的角度出发的,只采用抽象表达方式,独立于存储结构,数据的存储方式有多种不同的选择;而数据的存储结构是逻辑结构在计算机上的映射,它不能独立于逻辑结构而存在。数…...

搞AI?中小企业拿什么和大厂拼?
近期,苹果发布M4芯片,号称“比当今任何AI PC的任何神经引擎都强!”紧随其后微软携“CopilotPCs”的概念加入AI PC激战。截至目前,包括联想、惠普、华为等多家主流PC厂商在内,已经至少推出了超50款AI PC产品。 AI重塑行…...

光伏电站阵列式冲击波声压光伏驱鸟器
光伏电站内鸟群的聚集可不是一件好事,鸟类排泄物,因其粘度大、具有腐蚀性的特点,一旦堆积在太阳能板上,会严重影响光伏电站的发电效率。长期积累的鸟粪不仅难以清洗,还可能引发组件的热斑效应,严重时甚至可…...
Webrtc支持FFMPEG硬解码之解码实现(三)
前言 此系列文章分分为三篇, Webrtc支持FFMPEG硬解码之Intel(一)-CSDN博客 Webrtc支持FFMPEG硬解码之NVIDA(二)-CSDN博客 Webrtc支持FFMPEG硬解码之解码实现(三)-CSDN博客 AMD硬解目前还没找到可用解码器,欢迎留言交流 环境 Windows平台 VS2019 <...
RIP协议
RIP基本概念 RIP(Routing Information Protocol)是一种基于距离矢量的路由协议,用于在自治系统(AS)内的网关之间交换路由信息。RIP 是一种相对简单且广泛使用的内部网关协议(IGP),适…...
计算机视觉与深度学习实战,Python为工具,基于光流场的车流量计数应用
一、引言 随着科技的飞速发展,计算机视觉和深度学习技术在现代社会中的应用越来越广泛。其中,车流量计数作为智能交通系统的重要组成部分,对于城市交通管理和规划具有重要意义。本文旨在探讨以Python为工具,基于光流场的车流量计数应用,为智能交通系统的发展提供技术支撑。…...
插入排序(排序算法)
文章目录 插入排序详细代码 插入排序 插入排序,类似于扑克牌的玩法一样,在有序的数组中,扫描无序的数组,逐一的将元素插入到有序的数组中。 实现细节: 从第一个元素开始,该元素可以认为已经被排序取出下…...
【附带源码】机械臂MoveIt2极简教程(六)、第三个demo -机械臂的避障规划
系列文章目录 【附带源码】机械臂MoveIt2极简教程(一)、moveit2安装 【附带源码】机械臂MoveIt2极简教程(二)、move_group交互 【附带源码】机械臂MoveIt2极简教程(三)、URDF/SRDF介绍 【附带源码】机械臂MoveIt2极简教程(四)、第一个入门demo 【附带源码】机械臂Move…...

innovus:route secondary pg pin
我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 innovus route secondary pg pin分以下几步: #设置pg net连接 globalNetConnect VDD_AON -type pgpin -pin VNW #设置ndr rule,具体绕线层次跟signal绕…...
btstack协议栈实战篇--LE Peripheral - Test Pairing Methods
btstack协议栈---总目录_bt stack是什么-CSDN博客 目录 1.Main Application Setup 2.Packet Handler 3.btstack_main 4.log信息 首先先理解一下,ble中的central,Peripheral,master和slave的理解? 以下是对这些概念的理解: “Central”(中心设备):与“Maste…...

git下载项目登录账号或密码填写错误不弹出登录框
错误描述 登录账号或密码填写错误不弹出登录框 二、解决办法 控制面板\用户帐户\凭据管理器 找到对应的登录地址进行更新或者删除 再次拉取或者更新就会提示输入登录信息...
平移矩阵中的数学思考
《webgl编程指南》中,“平移矩阵”中相关值的得出 是基于“矩阵和向量相乘所得的等式”与“向量表达式”组成一个方程组 xaxbyczd xxTx 书中说,根据上面的方程组,可以很容易得出 a1、b0、c0、dTx 0、问题来了! 我也确实可以看…...

【机器学习】Qwen2大模型原理、训练及推理部署实战
目录 一、引言 二、模型简介 2.1 Qwen2 模型概述 2.2 Qwen2 模型架构 三、训练与推理 3.1 Qwen2 模型训练 3.2 Qwen2 模型推理 四、总结 一、引言 刚刚写完【机器学习】Qwen1.5-14B-Chat大模型训练与推理实战 ,阿里Qwen就推出了Qwen2&#x…...

JetLinks开源物联网平台社区版部署教程
1.上github搜素jetlinks 2.找到源代码,并且下载到本地。 3.项目下载完成之后,还需要另外下载三个核心依赖模块。在github找到jetlinks。 4.点击进去下载,下载完成之后,你会发现里面有三个文件夹是空白的,先不用理会&am…...

QT学习过程中遇到的问题自记
文章目录 前言问题1问题2问题3 前言 学习QT嵌入式实战开发(从串口通信到JSON通信微课视频版)的过程中遇到的几个小问题 问题1 1.将书中的示例代码导入自己的电脑,然后点击工程进去,不能运行,报错 no kits are enabled for this project… 我…...

自学网络安全的三个必经阶段(含路线图)
一、为什么选择网络安全? 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地,网络安全行业地位、薪资随之水涨船高。 未来3-5年,是安全行业的黄金发展期,提前踏入…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

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

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...