【SpringCloud——Seata分布式事务管理框架】

news2024/9/20 8:53:56

一、分布式事务存在的问题

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

分布式事务和传统形式的事务区别有什么?众所周知,我们采用微服务框架开发项目时,不同服务之间通过相互调用的方式完成业务处理,用以下案例来描述分布式业务存在的问题:

假设我们此时现在有一个商城系统,其中包括下单系统、用户系统、仓库系统。当我们进行下单时,就需要创建订单信息,同时需要对用户的余额进行扣减,还需要对仓储系统的商品数量进行减少,由于每个系统分别负责一部分的业务,且其拥有独立的数据库信息,因此我们就会有三个事务,订单创建、余额扣减、商品剩余量扣减。

下单系统主要代码如下:

    public Long create(Order order) {
        // 创建订单
        orderMapper.insert(order);
        try {
            // 扣用户余额
            accountClient.deduct(order.getUserId(), order.getMoney());
            // 扣库存
            storageClient.deduct(order.getCommodityCode(), order.getCount());

        } catch (FeignException e) {
            log.error("下单失败,原因:{}", e.contentUTF8(), e);
            throw new RuntimeException(e.contentUTF8(), e);
        }
        return order.getId();
    }

用户余额扣减代码:

    public void deduct(String userId, int money) {
        log.info("开始扣款");
        try {
            accountMapper.deduct(userId, money);
        } catch (Exception e) {
            throw new RuntimeException("扣款失败,可能是余额不足!", e);
        }
        log.info("扣款成功");
    }

商品数量扣减代码:

    public void deduct(String commodityCode, int count) {
        log.info("开始扣减库存");
        try {
            storageMapper.deduct(commodityCode, count);
        } catch (Exception e) {
            throw new RuntimeException("扣减库存失败,可能是库存不足!", e);
        }
        log.info("扣减库存成功");
    }

数据库信息如下:

用户:

订单:

商品:

 我们假设每件商品为200元,此时账户余额为1000元,当我们下单一件商品时,此时就会创建一个订单,且用户余额和商品数量分别减少200元和1件,当我们再次同时下单五件该商品时,此时显然用户的余额已经不足以支付1000元了,但是我们的订单已经创建,且事务已经提交,此时余额扣减就会触发事务回滚操作,即扣减失败了,但是订单已经生成了,且商品剩余量也已经减少了,这三个操作分别是独立的事务,任意一个失败都无法使其余两个事务回滚,那这显然不是我们想要的效果,我们理想的状态就是任意一个事务回滚了,其余两个事务也将进行回滚。

二、分布式事务理论基础

1、CAP定理

CAP是指:

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

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

1、一致性

用户访问分布式系统中的任意节点,得到的数据必须是一致的。

 节点1的数据一旦发生修改,节点2的数据必须进行同步,与节点1的数据保持一致。

2、可用性

用户访问集群中的任意健康节点时,必须能得到响应,而不是超时或者拒绝。

 当节点3发生故障时,就会对请求进行阻塞或者拒绝,此时节点3就是不可用的。

3、分区容错性

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

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

 起初节点1、2、3是互相连接的,即任意节点数据发生变化,其余两个节点的数据也将进行同步修改,但是假设节点3与其他两个节点的网络连接断开了,但是节点本身并没有故障,就会形成新的分区,此时一旦有人对节点2的数据进行了修改,并将修改信息同步到了节点1,此时用户访问不同的节点,假设访问节点1之后我们又访问了节点3,此时拿到的结果就是不一致的。此时就不满足一致性,如果我们非要满足一致性,我们怎么办?让节点3进入短暂的禁止访问状态,等待和节点2的网络通信回复,在此期间对于到来的一切请求都进行拒绝或者阻塞,这样一来确实一致性问题得到了解决,但是我们又说了可用性,即系统健康时就必须对请求做出响应,此时我们的节点3只是和其余两个节点不通,并没有不健康,所以这就是一种悖论,我们只能在一致性和可用性之间做出抉择!

CAP定理的主要内容:

  • 分布式系统节点通过网络连接,一定会出现分区问题(P)
  • 当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足

CP:保证了系统的一致性,但是牺牲了系统的可用性。

AP:保证了系统的可用性,但是牺牲了系统的一致性。

ES是CP还是AP:

此前我们搭建的ES集群,当其中某一个节点挂掉时,另外两个节点会把该节点的数据复制到自己本地,当出现故障的节点重新上线了,又会将copy过来的数据返还给该节点,这样做的目的是为了保证系统的一致性,从而牺牲掉了可用性,因此ES是CP。

2、BASE理论

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

  • Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
  • Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。
  • Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

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

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

