分布式事务总结

文章目录

    • 一、分布式事务基础
      • 什么是事务?
      • 本地事物
      • 分布式事务
      • 分布式事务的场景
    • 二、分布式事务解决方案
      • 全局事务
      • 可靠消息服务
      • TCC 事务
    • 三、Seata 分布式事务解决方案
      • 3.1 Seata-At模式
      • 3.2 秒杀项目集成 Seata
        • 启动 Seata-Server
        • 项目集成seata配置
        • AT模式代码实现
      • 3.3 Seata-TCC深度解析
        • TCC模型图
        • 模型设计
          • 扣钱业务逻辑
          • 加钱业务逻辑
          • 业务模型总结
        • 并发控制
        • 业务模型优化
        • 异常处理
          • 空回滚
          • 幂等
          • 防悬挂
        • 异常处理流程图
          • Try方法
          • Comfirm 方法
          • Cancel方法
        • TCC模式代码实现

一、分布式事务基础

什么是事务?

事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作

都成功,要么所有的操作都被撤销。简单地说,事务提供一种“要么什么都不做,要么做全套”机制

本地事物

本地事物其实可以认为是数据库提供的事务机制。说到数据库事务就不得不说,数据库事务中的四

大特性:

  • A:原子性(Atomicity),一个事务中的所有操作,要么全部完成,要么全部不完成

  • C:一致性(Consistency),在一个事务执行之前和执行之后数据库都必须处于一致性状态

  • I:隔离性(Isolation),在并发环境中,当不同的事务同时操作相同的数据时,事务之间互不影响

  • D:持久性(Durability),指的是只要事务成功结束,它对数据库所做的更新就必须永久的保存下来

数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单

元中的所有操作要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚

分布式事务

分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布

式系统的不同节点之上。

简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同

的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。

本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

分布式事务的场景

  • 单体系统访问多个数据库

    一个服务需要调用多个数据库实例完成数据的增删改操作

在这里插入图片描述

  • 多个微服务访问同一个数据库

    多个服务需要调用一个数据库实例完成数据的增删改操作

在这里插入图片描述

  • 多个微服务访问多个数据库

    多个服务需要调用一个数据库实例完成数据的增删改操作

    在这里插入图片描述

二、分布式事务解决方案

全局事务

全局事务基于DTP模型实现。DTP是由X/Open组织提出的一种分布式事务模型——X/Open

Distributed Transaction Processing Reference Model。它规定了要实现分布式事务,需要三种角色:

  • AP: Application 应用系统 (微服务)

  • TM: Transaction Manager 事务管理器 (全局事务管理)

  • RM: Resource Manager 资源管理器 (数据库)

整个事务分成两个阶段(2PC):

  • 阶段一: 表决阶段,所有参与者都将本事务执行预提交,并将能否成功的信息反馈发给协调者。

  • 阶段二: 执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地执行提交或者回

滚。

在这里插入图片描述

优点

  • 提高了数据一致性的概率,实现成本较低

缺点

  • 单点问题: 事务协调者宕机

  • 同步阻塞: 延迟了提交时间,加长了资源阻塞时间

  • 数据不一致: 提交第二阶段,依然存在commit结果未知的情况,有可能导致数据不一致

可靠消息服务

基于可靠消息服务的方案是通过消息中间件保证上、下游应用数据操作的一致性。假设有A和B两个

系统,分别可以处理任务A和任务B。此时存在一个业务流程,需要将任务A和任务B在同一个事务中处

理。就可以使用消息中间件来实现这种分布式事务。

RocketMQ事务消息流程图

在这里插入图片描述

1)事务消息发送及提交

(1) 发送消息(half消息)

(2) 服务端响应消息写入结果

(3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行)

(4) 根据本地事务状态执行Commit或者Rollback(Commit操作生产消息索引,消息对消费者可见)

2) 事务补偿

(1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”

(2) Producer收到回查消息,检查回查消息对于的本地事务的状态

(3) 根据本地事务状态,重新Commit或者Rollback

其中,补偿阶段用户解决消息Commit或者Rollback发生超时或者失效的情况

3) 事务消息状态

