springcloud分布式事务

文章目录

  • 一.为什么引入分布式事务?
  • 二.理论基础
    • 1.CAP定理
    • 2.BASE理论
  • 三.Seata
    • 1.微服务集成Seata
    • 2.XA模式(掌握)
    • 3.AT模式(重点)
    • 4.TCC模式(重点)
    • 5.Saga模式(了解)
  • 四.四种模式对比
  • 五.Seata高可用


一.为什么引入分布式事务?

事务的ACID原则

在这里插入图片描述

在大型的微服务项目中,每一个微服务都可能包含一个独立的数据库,当单个数据库的操作由于某种原因进行回滚操作了,其他数据库也会进行回滚操作吗?

设想以下案例:

微服务下单业务,在下单时会调用订单服务,创建订单并写入数据库。然后订单服务调用账户服务和库存服务:

  • 账户服务负责扣减用户余额
  • 库存服务负责扣减商品库存

在这里插入图片描述

但是当库存数据库中的库存数量小于扣减数呢?库存数据库会回滚,但是账户服务的数据库和订单服务的数据库依旧执行成功,这并不是我们希望看到的效果

在这里插入图片描述

所以说在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。

二.理论基础

1.CAP定理

1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标:

  • Consistency(一致性)
  • Availability(可用性)
  • Partition tolerance (分区容错性)

Eric Brewer 说,分布式系统无法同时满足这三个指标。
这个结论就叫做 CAP 定理。

在这里插入图片描述

CAP定理- Consistency

Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致

当进行数据更改的时候,node01的数据必须同步更新给node02,不然会导致查询出的数据不一致

在这里插入图片描述

CAP定理- Availability

Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝

在这里插入图片描述

CAP定理-Partition tolerance

Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。

Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务

所以说,Partition tolerance(分区容错):即系统在网络发生故障或分区时,仍然能够保持正常的运行

在这里插入图片描述

假设ode03和node02之间出现了网络故障,node01和node02是一个分区,而node03是一个分区

当出现分区时,如果想保证数据的一致性即Consistency,那么就必须等待node02和node03之间的网络恢复,从而node02能将数据同步给node03,但此时不能满足Availability (可用性),因为在等待网络恢复过程中,nbode03不能够进行访问

如果想保证Availability (可用性),那么node02分区和node03分区的数据就会不一致,从而出现不一致性,即没有满足Consistency(一致性)

可以发现,由于两两互斥,所以三种特性都不能同时满足

思考:elasticsearch集群是CP还是AP?

ES集群出现分区时,故障节点会被剔除集群,数据分片会重新分配到其它节点,保证数据一致。因此是低可用性,高一致性,属于CP

2.BASE理论

BASE理论是对CAP的一种解决思路,包含三个思想:

  • Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。

  • Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。

  • Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。


而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:

  • AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。
  • CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。

分布式事务模型

解决分布式事务,各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)。

这里的子系统事务,称为分支事务有关联的各个分支事务在一起称为全局事务

在这里插入图片描述

三.Seata

Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。

官网地址: http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析。

Seata架构

Seata事务管理中有三个重要的角色:

  • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。

  • TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。

  • RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

在这里插入图片描述

Seata提供了四种不同的分布式事务解决方案:

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入

  • TCC模式:最终一致的分阶段事务模式,有业务侵入

  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式

  • SAGA模式:长事务模式,有业务侵入


部署TC服务

需要注意:在配置Seata配置文件的时候,group和dataId必须和nacos配置管理的Data Id和Group一致

在这里插入图片描述
在这里插入图片描述

1.微服务集成Seata

1.引入seata相关依赖:

        <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><!--版本较低,1.3.0,因此排除--><exclusion><artifactId>seata-spring-boot-starter</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><!--seata starter 采用1.4.2版本--><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>${seata.version}</version></dependency>

2.配置application.yml,让微服务通过注册中心找到seata-tc-server:

seata:registry:# TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址# 参考tc服务自己的registry.conf中的配置,# 包括:地址、namespace、group、application-name 、clustertype: nacosnacos: # tcserver-addr: 127.0.0.1:8848namespace: ""group: DEFAULT_GROUPapplication: seata-tc-server # tc服务在nacos中的服务名称username: nacospassword: nacostx-service-group: seata-demo # 事务组,根据这个获取tc服务的cluster名称service:vgroup-mapping: # 事务组与TC服务cluster的映射关系seata-demo: SH

2.XA模式(掌握)

XA模式原理

XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。

在这里插入图片描述
在这里插入图片描述