解决分布式事务,各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)。 这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务。

 

三、认识Seata

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

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

 

四、分布式事务实践

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

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
  • TCC模式:最终一致的分阶段事务模式,有业务侵入
  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
  • SAGA模式:长事务模式,有业务侵入

1、XA模式

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

XA是规范,目前主流的数据库都实现了这种规范,实现的原理都是基于两阶段提交。

  • 正常情况:

  • 异常情况:

一阶段:

  • 事务协调者通知每个事务参与者分别执行本地事务
  • 本地事务执行完成后向事务协调者告知事务的执行状态,此时本地事务并没有提交,继续持有数据库锁

二阶段:

  • 事务协调者根据一阶段的报告判断下一步操作

     ①、如果一阶段所有分支事务都执行成功,则告知所有事务参与者提交事务。

     ②、如果一阶段任意一个事务失败,则告知所有事务参与者回滚事务。

下面我们来看看Seata当中的XA模型:

RM一阶段的工作:

  1. 注册分支事务到TC
  2. 执行分支业务sql但不提交
  3. 报告执行状态给TC

TC二阶段的工作:

  • TC检测各分支事务的执行状态

       a.如果都成功,通知所有RM提交事务

       b.如果有失败,通知所有RM回滚事务

RM二阶段的工作:

  • 接收TC的指令,提交或回滚事务。

 XA模式的优缺点:

  • 优点:
  1. 事务的强一致性,满足ACID原则。
  2. 常用数据库都支持,实现简单,并且没有代码侵入。
  • 缺点:
  1. 因为一阶段需要锁定数据库资源,等待二阶段结束才会释放,性能较差。
  2. 依赖关系型数据库实现事务。

如何利用Seata实现XA模式:

1、修改配置文件(每个参与事务的微服务),开启XA模式

seata:
  data-source-proxy-mode: XA

2、给发起全局事务的入口方法添加@GlobalTransactional注解

3、重启服务测试

2、AT模式

AT模式是对XA模式的弊端进行了完善(执行事务完并不会持续占有数据库资源)。我们来看看AT模式具体是怎么做的:

        在XA模式当中,一阶段分支事务执行完事务后会等待TC的通知进行事务的提交或者事务的回滚,等待过程中会持有DB锁防止其他事务进行操作,这样会大大降低性能,在AT模式下,分支事务不需要等待TC的通知即可提交事务,但是会生成一份数据快照(undo_log),该快照记录了数据修改前和修改后的值,当TC通知分支事务进行事务提交或者回滚时,如果是提交,分支事务则仅需要删除生成的快照即可,如果是回滚,则需要根据快照当中的信息恢复数据。

案例:用户余额扣减

IDmoney
1100

有一个分支事务要执行的SQL为:

update tb_account set money = money - 10 where id = 1

AT模式下,该分支事务的执行流程如下:

一阶段:

1)TM发起并注册全局事务到TC

2)TM调用分支事务

3)分支事务准备执行业务SQL

4)RM拦截业务SQL,根据where条件查询原始数据,形成快照

{

  “id”:1,“money”:100

}

5)RM执行业务SQL,提交本地事务,释放DB锁,此时money=90

6)RM报告本地事务状态给TC

二阶段:

1)TM通知TC事务结束

2)TC检查分支事务状态

   a)都成功,则通知RM删除快照

   b)有失败,通知RM根据快照恢复数据

流程图:

 XA模式与AT模式的区别:

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

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

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

 AT模式的弊端——脏写问题

 解决方案就是引入全局锁的概念,在释放DB锁之前,先要拿到全局锁,避免同一时刻有另外一个事务来操作当前持有的数据。

 这个全局锁就限制了哪个事务可以操作该数据,当事务1执行业务SQL前,会先获取全局锁,TC就会进行记录,此时事务1释放DB锁去等待TC的通知(删除快照或根据快照修复数据),事务2拿到了DB锁开始执行业务SQL,当事务2尝试获取全局锁时,就会发现获取不到,因为全局锁现在被事务1所持有,此时假设恰好TC告知事务1进行数据回滚,事务1则会重新获取DB锁,但是DB锁此时被事务2所持有,这样就形成了死锁,怎么办?事务2在尝试获取一段时间全局锁后一直拿不到,就会放弃获取全局锁,此时事务1就拿到了DB锁,进行快照修复即可。前提是两个事务均是由seata控制的事务才会这样。假设事务2不是由seata控制的事务将会是怎样的?

我们上面说过,事务1记录undo_log时会记录数据修改前后的值,当我们进行数据恢复时,发现money此时为80,此时需要我们进行报警处理,人工的根据undo_log当中的数据进行数据恢复。

