SpringCloud Alibaba Seata2.0分布式事务AT模式实践总结

这里我们划分订单、库存与支付三个module来实践Seata的分布式事务。

依赖版本(jdk17):

<spring.boot.version>3.1.7</spring.boot.version>
<spring.cloud.version>2022.0.4</spring.cloud.version>
<spring.cloud.alibaba.version>2022.0.0.0-RC2</spring.cloud.alibaba.version>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope>
</dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring.cloud.version}</version><type>pom</type><scope>import</scope>
</dependency>
<!--springcloud alibaba 2022.0.0.0-RC2-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring.cloud.alibaba.version}</version><type>pom</type><scope>import</scope>
</dependency>

【1】构建基础module

这里我们构建order、account和storage三个module,也就是演示订单的流程:创建订单、扣减库存、扣减账户余额。

Seata分TC、TM和RM三个角色,TC(Server端)为单独服务端部署,TM和RM(Client端)由业务系统集成。

整体module如下,module之间使用openfign进行调用。以订单模块为例进行说明,其他两个相似。

seata-account-service2003
seata-order-service2001
seata-storage-service2002

① pom依赖

<!-- nacos -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--alibaba-seata-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!--openfeign-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--loadbalancer-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

② yml配置

server:port: 2001spring:application:name: seata-order-servicecloud:nacos:discovery:server-addr: localhost:8848         #Nacos服务注册中心地址# ==========applicationName + druid-mysql8 driver===================datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_order?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueusername: rootpassword: 123456
# ========================mybatis===================
mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.jane.cloud.entitiesconfiguration:map-underscore-to-camel-case: true# ========================seata===================
seata:registry:type: nacosnacos:server-addr: 127.0.0.1:8848namespace: ""group: SEATA_GROUPapplication: seata-servertx-service-group: default_tx_group # 事务组,由它获得TC服务的集群名称service:vgroup-mapping: # 点击源码分析default_tx_group: default # 事务组与TC服务集群的映射关系data-source-proxy-mode: ATlogging:level:io:seata: info

这里重点关注Seata的配置,相比1.0版本,Seata2.0版本无论是server端还是client端配置均进行了优化。

③ 创建undo_log表

undo_log建表、配置参数(仅AT模式)。SQL脚本 地址:https://github.com/apache/incubator-seata/blob/2.x/script/client/at/db/mysql.sql


-- for AT mode you must to init this sql for you business database. the seata server not need it.
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 = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);

最终数据库效果如下图:

在这里插入图片描述

【2】不添加@GlobalTransactional进行测试

如下所示,我们在订单处理方法里面进行远程调用扣减库存、扣减账户。service方法未添加@GlobalTransactional注解,也就是不启用分布式事务。

