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

Seata1.5.2学习(二)——使用分布式事务锁@GlobalLock

目录

一、创建数据库 

二、配置consumer-service

1.pom.xml

2.application.properties

3.启动类

4.其他代码

三、配置provider-service

1.pom.xml

2.application.properties

3.启动类

4.其他代码

四、分布式事务问题演示与解决办法 

(一)分布式事务问题演示

(二)解决办法:使用@GlobalTransactional全局事务注解

五、SQL回滚日志——beforeImage和afterImage

六、@GlobalLock

(一)数据库脏写

(二)GlobalLock是如何预防数据库脏写的

七、面试重点:Seata分布式事务流程

1.一阶段 

2.二阶段-回滚

3.二阶段-提交


一、创建数据库 

每个库都创建下面的表:

-- 分布式事务回滚使用, sql在源码文件夹的 script\client\at\db 目录下
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',`xid`           VARCHAR(128) 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 = utf8mb4 COMMENT ='AT transaction mode undo table';CREATE TABLE `user` (`id` int(11) NOT NULL,`name` varchar(45) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

二、配置consumer-service

1.pom.xml

添加依赖

<!-- seata -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!-- mybatis plus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency>
<!-- 连接MySQL数据库 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>

2.application.properties

添加下面的配置

#数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.157.102:3306/seata-user?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456# 用于在分布式中区分是哪个服务,应该保持唯一
seata.application-id=${spring.application.name}# 获取配置
seata.config.type=nacos
seata.config.nacos.server-addr=192.168.157.102:8848
seata.config.nacos.namespace=seata
seata.config.nacos.group=SEATA_GROUP
seata.config.nacos.dataId=seata-server
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos# 获取服务
seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=192.168.157.102:8848
seata.registry.nacos.namespace=seata
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos

3.启动类

@SpringBootApplication
@EnableFeignClients // 开启openFeign注解扫描
@MapperScan("com.javatest.mapper")
// @EnableAutoDataSourceProxy是 Seata 提供的一个注解,用于启用数据源代理功能。
// 它的主要目的是为数据源创建一个代理对象,以便在分布式事务场景中进行拦截和增强。
@EnableAutoDataSourceProxy
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class,args);}
}

4.其他代码

User实体类

@TableName("user")
public class User {@TableIdprivate Integer id;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}
}

UserMapper

public interface UserMapper extends BaseMapper<User> {
}

UserService

@Service
public class UserServiceImpl {@Autowiredprivate UserMapper userMapper;@Autowiredprivate ProviderService providerService;public void saveUser(User user) {userMapper.insert(user); // 操作第一个数据库// 发起远程调用,操作第二个数据库providerService.createUser(user);}
}

ProvicerService 中,新增:

@FeignClient(name = "provider-service")
public interface ProviderService {@GetMapping("/saveu") // /saveu这个接口名字必须与provider-service的controller中的一致String createUser(@SpringQueryMap User user);
}

新增 SeataController: 

@RestController
public class SeataController {@Autowiredprivate UserServiceImpl userService;@GetMapping("/saveUser")public String save(User user) {userService.saveUser(user);return "success-----------";}
}

三、配置provider-service

1.pom.xml

添加依赖

<!-- seata -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!-- mybatis plus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency>
<!-- 连接MySQL数据库 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>

2.application.properties

添加下面的配置,注意provider-service使用的是seata-user2数据库

#数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.157.102:3306/seata-user2?useSSL=false
spring.datasource.username=root
spring.datasource.password=123456# 用于在分布式中区分是哪个服务,应该保持唯一
seata.application-id=${spring.application.name}# 获取配置
seata.config.type=nacos
seata.config.nacos.server-addr=192.168.157.102:8848
seata.config.nacos.namespace=seata
seata.config.nacos.group=SEATA_GROUP
seata.config.nacos.dataId=seata-server
seata.config.nacos.username=nacos
seata.config.nacos.password=nacos# 获取服务
seata.registry.type=nacos
seata.registry.nacos.application=seata-server
seata.registry.nacos.server-addr=192.168.157.102:8848
seata.registry.nacos.namespace=seata
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.username=nacos
seata.registry.nacos.password=nacos

3.启动类

@SpringBootApplication
@EnableAutoDataSourceProxy
@MapperScan("com.javatest.mapper")
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class,args);}
}