AT模式的优缺点:

AT模式的优点:

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

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

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

AT模式的缺点:

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

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

3、TCC模式

TCC模式与AT模式非常相似,每阶段都是独立事务,区别在于TCC是通过人工代码控制来实现数据恢复,需要实现三个方法:

  • Try:资源的检测和预留。
  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
  • Cancel:预留资源释放,可以理解为try的反向操作。

举例:一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。

  • 阶段一( Try ):检查余额是否充足,如果充足则冻结金额增加30元,可用余额扣除30

初始余额:

 余额充足,可以冻结:

此时,总金额 = 冻结金额 + 可用金额,数量依然是100不变。事务直接提交无需等待其它事务。

  • 阶段二(Confirm):假如要提交(Confirm),则冻结金额扣减30

确认可以提交,不过之前可用金额已经扣减过了,这里只要清除冻结金额就好了:

 此时,总金额 = 冻结金额 + 可用金额 = 0 + 70 = 70元

  • 阶段二(Canncel):如果要回滚(Cancel),则冻结金额扣减30,可用余额增加30

需要回滚,那么就要释放冻结金额,恢复可用金额:

TCC模型:

 可以看到,TCC模式和AT模式极其相似,但是区别在于,AT模式采用全局锁+undo_log的方式进行事务提交和回滚,这里我们不再使用锁,而是使用try-confirm-cancel的方式对操作进行记录。当TC通知分支事务可以提交的时候,分支事务直接执行confirm方法即可,通知回滚时则执行cancel方法。

TCC模式的优缺点:

TCC模式的每个阶段是做什么的?

  • Try:资源检查和预留

  • Confirm:业务执行和提交

  • Cancel:预留资源的释放

TCC的优点是什么?

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

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

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

TCC的缺点是什么?

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

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

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

 TCC模式的两个重要问题:事务悬挂和空回滚

  • 事务悬挂

对于已经空回滚的业务,之前被阻塞的try操作恢复,继续执行try,就永远不可能confirm或cancel ,事务一直处于中间状态,这就是业务悬挂

执行try操作时,应当判断cancel是否已经执行过了,如果已经执行,应当阻止空回滚后的try操作,避免悬挂

  • 空回滚

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

 执行cancel操作时,应当判断try是否已经执行,如果尚未执行,则应该空回

TCC模式代码实现: 

  • 引入资源预留表