public void create(Order order){//xid全局事务id的检查,重要String xid = RootContext.getXID();//1 新建订单log.info("---------------开始新建订单: "+"\t"+"xid: "+xid);//订单新建时默认初始订单状态是零order.setStatus(0);int result = orderMapper.insertSelective(order);// 插入订单成功后获得插入mysql的实体对象Order orderFromDB = null;if(result > 0){// 从mysql里面查出刚插入的记录orderFromDB = orderMapper.selectOne(order);log.info("-----> 新建订单成功,orderFromDB info: "+orderFromDB);System.out.println();//2 扣减库存log.info("-------> 订单微服务开始调用Storage库存,做扣减count");storageFeignApi.decrease(orderFromDB.getProductId(),orderFromDB.getCount());log.info("-------> 订单微服务结束调用Storage库存,做扣减完成");System.out.println();//3 扣减账户余额log.info("-------> 订单微服务开始调用Account账号,做扣减money");accountFeignApi.decrease(orderFromDB.getUserId(),orderFromDB.getMoney());log.info("-------> 订单微服务结束调用Account账号,做扣减完成");System.out.println();//4 修改订单状态//将订单状态从零修改为1,表示已经完成log.info("-------> 修改订单状态");orderFromDB.setStatus(1);Example whereCondition = new Example(Order.class);Example.Criteria criteria = whereCondition.createCriteria();criteria.andEqualTo("userId",orderFromDB.getUserId());criteria.andEqualTo("status",0);int updateResult = orderMapper.updateByExampleSelective(orderFromDB, whereCondition);log.info("-------> 修改订单状态完成"+"\t"+updateResult);log.info("-------> orderFromDB info: "+orderFromDB);}System.out.println();log.info("---------------结束新建订单: "+"\t"+"xid: "+xid);}

那么假设account抛了异常,此时应该插入了订单并扣减了库存,但是账户余额没有扣除。我们修改AccountServiceImpl 使其超时:

public class AccountServiceImpl implements AccountService
{@ResourceAccountMapper accountMapper;/*** 扣减账户余额*/@Overridepublic void decrease(Long userId, Long money){log.info("------->account-service中扣减账户余额开始");accountMapper.decrease(userId,money);myTimeOut();log.info("------->account-service中扣减账户余额结束");}/*** 模拟超时异常,全局事务回滚*/private static void myTimeOut(){try { TimeUnit.SECONDS.sleep(65); } catch (InterruptedException e) { e.printStackTrace(); }}
}

此时我们查看数据库,可以验证上述推测是正确的:插入了订单并扣减了库存,但是账户余额没有扣除。其实这也就是我们为什么要引入分布式事务处理的原因:在分布式体系中,@Transactional 注解只能控制本地事务,不能控制其他服务事务,故而会引起分布式事务问题。

【3】添加@GlobalTransactional进行测试

如下所示,我们修改create方法如下:

# 添加全局事务注解
@GlobalTransactional(name = "create-order",rollbackFor = Exception.class) //AT
public void create(Order order)
{//xid全局事务id的检查,重要String xid = RootContext.getXID();//1 新建订单log.info("---------------开始新建订单: "+"\t"+"xid: "+xid);//订单新建时默认初始订单状态是零order.setStatus(0);//...
}

再次进行测试,仍然让account抛出异常。此时我们看控制台发现均已进行了回滚:

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

在这里插入图片描述
数据库订单表、账户表以及库存表并没有产生脏数据,说明分布式事务生效。


我们再次测试,在account方法处打上断点,那么order模块会抛出超时异常:feign.RetryableException: Read timed out 。

这时TC就会让各个分支事务进行回滚,比如库存模块:

在这里插入图片描述

在Seata的控制台,我们可以看到目前只有account持有全局锁:

在这里插入图片描述

事务信息界面目前状态为RollbackRetrying,也就是重复尝试回滚(因为我们debug中)。

在这里插入图片描述
此时order分支二阶段回滚也已成功,但是全局事务回滚还未成功。

在这里插入图片描述

我们释放account的断点,使其二阶段回滚成功,此时全局事务也会回滚成功。

在这里插入图片描述


正常逻辑会有一个全局事务ID即XID和三个分支事务ID即branchID。XID和branchID是一对多的关系,表示分支事务在某个全局事务下。

在这里插入图片描述
在这里插入图片描述
当某个服务出现异常情况后,TM会告诉TC需要回滚,TC会通知各个分支事务进行回滚。回滚时会根据undo_log表的rollback_info信息还原数据行,并删除undo_log记录

2024-06-26T15:50:08.939+08:00  INFO 37872 --- [h_RMROLE_1_1_40] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:xid=172.18.6.49:8091:3801574378475941899,branchId=3801574378475941900,branchType=AT,resourceId=jdbc:mysql://localhost:3306/seata_order,applicationData={"skipCheckLock":true}
2024-06-26T15:50:08.942+08:00  INFO 37872 --- [h_RMROLE_1_1_40] io.seata.rm.AbstractRMHandler            : Branch Rollbacking: 172.18.6.49:8091:3801574378475941899 3801574378475941900 jdbc:mysql://localhost:3306/seata_order
2024-06-26T15:50:09.003+08:00  INFO 37872 --- [h_RMROLE_1_1_40] i.s.r.d.undo.AbstractUndoLogManager      : xid 172.18.6.49:8091:3801574378475941899 branch 3801574378475941900, undo_log deleted with GlobalFinished
2024-06-26T15:50:09.005+08:00  INFO 37872 --- [h_RMROLE_1_1_40] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_Rollbacked

整个事务回滚完毕后,状态会变为finished。

2024-06-26T15:50:09.224+08:00  INFO 37872 --- [nio-2001-exec-1] i.seata.tm.api.DefaultGlobalTransaction  : Suspending current transaction, xid = 172.18.6.49:8091:3801574378475941899
2024-06-26T15:50:09.226+08:00  INFO 37872 --- [nio-2001-exec-1] i.seata.tm.api.DefaultGlobalTransaction  : [172.18.6.49:8091:3801574378475941899] rollback status: Finished

正常业务日志

① TM开启全局事务

order模块控制台

DefaultGlobalTransaction  : Begin new global transaction [172.18.6.49:8091:3801574378475941903]

② 服务处理

  • 下订单
  • 扣库存
  • 扣账户

③ TM决定通知TC提交全局事务

order模块控制台

DefaultGlobalTransaction  : Suspending current transaction, xid = 172.18.6.49:8091:3801574378475941903
DefaultGlobalTransaction  : [172.18.6.49:8091:3801574378475941903] commit status: Committed

此时TC会通知各个分支提交事务。

④ 各个分支提交事务

order模块控制台

2024-06-26T16:00:01.954+08:00  INFO 37872 --- [h_RMROLE_1_2_40] i.s.c.r.p.c.RmBranchCommitProcessor      : rm client handle branch commit process:xid=172.18.6.49:8091:3801574378475941903,branchId=3801574378475941904,branchType=AT,resourceId=jdbc:mysql://localhost:3306/seata_order,applicationData={"skipCheckLock":true}
2024-06-26T16:00:01.955+08:00  INFO 37872 --- [h_RMROLE_1_2_40] io.seata.rm.AbstractRMHandler            : Branch committing: 172.18.6.49:8091:3801574378475941903 3801574378475941904 jdbc:mysql://localhost:3306/seata_order {"skipCheckLock":true}
2024-06-26T16:00:01.956+08:00  INFO 37872 --- [h_RMROLE_1_2_40] io.seata.rm.AbstractRMHandler            : Branch commit result: PhaseTwo_Committed

库存模块控制台

2024-06-26T16:00:01.965+08:00  INFO 40780 --- [h_RMROLE_1_2_40] i.s.c.r.p.c.RmBranchCommitProcessor      : rm client handle branch commit process:xid=172.18.6.49:8091:3801574378475941903,branchId=3801574378475941905,branchType=AT,resourceId=jdbc:mysql://localhost:3306/seata_storage,applicationData=null
2024-06-26T16:00:01.967+08:00  INFO 40780 --- [h_RMROLE_1_2_40] io.seata.rm.AbstractRMHandler            : Branch committing: 172.18.6.49:8091:3801574378475941903 3801574378475941905 jdbc:mysql://localhost:3306/seata_storage null
2024-06-26T16:00:01.967+08:00  INFO 40780 --- [h_RMROLE_1_2_40] io.seata.rm.AbstractRMHandler            : Branch commit result: PhaseTwo_Committed

账户模块控制台:

2024-06-26T16:00:01.976+08:00  INFO 41368 --- [h_RMROLE_1_1_40] i.s.c.r.p.c.RmBranchCommitProcessor      : rm client handle branch commit process:xid=172.18.6.49:8091:3801574378475941903,branchId=3801574378475941906,branchType=AT,resourceId=jdbc:mysql://localhost:3306/seata_account,applicationData=null
2024-06-26T16:00:01.980+08:00  INFO 41368 --- [h_RMROLE_1_1_40] io.seata.rm.AbstractRMHandler            : Branch committing: 172.18.6.49:8091:3801574378475941903 3801574378475941906 jdbc:mysql://localhost:3306/seata_account null
2024-06-26T16:00:01.982+08:00  INFO 41368 --- [h_RMROLE_1_1_40] io.seata.rm.AbstractRMHandler            : Branch commit result: PhaseTwo_Committed

order模块控制台(注意这个和第一次的区别):order模块是先{"skipCheckLock":true} 进行分支提交,等其他分支提交完后再进行applicationData=null分支提交。

2024-06-26T16:00:01.993+08:00  INFO 37872 --- [h_RMROLE_1_3_40] i.s.c.r.p.c.RmBranchCommitProcessor      : rm client handle branch commit process:xid=172.18.6.49:8091:3801574378475941903,branchId=3801574378475941907,branchType=AT,resourceId=jdbc:mysql://localhost:3306/seata_order,applicationData=null
2024-06-26T16:00:01.994+08:00  INFO 37872 --- [h_RMROLE_1_3_40] io.seata.rm.AbstractRMHandler            : Branch committing: 172.18.6.49:8091:3801574378475941903 3801574378475941907 jdbc:mysql://localhost:3306/seata_order null
2024-06-26T16:00:01.994+08:00  INFO 37872 --- [h_RMROLE_1_3_40] io.seata.rm.AbstractRMHandler            : Branch commit result: PhaseTwo_Committed

XID:172.18.6.49:8091:3801574378475941903
order分支ID:3801574378475941904、3801574378475941907
库存分支ID:3801574378475941905
账户分支ID:3801574378475941906

至此完成了分布式事务的提交。

【4】AT模式如何做到对业务的无侵入

官网地址:Seata 是什么? Seata默认是AT模式,两阶段提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

  • 二阶段:

    • 提交异步化,非常快速地完成。
    • 回滚通过一阶段的回滚日志进行反向补偿。

① 一阶段加载

在一阶段,Seata 会拦截“业务 SQL”,

  • 1 解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,
  • 2 执行“业务 SQL”更新业务数据,在业务数据更新之后,
  • 3 其保存成“after image”,最后生成行锁。

以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。
在这里插入图片描述

② 二阶段提交

二阶段如是顺利提交的话,因为“业务 SQL”在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

在这里插入图片描述

③ 二阶段回滚

二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。

回滚方式便是用“before image”还原业务数据。但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”:

  • 如果两份数据完全一致就说明没有脏写,可以还原业务数据,
  • 如果不一致就说明有脏写,出现脏写就需要转人工处理。

在这里插入图片描述

【5】复习:@Transactional事务注解什么时候失效

@Transactional 注解可能会在以下几种情况下失效:

  1. 非public方法:当@Transactional注解应用在非public修饰的方法上时,事务管理将不会生效。这是因为在Spring AOP(面向切面编程)代理机制中,事务拦截器仅对public方法进行拦截处理。

  2. 自我调用:在一个类的内部,一个带有@Transactional注解的方法直接被另一个没有事务注解的方法调用时,事务可能不会生效。因为在这种情况下,调用是直接发生的,没有经过Spring的代理对象,从而不会触发事务管理。

  3. 未被Spring管理的类:如果带有@Transactional注解的类没有被Spring容器管理(例如,没有使用@Component、@Service等注解标记,或者没有在Spring配置中声明),事务注解将不起作用。

  4. 异常处理不当:如果在事务方法中捕获并吞没了应导致事务回滚的异常(通常是未检查异常,如RuntimeException),事务可能不会回滚。除非在@Transactional注解中明确指定了rollbackFor属性来捕获特定类型的异常。

  5. 代理模式问题:使用基于Java接口的代理时(Spring默认的代理方式),只有通过接口调用的方法才会被代理增强,如果直接在实现类上调用方法(而不是通过接口),事务可能不会生效。

  6. 事务传播行为设置不当:如果方法的事务传播行为配置不当(例如,一个事务方法调用另一个事务方法时,传播行为设置为PROPAGATION_NOT_SUPPORTEDPROPAGATION_NEVER),可能导致事务不生效或被挂起。

  7. 缺少数据库事务支持:如果底层数据访问技术不支持事务管理,如使用了非事务性的JDBC连接或某些NoSQL数据库操作,即使@Transactional注解正确使用,也无法实现事务管理。

要确保@Transactional注解能正常工作,需要确保遵循正确的使用方式,并注意上述可能引起事务失效的情况。

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

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

相关文章

计算机监控软件有哪些?10款常年霸榜的计算机监控软件

计算机监控软件是企业管理和保护信息安全的重要工具&#xff0c;它们帮助企业管理者监督员工的计算机使用行为&#xff0c;确保工作效率、数据安全以及合规性。在众多监控软件中&#xff0c;有些产品因其卓越的功能、易用性、安全性以及持续获得的良好市场反馈而常年占据行业领…

C++——探索智能指针的设计原理

前言: RAII是资源获得即初始化&#xff0c; 是一种利用对象生命周期来控制程序资源地手段。 智能指针是在对象构造时获取资源&#xff0c; 并且在对象的声明周期内控制资源&#xff0c; 最后在对象析构的时候释放资源。注意&#xff0c; 本篇文章参考——C 智能指针 - 全部用法…

IBCS 虚拟专线——让企业用于独立IP

在当今竞争激烈的商业世界中&#xff0c;企业的数字化运营对网络和服务器的性能有着极高的要求。作为一家企业的 IT 主管&#xff0c;我深刻体会到了在网络和服务器配置方面所面临的种种挑战&#xff0c;以及 IBCS 虚拟专线带来的革命性改变。 我们企业在业务扩张的过程中&…

msvcr120.dll文件下载的高级教程,修复msvcr120.dll 详细步骤分享

当电脑系统或特定应用程序无法找到或访问到msvcr120.dll文件时&#xff0c;便会导致错误消息的出现&#xff0c;例如“找不到 msvcr120.dll”、“msvcr120.dll丢失”等。这篇文章将大家讨论关于msvcr120.dll文件的内容、msvcr120.dll丢失问题的解决方法&#xff0c;其中最常见的…

web使用cordova打包Andriod

一.安装Gradel 1.下载地址 Gradle Distributions 2.配置环境 3.测试是否安装成功 在cmd gradle -v 二.创建vite项目 npm init vitelatest npm install vite build 三.创建cordova项目 1.全局安装cordova npm install -g cordova 2. 创建项目 cordova create cordova-app c…

VuePress日常使用

本篇来讲解下更多关于 VuePress 的基本用法 ‍ 配置首页 现在的页面太简单了&#xff0c;我们可以对项目首页进行配置&#xff0c;修改 docs/README.md &#xff08;这些配置是什么后面会说&#xff09;&#xff1a; --- home: true heroImage: https://s3.bmp.ovh/imgs/20…

Mac可以读取NTFS吗 Mac NTFS软件哪个好 mac ntfs读写工具免费

在跨操作系统环境下使用外部存储设备时&#xff0c;特别是当Windows系统的U盘被连接到Mac电脑时&#xff0c;常常会遇到文件系统兼容性的问题。由于Mac OS原生并不完全支持对NTFS格式磁盘的读写操作&#xff0c;导致用户无法直接在Mac上向NTFS格式的U盘或硬盘写入数据。下面我们…

揭示隐藏的模式:秩和检验和单因素方差分析的实战指南【考题】

1.研究一种新方法对于某实验结果准确性提高的效果&#xff0c;并将其与原有方法进行比较&#xff0c;结果见下表&#xff0c;请评价两者是否有不同? (行无序&#xff0c;列有序)-->单方向有序-->两独立样本的秩和检验) 如下图所示&#xff0c;先将相关数据导入spss。 图…

什么是指令微调(LLM)

经过大规模数据预训练后的语言模型已经具备较强的模型能力&#xff0c;能够编码丰富的世界知识&#xff0c;但是由于预训练任务形式所限&#xff0c;这些模型更擅长于文本补全&#xff0c;并不适合直接解决具体的任务。 指令微调是相对“预训练”来讲的&#xff0c;预训练的时…

SpringBoot3基础用法

技术和工具「!喜新厌旧」 一、背景 最近在一个轻量级的服务中&#xff0c;尝试了最新的技术和工具选型&#xff1b; 即SpringBoot3&#xff0c;JDK17&#xff0c;IDEA2023&#xff0c;Navicat16&#xff0c;虽然新的技术和工具都更加强大和高效&#xff0c;但是适应采坑的过程…

企业网络安全必知的三大访问控制模型

在当今信息化社会中&#xff0c;信息系统的安全性成为了组织和个人关注的焦点。随着信息技术的不断发展和应用&#xff0c;信息系统的复杂性和规模不断扩大&#xff0c;系统中存储和处理的信息量也日益增长。 一、引言 在当今信息化社会中&#xff0c;信息系统的安全性成为了组…

【知识点篇]《计算机组成原理》之计算机系统概述