seata的XA模式做了一些调整,但大体相似:

  • RM一阶段的工作:
    • 注册分支事务到TC
    • 执行分支业务sql但不提交
    • 报告执行状态到TC
      TC二阶段的工作:
  • TC检测各分支事务执行状态
    • 如果都成功,通知所有RM提交事务
    • 如果有失败,通知所有RM回滚事务
      RM二阶段的工作:
  • 接收TC指令,提交或回滚事务

在这里插入图片描述
XA模式的优点是什么?

  • 事务的强一致性,满足ACID原则。
  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差,低可用
  • 数据操作导致的不一致需要进行回滚操作,故需要依赖关系型数据库实现事务

实现XA模式

Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:

1.修改application.yml文件(每个参与事务的微服务),开启XA模式
在这里插入图片描述
2.给发起全局事务的入口方法添加 @GlobalTransactional注解,本例中是OrderServiceImpl中的create方法:
在这里插入图片描述

3.AT模式(重点)

AT模式原理

AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。

阶段一RM的工作:

  • 注册分支事务

  • 记录undo-log(数据快照)

  • 执行业务sql并提交

  • 报告事务状态

阶段二提交时RM的工作:

  • 删除undo-log即可

阶段二回滚时RM的工作:

  • 根据undo-log恢复数据到更新前

在这里插入图片描述

例如,

一个分支业务的SQL是这样的:update tb_account set money = money - 10 where id = 1

在这里插入图片描述

AT模式与XA模式最大的区别是什么?

XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。

XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。

XA模式强一致;AT模式最终一致


AT模式的脏写问题

假设有以下场景:

事务1和事务2都受AT模式的Seata管理,当事务1更改数据库中的字段时(例如money字段),首先是获取了DB锁,保存了此刻的字段值快照,当执行完业务后归还了DB锁并提交了事务,但事务2紧接着获得了DB锁操作了相同的字段,也提交了事务,此时字段(money)就进行了两次更改,但是事务1并不知道发生了除它以外的操作,更糟糕的是TC此时发现数据库操作失败需要进行回滚,事务1就立马按照数据快照的数据进行更新数据库,从而导致产生了脏写问题

在这里插入图片描述

那么该如何避免脏写问题呢?


AT模式的写隔离

所谓的写隔离其实就是对全局事务增加了全局锁,全局锁由TC记录,当前正在操作某行数据的事务,该事务持有全局锁,具备执行权。

还是刚才的例子,当业务1要提交事务之前,事务1拿到了全局锁,拥有这个字段的执行权(即受AT模式管理的Seata中的所有事务,对这个字段的操作权有且仅有事务1),TC会将事务,表,字段等信息存入数据库表中,当事务2想要操作相同字段并且提交的时候,它也需要获取这个字段的全局锁,但此时发现操作这个字段的全局锁已经有事务拿到了,事务2就只能进行等待状态(等待时间默认为30次,间隔为10ms,这样做是为了避免事务1进行回滚而事务2占用DB锁,但是事务1又占用了全局锁所造成的死锁状态),当等待时间过去后,事务1拿到了DB锁操作数据库回滚完成,释放全局锁,这个中间字段只有事务1在进行操作,成功避免了脏写问题
在这里插入图片描述

但是如果不是受Seata管理的事务2呢?因为不受Seata管理就没有全局锁,事务2就可以自由操作和事务1相同的字段了(前提是拿到DB锁)

AT模式中引入了两个快照机制,一个是before-image(操作前的数据快照)和after-image(操作后的数据快照),当事务1进行回滚的时候会判断after-image是否和现在的字段数据一致,一致则回滚,不一致则记录异常,发送警告,人工介入

在这里插入图片描述


AT模式的优点:

  • 一阶段完成直接提交事务,释放数据库资源,性能比较好

  • 利用全局锁实现读写隔离

  • 没有代码侵入,框架自动完成回滚和提交

AT模式的缺点:

  • 两阶段之间属于软状态,属于最终一致

  • 框架的快照功能会影响性能,但比XA模式要好很多


实现AT模式

AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,因此实现非常简单。

1.导入Sql文件:seata-at.sql,其中lock_table导入到TC服务关联的数据库,undo_log表导入到微服务关联的数据库

2.修改application.yml文件,将事务模式修改为AT模式即可

seata:data-source-proxy-mode: AT # 开启数据源代理的AT模式

4.TCC模式(重点)

TCC模式原理

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:

  • Try:资源的检测和预留;

  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。

  • Cancel:预留资源释放,可以理解为try的反向操作。