事务消息共有三种状态,提交状态,回查状态,中间状态:

  • TransactionStatus.CommitTransaction: 提交事务,它允许消费者消费此消息
  • TransactionStatus.RollbackTransaction: 回滚事务,它代表消息将被删除,不允许被消费
  • TransactionStatus.Unknown: 中间状态,它代表需要消息队列来确认状态

消息生产者实现

发送代码如下:

Message<OperateIntergralVo> message = MessageBuilder.withPayload(vo).setHeader("orderNo",orderNo).build();
TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction("tx_group", "tx_topic", message, orderNo);
String sendStatus = sendResult.getSendStatus().name();
String localTXState = sendResult.getLocalTransactionState().name();
og.info(">>>> send status={},localTransactionState={} <<<<",sendStatus,localTXState);
if(sendResult.getLocalTransactionState().equals(LocalTransactionState.COMMIT_MESSAGE)){return "退款成功";
}else{return "退款失败";
}

创建事务消息生产者端的消息监听器,注意是生产者,不是消费者,我们需要实现的是RocketMQLocalTransactionListener接口,代码如下:

@RocketMQTransactionListener(txProducerGroup = "tx_group")
@Slf4j
public class OrderTXMsgListener implements RocketMQLocalTransactionListener {@Autowiredprivate IOrderInfoService orderInfoService;@Overridepublic RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {log.info("执行本地事务");RocketMQLocalTransactionState result = RocketMQLocalTransactionState.COMMIT;try {String orderNo = (String) arg;orderInfoService.changeOrderStatusToRefund(orderNo);} catch (Exception e) {result = RocketMQLocalTransactionState.ROLLBACK;}return result;}@Overridepublic RocketMQLocalTransactionState checkLocalTransaction(Message msg) {String orderNo = (String) msg.getHeaders().get("orderNo");if(!StringUtils.isEmpty(orderNo)){OrderInfo orderInfo = orderInfoService.getOrderStatus(orderNo);if(OrderInfo.STATUS_REFUND.equals(orderInfo.getStatus())){return RocketMQLocalTransactionState.COMMIT;}}return RocketMQLocalTransactionState.ROLLBACK;}
}

优缺点:

  • 优点:实现最终一致性,并发较高,性能较快
  • 缺点:异步执行,无法直接获取到远程服务的返回结果
  • 缺点:场景限制,只有消费者无论如何都能执行成功的场景,才适合采用此方案
  • 缺点:可能存在短暂的不一致

TCC 事务

TCC 即为 Try Confirm Cancel,它属于补偿型分布式事务。TCC 实现分布式事务一共有三个步骤:

  • Try:尝试待执行的业务

这个过程并未执行业务,只是完成所有业务的一致性检查,并预留好执行所需的全部资源

  • Confirm:确认执行业务

确认执行业务操作,不做任何业务检查, 只使用Try阶段预留的业务资源。通常情况下,采用TCC

则认为 Confirm 阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的

出错了,需引入重试机制或人工处理。

  • Cancel:取消待执行的业务

取消Try阶段预留的业务资源。通常情况下,采用TCC则认为Cancel阶段也是一定成功的。若

Cancel阶段真的出错了,需引入重试机制或人工处理。

在这里插入图片描述

TCC 两阶段提交与 XA 两阶段提交的区别是:

  • XA 是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。

  • TCC 是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。

TCC 事务的优缺点:

  • 优点:把数据库层的二阶段提交上提到了应用层来实现,规避了数据库层的2PC性能低下问题。

  • 缺点:TCC的Try、Confirm和Cancel操作功能需业务提供,开发成本高。

三、Seata 分布式事务解决方案

2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And

Rollback),其愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们

遇到的分布式事务方面的所有难题。后来更名为 Seata,意为:Simple Extensible Autonomous

Transaction Architecture,是一套分布式事务解决方案。

Seata 的设计目标是对业务无侵入,因此从业务无侵入的2PC方案着手,在传统2PC的基础上演进。