1.1 计算机发展历程 世界上第一台电子数字计算机 1946年&#xff0c;ENIAC(Electronic Numerical Integrator And Computer)在美国宾夕法尼亚大学研制成功。性能低&#xff0c;耗费巨大&#xff0c;但却是科学史上的一次划时代的创新&#xff0c;奠定了电子计算机的基础&#x…

stm32学习笔记---ADC模数转换器(理论部分)

目录 ADC简介 什么叫逐次逼近型&#xff1f; STM32 ADC框图 模数转换器外围线路 ADC基本结构图 输入通道 规则组的四种转换模式 第一种&#xff1a;单次转换非扫描模式 第二种&#xff1a;连续转换&#xff0c;非扫描模式 第三种&#xff1a;单次转换&#xff0c;扫描…

如何利用React和Python构建强大的网络爬虫应用

如何利用React和Python构建强大的网络爬虫应用 引言&#xff1a; 网络爬虫是一种自动化程序&#xff0c;用于通过互联网抓取网页数据。随着互联网的不断发展和数据的爆炸式增长&#xff0c;网络爬虫越来越受欢迎。本文将介绍如何利用React和Python这两种流行的技术&#xff0c…

5个大气的wordpress付费主题

Sesko赛斯科wordpress外贸主题 适合用于重型机械设备公司建外贸官方网站的橙红色wordpress外贸主题。 https://www.jianzhanpress.com/?p5886 Polar钋啦wordpress外贸主题 制造业wordpress网站模板&#xff0c;适合生产制造企业官方网站使用的wordpress外贸主题。 https:/…

