Seata,让分布式事务不再是难题!实战分享带你领略Seata的魅力!
ccwgpt 2025-05-02 16:54 39 浏览 0 评论
终身学习、乐于分享、共同成长!
前言
Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务),不过该服务在2023年开始停止支持了。
作为一个分布式事务框架,Seata可以支持多种数据源和多个服务的协同工作,从而实现分布式事务的管理。它的出现大大简化了开发人员的工作。下面,我们就来探究一下Seata的魅力,看看它究竟是如何让分布式事务变得不再困难的。
官网:
https://seata.io/zh-cn/index.html(opens new window)
源码:
https://github.com/seata/seata(opens new window)
官方Demo:
https://github.com/seata/seata-samples(opens new window)
本文使用的seata版本:v1.6.1
Seata的核心
Seata的核心组件包括三个部分:
- Transaction Coordinator(TC):事务协调器,负责全局事务的协调和管理。
- Resource Manager(RM):资源管理器,负责管理分支事务所涉及的资源。
- Transaction Manager(TM):事务管理器,提供全局事务的创建、提交、回滚等操作。
其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
Seata的工作流程如下:
- TM请求TC开启一个全局事务。TC会生成一个XID作为该全局事务的编号。XID,会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起。当一进入事务方法中就会生成XID ,global_table表存储的就是全局事务信息。
- RM请求TC将本地事务注册为全局事务的分支事务,通过全局事务的XID进行关联。当运行数据库操作方法,branch_table表存储事务参与者信息。
- TM请求TC告诉XID对应的全局事务是进行提交还是回滚。
- TC驱动RM们将XID对应的自己的本地事务进行提交还是回滚。
seata-server(TC)
- global_table:全局事务表,记录全局事务的基本信息,例如全局事务ID、事务状态等。每当有一个全局事务发起后,就会在该表记录全局事务ID。
- branch_table:分支事务表,记录分支事务的基本信息,例如分支事务ID、所属全局事务ID、分支事务状态等。
- lock_table:全局锁,记录分布式锁的信息,例如锁定的资源、锁的持有者等。
Seata快速开始
使用官方实验例子,模拟用户购物下单的业务操作。原理图如下:
Seata具体实现步骤:
- TM端使用@GlobalTransaction进行全局事务的开启、提交、回滚。
- TM端开始远程调用业务服务。
- RM端(seata客户端)通过扩展DataSourceProxy,实现自动生成undo_log与TC上报。
- TM告知TC提交/回滚事务。
- TC通知RM各自提交/回滚事务操作,同时删除undo_log。
搭建seata-server(TC 协调者)
- 下载seata-server
我用的seata是截止目前最新的版本1.6.1,下载地址:
https://github.com/seata/seata/releases
下载后解压文件如下:
- 修改seata/conf/application.yml配置文件
# Copyright 1999-2019 Seata.io Group.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${user.home}/logs/seata
extend:
logstash-appender:
destination: 127.0.0.1:4560
kafka-appender:
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
console:
user:
username: seata
password: seata
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: nacos
nacos:
server-addr: 127.0.0.1:8848 # nacos的ip端口
group: DEFAULT_GROUP # 对应的组,默认为DEFAULT_GROUP
#namespace: 707f9b70-7c86-4278-bd74-83cef490a824 # 对应的命名空间,在nacos中配置
username: nacos
password: nacos
data-id: seata-server.properties # nacos中存放seata的配置文件,后面会提该文件的使用方式,相当于seata服务启动的时候需要注册到nacos,并使用nacos中的配置文件
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
server-addr: 127.0.0.1:8848 # nacos的ip端口
group: DEFAULT_GROUP # 对应的组,默认为DEFAULT_GROUP
#namespace: 707f9b70-7c86-4278-bd74-83cef490a824 # 对应的命名空间,在nacos中配置
username: nacos
password: nacos
store:
# support: file 、 db 、 redis
mode: file
# server:
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
security:
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
seata.config.type 与seata.registry.type 都要修改为nacos。
修改config与registry中nacos的配置,其中namespace与group须提前在nacos中进行配置。
- 在Nacos配置中心新增dataid为seata-server.properties的properties配置。
内容如下:
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none
#Transaction routing rules configuration, only for the client
# 此处的mygroup名字可以自定义,只修改这个值即可
service.vgroupMapping.mygroup=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
#Log rule configuration, for client and server
log.exceptionRate=100
#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
# 默认为file,一定要改为db,我们自己的服务启动会连接不到seata
store.mode=db
store.lock.mode=db
store.session.mode=db
#Used for password encryption
#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
# 修改mysql的配置
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
# 指定seata的数据库,下面会提
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.xaerNotaRetryTimeout=60000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=false
#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
【注意】
- 修改service.vgroupMapping.mygroup=default该值,其中mygroup可以自定义,后面我们自己的服务启动时,配置文件中需要指定该group。
- 修改store.mode store.lock.mode store.session.mode这三个值为db,才能让seata连接到下面的数据库中。
- 修改store.db配置项下的配置,连接到自己的数据库。
- 该文件参数的具体作用参考官网:seata配置文件详解(官网 (opens new window))
- 该配置源文件存放在seata目录下:seata/script/config-center/config.txt
- 在mysql数据库中新建seata所用到的数据库及数据表
数据库的名称与上述seata配置文件中的store.db.url保持一致。
添加seata的配置表,sql文件存放在:
seata/script/server/db/mysql.sql中
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS `distributed_lock`
(
`lock_key` CHAR(20) NOT NULL,
`lock_value` VARCHAR(20) NOT NULL,
`expire` BIGINT,
primary key (`lock_key`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
- 启动seata-server
sh seata-server.sh -p 8091 -h 127.0.0.1
参数解释:
-h: 将地址暴露给注册中心,其他服务可以访问seata服务
-p:要侦听的端口
启动成功后,即可访问http://127.0.0.1:7091/#/login (opens new window)该地址进入seata的webui,用户名与密码默认为seata,用户名和密码可在application.yml配置项:console.user中修改。
并且查看nacos中的服务注册中心是否可以看到seata-server
至此,seata-server就搭建成功了。
搭建seata-client(TM、RM)
再来回顾一下前面的业务场景,如图:
因此这里需要搭建三个客户端应用:tm-server、order-server、storage-server,源码:
工程源码:
https://gitee.com/it-codelab/springcloudalibaba-study/tree/main/seata-demo
新建数据库表,只有order-server、storage-server这两个应用需要用到数据库,新建两个对应的数据库:
- storage数据库对应的库表
create table storage
(
id bigint(11) auto_increment
primary key,
product_id bigint(11) null comment '产品id',
total int null comment '总库存',
used int null comment '已用库存'
)
charset = utf8;
create table undo_log
(
id bigint auto_increment
primary key,
branch_id bigint not null,
xid varchar(100) not null,
context varchar(128) not null,
rollback_info longblob not null,
log_status int not null,
log_created datetime not null,
log_modified datetime not null,
ext varchar(100) null,
constraint ux_undo_log
unique (xid, branch_id)
)
charset = utf8;
- order数据库对应的库表
create table t_order
(
id bigint(11) auto_increment
primary key,
user_id bigint(11) null comment '用户id',
product_id bigint(11) null comment '产品id',
count int null comment '数量',
money decimal(11) null comment '金额',
status int(1) null comment '订单状态:0:创建中;1:已完成'
)
charset = utf8;
create table undo_log
(
id bigint auto_increment
primary key,
branch_id bigint not null,
xid varchar(100) not null,
context varchar(128) not null,
rollback_info longblob not null,
log_status int not null,
log_created datetime not null,
log_modified datetime not null,
ext varchar(100) null,
constraint ux_undo_log
unique (xid, branch_id)
)
charset = utf8;
每个RM服务对应的数据库都需要添加undo_log表。
主要配置以及核心代码:
- 添加seata依赖,三个应用都一样
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.6.1</version>
</dependency>
- 在bootstrap.yml文件加入seata的相关配置
seata:
enabled: true
#application-id: ${spring.application.name}
# 事务群组(可以每个应用独立取名,也可以使用相同的名字),要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
tx-service-group: my_test_tx_group # 事务分组名称,要和服务端对应
service:
vgroup-mapping:
my_test_tx_group: default # key是事务分组名称 value要和服务端的机房名称保持一致
config:
type: nacos
# 需要和server在同一个注册中心下
nacos:
#namespace: cab17056-9954-4e45-9223-eb33692f60f7
serverAddr: 127.0.0.1:8848
# 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
username: "nacos"
password: "nacos"
namespace: 707f9b70-7c86-4278-bd74-83cef490a824
registry:
type: nacos
nacos:
# 需要和server端保持一致,即server在nacos中的名称,默认为seata-server
application: seata-server
server-addr: 127.0.0.1:8848
group: DEFAULT_GROUP
#namespace: cab17056-9954-4e45-9223-eb33692f60f7
username: "nacos"
password: "nacos"
这里有一个深坑,启动应用后会报如下错误:
seata can not get cluster name in registry config ‘service.vgroupMapping.XXX‘
解决办法就是需要再Nacos配置中心增加service.vgroupMapping.XXX配置,XXX是可以自定义的,value默认为default。
注:核心业务代码可查看项目源码:
https://gitee.com/it-codelab/springcloudalibaba-study/tree/main/seata-demo
AT模式原理
AT模式的核心优势是对业务无侵入,是一种改进后的两阶段提交。
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。
其设计思路如图:
- 第一阶段
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。核心在于对业务sql进行解析,转换成undolog,并同时入库,这是怎么做的呢?先抛出一个概念DataSourceProxy代理数据源,通过名字大家大概也能基本猜到是什么个操作,后面做具体分析
参考官方文档:
https://seata.io/zh-cn/docs/dev/mode/at-mode.html
- 第二阶段
第一种情况:分布式事务操作成功,则TC通知RM异步删除undolog。
第二种情况:分布式事务操作失败,TM向TC发送回滚请求 TC收到回滚请求后,向RM发送回滚请求 RM收到协调器TC发来的回滚请求 通过XID和Branch ID找到相应的回滚日志记录 通过回滚记录生成反向的更新SQL并执行,以完成分支的回滚。
完整执行流程:
读写隔离机制
写隔离
- 一阶段本地事务提交前,需要确保先拿到 全局锁 。
- 拿不到 全局锁 ,不能提交本地事务。
- 拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
以一个示例来说明:
两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的 全局锁 ,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁 。
tx1 二阶段全局提交,释放 全局锁 。tx2 拿到 全局锁 提交本地事务。
如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。
此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功。
因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生 脏写 的问题。
读隔离
在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted) 。
如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回。
出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。
总结
- alibaba seata框架总体使用下来是比较简单的,AT模式对业务代码零侵入的优势特点使得seata在业界得到了广泛的应用。
- 框架使用起来越简单,那它内部的实现原理就越复杂、越抽象,本文也只是对seata的使用入了门,更原理性的内容后续持续深挖钻研。
相关推荐
- Xtreme套件Xtreme Suite Pro正式发布v17.0.0
-
Codejock软件公司的Xtreme套件(XtremeSuite)包含了三种流行的组件:Xtreme命令工具栏(XtremeCommandBars)——把需要创建的具有改进对接算法的所有组件...
- Wine能不能跑Win程序?信创操作系统下运行Windows应用的条件!
-
原文链接:「链接」Hello,大家好啊,今天给大家带来一篇信创操作系统上使用Wine运行Windows应用程序的条件的文章,欢迎大家分享点赞,点个在看和关注吧!在日常使用国产信创操作系统(如统...
- VC界面开发组件Xtreme Toolkit Pro全新发布v17.0.0
-
Codejock软件公司的XtremeToolkitPro是屡获殊荣的VC界面库,是MFC开发中最全面界面控件套包,它提供了Windows开发所需要的11种主流的VisualC++MFC控件,...
- 机器视觉软件开发新人入门必看 --机器视觉软件开发学习路径
-
机器视觉是机械、运动、控制、光学、软件、算法于一体的交叉学科,对于学工科的人来说,机械、运动、控制都有一定的了解,对于软件、算法、光学不是很了解。一台设备,有一个到二个机械设计师或者结构工程师,那么这...
- 数控变频器的研究与实现(数控变频器的研究与实现思考题)
-
一般变频器具有两种控制方式:控制面板控制方式和串行通信数据控制方式。控制面板控制方式利用变频器自带控制面板进行手动操控,一般应用于非自动控制场合。在自动化程度越来越高的工业生产现场以及机电一体化的数控...
- 实用 | 分享几个非常实用的开源项目
-
前言本次分享几个实用的、值得学习使用的嵌入式相关开源项目,下面列举的这些基本上都在本公众号分享过,详细介绍及使用可查看往期笔记。protobufProtocolBuffers,是Google公司开发...
- Windows桌面应用程序常用开发框架的设计案例全面展示
-
Windows桌面应用程序是我们日常生活中不可或缺的一部分,而开发这些应用程序需要使用相应的框架。本文将全面介绍常用的Windows桌面应用程序开发框架,帮助您了解并选择适合的开发工具。一、原生的Wi...
- .NET9 FCall/QCall调用约定(.net 调用存储过程)
-
蓝字江湖评谈设为关注前言FCall/Qcall是托管与非托管之间的调用约定,双方需要一个契约,以弥合彼此的互相/单向调用。非托管调用约定先了解下非托管约定,一般有四种,分别为thiscall,std...
- BCGControlBar Pro for MFC v24.4正式发布
-
BCGControlBar(BusinessComponentsGalleryControlBar)专业版是MFC的一个扩展库,您可以用来构建类似于MicrosoftOffice2000/X...
- MFC多文档视图(mfc 多文档)
-
你可以因为现任不好而分手,但千万不要认为别人更好,永远有人更好,眼下便是更好。。。----网易云热评一、多文档视图架构程序1、特点:可以管理多个文档。(可以有多个文档类对象)2、相关类CWinA...
- MFC扩展库BCGControlBar Pro v33.5新版亮点:Ribbon Bar等全新升级
-
BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。我们的组件可以轻松地集成到您的应用程序中,并为您节省数百个开发和调试时间。BCGControlBar专业版v3...
- 山东新华电脑学院4G软件专业明星优秀作品展
-
项目实战工程师:向修艺年龄:18岁班级:4G软件1501班座右铭:付出才会有收获导师寄语:自学能力和实践能力都非常出色,并且学习认真做事责任心强,是不可多得的人才。相信将来如果能获得机会,发挥自己的...
- MFC转QT:Qt基础知识(mfc获取当前日期和时间信息)
-
1.Qt框架概述Qt的历史和版本Qt是一个跨平台的C++应用程序开发框架,由挪威公司Trolltech(现为QtCompany)于1991年创建。Qt的发展历程:1991年:Qt项目启动1995年...
- MFC转QT:Qt高级特性 - 事件系统(mfc读取txt文件每一行数据)
-
Qt事件处理机制Qt的事件系统是整个框架的核心基础之一,负责处理用户输入、窗口系统消息和应用内部的通信。相比MFC的消息映射系统,Qt的事件处理机制更加灵活和直观。基本概念事件(Event)是Qt框...
- MFC用户界面套包BCGControlBar Pro for MFC发布v25.0
-
BCGControlBar(BusinessComponentsGalleryControlBar)专业版是MFC的一个扩展库,您可以用来构建类似于MicrosoftOffice2000/X...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Xtreme套件Xtreme Suite Pro正式发布v17.0.0
- Wine能不能跑Win程序?信创操作系统下运行Windows应用的条件!
- VC界面开发组件Xtreme Toolkit Pro全新发布v17.0.0
- 机器视觉软件开发新人入门必看 --机器视觉软件开发学习路径
- 数控变频器的研究与实现(数控变频器的研究与实现思考题)
- 实用 | 分享几个非常实用的开源项目
- Windows桌面应用程序常用开发框架的设计案例全面展示
- .NET9 FCall/QCall调用约定(.net 调用存储过程)
- BCGControlBar Pro for MFC v24.4正式发布
- MFC多文档视图(mfc 多文档)
- 标签列表
-
- MVC框架 (46)
- spring框架 (46)
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- laravel框架 (46)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- mfc框架 (52)
- grpc框架 (55)
- ppt框架 (48)
- 内联框架 (52)
- winform框架 (46)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)