它把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分

支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个关系数据

库的本地事务。

3.1 Seata-At模式

Seata主要由三个重要组件组成:

  • TC:Transaction Coordinator 事务协调器,管理全局的分支事务的状态,用于全局性事务的提交

和回滚。

  • TM:Transaction Manager 事务管理器,用于开启、提交或者回滚全局事务。

  • RM:Resource Manager 资源管理器,用于分支事务上的资源管理,向TC注册分支事务,上报分

支事务的状态,接受TC的命令来提交或者回滚分支事务。

在这里插入图片描述

Seata-AT模式的执行流程如下:

  1. A服务的TM向TC申请开启一个全局事务,TC就会创建一个全局事务并返回一个唯一的XID

  2. A服务的RM向TC注册分支事务,并及其纳入XID对应全局事务的管辖

  3. A服务执行分支事务,向数据库做操作4. A服务开始远程调用B服务,此时XID会在微服务的调用链上传播

  4. B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖

  5. B服务执行分支事务,向数据库做操作

  6. 全局事务调用链处理完毕,TM根据有无异常向TC发起全局事务的提交或者回滚

  7. TC协调其管辖之下的所有分支事务, 决定是否回滚

Seata-AT模式实现2PC与传统2PC的差别

  1. 架构层次方面,传统2PC方案的 RM 实际上是在数据库层,RM本质上就是数据库自身,通过XA协

议实现,而 Seata 的 RM 是以 jar 包的形式作为中间件层部署在应用程序这一侧的。

  1. 两阶段提交方面,传统2PC无论第二阶段的决议是commit还是rollback,事务性资源的锁都要保

持到Phase2完成才释放。而 Seata 的做法是在 Phase1 就将本地事务提交,这样就可以省去Phase2

持锁的时间,整体提高效率。

3.2 秒杀项目集成 Seata

启动 Seata-Server

详情请看部署文档/seata-server部署文档.md

项目集成seata配置

详情请看 集成文档/seata客户端集成文档.md

AT模式代码实现

分布式事务发起方只需要贴@GlobalTransactional注解即可

分支分布式事务贴上@Transactional即可

3.3 Seata-TCC深度解析

TCC模型图

在这里插入图片描述

模型设计

业务场景

1.账户支付,用户账户金额减少

2.账户退款,用户账户金额增加

表设计