4.其他代码

User实体类

@TableName("user")
public class User {@TableIdprivate Integer id;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}
}

UserMapper 

public interface UserMapper extends BaseMapper<User> {
}

UserService

@Service
public class UserServiceImpl {@Autowiredprivate UserMapper userMapper;public void saveUser(User user) {userMapper.insert(user); // 操作第二个数据库}
}

在ProviderController中,新增 :

@Autowired
private UserServiceImpl userService;@GetMapping("/saveu")
public String createUser(User user) {userService.saveUser(user);return "success-----------";
}

四、分布式事务问题演示与解决办法 

(一)分布式事务问题演示

按照上面配置,我们在Consumer-service的saveUser方法中制作一个错误:

@Service
public class UserServiceImpl {@Autowiredprivate UserMapper userMapper;@Autowiredprivate ProviderService providerService;public void saveUser(User user) {userMapper.insert(user); // 操作第一个数据库// 发起远程调用,操作第二个数据库providerService.createUser(user);int i = 1 / 0;}
}

启动项目consumer-service和provider-service,浏览器输入:

http://localhost:8888/saveUser?id=1&name=张三

运行后控制台报错,2张表的数据都存入成功:


        当添加@Transactional后,第一个数据库不会写入,第二个数据库会成功写入,因为第一个数据库会因为代码未执行成功,被@Transactional拦截回滚,而第二个数据库没有添加@Transactional,所以不会被回滚,数据会被写入。

清空2个数据库,重启consumer-service服务,浏览器再次输入:

http://localhost:8888/saveUser?id=1&name=张三

结果: 

这种现象就是分布式事务问题,导致的数据不一致。

注意:

  • 从Spring Boot 1.0 开始,Spring Boot 默认启用了事务管理,因此通常不需要显式添加 @EnableTransactionManagement。
  • 只有在自定义事务管理器、非 Spring Boot 项目或禁用自动配置时,才需要显式添加 @EnableTransactionManagement。
  • 如果不确定事务管理是否启用,可以通过日志或测试方法验证。

(二)解决办法:使用@GlobalTransactional全局事务注解

@Service
public class UserServiceImpl {@Autowiredprivate UserMapper userMapper;@Autowiredprivate ProviderService providerService;//    @Transactional@GlobalTransactional // 开启全局事务public void saveUser(User user) {userMapper.insert(user); // 操作第一个数据库// 发起远程调用,操作第二个数据库providerService.createUser(user);int i = 1 / 0;}
}

        清理数据库,开启@GlobalTransactional,重启consumer-service,然后再次访问代码仍然报错,但是数据库中没有数据,证明分布式事务生效。

也可以在@GlobalTransactional后面添加下面的代码

@GlobalTransactional(rollbackFor = RuntimeException.class)

@GlobalTransactional不一定非要放在Service中,放在Controller上也可以。

五、SQL回滚日志——beforeImage和afterImage