临时文件上传系统Plik

什么是 Plik &#xff1f; Plik 是一个基于 Go 语言的可扩展且用户友好的临时文件上传系统&#xff08;类似于 Wetransfer&#xff09;。 软件主要特点&#xff1a; 强大的命令行客户端易于使用的 Web 用户界面多个数据后端&#xff1a;文件、OpenStack Swift、S3、Google Clo…

用pycharm进行python爬虫的步骤

使用 pycharm 进行 python 爬虫的步骤&#xff1a;下载并安装 pycharm。创建一个新项目。安装 requests 和 beautifulsoup 库。编写爬虫脚本&#xff0c;包括获取页面内容、解析 html 和提取数据的代码。运行爬虫脚本。保存和处理提取到的数据。 用 PyCharm 进行 Python 爬虫的…

轻量级模型,重量级性能,TinyLlama、LiteLlama小模型火起来了

小身板&#xff0c;大能量。 当大家都在研究大模型&#xff08;LLM&#xff09;参数规模达到百亿甚至千亿级别的同时&#xff0c;小巧且兼具高性能的小模型开始受到研究者的关注。 小模型在边缘设备上有着广泛的应用&#xff0c;如智能手机、物联网设备和嵌入式系统&#xff0…

java用pdf.js在线预览pdf文件(jeecg框架)

最近在jeecg框架的后台要做一个pdf在线预览的页面功能&#xff0c;可是每次点预览都是下载&#xff0c;所以就要解决这个问题&#xff0c;现在解决了&#xff0c;记录一下&#xff0c;防止后面踩坑。 先放代码&#xff1a; 下面是点“预览”按钮的点击事件&#xff0c;代码放…

【Mac】iTerm for mac(终端工具)软件介绍及安装教程

软件介绍 iTerm 是 macOS 上一个非常受欢迎的终端仿真器&#xff0c;提供了比默认的 Terminal 应用更多的功能和定制选项。它是一款开源软件&#xff0c;主要用于命令行界面的操作和开发者工具。 主要特点和功能&#xff1a; 分页和标签&#xff1a; iTerm 允许用户在单个窗…