CREATE TABLE `account_freeze_tbl` (
  `xid` varchar(128) NOT NULL,
  `user_id` varchar(255) DEFAULT NULL COMMENT '用户id',
  `freeze_money` int(11) unsigned DEFAULT '0' COMMENT '冻结金额',
  `state` int(1) DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',
  PRIMARY KEY (`xid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

其中:

  1. xid:是全局事务id

  2. freeze_money:用来记录用户冻结金额

  3. state:用来记录事务状态

  • 声明TCC接口
@LocalTCC
public interface AccountTCCService {

    @TwoPhaseBusinessAction(name = "deduct",commitMethod = "confirm",rollbackMethod = "cancel")
    void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,
                @BusinessActionContextParameter(paramName = "money") int money);

    boolean confirm(BusinessActionContext context);

    boolean cancel(BusinessActionContext context);
}
  • 编写实现类
@Service
@Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {
    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper accountFreezeMapper;


    @Override
    public void deduct(String userId, int money) {
        //0、获取事务ID
        String xid = RootContext.getXID();
        //判断业务悬挂,判断freeze中是否有冻结记录,如果有,一定是CANCEL执行过,我要拒绝业务
        AccountFreeze oldFreeze = accountFreezeMapper.selectById(xid);
        if (oldFreeze == null){
            //1、扣减可用余额
            accountMapper.deduct(userId,money);
            //2、冻结余额数据新增,事务状态
            AccountFreeze accountFreeze = new AccountFreeze();
            accountFreeze.setUserId(userId);
            accountFreeze.setFreezeMoney(money);
            accountFreeze.setState(AccountFreeze.State.TRY);
            accountFreeze.setXid(xid);
            accountFreezeMapper.insert(accountFreeze);
        }
    }

    @Override
    public boolean confirm(BusinessActionContext context) {
        //1、获取事务ID
        String xid = context.getXid();
        //2、根据ID删除冻结记录
        int count = accountFreezeMapper.deleteById(xid);
        return count == 1;

    }

    @Override
    public boolean cancel(BusinessActionContext context) {
        //1、获取事务ID
        String xid = context.getXid();
        //获取参数
        Map<String, Object> actionContext = context.getActionContext();
        String userId = (String) actionContext.get("userId");
        Integer money = (Integer) actionContext.get("money");
        //空回滚的判断,判断
        AccountFreeze accountFreeze1 = accountFreezeMapper.selectById(xid);
        if (accountFreeze1 == null){
            //证明try没执行,需要空回滚
            accountFreeze1 = new AccountFreeze();
            accountFreeze1.setUserId(userId);
            accountFreeze1.setFreezeMoney(0);
            accountFreeze1.setState(AccountFreeze.State.CANCEL);
            accountFreeze1.setXid(xid);
            accountFreezeMapper.insert(accountFreeze1);
            return true;
        }
        //判断幂等
        if (accountFreeze1.getState() == 2){
            //已经处理过CANCEL,无需重复处理
            return true;
        }
        //2、反向更新,恢复可用金额
        accountMapper.refund(userId,money);
        //3、将冻结金额清零,状态改为cancel
        AccountFreeze accountFreeze = new AccountFreeze();
        accountFreeze.setXid(xid);
        accountFreeze.setUserId(userId);
        accountFreeze.setFreezeMoney(0);
        accountFreeze.setState(AccountFreeze.State.CANCEL);
        int count = accountFreezeMapper.updateById(accountFreeze);
        return count == 1;
    }
}

4、SAGA模式

原理:

在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

Saga也分为两个阶段:

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

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

 SAGA模式优缺点:

优点:

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

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

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

缺点:

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

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

 5、四种模式对比

 

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

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

相关文章

【GIS教程】使用高程数据在UE5中创建真实山脉模型

在数字孪生项目中&#xff0c;我们经常需要使用真实的山脉地形作为城市模型展示的基础。然而&#xff0c;UE5的默认地形系统过于复杂&#xff0c;无法像3D模型那样进行实时修改。因此&#xff0c;本教程将指导您如何将高程山脉作为模型导入到UE5引擎中&#xff0c;而不是使用UE…

【微服务】SpringBoot 插件化开发模式详细总结

目录 一、前言 1.1 使用插件的好处 1.1.1 模块解耦 1.1.2 提升扩展性和开放性 1.1.3 方便第三方接入 1.2 插件化常用实现思路 二、Java常用插件实现方案 2.1 serviceloader方式 2.1.1 java spi 2.1.2 java spi 简单案例 2.2 自定义配置约定方式 2.2.1 添加配置文件…

36.SpringBoot实用篇—运维

目录 一、实用篇—运维。 &#xff08;1&#xff09;程序打包与运行&#xff08;Windows版&#xff09;。 &#xff08;2&#xff09;spring-boot-maven-plugin插件作用。 &#xff08;3&#xff09;程序打包与运行&#xff08;Linux版&#xff09;。 &#xff08;4&#…

syntax error: unexpected end of file

运行工程报错误&#xff1a;liporepo.sh: line 2: command not found liporepo.sh: line 19: syntax error: unexpected end of file Command PhaseScriptExecution failed with a nonzero exit code 由于.sh文件的格式为dos格式。而linux只能执行格式为unix格式的脚本。因为在…

【svopro】代码梳理

SVO2系列之深度滤波DepthFiltersvo_noteSVO&#xff08;SVO: fast semi-direct monocular visual odometry&#xff09;SVO 半直接视觉里程计【DepthFilter】深度滤波器【svopro】代码梳理 svo processFrame代码梳理 1.0 processFrame主流程1.1 sparseImageAlignment1.1.1 核心…

100天精通Golang(基础入门篇)——第6天: 深入解析Go语言中的运算符

&#x1f337; 博主 libin9iOak带您 Go to Golang Language.✨ &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &#x1f30a; 《I…

Kafka详解(二)

Kafka命令行操作 [aahadoop102 ~]$ cd /opt/module/kafka/bin/ [aahadoop102 bin]$ ll 可以看到自带了zookeeper主题命令行操作 查看操作主题命令需要的参数 [aahadoop102 kafka]$ bin/kafka-topics.sh重要的参数如下查看当前服务器中的所有topic [aahadoop102 kafka]$ bin…

Socket 编程:基础概念辨析

文章目录 参考Socket APIBSD UNIX 操作系统BSD UNIX 与 Socket API Socket套接字套接字地址套接字 VS 套接字地址套接字的表示方法 TCP 套接字与 UDP 套接字TCP 套接字监听套接字连接套接字 UDP套接字 TCP 服务器端与 TCP 客户端通信的基本流程服务器端客户端 参考 项目描述刘…

大学物理(上)-期末知识点结合习题复习(4)——质点运动学-动能定理 力做功 保守力与非保守力 势能 机械能守恒定律 完全弹性碰撞

目录 1.力做功 恒力作用下的功 变力的功 2.动能定理 3.保守力与非保守力 4.势能 引力的功与弹力的功 引力势能与弹性势能 5.保守力做功与势能的关系 6.机械能守恒定律 7.完全弹性碰撞 题1 题目描述 题解 题2 题目描述 题解 1.力做功 物体在力作用下移动做功…

软件测试商城项目优惠券超发问题该怎么测试?

在拼夕夕面试中&#xff0c;面试官问了一连串经典的问题&#xff1a;“优惠券库存是怎么扣减的&#xff1f;开发为了解决超发优惠券问题而设计的方案&#xff0c;你了解过吗&#xff1f;你又是如何测试的呢&#xff1f;” 当时听到这些问题还挺懵的&#xff0c;没遇到过超发问…

MIsc(5)

ningen 打开后是一张生物图片&#xff0c;利用010打开后文件头没问题的&#xff0c;但是在文件末尾好像是包含了一个txt文件 拿到kali里利用binwalk分离出一个zip文件&#xff0c;但是需要密码打开 既然提示了资环4位数字的密码&#xff0c;那就可以直接爆破 获得flag 小明的保…

KCD 北京站报名开启 | 云原生英雄帖,邀您京城聚首

各位云原生社区侠客&#xff0c; 仲夏十七&#xff0c;风云际会。 江湖号召&#xff0c;原生论道。 2023 年 6 月 17 日&#xff0c;京城将迎来一场关于云原生的武林盛会——Kubernetes Community Days (KCD) 2023 北京站。 经过一轮公开的议题招募&#xff0c;KCD Beijing…

Jetpack Compose —— Image

在 Jetpack Compose 中&#xff0c;Image 是一个重要的组件&#xff0c;用于显示图像和处理图像相关的操作。 一、Image在Compose中的简单使用 二、如何网络调用Image 一、Image在Compose中的简单使用 首先&#xff0c;让我们了解一下 Image 组件的基本用法。要在 Jetpack C…

复习之linux系统中的软件管理

一、linux系统中软件包 1.软件包的类型 "注意在rhel8中只能使用绿色软件,源码编译软件和rpm软件" 类型支持的条件DEBUBlinux DEBlinux&#xff08;用不了&#xff09;RPM#redhat centOS fadorabz2|gz|xz#1.需要源码安装需要编译 #2.绿色软件,直接可用 #ntfs-3g_nt…

2023下半年杭州/广州/东莞/深圳软考(中/高级)认证,这里报名

软考是全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;项目&#xff0c;是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试&#xff0c;既属于国家职业资格考试&#xff0c;又是职称资格考试。 系统集成…

如何成为一名全职创作者——程序员篇

哈喽大家好&#xff0c;我是咸鱼 今天跟大家分享一篇文章&#xff0c;这篇文章的作者 Gergely Orosz 是一名程序员&#xff0c;他从 Uber 辞职以后&#xff0c;就当起了全职创作者 他通过写文章、卖课程、做视频等谋生&#xff0c;今天这篇文章是他对这种商业模式的思考&…

内网渗透-windows远程用户管理

文章目录 0x01 获取window权限&#xff08;管理员&#xff09;0x02 添加用户&#xff0c;并且到管理员组0x03 开启远程桌面0x04 添加到远程桌面组0x05 进行远程0x06 免责声明 0x01 获取window权限&#xff08;管理员&#xff09; 使用cs上线用户 kali:192.168.253.234 window …

DVWA-10.XSS (DOM)

大约 “跨站点脚本 &#xff08;XSS&#xff09;”攻击是一种注入问题&#xff0c;其中恶意脚本被注入到原本良性和受信任的网站上。 当攻击者使用 Web 应用程序发送恶意代码&#xff08;通常以浏览器端脚本的形式&#xff09;时&#xff0c;就会发生 XSS 攻击&#xff0c; 给…

报错:HttpMessageNotReadableException: JSON parse error: Unexpected end-of-input

问题&#xff1a; 测试接口发送请求时后端报错&#xff1a;org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: (PushbackInputStream); line: 1,…

Ubuntu18.04安装jdk1.8

1. 下载jdk 下载地址&#xff1a;jdk1.8下载地址 选择需要下载的jdk 2. 安装jdk # 创建用于存放jdk的文件夹 sudo mkdir /usr/local/java# 解压jdk压缩文件到 /usr/local/java/ sudo tar -zxvf jdk-8u371-linux-x64.tar.gz -C /usr/local/java/# 在配置文件中添加java 环境变…