        @GlobalTransactional的原理就是sql执行前后的beforeImage和afterImage,日志文件就在undo_log表中,但是因为已经回滚完毕,undo_log表中的日志数据会被删除,下面演示undo_log表中的日志数据:

将rollback_info存储的内容复制出来,到JSON在线解析格式化验证 - JSON.cn这个网站格式化:

如果你使用的是Navicat,右键这个字段内容,另存为json再格式化 

 

这段json日志就是分支回滚日志,最外层是大的事务,大事务里面有2个小的本地事务,每一个小的本地事务都称为分支事务。

beforeImage:sql执行之前,没有数据

afterImage:sql执行之后,有数据

Seata会根据这个日志进行提交和回滚。

六、@GlobalLock

(一)数据库脏写

@Service
public class UserServiceImpl {@Autowiredprivate UserMapper userMapper;@Autowiredprivate ProviderService providerService;//    @Transactional// 开启全局事务@GlobalTransactional(rollbackFor = RuntimeException.class)public void saveUser(User user) {
//        userMapper.insert(user); // 操作第一个数据库userMapper.updateById(user);try {Thread.sleep(20000);} catch (InterruptedException e) {}// 发起远程调用,操作第二个数据库providerService.createUser(user);int i = 1 / 0;}public void updateById(User user) {userMapper.updateById(user); // 更新第一个数据库}
}
@RestController
public class SeataController {@Autowiredprivate UserServiceImpl userService;@GetMapping("/saveUser")public String save(User user) {userService.saveUser(user);return "success-----------";}@GetMapping("/updateUser")public String update(User user) {userService.updateById(user);return "success-----------";}
}

将seata-user数据库的user表添加数据,seata-user2数据库表中没有数据

重启consumer-service先访问/saveUser修改表中的值,数据修改成功

http://localhost:8888/saveUser?id=100&name=小乔

在20s之内,访问/updateUser,数据修改成功

http://localhost:8888/updateUser?id=100&name=周瑜

此时,后台会一直尝试回滚,并且一直提示回滚失败

将表中的数据手动改回小乔才能回滚成功,这种情况只能手动修改

 

回滚完成后,数据会回到最开始的张三:

(二)GlobalLock是如何预防数据库脏写的

GlobalLock:全局锁,必须配合 @Transactional 和 update 语句,才能使用

作用:提交数据前,先找 seata 获取全局锁,如果加锁失败就报异常,比如:

@Service
public class UserServiceImpl {@Autowiredprivate UserMapper userMapper;@Autowiredprivate ProviderService providerService;// 开启全局事务@GlobalTransactional(rollbackFor = RuntimeException.class)public void saveUser(User user) {
//        userMapper.insert(user); // 操作第一个数据库userMapper.updateById(user);try {Thread.sleep(20000);} catch (InterruptedException e) {}// 发起远程调用,操作第二个数据库providerService.createUser(user);int i = 1 / 0;}@GlobalLock@Transactionalpublic void updateById(User user) {userMapper.updateById(user); // 更新第一个数据库}
}

浏览器分别访问: 

http://localhost:8888/saveUser?id=100&name=小乔http://localhost:8888/updateUser?id=100&name=周瑜http://localhost:8888/saveUser?id=100&name=小乔 

控制台报错:获取锁失败,脏数据未写入

这里获取锁报错是因为,saveUser方法上,使用了@GlobalTransactional 注解,该注解会自动在seata上添加一条全局锁信息,比如:

 

这些数据过一段时间会自动清除。

执行updateById方法时,因为有@GlobalLock注解,所以也会向seata发送请求,给id=100的数据加全局锁,但是全局锁已经存在了,所以该条数据不会被修改。

七、面试重点:Seata分布式事务流程

流程图:

Seata中有3个角色:

  • TC(Transaction Coordinator)——事务协调者:驱动全局事务提交或回滚,其实就是 Seata服务端。
  • TM(Transaction Manager)——事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务,其实就是Seata客户端。
  • RM(Resource Manager)——资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

TM 和 RM 都在应用程序中

seata 分布式事务分为两个阶段,以下面的 sql 为例:

update user set name = '张三' where name = '小乔';

1.一阶段 

  1. 通过GlobalTransactional注册全局事务,会得到一个XID(全局事务ID)
  2. 解析SQL:得到SQL的类型(UPDATE),表名(user),条件(name = '哈哈')等相关的信息(元数据)。
  3. 保存原镜像beforeImage:根据解析得到的条件信息,生成查询语句,定位数据,比如:
    select id, name from user where name = '哈哈';
  4. 执行业务SQL:更新这条记录的name为 '亚瑟'。
  5. 保存新镜像afterImage:根据前镜像的结果,通过主键定位数据,比如:
    select id, name, since from product where id = 1;
  6. 插入回滚日志:把新老镜像数据以及业务SQL相关的信息组成一条回滚日志记录,插入到UNDO_LOG表中。
  7. 本地事务提交之前,向TC注册分支事务:申请 user 表中,主键值等于1的记录的全局锁
  8. 本地事务提交:业务数据的更新和前面步骤中生成的UNDO_LOG一并提交。
  9. 将本地事务提交的结果上报给TC
  10. 调用,执行另一个事务,并传递XID(放到 Request Header 中,TX_XID)

2.二阶段-回滚

