首页 文章 精选 留言 我的

精选列表

搜索[单机],共4013篇文章
优秀的个人博客,低调大师

突破极限:高负载场景下的单机 300M 多行正则日志采集不是梦

作者:裘文成(翊韬) 问题背景 在当今数字化时代,日志数据已成为企业 IT 运营和业务分析的关键资源。然而,随着业务规模的扩大和系统复杂度的提升,日志数据的体量呈现爆发式增长,给日志采集和处理系统带来了巨大挑战。最近,我们遇到了一个典型案例,充分体现了当前日志服务采集在高负载场景下面临的困境,以下为客户现状: 海量日志与正则采集:客户的某项业务产生了数量巨大的多行日志,并且需要通过正则表达式进行日志解析。这种复杂的采集模式本身就对系统资源提出了较高要求。 关键业务影响:这些日志数据和客户的核心业务分析任务直接相关。过高的采集延迟会影响数据分析的准确性。 采集性能瓶颈:客户根据 iLogtail 启动参数配置文档【1】对 iLogtail 的线程数等进行了调整,在压测时采集速度依然只有 90M/s,但实际压测时的日志生成速度在 200M/s,远超采集速度。这导致了日志采集出现近 1 小时的延迟。 业务需求升级:客户计划进一步增加压测量,预计写入流量将达到 300MB/s。这将进一步加剧采集延迟问题。 业务负载高:客户的业务已经占据了大部分的 CPU 资源,比较困难继续为 iLogtail 提供更多的资源。 技术难点 在收到客户反馈后,我们立即着手分析问题并制定优化策略。通过获取客户的测试日志样本并进行深入测试,我们发现了以下关键技术难点: 性能瓶颈 挑战: 即使在优化的测试环境中,我们也无法达到客户期望的 300MB/s 处理速度。 数据: 采用 16 线程并行处理,最高吞吐量仅为约 270MB/s。 影响: 无法满足客户的性能需求,可能导致日志处理延迟和数据分析滞后。 资源消耗与业务冲突 挑战: 为接近目标性能,iLogtail 需要占用大量系统资源。 数据: 需要 16 个线程才能达到 270MB/s 的处理速度。 影响: 高强度的资源占用严重影响服务器上的其他业务进程,可能导致整体系统性能下降。 线程扩展效益递减 挑战: 简单增加处理线程数量并不能线性提升性能,达到一定线程数后,性能增益呈现边际递减趋势。 影响: 表明仅依靠增加硬件资源难以实现质的突破,需要从算法层面进行优化。 优化过程与成果 怕看官们等不及,在深入技术细节之前,让我们先一睹为快,直观呈现这次优化的成果: 性能质的飞跃:成功将采集速率提升至超过 300MB/s,完全满足客户需求。 资源利用大幅优化:在保持高性能的同时,将所需线程数从16减少到8,显著降低了 CPU 占用。 创新解决方案:针对资源受限的场景,我们推出了 IngestProcessor 方案。使用这种方案,iLogtail 仅需 1 个线程就能实现 320MB/s 的采集速度,为客户提供了极致的资源效率选择。 本文分析和测试,皆在以下硬件环境进行测试 硬件环境 计算资源:阿里云 ECS 实例(规格:ecs.c5.8xlarge) 存储资源:PL3 规格的 ESSD 云盘 为避免磁盘 I/O 出现瓶颈,在高日志量的输出和采集场景,我们推荐使用 PL3 规格的 ESSD 云盘。本文将基于该规格的 ESSD 云盘进行性能分析,详情可参考 ESSD 云盘官方文档【2】 多行日志采集性能提升 初步观察 为深入了解性能瓶颈,我们首先将注意力放在多行日志的处理性能上。 客户的日志主要是多行格式。 多行日志采集步骤在日志正则处理之前,可能对整体性能产生重大影响。 在深入研究多行日志采集性能时,我们观察到了一个令人震惊的现象。尽管预期多行日志处理会对性能产生一定影响,但实际测试结果却远远超出了我们的初步估计。在统一的单线程环境下,我们记录到以下数据: 单行日志采集速度:425MB/s 多行日志采集速度:98MB/s 这近乎 80% 的性能下降不仅令人惊讶,更引发了我们对多行采集算法实现的深度思考: 性能差距:从 425MB/s 骤降至 98MB/s,这种程度的性能退化远远超出了我们对多行处理的初始预期。 异常性:如此巨大的差异明显超出了正常的多行处理开销,显然存在一个重大的性能瓶颈。 算法效率质疑:这一现象使我们不得不重新审视当前多行采集算法的实现效率。可能存在某些未优化的操作或不必要的重复计算。 iLogtail 多行日志处理原理 iLogtail 的多行日志合并功能基于特定的日志格式将分散的多行数据聚合为完整事件。其工作流程如下: 用户配置行首正则表达式。 iLogtail 对每行日志开头应用此正则。 若某行不匹配,iLogtail 继续等待直至找到匹配的行首。 举个例子,假设我们有如下的日志格式,通常我们会配置行首正则为 \d+-\d+-\d+\s\d+:\d+:\d+.\d+\s.*,iLogtail 会拿着这个正则对每行进行匹配,将这些单行日志合并成一个完整的多行日志。 2024-03-15 14:23:45.678 ERROR 987654 --- [TaskExecutor-1] c.e.d.s.TaskScheduler : Failed to process task due to unexpected exception java.lang.NullPointerException: Cannot invoke "com.example.data.model.Task.getPriority()" because "task" is null at com.example.data.processor.TaskProcessor.processTask(TaskProcessor.java:123) at com.example.data.scheduler.TaskScheduler.lambda$scheduleTask$1(TaskScheduler.java:89) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.IllegalArgumentException: Task ID cannot be null or empty at com.example.data.validator.TaskValidator.validateTask(TaskValidator.java:45) at com.example.data.processor.TaskProcessor.processTask(TaskProcessor.java:115) ... 4 common frames omitted 性能瓶颈分析 深入 iLogtail 的实现机制,我们发现性能瓶颈的关键在于其正则匹配方法。 iLogtail 使用 boost::regex_match 函数进行全量匹配,这在处理大规模日志时会产生显著的性能开销。 bool BoostRegexMatch(const char* buffer, size_t size, const boost::regex& reg, string& exception) { // ... if (boost::regex_match(buffer, buffer + size, reg)) return true; // ... } 对于之前提到的日志示例,正则表达式会对第一行的全部 253 个字符进行匹配,这在处理大量日志时会导致性能下降。 为了量化这一性能问题,我编写了测试代码进行实验,目的是观察随着与行首正则无关的日志长度增加(即 .* 匹配的部分),boost::regex_match 的执行时间如何变化。 static void BM_Regex_Match(int batchSize) { std::string buffer = "2024-07-19 15:02:16.055 INFO "; std::string regStr = "\\d+-\\d+-\\d+\\s\\d+:\\d+:\\d+\\.\\d+\\s.*"; boost::regex reg(regStr); std::ofstream outFile("BM_Regex_Match.txt", std::ios::trunc); outFile.close(); for (int i = 0; i < 1000; i++) { std::ofstream outFile("BM_Regex_Match.txt", std::ios::app); buffer += "a"; int count = 0; uint64_t durationTime = 0; for (int j = 0; j < batchSize; j++) { count++; uint64_t startTime = GetCurrentTimeInMicroSeconds(); if (!boost::regex_match(buffer, reg)) { std::cout << "error" << std::endl; } durationTime += GetCurrentTimeInMicroSeconds() - startTime; } outFile << i << '\t' << "durationTime: " << durationTime << std::endl; outFile << i << '\t' << "process: " << formatSize(buffer.size() * (uint64_t)count * 1000000 / durationTime) << "/s" << std::endl; outFile.close(); } } int main(int argc, char** argv) { logtail::Logger::Instance().InitGlobalLoggers(); std::cout << "BM_Regex_Match" << std::endl; BM_Regex_Match(10000); return 0; } 通过这个实验,可以观察到一个关键现象: 随着与行首正则无关的日志长度增加(即 .* 匹配的那部分日志),boost::regex_match 的执行时间也呈线性增长。 基于实验结果,我们可以得出以下结论: 全量匹配的低效性:boost::regex_match 对整行进行匹配,即使只有行首部分是关键的。 资源浪费:匹配时间与日志行长度呈线性关系,大部分匹配时间花在了与实际分割逻辑无关的内容上(.* 匹配的部分),这在处理大量长行日志时会导致严重的性能下降。 性能优化 基于性能瓶颈分析,我们确定了关键的优化方向:实现部分匹配。这种方法只对日志行首进行匹配,而不是整行,有望显著提高处理效率。 为了避免重复造轮子,在调研后,我们发现 Boost 库提供了一个替代方案:boost::regex_search 函数,通过适当配置,这个函数能够精确满足我们的需求。以下是优化后的代码实现: bool BoostRegexSearch(const char* buffer, size_t size, const boost::regex& reg, string& exception) { // ... if (boost::regex_search(buffer, buffer + size, what, reg, boost::match_continuous)) { return true; } // ... } 关键改进点: 使用 boost::regex_search 替代 boost::regex_match。 添加 boost::match_continuous 标志,确保只匹配前缀,如果字符串的开头子串满足正则表达式,就会返回成功。 这种实现方式允许我们精确控制匹配过程,只关注日志行首,这正是多行日志处理所需要的。 为了量化这一优化的效果,和 boost::regex_match 一样,我也对 boost::regex_search 根据日志长度进行了测试。可以发现,新方案的执行时间基本保持稳定,不受日志长度影响。 优化后的多行算法,实际采集效果 我们对使用优化后多行算法的 iLogtail,进行了多行日志采集,以下是测试的详细结果: 多行采集性能飞跃 优化后的多行采集速度从 98MB/s 提升到 350MB/s 性能提升幅度:257%(约 3.57 倍) 接近单行采集性能 优化后的多行采集速度(350MB/s)已接近单行采集(425MB/s) 相对于单行采集的性能比:82.35% 资源利用效率 在保持单线程的情况下实现显著性能提升 体现了算法优化在提高资源利用效率方面的巨大潜力 用户体验优化 在 iLogtail 的已有实现中,用户配置的行首正则表达式通常包含 .*后缀。这是由于之前的匹配机制会匹配整行内容,为了让客户不改动采集配置,只需要升级 iLogtail 版本 2.1 及以上,就能享受到该多行采集性能优化,我们设计了一个以下兼容性策略: 正则表达式解析: 在处理用户配置时,iLogtail 会自动分析正则表达式。 如果检测.*后缀的存在,iLogtail 动态调整正则表达式,移除.*后缀。 正则匹配双方案 改进多行日志性能后的多行正则匹配采集 在优化多行日志采集性能后,我们进一步探讨了将改进后的多行采集与正则提取相结合的效果。下面详细分析了这种组合方案的性能表现及其实际应用价值。在 8 线程下, iLogtail 的多行日志采集性能已经可以到 370MB/s,已经足够满足客户的采集速度需求。 采集速率提升 优化后的采集速率从 270MB/s 提升到 370MB/s 性能提升幅度:37% 资源利用优化 线程数从 16 减少到 8,减少了 50% 在减少一半 CPU 资源的同时,仍然实现了显著的性能提升 正则提取的限制 由于正则提取需要对日志进行全量匹配,多行采集的部分优化手段在此无法应用 本地资源紧张 - 快速迁移 IngestProcessor 尽管我们成功地在 8 线程下实现了 iLogtail 对多行日志的采集和正则提取,但这种方法仍然面临着一些挑战: 高资源需求:需要占用 8 个 CPU 核心,对机器资源造成显著压力。 客户端限制:并非所有客户都有能力或意愿增加机器资源。 可扩展性问题:随着业务压力增加,客户端资源可能成为瓶颈。 为了应对这些挑战,阿里云日志服务推出了一个创新的解决方案:写入处理器(IngestProcessor)。这种方法不仅有效解决了资源限制问题,还大幅提高了处理效率。 工作原理 数据流:通过 iLogtail 采集的日志数据首先经过 IngestProcessor。 处理位置:数据处理过程在日志服务中完成,而非客户端。 资源优化:这种方法显著减少了客户端资源占用,释放计算能力。 注意事项 IngestProcessor不支持日志聚合(将多个日志合并为一个)。 该功能需要额外计费。详细信息请参考阿里云官方文档:https://help.aliyun.com/zh/sls/user-guide/overview-of-sls-data-processing。 IngestProcessor 不仅可以解决资源限制问题,还提供了丰富的数据处理能力: 字段提取:从原始日志字段中通过正则表达式、Key-Value 格式、JSON 等解析方式提取出新的字段。 扩展字段:为原始日志添加新的字段。 丢弃字段:删除原始日志的部分字段。 数据脱敏:将原始日志的敏感信息进行脱敏处理。 数据过滤:丢弃原始日志的部分数据。 由于 IngestProcessor 使用的 SPL 语法和 iLogtail 使用的 SPL 语法一致,因此我们可以直接把 iLogtail 使用的 SPL 语句复制到 IngestProcessor 上,实现快速迁移。 我们在 10 个 shard 的环境下进行了详细的性能测试。以下是使用的 SPL 语句和测试结果: 资源利用效率: IngestProcessor 方案将客户端 CPU 占用从 16 核心降至 1 核心 ,减少了 93.75% 。 同时保持了 320MB/s 的高采集速率,与不解析正则时的多行采集极限速率接近。 性能平衡: 虽然采集速率略有下降,但资源占用的大幅减少使得整体效率显著提升。 对于资源受限的环境,这种轻微的速度降低是完全可以接受的。 可扩展性: 通过将处理负载转移到云端,客户端获得了更大的扩展空间。 这种方案为处理更大规模的日志数据提供了可能性。 实际应用价值 成本效益:客户可以通过评估硬件资源成本和 IngestProcessor 使用成本,灵活选择适合自己的方案,降低了总体拥有成本。 灵活部署:使客户能在资源受限的环境中部署高级日志处理功能。 快速迁移:与 iLogtail 使用相同的 SPL 语法,便于现有用户快速采用新方案。 结论 通过这次全面的性能测试和优化,我们可以得出以下结论: 高效性:优化后的 iLogtail 能够在 8 线程下稳定地处理 300MB/s 的多行日志数据并进行正则提取,展现了卓越的性能。 灵活的计算迁移方案:在传统的基于 iLogtail 进行日志采集和处理的方案外,结合 iLogtail 的高效采集和 IngestProcessor 的强大处理能力,我们实现了一个既高效又灵活的日志处理方案,能够在 1 线程下稳定地处理 300MB/s 的多行日志数据并进行正则提取。这种组合能够满足各种复杂的日志处理需求,而不会对客户端性能造成额外负担。 注意事项:正则提取的性能还受到正则表达式本身复杂度的影响。如果正则表达式设计得过于复杂,或者包含大量的回溯操作,可能会导致匹配效率显著下降,尤其是在处理大规模数据时。因此,在编写正则表达式时,应尽量优化其结构,避免不必要的嵌套和冗余匹配。 相关链接: 【1】iLogtail 启动参数配置文档: https://help.aliyun.com/zh/sls/user-guide/configure-the-startup-parameters-of-logtail 【2】ESSD 云盘官方文档: https://help.aliyun.com/zh/ecs/user-guide/essds

优秀的个人博客,低调大师

突破极限: 高负载场景下的单机300M多行正则日志采集不是梦

作者:裘文成(翊韬) 问题背景 在当今数字化时代,日志数据已成为企业 IT 运营和业务分析的关键资源。然而,随着业务规模的扩大和系统复杂度的提升,日志数据的体量呈现爆发式增长,给日志采集和处理系统带来了巨大挑战。最近,我们遇到了一个典型案例,充分体现了当前日志服务采集在高负载场景下面临的困境,以下为客户现状: 海量日志与正则采集:客户的某项业务产生了数量巨大的多行日志,并且需要通过正则表达式进行日志解析。这种复杂的采集模式本身就对系统资源提出了较高要求。 关键业务影响:这些日志数据和客户的核心业务分析任务直接相关。过高的采集延迟会影响数据分析的准确性。 采集性能瓶颈:客户根据 iLogtail 启动参数配置文档【1】对 iLogtail 的线程数等进行了调整,在压测时采集速度依然只有 90M/s,但实际压测时的日志生成速度在 200M/s,远超采集速度。这导致了日志采集出现近 1 小时的延迟。 业务需求升级:客户计划进一步增加压测量,预计写入流量将达到 300MB/s。这将进一步加剧采集延迟问题。 业务负载高:客户的业务已经占据了大部分的 CPU 资源,比较困难继续为 iLogtail 提供更多的资源。 技术难点 在收到客户反馈后,我们立即着手分析问题并制定优化策略。通过获取客户的测试日志样本并进行深入测试,我们发现了以下关键技术难点: 性能瓶颈 挑战: 即使在优化的测试环境中,我们也无法达到客户期望的 300MB/s 处理速度。 数据: 采用 16 线程并行处理,最高吞吐量仅为约 270MB/s。 影响: 无法满足客户的性能需求,可能导致日志处理延迟和数据分析滞后。 资源消耗与业务冲突 挑战: 为接近目标性能,iLogtail 需要占用大量系统资源。 数据: 需要 16 个线程才能达到 270MB/s 的处理速度。 影响: 高强度的资源占用严重影响服务器上的其他业务进程,可能导致整体系统性能下降。 线程扩展效益递减 挑战: 简单增加处理线程数量并不能线性提升性能,达到一定线程数后,性能增益呈现边际递减趋势。 影响: 表明仅依靠增加硬件资源难以实现质的突破,需要从算法层面进行优化。 优化过程与成果 怕看官们等不及,在深入技术细节之前,让我们先一睹为快,直观呈现这次优化的成果: 性能质的飞跃:成功将采集速率提升至超过 300MB/s,完全满足客户需求。 资源利用大幅优化:在保持高性能的同时,将所需线程数从16减少到8,显著降低了 CPU 占用。 创新解决方案:针对资源受限的场景,我们推出了 IngestProcessor 方案。使用这种方案,iLogtail 仅需 1 个线程就能实现 320MB/s 的采集速度,为客户提供了极致的资源效率选择。 本文分析和测试,皆在以下硬件环境进行测试 硬件环境 计算资源:阿里云 ECS 实例(规格:ecs.c5.8xlarge) 存储资源:PL3 规格的 ESSD 云盘 为避免磁盘 I/O 出现瓶颈,在高日志量的输出和采集场景,我们推荐使用 PL3 规格的 ESSD 云盘。本文将基于该规格的 ESSD 云盘进行性能分析,详情可参考 ESSD 云盘官方文档【2】 多行日志采集性能提升 初步观察 为深入了解性能瓶颈,我们首先将注意力放在多行日志的处理性能上。 客户的日志主要是多行格式。 多行日志采集步骤在日志正则处理之前,可能对整体性能产生重大影响。 在深入研究多行日志采集性能时,我们观察到了一个令人震惊的现象。尽管预期多行日志处理会对性能产生一定影响,但实际测试结果却远远超出了我们的初步估计。在统一的单线程环境下,我们记录到以下数据: 单行日志采集速度:425MB/s 多行日志采集速度:98MB/s 这近乎 80% 的性能下降不仅令人惊讶,更引发了我们对多行采集算法实现的深度思考: 性能差距:从 425MB/s 骤降至 98MB/s,这种程度的性能退化远远超出了我们对多行处理的初始预期。 异常性:如此巨大的差异明显超出了正常的多行处理开销,显然存在一个重大的性能瓶颈。 算法效率质疑:这一现象使我们不得不重新审视当前多行采集算法的实现效率。可能存在某些未优化的操作或不必要的重复计算。 iLogtail 多行日志处理原理 iLogtail 的多行日志合并功能基于特定的日志格式将分散的多行数据聚合为完整事件。其工作流程如下: 用户配置行首正则表达式。 iLogtail 对每行日志开头应用此正则。 若某行不匹配,iLogtail 继续等待直至找到匹配的行首。 举个例子,假设我们有如下的日志格式,通常我们会配置行首正则为 \d+-\d+-\d+\s\d+:\d+:\d+.\d+\s.*,iLogtail 会拿着这个正则对每行进行匹配,将这些单行日志合并成一个完整的多行日志。 2024-03-15 14:23:45.678 ERROR 987654 --- [TaskExecutor-1] c.e.d.s.TaskScheduler : Failed to process task due to unexpected exception java.lang.NullPointerException: Cannot invoke "com.example.data.model.Task.getPriority()" because "task" is null at com.example.data.processor.TaskProcessor.processTask(TaskProcessor.java:123) at com.example.data.scheduler.TaskScheduler.lambda$scheduleTask$1(TaskScheduler.java:89) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.IllegalArgumentException: Task ID cannot be null or empty at com.example.data.validator.TaskValidator.validateTask(TaskValidator.java:45) at com.example.data.processor.TaskProcessor.processTask(TaskProcessor.java:115) ... 4 common frames omitted 性能瓶颈分析 深入 iLogtail 的实现机制,我们发现性能瓶颈的关键在于其正则匹配方法。 iLogtail 使用 boost::regex_match 函数进行全量匹配,这在处理大规模日志时会产生显著的性能开销。 bool BoostRegexMatch(const char* buffer, size_t size, const boost::regex& reg, string& exception) { // ... if (boost::regex_match(buffer, buffer + size, reg)) return true; // ... } 对于之前提到的日志示例,正则表达式会对第一行的全部 253 个字符进行匹配,这在处理大量日志时会导致性能下降。 为了量化这一性能问题,我编写了测试代码进行实验,目的是观察随着与行首正则无关的日志长度增加(即 .* 匹配的部分),boost::regex_match 的执行时间如何变化。 static void BM_Regex_Match(int batchSize) { std::string buffer = "2024-07-19 15:02:16.055 INFO "; std::string regStr = "\\d+-\\d+-\\d+\\s\\d+:\\d+:\\d+\\.\\d+\\s.*"; boost::regex reg(regStr); std::ofstream outFile("BM_Regex_Match.txt", std::ios::trunc); outFile.close(); for (int i = 0; i < 1000; i++) { std::ofstream outFile("BM_Regex_Match.txt", std::ios::app); buffer += "a"; int count = 0; uint64_t durationTime = 0; for (int j = 0; j < batchSize; j++) { count++; uint64_t startTime = GetCurrentTimeInMicroSeconds(); if (!boost::regex_match(buffer, reg)) { std::cout << "error" << std::endl; } durationTime += GetCurrentTimeInMicroSeconds() - startTime; } outFile << i << '\t' << "durationTime: " << durationTime << std::endl; outFile << i << '\t' << "process: " << formatSize(buffer.size() * (uint64_t)count * 1000000 / durationTime) << "/s" << std::endl; outFile.close(); } } int main(int argc, char** argv) { logtail::Logger::Instance().InitGlobalLoggers(); std::cout << "BM_Regex_Match" << std::endl; BM_Regex_Match(10000); return 0; } 通过这个实验,可以观察到一个关键现象: 随着与行首正则无关的日志长度增加(即 .* 匹配的那部分日志),boost::regex_match 的执行时间也呈线性增长。 基于实验结果,我们可以得出以下结论: 全量匹配的低效性:boost::regex_match 对整行进行匹配,即使只有行首部分是关键的。 资源浪费:匹配时间与日志行长度呈线性关系,大部分匹配时间花在了与实际分割逻辑无关的内容上(.* 匹配的部分),这在处理大量长行日志时会导致严重的性能下降。 性能优化 基于性能瓶颈分析,我们确定了关键的优化方向:实现部分匹配。这种方法只对日志行首进行匹配,而不是整行,有望显著提高处理效率。 为了避免重复造轮子,在调研后,我们发现 Boost 库提供了一个替代方案:boost::regex_search 函数,通过适当配置,这个函数能够精确满足我们的需求。以下是优化后的代码实现: bool BoostRegexSearch(const char* buffer, size_t size, const boost::regex& reg, string& exception) { // ... if (boost::regex_search(buffer, buffer + size, what, reg, boost::match_continuous)) { return true; } // ... } 关键改进点: 使用 boost::regex_search 替代 boost::regex_match。 添加 boost::match_continuous 标志,确保只匹配前缀,如果字符串的开头子串满足正则表达式,就会返回成功。 这种实现方式允许我们精确控制匹配过程,只关注日志行首,这正是多行日志处理所需要的。 为了量化这一优化的效果,和 boost::regex_match 一样,我也对 boost::regex_search 根据日志长度进行了测试。可以发现,新方案的执行时间基本保持稳定,不受日志长度影响。 优化后的多行算法,实际采集效果 我们对使用优化后多行算法的 iLogtail,进行了多行日志采集,以下是测试的详细结果: 多行采集性能飞跃 优化后的多行采集速度从 98MB/s 提升到 350MB/s 性能提升幅度:257%(约 3.57 倍) 接近单行采集性能 优化后的多行采集速度(350MB/s)已接近单行采集(425MB/s) 相对于单行采集的性能比:82.35% 资源利用效率 在保持单线程的情况下实现显著性能提升 体现了算法优化在提高资源利用效率方面的巨大潜力 用户体验优化 在 iLogtail 的已有实现中,用户配置的行首正则表达式通常包含 .*后缀。这是由于之前的匹配机制会匹配整行内容,为了让客户不改动采集配置,只需要升级 iLogtail 版本 2.1 及以上,就能享受到该多行采集性能优化,我们设计了一个以下兼容性策略: 正则表达式解析: 在处理用户配置时,iLogtail 会自动分析正则表达式。 如果检测.*后缀的存在,iLogtail 动态调整正则表达式,移除.*后缀。 正则匹配双方案 改进多行日志性能后的多行正则匹配采集 在优化多行日志采集性能后,我们进一步探讨了将改进后的多行采集与正则提取相结合的效果。下面详细分析了这种组合方案的性能表现及其实际应用价值。在 8 线程下, iLogtail 的多行日志采集性能已经可以到 370MB/s,已经足够满足客户的采集速度需求。 采集速率提升 优化后的采集速率从 270MB/s 提升到 370MB/s 性能提升幅度:37% 资源利用优化 线程数从 16 减少到 8,减少了 50% 在减少一半 CPU 资源的同时,仍然实现了显著的性能提升 正则提取的限制 由于正则提取需要对日志进行全量匹配,多行采集的部分优化手段在此无法应用 本地资源紧张 - 快速迁移 IngestProcessor 尽管我们成功地在 8 线程下实现了 iLogtail 对多行日志的采集和正则提取,但这种方法仍然面临着一些挑战: 高资源需求:需要占用 8 个 CPU 核心,对机器资源造成显著压力。 客户端限制:并非所有客户都有能力或意愿增加机器资源。 可扩展性问题:随着业务压力增加,客户端资源可能成为瓶颈。 为了应对这些挑战,阿里云日志服务推出了一个创新的解决方案:写入处理器(IngestProcessor)。这种方法不仅有效解决了资源限制问题,还大幅提高了处理效率。 工作原理 数据流:通过 iLogtail 采集的日志数据首先经过 IngestProcessor。 处理位置:数据处理过程在日志服务中完成,而非客户端。 资源优化:这种方法显著减少了客户端资源占用,释放计算能力。 注意事项 IngestProcessor不支持日志聚合(将多个日志合并为一个)。 该功能需要额外计费。详细信息请参考阿里云官方文档:https://help.aliyun.com/zh/sls/user-guide/overview-of-sls-data-processing。 IngestProcessor 不仅可以解决资源限制问题,还提供了丰富的数据处理能力: 字段提取:从原始日志字段中通过正则表达式、Key-Value 格式、JSON 等解析方式提取出新的字段。 扩展字段:为原始日志添加新的字段。 丢弃字段:删除原始日志的部分字段。 数据脱敏:将原始日志的敏感信息进行脱敏处理。 数据过滤:丢弃原始日志的部分数据。 由于 IngestProcessor 使用的 SPL 语法和 iLogtail 使用的 SPL 语法一致,因此我们可以直接把 iLogtail 使用的 SPL 语句复制到 IngestProcessor 上,实现快速迁移。 我们在 10 个 shard 的环境下进行了详细的性能测试。以下是使用的 SPL 语句和测试结果: 资源利用效率: IngestProcessor 方案将客户端 CPU 占用从 16 核心降至 1 核心 ,减少了 93.75% 。 同时保持了 320MB/s 的高采集速率,与不解析正则时的多行采集极限速率接近。 性能平衡: 虽然采集速率略有下降,但资源占用的大幅减少使得整体效率显著提升。 对于资源受限的环境,这种轻微的速度降低是完全可以接受的。 可扩展性: 通过将处理负载转移到云端,客户端获得了更大的扩展空间。 这种方案为处理更大规模的日志数据提供了可能性。 实际应用价值 成本效益:客户可以通过评估硬件资源成本和 IngestProcessor 使用成本,灵活选择适合自己的方案,降低了总体拥有成本。 灵活部署:使客户能在资源受限的环境中部署高级日志处理功能。 快速迁移:与 iLogtail 使用相同的 SPL 语法,便于现有用户快速采用新方案。 结论 通过这次全面的性能测试和优化,我们可以得出以下结论: 高效性:优化后的 iLogtail 能够在 8 线程下稳定地处理 300MB/s 的多行日志数据并进行正则提取,展现了卓越的性能。 灵活的计算迁移方案:在传统的基于 iLogtail 进行日志采集和处理的方案外,结合 iLogtail 的高效采集和 IngestProcessor 的强大处理能力,我们实现了一个既高效又灵活的日志处理方案,能够在 1 线程下稳定地处理 300MB/s 的多行日志数据并进行正则提取。这种组合能够满足各种复杂的日志处理需求,而不会对客户端性能造成额外负担。 注意事项:正则提取的性能还受到正则表达式本身复杂度的影响。如果正则表达式设计得过于复杂,或者包含大量的回溯操作,可能会导致匹配效率显著下降,尤其是在处理大规模数据时。因此,在编写正则表达式时,应尽量优化其结构,避免不必要的嵌套和冗余匹配。 相关链接: 【1】iLogtail 启动参数配置文档: https://help.aliyun.com/zh/sls/user-guide/configure-the-startup-parameters-of-logtail 【2】ESSD 云盘官方文档: https://help.aliyun.com/zh/ecs/user-guide/essds

优秀的个人博客,低调大师

单机服务器如何充分利用服务器性能,达到最大并发量?

23号当天12点一瞬间有大量的用户涌进来,具体多少人不好说,我看TCP链接是6千左右。 我的服务器配置如下: 数据库是使用阿里云的PolarDB,8核32GB,有读写分离,最大IOPS是64000,我查看过数据库相关压力,数据库几乎没有波动,排除是数据库原因造成的,我们的业务相对简单,且大部分查询是单表查询,数据量很小。 服务器是使用阿里云的ECS,8核16G,CPU占用最高不到百分之二十,基本都在百分之五左右,内存占用也没有什么波动,CPU和内存没有充分得到利用。 我是使用的spring boot 部署,容器是tomcat,配置如下: server.tomcat.accept-count=500 server.tomcat.max-connections=30000 server.tomcat.max-threads=2000 server.servlet.session.timeout=3600s server.max-http-header-size=1024000 我启动的时候给JVM分配了内存,命令如下: nohup java -server -Xms10240m -Xmx10240m -jar xxx.jar & 另外我使用的是nginx代理到tomcat的,当天高峰期服务瘫痪的时候,nginx部署的另外一个静态页也同时不能访问,所以我怀疑是nginx或tomcat在哪一个环节出现了问题。 以下是当天12点-16点的一些服务器监控截图: 所以我想请教一下大家,这是哪个环节的问题造成了瓶颈,有哪些优化方案?

优秀的个人博客,低调大师

openGauss单机版从2.1.0经停3.0.0升级至5.0.0

前言 如前文所述,我们的小demo项目起初安装了openGauss的2.1.0版本,由于2.1.0不是长期维护(LTS)版本,所以要升级到5.0.0LTS。考虑到虽然是DEMO项目,但也有些体验用户,所以为了保障业务连续性,决定还是按照升级的方式,而不采取卸载重装的方式。本篇从2.1.0经停3.0.0再到5.0.0版本升级过程的简要总结,供朋友们参考和指导。 升级路径: 官网对于从2.1.0到5.0.0的升级路径是不保证成功的,但是对于2.0.0的升级路径是支持的,可以从2.0.0到3.0.0,也可以从2.0.0直接到5.0.0,当然3.0.0也可以到5.0.0,如下图所示: 参考:https://docs-opengauss.osinfra.cn/zh/docs/5.0.0/docs/DatabaseOMGuide/%E5%8D%87%E7%BA%A7%E5%89%8D%E5%BF%85%E8%AF%BB.html 下面开始升级的操作过程: 1.查看数据库版本和状态信息 gs_om -t status --detail gsql -p 26000 -d postgres -U omm gaussdb –V 2.检查OS gs_checkos -i A (该命令需在root用户下执行,可选执行) 3.检查数据库和表信息 创建了一个demo项目用的数据库,以及用到的表,表里面有部分测试数据。 select count(1) from device; 4.备份数据(商用环境务必执行,内部调测环境建议执行) 因升级如果失败,存在数据库不能启动且不能回退的风险,因此强烈建议备份数据,具体备份策略可根据实际业务情况确定。 5.创建3.0.0版本升级目录,目录命名可自行确定,建议见名知意 偷懒起见,我把5.0.0的升级目录也直接创建了,放在software目录下,可根据个人情况自行制定。 mkdir gaussdb_upgrade3.0.0 6.下载3.0.0版本软件包 进入创建好的3.0.0的目录:通过wget下载3.0.0的软件包,注意查看操作系统及版本,制式正确。 wget:https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-3.0.0-openEuler-64bit-all.tar.gz 注意:尽管3.0.5是3.0.x最新的包,但是由于3.0.5版本发布日期比5.0.0更晚,因此不支持3.0.5到5.0.0的升级路径。我刚开始用了这个包走了好多弯路,在HW朋友们的帮助和提醒下,才后知后觉的了解到这点。 7.解压 (这部分和安装类似,就不做详细展开) tar zxvf openGauss-3.0.0-openEuler-64bit-all.tar.gz tar zxvf openGauss-3.0.0-openEuler-64bit-om.tar.gz 8.执行preinstall (这部分和安装类似,就不做详细展开) 进入script目录,执行preinstall命令,注意要用root用户执行: ./gs_preinstall -U omm -G dbgrp -X /opt/software/openGauss/5.0.1/script/clustcfg/clust_config.xml 执行过程会询问是否创建omm用户,回复yes。 9.修改目录权限 如不修改会提示文件无访问权限。 chmod 755 -R /opt/software/gaussdb_upgrade3.0.0/ 10.执行升级过程 关于openGauss就地升级和灰度升级的区别,主要在于灰度升级的时候业务不中断。切换至omm用户,执行升级命令,注意确认路径在3.0.0。 gs_upgradectl -t auto-upgrade -X /opt/software/openGauss/5.0.1/script/clustcfg/clust_config.xml --grey 升级成功。 11.检查升级效果 检查数据库版本: 检查数据库和表存在: 12.提交升级 若经过3.0.0升级至5.0.0,则一定要提交升级,否则在升级5.0的时候会报错。另外需注意,提交升级之后就不能再回退至升级前的版本了。 gs_upgradectl -t commit-upgrade -X /opt/software/openGauss/5.0.1/script/clustcfg/clust_config.xml 13.再次查看数据库版本和状态信息 gs_om -t status –detail ---至此,版本已成功升级至3.0.0 14.创建5.0.0版本升级目录 (过程上需要记录,但我们偷懒在前面已经一并创建了) mkdir gaussdb_upgrade5.0.0 15.下载5.0.0版本软件包 wget:https://opengauss.obs.cn-south-1.myhuaweicloud.com/5.0.0/x86_openEuler/openGauss-5.0.0-openEuler-64bit-all.tar.gz 仍然要确认好操作系统和版本以及制式。 16.解压5.0.0软件包 tar zxvf openGauss-3.0.0-openEuler-64bit-all.tar.gz tar zxvf openGauss-3.0.0-openEuler-64bit-om.tar.gz 17.执行5.0.0preinstall (这部分和安装类似,就不做详细展开) 进入5.0.0的script目录,执行5.0.0的preinstall,注意要用root用户执行。 18.修改目录权限 chmod 755 -R /opt/software/gaussdb_upgrade5.0.0/ 19.执行升级过程 (推荐灰度升级方式) 切换至omm用户,执行升级过程,确认目录: gs_upgradectl -t auto-upgrade -X /opt/software/openGauss/5.0.1/script/clustcfg/clust_config.xml –grey 说明:命令格式和3.0.0的升级一样。 20.检查升级效果 检查数据库版本 检查数据库和表数据 21.检查无误后提交升级 gs_upgradectl -t commit-upgrade -X /opt/software/openGauss/5.0.1/script/clustcfg/clust_config.xml 22.再次查看数据库版本和状态信息 至此,数据库已经成功从2.1.0升级至5.0.0了,由于官网的不保证,升级之前还是很忐忑的,事实证明了openGauss实际是支持的,这是一种低调的实力。 说明: 请大家忽略xml文件路径中5.0.1的干扰,整个过程和5.0.1没有关系,只是前面安装5.0.1版本时把xml文件放置在这个目录下,升级时懒得改了就没动。 从2.1.0也可以越过3.0.0直接升级至5.0.0,按照文中的操作步骤略作调整也可实现,考虑篇幅所限不单独罗列了。 本文内容来自于数据库领域资深技术专家赵锋老师,希望我们的文章正好能解决你的问题。

优秀的个人博客,低调大师

十个网络优化改造案例之一 交换机由单机改造为双机热备

主要内容及技术 Cisco交换机配置HSRP实现双机热备 关键字:Cisco、HSRP、多模光纤、光模块选择 前言: 在设计网络的时候,最先要避免的就是“单点故障”。什么是“单点故障”?简单的说,就是担任核心转发任务的设备只有一台,当这台设备负载过重或者是遇到其他故障的时候停机,从而使得网络出现瘫痪等问题。为了避免“单点故障”的出现,必须为担任核心转发任务的设备配置热备份,也就是两台设备,一主一备,当主用设备发生故障的时候,能够将数据转发任务自动的切换到备份设备上。 热备份技术在网络中非常常用,不仅是交换机和路由器,就连服务器也会使用到热备份技术。交换路由的热备份技术常用的有HSRP(Cisco私有协议)、VRRP等。本案例就以HSRP技术为例,讲解在实际工作中如何使用HSRP来实现热备份的。 一、项目需求 某国有企业的数据中心的办公区域网络目前只有一台Cisco 6509在担任汇聚层交换机任务,考虑到只有一台交换机工作,网络可靠性比较低,无法实现热备的功能。现甲方要求增加一台Cisco 6509,和原有的Cisco 6509配置成HSRP,实现热备份的功能。 现行的网络结构如下图所示: Cisco 6509的主机名为DC1-C2#26U-Office-6509-1,其中的Gi1/0/48和Gi1/0/47接口分别接在两台Juniper SRX 650防火墙上(这两台防火墙均为透明模式防火墙,已经做了双机热备)。 需要说明一下这里的Cisco 6509的主机名命名格式: 机房名称-机柜名称#上架位置-网络位置-型号-序号 l DC1表示该设备在名称为DC1的机房内; l C2#26U表示该设备在C排2号机柜,设备上端位于该机柜26U的位置 l Office表示这是办公区域的交换机 l 6509表示这个设备的型号是Cisco 6509 l 1表示这是第一台交换机 图例:Cisco 6509交换机 二、前期准备 2.1、实地调研 实地调研的目的是看看机房内有没有预留该案例实施的环境。调研的内容有以下几点内容: 1、有无足够的空间放置备用的Cisco 6509,该设备需要占用机柜14U。本案例找到的机柜为C5柜,中间隔着C3、C4柜。 2、投运中的Cisco 6509上还有无空余的接口,至少两个千兆光纤接口,本案例找到的接口是Gi1/0/45—47都空余。 3、根据主用Cisco 6509与备用Cisco 6509之间的距离,准备足够长度的光纤。由于主用设备和备用设备之间隔着两个机柜,并且光纤要求从地板下方穿越,所以准备的光纤长度为10m。 2.2、测试备用设备 备用设备需要进行如下测试: 1、查看备用设备的板卡是否齐全,除了引擎版以外,至少拥有一块48口的千兆光纤接口板卡(简称48口千兆光板)。下图展示了该板卡。 2、检查备用设备是否能够正常启动,并且没有任何报错信息。这个需要在备用设备上插上Console线,看设备的启动信息。在设备稳定启动以后,不能出现风扇、内存等硬件的报错信息,不能出现故障电源模块。通过show module查看发现状态都应该是正常状态。 show module看见光纤模块的名称有“SPF”字样,说明必须使用单模光纤。 3、查看备用设备的IOS软件版本信息,可以通过show version查看。如果备用设备的IOS软件版本比主用设备的新,则不必管;如果备用设备的IOS的软件版本低于主用设备的IOS软件版本,则必须给备用设备的IOS进行升级操作。 查看软件的版本信息: xxx-RA6509#showversion CiscoInternetworkOperatingSystemSoftware IOS(tm)s72033_rpSoftware(s72033_rp-ADVENTERPRISEK9_WAN-M),Version12.2(18)SXF9,RELEASESOFTWARE(fc1) …… ROM:SystemBootstrap,Version12.2(14r)S9,RELEASESOFTWARE(fc1) BOOTLDR:s72033_rpSoftware(s72033_rp-ADVENTERPRISEK9_WAN-M),Version12.2(18)SXF9,RELEASESOFTWARE(fc1) Xxx-RA6509uptimeis1year,12weeks,6hours,53minutes TimesinceXxx-RA6509switchedtoactiveis1year,12weeks,6hours,48minutes SystemreturnedtoROMbys/wreset(SPbywatchdogNMIatPC0xBFC25FD4,address0x0) Systemrestartedat09:59:28CCTWedSep92015 p_w_picpathfileis"disk1:s72033-adventerprisek9_wan-mz.122-18.SXF9.bin" 三、进入实施步骤 3.1、收集设备的配置信息 在收集完成硬件信息以后,下面的工作就是要收集设备的配置信息了。本案例需要收集的信息是主用设备正在运行的配置信息。建议把show running-config显示的配置信息全部拷贝下来,取关键部位的配置进行查看。 本案例的关键位置主要是VLAN接口的IP地址配置信息和路由信息,经查看配置信息如下: interfaceLoopback0 ipaddress10.115.129.14255.255.255.255 noshutdown ! interfaceVlan1 interfaceVlan10 ipaddress10.115.128.57255.255.255.248 noshutdown ! interfaceVlan130 descriptionoffice ipaddress10.115.130.254255.255.255.0 noshutdown ! ! iproute0.0.0.0/010.115.128.49 iproute9.234.3.211255.255.255.25510.115.128.58 iproute9.234.3.253255.255.255.25510.115.128.58 iproute9.234.21.210255.255.255.25510.115.128.58 iproute9.234.21.211255.255.255.25510.115.128.58 iproute10.2.1.2255.255.255.25510.115.128.58 iproute10.3.1.2255.255.255.25510.115.128.58 iproute10.4.1.2255.255.255.25510.115.128.58 iproute10.5.1.2255.255.255.25510.115.128.58 iproute10.8.1.0255.255.255.010.115.128.58 …… iproute192.168.45.223255.255.255.25510.115.128.58 iproute192.168.45.240255.255.255.25510.115.128.58 iproute192.168.45.241255.255.255.25510.115.128.58 iproute199.198.18.40255.255.255.25510.115.128.58 通过路由信息可以看到,该设备的路由信息都是写的静态路由,而且下一跳地址都是10.115.128.58,对照interface vlan 10的配置信息,可以推断出VLAN 10是连接核心设备的互联VLAN,而VLAN130是办公区域的业务VLAN。 根据VLAN 10和VLAN 130当前配置的IP地址,可以进行HSRP虚拟IP地址的规划: VLAN ID 虚拟IP 主用设备IP 备用设备IP 10 10.115.128.57 10.115.128.61 10.115.128.62 130 10.115.130.254 10.115.130.252 10.115.130.253 对于VLAN10的地址,由于10.115.128.58、10.115.128.59、10.115.128.60已经被上行设备使用,所以主用设备IP地址为10.115.128.61-62,虚拟IP地址仍然使用10.115.128.57。这里需要注意的就是:生效的IP地址如非万不得已不要更改,否则会导致上下行设备的静态路由大规模更改。 对于VLAN 130的地址是一个/24的大段,IP地址足够,所以可以让10.115.130.252当主用设备的IP地址,10.115.130.253当备用设备的IP地址。虚拟地址仍然使用10.115.130.254,也防止了主机大规模的修改默认网关。 静态路由可以完全粘贴到备用设备中。 3.2、画改造之后的拓扑图 根据配置命令和之前调研的结果,就可以画出网络改造之后的拓扑图了。 a、 连接线的修改 在网络改造之前,Cisco 6509-1的Gi 1/0/47接口连接在Juniper-SRX650-2上,Gi 1/0/48接口连接在Juniper-SRX650-1上。两个接口都是Trunk模式,允许通过的VLAN ID是10和30。 在网络改造以后,Cisco 6509-1和Cisco 6509-2之间使用Gi 1/0/47互为心跳线。Cisco 6509-2的Gi 1/0/48接口连接在Juniper-SRX650-2上。 综上所述:Cisco 6509-1和Cisco 6509-2的Gi 1/0/47和Gi 1/0/48接口都是Trunk模式,允许通过的VLAN ID是10和30。 b、画拓扑图: 3.3、准备配置脚本 a、Cisco6509-1的配置命令 track1iproute0.0.0.00.0.0.0reach interfaceVlan1 interfaceVlan10 ipaddress10.115.128.61255.255.255.248 standby10ip10.115.128.57 standby10pri105 standby10pree standby10track1dec10 ! interfaceVlan130 descriptionoffice ipaddress10.115.130.252255.255.255.0 standby130ip10.115.130.254 standby130pri105 standby130pree standby130track1dec10 ! b、Cisco6509-2的配置命令 interfacegi1/0/47 switchtrunkendo switchmodetrunk switchtrunkallvlan10,130 interfacegi1/0/48 switchtrunkendo switchmodetrunk switchtrunkallvlan10,130 interfaceVlan1 interfaceVlan10 ipaddress10.115.128.62255.255.255.248 standby10ip10.115.128.57 standby10pri100 standby10pree standby10track1dec10 ! interfaceVlan130 descriptionoffice ipaddress10.115.130.253255.255.255.0 standby130ip10.115.130.254 standby130pri100 standby130pree standby130track1dec10 ! iproute0.0.0.0/010.115.128.49 iproute9.234.3.211255.255.255.25510.115.128.58 iproute9.234.3.253255.255.255.25510.115.128.58 iproute9.234.21.210255.255.255.25510.115.128.58 iproute9.234.21.211255.255.255.25510.115.128.58 iproute10.2.1.2255.255.255.25510.115.128.58 iproute10.3.1.2255.255.255.25510.115.128.58 iproute10.4.1.2255.255.255.25510.115.128.58 iproute10.5.1.2255.255.255.25510.115.128.58 iproute10.8.1.0255.255.255.010.115.128.58 …… iproute192.168.45.223255.255.255.25510.115.128.58 iproute192.168.45.240255.255.255.25510.115.128.58 iproute192.168.45.241255.255.255.25510.115.128.58 iproute199.198.18.40255.255.255.25510.115.128.58 四、总结 本案例所涉及的知识点只有HSRP一个,看起来比较简单。但在实际的工作中需要考虑的问题依然不少。例如: 1、主用设备和备用设备当前的运行状况需要进行检查。对于正在使用的主用设备,需要先进行一次巡检,确保当前运行状况是正常的。否则在实施过程中主用设备出现故障是一件非常麻烦的事情。对于备用设备一定要经过测试,在完全没有硬件故障以后才可以上架。 2、对于光纤的选择,一定要进入机房,确定光纤的长度。光纤过短会导致项目无法实施下去,光纤过长会造成资源浪费。 3、在客户无法提供详细拓扑图的情况下,对于设备的连线位置,设备的配置信息一定要弄清楚,要不然到了实施现场只能是抓瞎了。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册