CREATE TABLE `user_account` (`user_id` varchar(100) NOT NULL COMMENT '用户UID',`gmt_create` datetime NOT NULL COMMENT '创建时间',`gmt_modified` datetime NOT NULL COMMENT '修改时间',`amount` bigint(20) NOT NULL COMMENT '用户余额',PRIMARY KEY (`user_id`),KEY `idx_gmt_create` (`gmt_create`),KEY `idx_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
扣钱业务逻辑

场景: 账户A上有100元,要扣除其中的30元

Try: 检查余额,扣除其中30元;

在这里插入图片描述

Confirm: 空提交

在这里插入图片描述

Cancel: 返还扣除的30元

在这里插入图片描述

加钱业务逻辑

Try: 空操作;

在这里插入图片描述

Confirm: 增加可用金额30元

在这里插入图片描述

Cancel: 空操作

在这里插入图片描述

业务模型总结

在这里插入图片描述

并发控制

账户A上有100元,事务T1要扣除其中30元,事务T2也要扣除30元,出现并发

Try: 检查余额,扣除其中30元

在这里插入图片描述

T2 Confirm: 空提交

在这里插入图片描述

T1 Cancel: 释放T1预留的30元

在这里插入图片描述

业务模型优化

表增加冻结金额字段

CREATE TABLE `user_account` (`user_id` varchar(100) NOT NULL COMMENT '用户UID',`gmt_create` datetime NOT NULL COMMENT '创建时间',`gmt_modified` datetime NOT NULL COMMENT '修改时间',`amount` bigint(20) NOT NULL COMMENT '用户余额',`freezed_amount` bigint(20) unsigned DEFAULT '0',PRIMARY KEY (`user_id`),KEY `idx_gmt_create` (`gmt_create`),KEY `idx_gmt_modified` (`gmt_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

扣钱为例: 账户上有100元,要扣除其中30元(此时里面的可用金额=amount-freezed_amount)

Try: 检查余额,扣除其中30元(freezed_amount=freezed_amount+30)

在这里插入图片描述

Confirm: 扣除30元( amount=amount-30 freezed_amount=freezed_amount-30)

在这里插入图片描述

Cancel: 释放预留的30元(freezed_amount=freezed_amount-30)

在这里插入图片描述

加钱为例: 账户上有100元,要加30元(此时里面的可用金额=amount-freezed_amount)

Try: 空操作

在这里插入图片描述

Confirm: 增加30元( amount=amount+30)

在这里插入图片描述

Cancel: 空操作

在这里插入图片描述

异常处理
空回滚

Try 方法未执行,Cancel 执行了

出现原因:

  1. Try 超时
  2. 分布式事务回滚,触发 Cancel
  3. 未收到 Try,收到 Cancel

解决方案: Cancel 方法需要识别出是否执行 Try 方法,如果执行了就正常执行 Cancel,如果没有就直接结束

增加事务日志表来实现这个功能.

CREATE TABLE `account_transaction` (`tx_id` varchar(100) NOT NULL COMMENT '事务Txid',`action_id` varchar(100) NOT NULL COMMENT '分支事务id',`gmt_create` datetime NOT NULL COMMENT '创建时间',`gmt_modified` datetime NOT NULL COMMENT '修改时间',`user_id` varchar(100) NOT NULL COMMENT '账户Uid',`amount` varchar(100) NOT NULL COMMENT '变动金额',`type` varchar(100) NOT NULL DEFAULT '' COMMENT '变动类型',PRIMARY KEY (`tx_id`,`action_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
幂等

多次调用二阶段方法

出现原因:

  • 网络异常
  • 分支事务所在服务器宕机

解决方案: 做幂等性处理

CREATE TABLE `account_transaction` (`tx_id` varchar(100) NOT NULL COMMENT '事务Txid',`action_id` varchar(100) NOT NULL COMMENT '分支事务id',`gmt_create` datetime NOT NULL COMMENT '创建时间',`gmt_modified` datetime NOT NULL COMMENT '修改时间',`user_id` varchar(100) NOT NULL COMMENT '账户Uid',`amount` varchar(100) NOT NULL COMMENT '变动金额',`type` varchar(100) NOT NULL DEFAULT '' COMMENT '变动类型',`state` smallint(4) NOT NULL COMMENT '状态: 1.初始化 2.已提交 3.已回滚',PRIMARY KEY (`tx_id`,`action_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
防悬挂

Cancel 比 Try 先执行

出现原因:

  1. Try 超时(拥堵)

  2. 分布式事务回滚触发 Cancel

  3. 拥堵的 Try 到达

要允许空回滚,但是要拒绝空回滚之后的 Try 方法.

解决方案: 在Try方法中, 如果根据全局事务ID能查询出数据出来,说明在try方法之前执行了空回滚,此时就不能进行try方法。否则就正常执行try方法.

异常处理流程图
Try方法

在这里插入图片描述

Comfirm 方法

在这里插入图片描述

Cancel方法

在这里插入图片描述

TCC模式代码实现

分布式事务发起方只需要贴@GlobalTransactional注解即可

分支事务需要完成下面步骤:

1.在接口上贴上@LocalTCC@TwoPhaseBusinessAction注解,具体配置如下:

@LocalTCC
public interface IUsableIntegralService {/*** 增加积分*/@TwoPhaseBusinessAction(name = "incrIntergralTry", commitMethod = "incrIntergralCommit", rollbackMethod = "incrIntergralRollback")void incrIntergralTry(@BusinessActionContextParameter(paramName = "operateIntergralVo") OperateIntergralVo operateIntergralVo, BusinessActionContext context);void incrIntergralCommit(BusinessActionContext context);void incrIntergralRollback(BusinessActionContext context);
}

2.添加实现类,实现try,confirm,cancel方法逻辑即可

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

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

相关文章

openstack(2)

目录 块存储服务 安装并配置控制节点 安装并配置一个存储节点 验证操作 封装镜像 上传镜像 块存储服务 安装并配置控制节点 创建数据库 [rootcontroller ~]# mysql -u root -pshg12345 MariaDB [(none)]> CREATE DATABASE cinder; MariaDB [(none)]> GRANT ALL PR…

1、Docker概述与安装

相关资源网站&#xff1a; ● docker官网&#xff1a;http://www.docker.com ● Docker Hub仓库官网: https://hub.docker.com/ 注意&#xff0c;如果只是想看Docker的安装&#xff0c;可以直接往下拉跳转到Docker架构与安装章节下的Docker具体安装步骤&#xff0c;一步步带你安…

82基于matlab GUI的图像处理

基于matlab GUI的图像处理&#xff0c;功能包括图像一般处理&#xff08;灰度图像、二值图&#xff09;&#xff1b;图像几何变换&#xff08;旋转可输入旋转角度、平移、镜像&#xff09;、图像边缘检测&#xff08;拉普拉斯算子、sobel算子、wallis算子、roberts算子&#xf…

【Rust日报】2023-11-22 Floneum -- 基于 Rust 的一款用于 AI 工作流程的图形编辑器

Floneum -- 基于 Rust 的一款用于 AI 工作流程的图形编辑器 Floneum 是一款用于 AI 工作流程的图形编辑器&#xff0c;专注于社区制作的插件、本地 AI 和安全性。 Floneum 有哪些特性&#xff1a; 可视化界面&#xff1a;您无需任何编程知识即可使用Floneum。可视化图形编辑器可…

oled的使用 动态的变量 51

源码均在IIC手写程序中 外部中断实现变量加一 #include "reg52.h" #include "main.h" #include <intrins.h> #include "OLED.h" #include "bmp.h" #include "Delay.h" sbit LED1 P1^0; sbit LED2 P1^1; sbit LED3…

Python报错:AttributeError(类属性、实例属性)

Python报错&#xff1a;AttributeError&#xff08;类属性、实例属性&#xff09; Python报错&#xff1a;AttributeError 这个错误就是说python找不到对应的对象的属性&#xff0c;百度后才发现竟然是初始化类的时候函数名写错了 __init__应该有2条下划线&#xff0c;如果只有…

构建未来:云计算 生成式 AI 诞生科技新局面

目录 引言生成式 AI&#xff1a;开发者新伙伴云计算与生成式 AI 的无缝融合亚马逊云与生成式 AI 结合的展望/总结我用亚马逊云科技生成式 AI 产品打造了什么&#xff0c;解决了什么问题未来科技发展趋势&#xff1a;开发者的机遇与挑战结合实践看未来结语开源项目 引言 2023年…

SpectralGPT: Spectral Foundation Model 论文翻译1

遥感领域的通用大模型 2023.11.13在CVPR发表 原文地址&#xff1a;[2311.07113] SpectralGPT: Spectral Foundation Model (arxiv.org) 摘要 ​ 基础模型最近引起了人们的极大关注&#xff0c;因为它有可能以一种自我监督的方式彻底改变视觉表征学习领域。虽然大多数基础模型…

VSCode 连接远程服务器问题及解决办法

端口号不一样&#xff0c;需要在配置文件中添加Port Host 27.223.26.46HostName 27.223.*.*User userForwardAgent yesPort 14111输入密码后可以连接 在vscode界面&#xff0c;终端&#xff0c;生成公钥&私钥 ssh-keygen可以看到有id_rsa和id_rsa.pub两个文件生成&#…

C#,《小白学程序》第五课:队列(Queue)其一,排队的技术与算法

日常生活中常见的排队&#xff0c;软件怎么体现呢&#xff1f; 排队的基本原则是&#xff1a;先到先得&#xff0c;先到先吃&#xff0c;先进先出 1 文本格式 /// <summary> /// 《小白学程序》第五课&#xff1a;队列&#xff08;Queue&#xff09; /// 日常生活中常见…

nodejs微信小程序+python+PHP-青云商场管理系统的设计与实现-安卓-计算机毕业设计

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

C++算法 —— 贪心(4)

文章目录 1、分发饼干2、最优除法3、跳跃游戏Ⅱ4、跳跃游戏Ⅰ5、加油站6、单调递增的数字7、坏了的计算器 1、分发饼干 455. 分发饼干 其实看完这个题会发现&#xff0c;如果给定的两个数组不排序的话会非常难受&#xff0c;所以无论怎样&#xff0c;先排序。接下来需要比较两…

项目管理套路:看这一篇绝对够用❤️

写论文必不可少的&#xff0c;就是创建代码并进行实验。好的项目管理可以让实验进行得更加顺利。本篇博客以一次项目实践为例&#xff0c;介绍项目管理的方法&#xff0c;以及可能遇到的问题&#xff0c;并提供一些可行的解决方案。 目录 项目管理工具开始第一步版本管理十分关…

【JavaWeb】TomcatJavaWebHTTP

Tomcat&JavaWeb&HTTP 文章目录 Tomcat&JavaWeb&HTTP一、Tomcat1.1 版本选择及安装1.2 目录1.3 WEB项目部署的方式 二、IDEA中Java Web开发部署流程三、HTTP协议3.1 发展历程3.2 HTTP协议的会话方式3.3 请求报文3.4 响应报文 一、Tomcat Tomcat是Apache 软件基…

php xml数据转数组两种方式

目录 方法一、可以使用simplexml_load_string()函数将XML数据转换为数组。 方法二、使用PHP内置的DOMDocument类来将XML数据转换为数组的方法 方法一、可以使用simplexml_load_string()函数将XML数据转换为数组。 $xmlData <root><name>John Doe</name>&l…

NX二次开发UF_CSYS_create_matrix 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CSYS_create_matrix Defined in: uf_csys.h int UF_CSYS_create_matrix(const double matrix_values [ 9 ] , tag_t * matrix_id ) overview 概述 Creates a 3 x 3 matrix. 创建…

nodejs+vue+python+PHP+微信小程序-青云商场管理系统的设计与实现-安卓-计算机毕业设计

研究步骤、措施&#xff1a; &#xff08;1&#xff09;与指导老师确定系统主要功能&#xff1b; &#xff08;2&#xff09;做需求分析及功能模块划分&#xff1b; &#xff08;3&#xff09;指导老师通过后&#xff0c;设计出用例图&#xff0c;E-R图&#xff0c;功能模块图 …

【XSLVGL2.0】如何新增一种语言和词条

XSLVGL2.0 开发手册 【XSLVGL2.0】如何新增一种语言和词条 1、概述2、以外置资源的方式增加词条3、以内置资源的方式增加词条4、使用方法1、概述 本文件旨在介绍新增一种语言词条的方法 2、以外置资源的方式增加词条 假设项目需要增加一种英文的词条。一般地,我们采用国际…

老牌开源 SVG 编辑器 SVGEdit 是如何架构的?

大家好&#xff0c;我是前端西瓜哥。这次简单看看 SVGEdit 的架构。 SVGEdit 的版本为 7.2.0。 SVGEdit 一款非常老牌的 SVG 图形编辑器&#xff0c;用于编辑处理 SVG&#xff0c;start 数目前是 5.8k。 它的优点在于经过多年的开发&#xff0c;完成度高&#xff0c;较为成熟&a…

大众博客系统测试报告【改】

一、项目背景 大众博客系统采用前后端分离的方法来实现&#xff0c;同时使用了数据库来存储相关的数据&#xff0c;同时将其部署到云服务器上。前端主要有四个页面构成&#xff1a;登录页、列表页、详情页以及编辑页&#xff0c;以上模拟实现了最简单的大众博客系统。其结合后端…