  1. 收到TC的分支回滚请求,开启一个本地事务,执行如下操作:
  2. 通过XID和Branch ID(分支事务ID)查找到相应的UNDO_LOG记录。
  3. 数据校验:拿UNDO_LOG 中的afterImage与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。
  4. 根据UNDO_LOG中的beforeImage和业务SQL的相关信息生成并执行回滚的语句:
  5. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给TC。

3.二阶段-提交

  1. 收到TC的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
  2. 异步任务阶段的分支提交请求将异步和批量地删除相应UNDO_LOG记录。

相关文章:

Seata1.5.2学习(二)——使用分布式事务锁@GlobalLock

目录 一、创建数据库 二、配置consumer-service 1.pom.xml 2.application.properties 3.启动类 4.其他代码 三、配置provider-service 1.pom.xml 2.application.properties 3.启动类 4.其他代码 四、分布式事务问题演示与解决办法 (一)分布式事务问题演示 (二)…...

三级分类bug解决

文章目录 前端后端 前端 <!DOCTYPE html> <html xmlns:th"http://www.thymeleaf.org" lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…...

华为 网络安全 认证

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 华为 网络安全 认证&#xff1a;保障信息安全的重要一环 在数字化时代的今天&#xff0c;网络安全成为了企业和个人都需要高度重视的问题。尤其是在企业信息化的…...

企业金融数字场景平台:架构设计、实践与未来趋势

随着数字化转型的深入&#xff0c;企业金融领域正经历着前所未有的变革。中国民生银行信息科技部赵鑫团队提出的《企业金融数字场景平台》架构设计&#xff0c;不仅展现了金融科技的前沿应用&#xff0c;也为行业提供了宝贵的实践经验和未来发展的新视角。 架构设计&#xff1…...

网络运维学习笔记 019 HCIA-Datacom综合实验03

文章目录 综合实验3实验需求一&#xff1a;A公司网络规划二&#xff1a;B公司网络规划 配置一、ip、vlan、vlanif&#xff0c;stp、eth-trunkSW1SW2SW3R1 二、ospfSW1R1 三、NATR1ISP 四、拒绝ping允许httpSW1 五、右半部分vlan、dhcp、ospf、NATSW4R2 综合实验3 实验需求 一&…...

Python--函数进阶(上)

1. 参数深入理解 1.1 参数传递的内存机制 Python中参数传递的是内存地址&#xff08;引用传递&#xff09;&#xff0c;而非值拷贝。这意味着&#xff1a; 可变对象&#xff08;列表、字典&#xff09;在函数内修改会影响外部变量。不可变对象&#xff08;数字、字符串&…...

Linux 常见面试题汇总

在当今数字化时代&#xff0c;Linux 作为一种开源、稳定且高效的操作系统&#xff0c;在服务器领域占据着举足轻重的地位。无论是运维工程师、开发人员还是系统管理员&#xff0c;掌握 Linux 相关知识都成为了必备技能。这篇博客将为大家汇总一些常见的 Linux 面试题&#xff0…...

网络运维学习笔记 015网工初级(HCIA-Datacom与CCNA-EI)NAT网络地址转换

文章目录 NAT(Network Address Translation&#xff0c;网络地址转换)思科&#xff1a;1&#xff09;PAT2&#xff09;静态端口转换 华为&#xff1a;1&#xff09;EasyIP2&#xff09;NAT Server静态NAT&#xff1a;动态NAT&#xff1a;实验1&#xff1a;在R1上配置NAPT让内网…...

蓝桥杯刷题25.2.22|打卡

一、幸运数 3491 谨记&#xff1a;使用函数&#xff0c;拆分成多个小问题&#xff0c;不容易出错 #include <iostream> using namespace std; //计算位数 int check(int a){int count0;while(a){aa/10;count;}return count; } bool fun(int sum){int countcheck(sum);int…...

学习笔记-沁恒第五讲-米醋

一&#xff0c;设置音量 上次 这次 #include "uart.h" #include "debug.h" void audio_init() { Usart3_Init(); } void audio_play(u8 num) { u8 string[]{0x7e,0x05,0x41,0x00,num,0x05^0x41^0x00^num,0xef}; u8 i; for(i0;i<7;i) { USART_Se…...

骁勇善战的量化利器:多因子模型【量化理论】

我叫补三补四&#xff0c;很高兴见到大家&#xff0c;欢迎一起学习交流和进步 今天来讲一讲alpha策略制定后的测试问题 风险模型雏形 股票因子受多种因素影响&#xff0c;其价格由多种因素决定&#xff0c;所谓的多因子策略就是要发掘诸如此类的因子&#xff0c;以一种合理的方…...

Android Loader机制解析

参考: Android Loader 机制...

使用Docker部署SearXNG

SearXNG 搜索引擎 SearXNG 是一个整合了超过70个搜索服务结果的免费的私有互联网搜索引擎&#xff0c;用户不会被网站跟踪或被建立档案进行特征分析&#xff0c;良好地保障了用户的隐私。知识库可以有效地弥补大模型的知识欠缺问题&#xff0c;但依旧无法补充或弥补知识库和大…...

C# ConcurrentQueue 使用详解

总目录 前言 在C#多线程编程中&#xff0c;数据共享如同走钢丝——稍有不慎就会引发竞态条件&#xff08;Race Condition&#xff09;或死锁。传统Queue<T>在并发场景下需要手动加锁&#xff0c;而ConcurrentQueue<T>作为.NET Framework 4.0 引入的线程安全集合&a…...

armv7l

在 **ARMv7l** 中&#xff0c;最后的字符是字母 **“l”**&#xff08;小写字母 “L”&#xff09;&#xff0c;而不是数字 **“1”**。 --- ### 1. **ARMv7l 的含义** - **ARMv7**&#xff1a;指的是 **ARM 架构的第 7 代版本**&#xff0c;是一种广泛应用于嵌入式系统&…...

spring中关于Bean的复习(IOC和DI)

文章目录 1.spring程序开发步骤1.1 导入spring开发的基本包坐标1.2 编写Dao接口和实现类1.3 创建spring核心配置文件1.4 在spring配置文件中配置UserDaoImpl1.5 使用Spring的Api获得Bean实例 2. Bean实例化的三种方式2.1 无参构造方法实例化2.2 工厂静态方法实例化2.3 工厂实例…...

Docker内存芭蕾:优雅调整容器内存的极限艺术

title: “&#x1f4be; Docker内存芭蕾&#xff1a;优雅调整容器内存的极限艺术” author: “Cjs” date: “2025-2-23” emoji: “&#x1fa70;&#x1f4a5;&#x1f4ca;” 当你的容器变成内存吸血鬼时… &#x1f680; 完美内存编排示范 &#x1f4dc; 智能内存管家脚本…...

一周学会Flask3 Python Web开发-flask3上下文全局变量session,g和current_app

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili flask3提供了session,g和current_app上下文全局变量来方便我们操作访问数据。 以下是一个表格&#xff0c;用于比较Flask中的…...

【蓝桥杯单片机】客观题

一、第十三届省赛&#xff08;一&#xff09; 二、第十三届省赛&#xff08;二&#xff09;...

QT中经常出现的用法:组合

在 C 中&#xff0c;一个类包含另一个类的对象称为组合&#xff08; Composition &#xff09;。这是一种常见的设计模式&#xff0c;用 于表示一个类是由另一个类的对象组成的。这种关系通常表示一种 " 拥有 " &#xff08; "has-a" &#xff09;的关系。…...

下载CentOS 10

1. 进入官网&#xff1a;https://www.centos.org/ 2. 点击右上角的Download进入下载页面。 3. 选择对应的CPU架构&#xff0c;点击ISOs下面的Mirrors开始下载。...

第9章:LangChain让大模型结构化输出

文章详细介绍了LangChain4j中如何使用结构化输出&#xff08;Structured Outputs&#xff09;。主要内容包括&#xff1a; JSON Schema&#xff1a;通过指定JSON Schema&#xff0c;LLM可以生成符合结构的输出。工具&#xff08;Tools&#xff09;&#xff1a;通过工具调用实现…...

ES6 新特性,优势和用法?

ES6 新特性&#xff0c;优势和用法&#xff1f; ES6&#xff08;ECMAScript 2015&#xff09;引入了许多新特性&#xff0c;这些特性让 JavaScript 变得更加强大、简洁和易于使用。下面为你详细介绍一些常见的 ES6 新特性、它们的优势以及用法。 1. 块级作用域&#xff1a;le…...

简说spring 的设计模式

spring 的设计模式&#xff08;23种…) &#xff08;面试题&#xff09;说说BeanFactory和FactoryBean的实现原理和区别&#xff1f; spring 中你还知道哪些设计模式&#xff1f;&#xff1f; 1.简单工厂模式 实质&#xff1a; 由一个工厂类根据传入的参数&#xff0c;动态决…...

【 Avalonia UI 语言国际化 I18n】图文结合教学,保姆级教学,语言国际化就是这么简单(.Net C#)

完整项目地址 github : https://github.com/Crazy-GrowUp/AvaloniaI18nTest/tree/master gitee :https://gitee.com/jack_of_disco/avalonia-i18n-test 0.项目新建 Properties 文件夹 对应的项目配置文件里面就会增加 <Folder Include"Properties\" /> 1.项…...

Spring Boot 日志管理(官网文档解读)

摘要 本篇文章详细介绍了SpringBoot 日志管理相关的内容&#xff0c;文章主要参考官网文章的描述内容&#xff0c;并在其基础上进行一定的总结和拓展&#xff0c;以方便学习Spring Boot 的小伙伴能快速掌握Spring Boot 日志管理相关的内容。 日志实现方式 Sping Boot 的日志管…...

Nginx学习笔记:常用命令端口占用报错解决Nginx核心配置文件解读

Nginx 1. 基础命令1.1 重新加载systemd配置1.2 停止Nginx服务1.3 启动Nginx服务1.4 重启Nginx服务1.5 查看Nginx服务状态1.6 测试配置和重载Nginx 2. 额外命令2.1 启用开机自启2.2 禁用开机自启2.3 强制关闭所有Nginx进程 3. Nginx端口占用解决方案3.1 查找占用端口8090的进程3…...

【玩转 Postman 接口测试与开发2_020】(完结篇)DIY 实战:随书示例 API 项目本地部署保姆级搭建教程(含完整调试过程)

《API Testing and Development with Postman》最新第二版封面 文章目录 最新版《Postman 接口测试与开发实战》示例 API 项目本地部署保姆级搭建教程1 前言2 准备工作3 具体部署3.1 将项目 Fork 到自己名下3.2 创建虚拟环境并安装依赖3.3 初始运行与项目调试 4 示例项目的用法…...

高清下载油管视频到本地

下载工具并安装: yt-dlp官网地址&#xff1a; GitHub - yt-dlp/yt-dlp: A feature-rich command-line audio/video downloader ffmpeg官网地址&#xff1a; Download FFmpeg 注&#xff1a;记住为其添加环境变量 操作命令&#xff1a; 该指令表示以720p码率下载VIDEO_UR…...

网络运维学习笔记 021 HCIA-Datacom新增知识点02 SDN与NFV概述

SDN与NFV概述 经典IP网络是一个分布式的、对等控制的网络。 每台网络设备存在独立的数据平面、控制平面和管理平面。 设备的控制平面对等的交互路由协议&#xff0c;然后独立的生成数据平面指导报文转发。 它的优势在于设备与协议解耦&#xff0c;厂家间的兼容性较好且故障场景…...