链路追踪 SkyWalking 源码分析——Collector Storage 存储组件
liebian365 2024-10-15 13:51 21 浏览 0 评论
摘要: 原创出处 http://www.iocoder.cn/SkyWalking/collector-storage-module/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文主要基于 SkyWalking 3.2.6 正式版
- 1. 概述
- 2. apm-collector-core
- 3. collector-storage-define
- 4. collector-storage-h2-provider
- 5. collector-storage-es-provider
1. 概述
本文主要分享 SkyWalking Collector Storage 存储组件。顾名思义,负责将调用链路、应用、应用实例等等信息存储到存储器,例如,ES 、H2 。
友情提示:建议先阅读 《SkyWalking 源码分析 —— Collector 初始化》 ,以了解 Collector 组件体系。
FROM https://github.com/apache/incubating-skywalking
下面我们来看看整体的项目结构,如下图所示 :
- apm-collector-core 的 data 和 define 包 :数据的抽象。
- collector-storage-define :定义存储组件接口。
- collector-storage-h2-provider :基于 H2 的 存储组件实现。该实现是单机版,建议仅用于 SkyWalking 快速上手,生产环境不建议使用。
- collector-storage-es-provider :基于 Elasticsearch 的集群管理实现。生产环境推荐使用。
下面,我们从接口到实现的顺序进行分享。
2. apm-collector-core
apm-collector-core 的 data 和 define 包,如下图所示:
我们对类进行梳理分类,如下图:
- Table :Data 和 TableDefine 之间的桥梁,每个 Table 定义了该表的表名,字段名们。
- TableDefine :Table 的详细定义,包括表名,字段定义( ColumnDefine )们。在下文中,StorageInstaller 会基于 TableDefine 初始化表的相关信息。
- Data :数据,包括一条数据的数据值们和数据字段( Column )们。在下文中,Dao 会存储 Data 到存储器中。另外,在 《SkyWalking 源码分析 —— Collector Streaming Computing 流式处理(一)》 中,我们也会看到对 Data 的流式处理通用封装。
2.1 Table
org.skywalking.apm.collector.core.data.CommonTable ,通用表。
- `TABLE_TYPE` 静态属性,表类型。目前只有 ES 存储组件使用到,下文详细解析。
- `COLUMN_` 前缀的静态属性,通用的字段名。
在 collector-storage-define 的 table 包下,我们可以看到所有 Table 类,以 "Table" 结尾。每个 Table 的表名,在每个实现类里,例如 ApplicationTable 。
2.2 TableDefine
org.skywalking.apm.collector.core.data.TableDefine ,表定义抽象类。
- `name` 属性,表名。
- `columnDefines` 属性,ColumnDefine数组。
- `#initialize()` 抽象方法,初始化表定义。例如:ApplicationEsTableDefine 。
不同的存储组件实现,有不同的 TableDefine 实现类,如下图:
- ElasticSearchTableDefine :基于 Elasticsearch 的表定义抽象类,在 collector-storage-es-provider 的 define 包下,我们可以看到所有 ES 的 TableDefine 类。
- H2TableDefine :基于 H2 的表定义抽象类,在 collector-storage-h2-provider 的 `define` 包下,我们可以看到所有 H2 的 TableDefine 类。
2.2.1 ColumnDefine
org.skywalking.apm.collector.core.data.ColumnDefine ,字段定义抽象类。
- `name` 属性,字段名。
- `type` 属性,字段类型。
在 collector-storage-xxx-provider 模块中,H2ColumnDefine 、ElasticSearchColumnDefine 实现 ColumnDefine 。
2.2.2 Loader
涉及到的类如下图所示:
org.skywalking.apm.collector.core.data.StorageDefineLoader ,调用 org.skywalking.apm.collector.core.define.DefinitionLoader ,从 org.skywalking.apm.collector.core.data.StorageDefinitionFile 中,加载 TableDefine 实现类数组。
另外,在 collector-storage-es-provider 和 collector-storage-h2-provider 里都有 storage.define 文件,如下图:
- StorageDefinitionFile 声明了读取该文件。
- 注意,DefinitionLoader 在加载时,两个文件都会被读取,最终在 StorageInstaller#defineFilter(List) 方法,进行过滤。
代码比较简单,中文注释已加,胖友自己阅读理解下。
2.3 Data
org.skywalking.apm.collector.core.data.Data ,数据抽象类。
- [dataXXX]() 前缀的属性,字段值们。
- `dataStrings` 属性的第一位,是 ID 属性。参见 构造方法的【第 51 行】 或者 `#setId(id)` 方法。
- [xxxColumns]() 后缀的属性,字段( Column )们。
- 通过上述两种属性 + 自身类,可以确定一条数据记录的表、字段类型、字段名、字段值。
- 继承 `org.skywalking.apm.collector.core.data.EndOfBatchQueueMessage` ,带是否消息批处理的最后一条标记的消息抽象类,`endOfBatch` 属性,在 《SkyWalking 源码分析 —— Collector Streaming Computing 流式处理(二)》「3. AggregationWorker」 详细解析。
- 继承 `org.skywalking.apm.collector.core.data.AbstractHashMessage` ,带哈希码的消息抽象类,`hashCode` 属性,在 《SkyWalking 源码分析 —— Collector Streaming Computing 流式处理(二)》「3. AggregationWorker」 详细解析。
- `#mergeData(Data)` 方法,合并传入的数据到自身。该方法被 `AggregationWorker#aggregate(message)` 调用,在 《SkyWalking 源码分析 —— Collector Streaming Computing 流式处理(二)》「3. AggregationWorker」 详细解析。
在 collector-storage-define 的 table 包下,我们可以看到所有 Data 类,非 "Table" 结尾,例如 Application 。
2.3.1 Column
org.skywalking.apm.collector.core.data.Column ,字段。
- `name` 属性,字段名。
- `operation` 属性,操作( Operation )。
2.3.2 Operation
org.skywalking.apm.collector.core.data.Operation ,操作接口。用于两个值之间的操作,例如,相加等等。目前实现类有:
- AddOperation :值相加操作。
- CoverOperation :值覆盖操作,即以新值为返回。
- NonOperation :空操作,即以老值为返回。
3. collector-storage-define
collector-cluster-define :定义存储组件接口。项目结构如下 :
3.1 StorageModule
org.skywalking.apm.collector.storage.StorageModule ,实现 Module 抽象类,集群管理 Module 。
#name() 实现方法,返回模块名为 "storage" 。
#services() 实现方法,返回 Service 类名:在 org.skywalking.apm.collector.storage.dao 包下的所有类 和 IBatchDAO。
3.2 table 包
在 org.skywalking.apm.collector.storage.table 包下,定义了存储模块所有的 Table 和 Data 实现类。
3.3 StorageInstaller
org.skywalking.apm.collector.storage.StorageInstaller ,存储安装器抽象类,基于 TableDefine ,初始化存储组件的表。
- `#defineFilter(List)` 抽象方法,过滤 TableDefine 数组中,非自身需要的。例如说,ElasticSearchStorageInstaller 过滤后,只保留 ElasticSearchTableDefine 对象。
- `#isExists(Client, TableDefine)` 抽象方法,判断表是否存在。
- `#deleteTable(Client, TableDefine)` 抽象方法,删除表。
- `#createTable(Client, TableDefine)` 抽象方法,创建表。
- `#install(Client)` 方法,基于 TableDefine ,初始化存储组件的表。
- 该方法会被 StorageModuleH2Provider 或 StorageModuleEsProvider 启动时调用。
3.4 dao 包
在 collector-storage-define 项目结构图,我们看到一共有两个 bao 包:
- org.skywalking.apm.collector.storage.base.dao ,系统的 DAO 接口。
- org.skywalking.apm.collector.storage.dao ,业务的 DAO 接口。
- 继承系统的 DAO 接口。
- 被 `collector-storage-xxx-provider` 的 `dao` 包实现。
3.4.1 系统 DAO
org.skywalking.apm.collector.storage.base.dao.DAO ,继承 Service 接口,DAO 接口。
无任何方法。
3.4.1.1 AbstractDAO
org.skywalking.apm.collector.storage.base.dao.AbstractDAO ,实现 DAO 接口,DAO 抽象基类。
- `client` 属性,数据操作客户端。例如,H2Client 、ElasticSearchClient 。
在 collector-storage-xxx-provider 模块中,H2DAO 、EsDAO 实现 AbstractDAO 。
3.4.1.2 IPersistenceDAO
org.skywalking.apm.collector.storage.base.dao.IPersistenceDAO ,实现 DAO 接口,持久化 DAO 接口,定义了 Data 的增删改查操作。
- `#get(id)` 接口方法,根据 ID 查询一条 Data 。
- `#deleteHistory(startTimestamp, endTimestamp)` 接口方法,删除时间范围内的 Data 们。
- `#prepareBatchInsert(data)` 接口方法,准备批量插入操作对象。例如:`CpuMetricEsPersistenceDAO#prepareBatchInsert(CpuMetric)` 方法,返回的是 org.elasticsearch.action.index.IndexRequestBuilder 对象。注意:
- 该方法不会发起具体的 DAO 操作,仅仅是创建插入操作对象,最终的执行在 `IBatchDAO#batchPersistence(List)`。
- 该方法创建的是批量插入操作对象们中的一个。
- `#prepareBatchUpdate(data)` 接口方法,准备批量更新操作对象。类似 #prepareBatchInsert(data)方法。
3.4.1.3 IBatchDAO
org.skywalking.apm.collector.storage.base.dao.IBatchDAO ,实现 DAO 接口,批量操作 DAO 接口。
- `#batchPersistence(List batchCollection)` 接口方法,通过执行批量操作对象数组,实现批量持久化数据。
- `batchCollection` 方法参数,通过 `IPersistenceDAO#prepareBatchInsert` 或 `IPersistenceDAO#prepareBatchUpdate` 方法,生成每个操作数组元素。
- 该方法会被 `PersistenceTimer#extractDataAndSave(…)` 或 `PersistenceWorker#onWork(…)` 方法调用,在 《SkyWalking 源码分析 —— Collector Streaming Computing 流式处理(二)》「4. PersistenceWorker」 详细解析。
在 collector-storage-xxx-provider 模块中,BatchH2DAO 、BatchEsDAO 实现 IBatchDAO 。
3.4.2 业务 DAO
在 StorageModule#services() 方法里,我们可以看到,业务 DAO 按照用途可以拆分成四种:
- Cache :缓存应用、应用实例、服务名
- Register :注册应用、应用实例、服务名
- Persistence :持久化,实际可以理解成批量持久化
- UI :SkyWaling UI 查询使用。
那么整理如下:
PackageDataCache / RegisterPersistenceUI关联文章registerApplication√
registerInstance√√√
registerServiceName√
jvmCpuMetric
√√
jvmCMetric
√√
jvmMemoryMetric
√√
jvmMemoryPoolMetric
√√
globalGlobalTrace
√√
instanceInstPerformance
√√
nodeNodeComponent
√√
nodeNodeMapping
√√
noderefNodeReference
√√
segmentSegmentCost
√√
segmentSegment
√√
serviceServiceEntry
√√
servicerefServiceReference
√√
4. collector-storage-h2-provider
collector-storage-h2-provider ,基于 H2 的存储组件实现。项目结构如下 :
该实现是单机版,建议仅用于 SkyWalking 快速上手,生产环境不建议使用。
由于生产环境主要使用 ES 的存储组件实现,所以本文暂不解析相关实现,感兴趣的胖友自己嗨起来。
5. collector-storage-es-provider
collector-storage-es-provider ,基于 ES 的存储组件实现。项目结构如下 :
实际使用时,通过 application.yml 配置如下:
JSON storage: elasticsearch: cluster_name: elasticsearch cluster_transport_sniffer: true cluster_nodes: 127.0.0.1:9300 index_shards_number: 2 index_replicas_number: 0 ttl: 7
- 生产环境下,推荐 Elasticsearch 配置成集群。
- cluster_name 、cluster_transport_sniffer 、cluster_nodes 、index_shards_number 、index_replicas_number 参数,Elasticsearch 相关参数。
- ttl :保留 N 天内的数据。超过 N 天的数据,将被自动滚动删除。
- 该功能目前版本暂未发布,需要等到 5.0 版本后。
- 《部署集群collector》
5.1 StorageModuleEsProvider
org.skywalking.apm.collector.storage.es.StorageModuleEsProvider ,实现 ModuleProvider抽象类,基于 ES 的存储组件服务提供者。
#name() 实现方法,返回组件服务提供者名为 "elasticsearch" 。
module() 实现方法,返回组件类为 StorageModule 。
#requiredModules() 实现方法,返回依赖组件为 "cluster" 。
#prepare(Properties) 实现方法,执行准备阶段逻辑。
- 第 71 至 75 行 :创建 `org.skywalking.apm.collector.client.elasticsearch.ElasticSearchClient` 对象。
- 第 77 至 82 行 :创建 DAO 对象们,并调用 #registerServiceImplementation() 父类方法,注册到 services 。
#start() 实现方法,执行启动阶段逻辑。
- 第 90 行 :调用 ElasticSearchClient#initialize() 方法,初始化 ZookeeperClient 。
- 第 93 至 94 行 :创建 ElasticSearchStorageInstaller 对象,初始化存储组件的表。在 「5.2.4 ElasticSearchStorageInstaller」 详细解析。
- 第 100 至 102 行 :创建 `org.skywalking.apm.collector.storage.es.StorageModuleEsRegistration` 对象,并注册信息到集群管理。在 《SkyWalking 源码分析 —— Collector Cluster 集群管理》 有详细解析。
- 第 105 至 107 行 :创建 `org.skywalking.apm.collector.storage.es.StorageModuleEsNamingListener`对象,并注册信息到集群管理。在 《SkyWalking 源码分析 —— Collector Cluster 集群管理》 有详细解析。
- 第 110 至 111 行 :创建 DataTTLKeeperTimer 对象。在 「5.4 DataTTLKeeperTimer」 详细解析。
#notifyAfterCompleted() 实现方法,执行启动完成逻辑。
- 第 115 行 :调用 DataTTLKeeperTimer#start() 方法,启动 DataTTLKeeperTimer 。在本文 「5.4 DataTTLKeeperTimer」 详细解析。
5.2 define 包
在 collector-storage-es-provider 项目结构图,我们看到一共有两个 define 包:
- org.skywalking.apm.collector.storage.es.base.define ,系统的 TableDefine 抽象类。
- org.skywalking.apm.collector.storage.es.define ,业务的 TableDefine 实现类。
- 继承系统的 TableDefine 抽象类。
5.2.1 ElasticSearchTableDefine
org.skywalking.apm.collector.storage.es.base.define.ElasticSearchTableDefine ,实现 TableDefine 接口,基于 Elasticsearch 的表定义抽象类。
- `#type()` 方法,文档元数据 _type 字段,参见 《Elasticsearch学习笔记》「_type」 。
- `#refreshInterval()` 抽象方法,文档索引刷新频率,参见 《Elasticsearch: 权威指南 ? 基础入门 ? 分片内部原理 ? 近实时搜索》「refresh API」。
5.2.2 ElasticSearchColumnDefine
org.skywalking.apm.collector.storage.es.base.define.ElasticSearchColumnDefine ,实现 ColumnDefine 抽象类,基于 ES 的字段定义。
- Type 枚举类:枚举 ES 字段类型。
5.2.3 业务 TableDefine 实现类
在 org.apache.skywalking.apm.collector.storage.es.define 包里,我们可以看到,所有基于 ES 的业务 TableDefine 实现类。例如:ApplicationEsTableDefine 。
整体 #refreshInterval() 方法返回的结果如下:
- 1 s
- CpuMetricEsTableDefine
- GCMetricEsTableDefine
- MemoryMetricEsTableDefine
- MemoryPoolMetricEsTableDefine
- 2 s
- InstPerformanceEsTableDefine
- NodeComponentEsTableDefine
- NodeMappingEsTableDefine
- NodeReferenceEsTableDefine
- ServiceEntryEsTableDefine
- ServiceReferenceEsTableDefine
- 2 s && WriteRequest.RefreshPolicy.IMMEDIATE
- 【WriteRequest.RefreshPolicy.IMMEDIATE】参见 `ApplicationEsRegisterDAO#save(Application)` 方法
- ApplicationEsTableDefine
- InstanceEsTableDefine
- ServiceNameEsTableDefine
- 5 s
- GlobalTraceEsTableDefine
- SegmentCostEsTableDefine
- 10 s
- SegmentEsTableDefine
5.2.4 ElasticSearchStorageInstaller
友情提示:ElasticSearchStorageInstaller 主要是对 Elasticsearch Java API 的使用,所以不熟悉的胖友,可以 Google 下。
org.skywalking.apm.collector.storage.es.base.define.ElasticSearchStorageInstaller,实现 StorageInstaller 抽象类, 基于 ES 存储安装器实现类。
- `#defineFilter(List)` 实现方法,过滤数组中,非 ElasticSearchTableDefine 的元素。
- `#createTable(Client, TableDefine)` 实现方法,创建 Elasticsearch 索引。
- SkyWalking 彭勇升 :`_index`和 `_type` 是 ES 特有的,考虑其他数据库接入,所以没有用他这个特性。
- SkyWalking QQ交流群( 392443393 ) ,小心 群友 :`_type` 本来就没做物理隔离,Lucene 层面也不存在,ES 6.x 已经废弃了。
- 《Elasticsearch 6.0 将移除 Type》
- `_id` :数据编号,String 类型。
- `_type` :`"type"` 。
- `_index` :TableDefine 定义的表名。
- `source` :Data 数据。
- 文档数据结构如下:
- 了解 Elasticsearch 的胖友可能有和笔者一样的疑惑,网络上很多文章把 `_index` 类比成关系数据库的 DB ,`_type` 类比成关系数据库的 Table ,和 SkyWalking 目前使用的方式不一致?
- `#deleteTable(Client, TableDefine)` 实现方法,删除 Elasticsearch 索引。
- `#isExists(Client, TableDefine)` 实现方法,判断 Elasticsearch 索引是否存在。
- 在方法里,笔者添加了一些 API 的说明,不熟悉的胖友,可以仔细阅读理解。
5.3 dao 包
在 collector-storage-es-provider 项目结构图,我们看到一共有两个 dao 包:
- org.skywalking.apm.collector.storage.es.base.dao ,系统的 DAO 抽象类。
- org.skywalking.apm.collector.storage.es.dao ,业务的 DAO 实现类。
- 继承系统的 DAO 抽象类。
5.3.1 EsDAO
org.skywalking.apm.collector.storage.es.base.dao.EsDAO ,实现 AbstractDAO 抽象类,基于 ES 的 DAO 抽象类。
- `#getMaxId(indexName, columnName)` 方法,获得索引名的指定字段的最大值。
- `#getMinId(indexName, columnName)` 方法,获得索引名的指定字段的最小值。
5.3.2 BatchEsDAO
org.skywalking.apm.collector.storage.es.base.dao.BatchEsDAO ,实现 IBatchDAO 接口,继承 EsDAO 抽象类,基于 ES 批量操作 DAO 实现类。
- `#batchPersistence(List)` 实现方法,将 org.elasticsearch.action.index.IndexRequestBuilder 和 org.elasticsearch.action.index.UpdateRequestBuilder 数组,创建成 org.elasticsearch.action.bulk.BulkRequestBuilder 对象,批量持久化。
- IndexRequestBuilder 和 UpdateRequestBuilder 的创建,在 「5.3.3 业务 DAO 实现类」 会看到。
5.3.3 业务 DAO 实现类
在 org.apache.skywalking.apm.collector.storage.es.dao 包里,我们可以看到,所有基于 ES 的业务 DAO 实现类。
实现代码易懂,胖友可以自己阅读。良心如我们,按照 DAO 的业务用途,推荐例子如下:
- Cache :ApplicationEsCacheDAO
- Register :ApplicationEsRegisterDAO
- Persistence :SegmentEsPersistenceDAO
- 此处可见 IndexRequestBuilder 和 UpdateRequestBuilder 的创建。
- UI :SegmentEsUIDAO
5.4 DataTTLKeeperTimer
org.skywalking.apm.collector.storage.es.DataTTLKeeperTimer ,过期数据删除定时器。通过该定时器,只保留 N 天内的数据。
- `#start()` 方法,启动定时任务。
- 第 49 行:创建延迟 1 小时,每 8 小时执行一次 `#delete()` 方法的定时任务。目前该行代码被注释,胖友可以等待 SkyWallking 5.0 版本的发布。
- `#delete()` 方法,删除过期数据。
- 第 54 至 66 行:计算删除的开始与结束时间,即指定时间的前一天。例如,2017-12-23 执行时,删除 2017-12-16 那天的数据。
- 第 69 行:调用 `#deleteJVMRelatedData(startTimestamp, endTimestamp)` 方法,删除 JVM 相关的数据。
- 第 70 行:调用 `#deleteTraceRelatedData(startTimestamp, endTimestamp)` 方法,删除 Trace 相关的数据。
如下是不会删除的数据的表:
- Application
- Instance
- ServiceName
- ServiceEntry
搜索微信号(ID:芋道源码),可以获得各种 Java 源码解析。
并且,回复【书籍】后,可以领取笔者推荐的各种 Java 从入门到架构的书籍。
相关推荐
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
-
明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
-
首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
-
齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...
- Qt界面——搭配QCustomPlot(qt platform)
-
这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
-
老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
-
用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
-
前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...
- 掌握Visual Studio项目配置【基础篇】
-
1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
-
随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...
- Visual Studio Community 2022(VS2022)安装图文方法
-
直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...
- Qt添加MSVC构建套件的方法(qt添加c++11)
-
前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...
- Qt为什么站稳c++GUI的top1(qt c)
-
为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...
- qt开发IDE应该选择VS还是qt creator
-
如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...
- Qt 5.14.2超详细安装教程,不会来打我
-
Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...
- Cygwin配置与使用(四)——VI字体和颜色的配置
-
简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
- Qt界面——搭配QCustomPlot(qt platform)
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
- 掌握Visual Studio项目配置【基础篇】
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
- Visual Studio Community 2022(VS2022)安装图文方法
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)