TCC模式冻结机制与AT模式中的快照恢复机制有很大的相同点:

TCC冻结机制其实是相当于恢复操作的数据(例如金额),而AT模式快照恢复是恢复全部的数据(操作之前的数据),TCC实现了不需要全局锁进行隔离取之代替的是冻结(相当于对操作的数据绑定了一个事务).TCC模式可以和AT模式混用

在这里插入图片描述

TCC的工作模型图:

在这里插入图片描述

需要注意的是Confirm实现的不是提交具体的事务,提交具体的事务其实在资源预留已经做完了,Confirm实现的是删除冻结记录操作,相当于TC确认所有事务执行无误,提交的全局事务

总结:

TCC的优点是什么?

  • 一阶段完成直接提交事务,释放数据库资源,性能好

  • 相比AT模型,无需生成快照,无需使用全局锁,性能最强

  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC的缺点是什么?

  • 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦

  • 软状态,事务是最终一致

  • 需要考虑Confirm和Cancel的失败情况,做好幂等处理


补充:

1.什么是幂等?

"幂等"是指对同一操作的多次执行具有相同的效果,不会导致不一致或意外的结果。换句话说,如果一个操作是幂等的,那么无论执行多少次,最终的状态都是相同的。

2.在TCC中的空回滚和业务悬挂

空回滚:当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。

业务悬挂:对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂

简单来说,空回滚就是在业务还没执行之前就进行回滚了,业务悬挂是在空回滚后继续执行了try逻辑

在这里插入图片描述


案例:改造account-service服务,利用TCC实现分布式事务

在这里插入图片描述

业务分析:

为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:

在这里插入图片描述

业务逻辑:

在这里插入图片描述

实现方法:

1.声明TCC接口

TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明,语法如下:

@LocalTCC
public interface AccountTCCService {/*** Try逻辑,@TwoPhaseBusinessAction中的name属性要与当前方法名一致,用于指定Try逻辑对应的方法* @param userId* @param money*/@TwoPhaseBusinessAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")void deduct(@BusinessActionContextParameter("userId") String userId,@BusinessActionContextParameter("money") int money);/**** 二阶段confirm确认方法、可以另命名,但要保证与commitMethod一致** @param context 上下文,可以传递try方法的参数* @return boolean 执行是否成功     **/boolean confirm(BusinessActionContext context);/*** 二阶段回滚方法,要保证与rollbackMethod一致* @param context* @return*/boolean cancel(BusinessActionContext context);
}

2.导入冻结表

3.实现AccountTCCService接口

package cn.itcast.account.service.impl;import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {@Autowiredprivate AccountMapper accountMapper;@Autowiredprivate AccountFreezeMapper accountFreezeMapper;@Override@Transactionalpublic void deduct(String userId, int money) {//获取全局事务idString xid = RootContext.getXID();AccountFreeze oldFreeze = accountFreezeMapper.selectById(xid);if(oldFreeze != null){return;}accountMapper.deduct(userId, money);//记录冻结的金额和事务AccountFreeze freeze = new AccountFreeze();freeze.setUserId(userId);freeze.setFreezeMoney(money);freeze.setState(AccountFreeze.State.TRY);freeze.setXid(xid);accountFreezeMapper.insert(freeze);}@Overridepublic boolean confirm(BusinessActionContext context) {String xid = context.getXid();int count = accountFreezeMapper.deleteById(xid);return count == 1;}@Overridepublic boolean cancel(BusinessActionContext context) {//查询冻结记录String xid = context.getXid();String userId = context.getActionContext("userId").toString();AccountFreeze freeze = accountFreezeMapper.selectById(xid);//空回滚判断if (freeze == null) {freeze = new AccountFreeze();freeze.setUserId(userId);freeze.setFreezeMoney(0);freeze.setState(AccountFreeze.State.CANCEL);freeze.setXid(xid);accountFreezeMapper.insert(freeze);return true;}//幂等处理if(freeze.getState() == AccountFreeze.State.CANCEL){return true;}//恢复可用余额accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());//将冻结的金额清零,状态改为CANCELfreeze.setFreezeMoney(0);freeze.setState(AccountFreeze.State.CANCEL);int count = accountFreezeMapper.updateById(freeze);return count == 1;}
}

需要注意的是:

1.空回滚判断需要在回滚业务中编写,且需要将记录插入到冻结表中,便于业务悬挂做出判断

2.对于业务悬挂需要先查询冻结表中是否有记录,如果有,一定是CANCEL执行过(因为对于没有回滚过的业务,在执行业务结束后对应的冻结表的字段一定为空,所以说有记录一定为CANCEL执行过),对于CANCEL执行过的事务说明全局事务已经完成,就必须拒绝此刻的try操作,否则会引起业务悬挂


5.Saga模式(了解)

Saga模式是SEATA提供的长事务解决方案。也分为两个阶段:

  • 一阶段:直接提交本地事务

  • 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚

Saga模式优点:

  • 事务参与者可以基于事件驱动实现异步调用,吞吐高

  • 一阶段直接提交事务,无锁,性能好

  • 不用编写TCC中的三个阶段,实现简单

缺点:

  • 软状态持续时间不确定,时效性差

  • 没有锁,没有事务隔离,会有脏写

在这里插入图片描述


四.四种模式对比

在这里插入图片描述

五.Seata高可用

什么是异地容灾?

确保在发生灾难性事件或紧急情况时,组织的业务能够迅速恢复正常运行。异地容灾的主要目标是最小化业务中断,并确保在灾难性事件后能够迅速恢复关键业务功能。

TC的异地多机房容灾架构

TC服务作为Seata的核心服务,一定要保证高可用和异地容灾。

在这里插入图片描述

当一个地方的集群出现问题的时候,TC服务能快速切换到另外一个集群,故需要实现配置管理的热更新,就需要nacos配置管理来实现了,实现方式主要是在nacos统一配置管理,然后在各个微服务读取nacos中的properties文件即可(在每个微服务的yml文件指定nacos的地址和配置文件名称即可)

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

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

相关文章

案例课4——智齿客服

1.公司介绍 智齿科技&#xff0c;一体化客户联络中心解决方案提供商。提供基于「客户联络中心」场景的一体化解决方案&#xff0c;包括公域私域、营销服务、软件BPO的三维一体化。 智齿科技不断整合前沿的人工智能及大数据技术&#xff0c;已构建形成呼叫中心、机器人「在线语音…

Python中函数的递归调用

函数调用自己的编程方式被称为函数的递归调用。递归通常能够将一个大型的复杂问题的递归条件&#xff0c;一层一层的回溯到终止条件&#xff0c;然后再根据终止条件的运算结果&#xff0c;一层一层的递进运算到满足全部的递归条件。它能够使用少量程序描述出解题过程中的重复运…

主机访问Android模拟器网络服务方法

0x00 背景 因为公司的一个手机app的开发需求&#xff0c;要尝试链接手机开启的web服务。于是在Android Studio的Android模拟器上尝试连接&#xff0c;发现谷歌给模拟器做了网络限制&#xff0c;不能直接连接。当然这个限制似乎从很久以前就存在了。一直没有注意到。 0x01 And…

分销电商结算设计

概述 分销电商中涉及支付与结算&#xff1b;支付职责是收钱&#xff0c;结算则是出钱给各利益方&#xff1b; 结算核心围绕业务模式涉及哪些费用&#xff0c;以及这些费用什么时候通过什么出资渠道&#xff0c;由谁给到收方利益方&#xff1b; 结算要素组成费用项结算周期出…

区块链的可拓展性研究【03】扩容整理

为什么扩容&#xff1a;在layer1上&#xff0c;交易速度慢&#xff0c;燃料价格高 扩容的目的&#xff1a;在保证去中心化和安全性的前提下&#xff0c;提升交易速度&#xff0c;更快确定交易&#xff0c;提升交易吞吐量&#xff08;提升每秒交易量&#xff09; 目前方案有&…

详解进程管理(银行家算法、死锁详解)

处理机是计算机系统的核心资源。操作系统的功能之一就是处理机管理。随着计算机的迅速发展&#xff0c;处理机管理显得更为重要&#xff0c;这主要由于计算机的速度越来越快&#xff0c;处理机的充分利用有利于系统效率的大大提高&#xff1b;处理机管理是整个操作系统的重心所…

前后端联调神器《OpenAPI-Codegen》

在后端开发完接口之后&#xff0c;前端如果再去写一遍接口来联调的话&#xff0c;会很浪费时间&#xff0c;这个时候使用OpenAPI接口文档来生成Axios接口代码的话&#xff0c;会大大提高我们的开发效率。 Axios引入 Axios是一个基于Promise的HTTP客户端&#xff0c;用于浏览器…

Go压测工具

前言 在做Go的性能分析调研的时候也使用到了一些压测方面的工具&#xff0c;go本身也给我们提供了BenchMark性能测试用例&#xff0c;可以很好的去测试我们的单个程序性能&#xff0c;比如测试某个函数&#xff0c;另外还有第三方包go-wrk也可以帮助我们做http接口的性能压测&…

C# 任务并行类库Parallel调用示例

写在前面 Task Parallel Library 是微软.NET框架基础类库&#xff08;BCL&#xff09;中的一个&#xff0c;主要目的是为了简化并行编程&#xff0c;可以实现在不同的处理器上并行处理不同任务&#xff0c;以提升运行效率。Parallel常用的方法有For/ForEach/Invoke三个静态方法…

Element-UI定制化Tree 树形控件

1.复制 说明&#xff1a;复制Tree树形控件。 <script> export default {data() {return {data: [{label: 一级 1,children: [{label: 二级 1-1,children: [{label: 三级 1-1-1}]}]}, {label: 一级 2,children: [{label: 二级 2-1,children: [{label: 三级 2-1-1}]}, {l…

Linux:进程优先级与命令行参数

目录 1.进程优先级 1.1 基本概念 1.2 查看系统进程 1.3 修改进程优先级的命令 2.进程间切换 2.1 相关概念 2.2 Linux2.6内核进程调度队列&#xff08;了解即可&#xff09; 3.命令行参数 1.进程优先级 1.1 基本概念 cpu资源分配的先后顺序&#xff0c;就是指进程的优…

【C++】在类外部定义成员函数时,不应该再次指定默认参数值

2023年12月10日&#xff0c;周日下午 错误的代码 #include<iostream>class A { public:void fun(int a10); };void A::fun(int a10) //<----在这里报错 {}int main() {} 正确的代码 代码目前有一个问题&#xff0c;主要是在类外部定义成员函数时&#xff0c;不应该…

解密QQ号——C语言

题目&#xff1a; 有一串已加密的数字“6 3 1 7 5 8 9 2 4”解密规则&#xff1a;首先将第1个数字删除&#xff0c;紧接着将第2个数字放到这串数字的末尾&#xff0c;再将第3个数字删除并将第4个数字放到这串数字的末尾&#xff0c;再将第5个数删除 代码实现&#xff1a; #inc…

利用Node.js和cpolar实现远程访问,无需公网IP和路由器设置的完美解决方案

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

ESP32网络编程-OTA方式升级固件(基于Web浏览器)

OTA方式升级固件(基于Web浏览器) 文章目录 OTA方式升级固件(基于Web浏览器)1、ESP32的OTA介绍2、OTA升级固件方式3、软件准备4、硬件准备5、代码实现6、一种优雅方式实现Web方式OTA升级6.1 基础OTA代码6.2 新固件库代码在前面的文章中,我们在Arduino IDE的网络端口中,实现…

LeetCode 77.组合

题目&#xff1a; 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 方法&#xff1a;灵神-组合型回溯 剪枝 class Solution {private int k;private final List<Integer> path new ArrayList<>();…

反序列化 [网鼎杯 2020 朱雀组]phpweb 1

打开题目 我们发现这个页面一直在不断的刷新 我们bp抓包一下看看 我们发现index.php用post方式传了两个参数上去&#xff0c;func和p 我们需要猜测func和p两个参数之间的关系&#xff0c;可以用php函数MD5测一下看看 我们在响应处得到了一串密文&#xff0c;md5解密一下看看 发…

Windows11安装使用Oracle21C详细步骤<图文保姆级>新版本

Windows11安装使用Oracle21C详细步骤<图文保姆级>新版本 Database Software Downloads | Oracle 中国 下载完成后解压缩 双击setup.exe 打开安装页面 同意下一步 更改自己的路径点击下一步 输入密码 下一步安装等待即可 等待加载配置时间有点久 完成即可 使用 搜索…

【Kubernetes】四层代理Service

Service四层代理 一、Service概念原理1.1、为什么要有Service1.2、Service概述1.3、工作原理1.4、三类IP地址【1】Node Network&#xff08;节点网络&#xff09;【2】Pod network&#xff08;pod 网络&#xff09;【3】Cluster Network&#xff08;服务网络&#xff09; 二、S…

C++之异常处理

C语言传统的处理错误的方式 传统的错误处理机制&#xff1a; 1. 终止程序, 如assert. 缺陷: 用户难以接受, 如发生内存错误, 除0错误时就会终止程序. 如果assert括号里面的表达式结果为假, 那么assert就会中断程序并报错, 所以使用assert可以帮助我们在程序判断一些可能出错的…