异步日志:性能优化的金钥匙
一、背景
2024 年 4 月的一个宁静的夜晚,正当大家忙完一天的工作准备休息时,应急群里“咚咚咚”开始报警,提示我们余利宝业务的赎回接口成功率下降。
通过 Monitor 监控发现,该接口的耗时已经超过了网关配置的超时阈值(2s),我们临时调整超时阈值止血后,就在排查问题的根因。具体排查过程不是我这篇文章的重点,故忽略,但最终我们发现最近上线新加的相邻两行的日志中,时间相差近 1.5s,难道这就是问题的根源吗?
后来,我们去掉了这两行日志后紧急发布,事实证明我们的思路是对的。紧急发布后,该接口的耗时由之前的 2s 左右,优化到了 600ms 左右。后来我们分析发现:该接口在打印日志时,由于要实现日志脱敏,故在 Logger.info 入口处实现了脱敏功能,但是大日志脱敏比较耗时,从而导致该接口的同步调用耗时激增到 1.5s 左右(后面我们会说如何解决这个问题)。我的天呐,一行日志竟是性能优化的金钥匙!!!🤣
但这里有一个问题,我们是去掉了日志的打印,侵入了业务,同时该应用还是用的 log4j 的日志框架,log4j 原生框架是不支持日志异步化的,因此要从根本上业务无侵入的解决因日志而导致的性能问题,需要熟悉 log4j2 的框架原理。
二、原理
2.1 Log4j2 的优势
1)性能: Log4j2 使用基于 Lambda 的异步记录器,显著提高了日志记录的速度,减少了日志操作对应用性能的影响。相比之下,Logback 虽然也支持异步记录,但实现上不如 Log4j2 高效。通过减少对象创建、高效的字符串处理和池化技术,Log4j2 在高并发场景下表现更佳。
2)配置灵活性: 支持多种配置方式,包括 XML、JSON、YAML、properties 文件,甚至编程式配置,提供更大的灵活性。动态重新配置能力,允许在不重启应用的情况下修改日志配置。
3)插件架构: Log4j2 采用插件架构,几乎所有组件(如 Appenders、Layouts、Filters)都是可插拔的,易于扩展和自定义。内置丰富的插件库,开箱即用,简化集成过程。
4)内存和资源管理: 更高效的内存管理,减少内存泄漏的风险,尤其是在大量日志输出时。支持垃圾回收友好的设计,比如基于 Disrupter 的 RingBuffer 等数据结构减少 GC 压力。
5)可靠性: 强大的故障恢复机制,如重试和备用 Appenders,确保日志能够被记录下来,即使主要的日志输出目的地不可用。
6)先进的特性:
-
支持条件日志记录(Conditionals),可以根据运行时条件决定是否记录日志。
-
自动重新加载配置文件变化,无需重启应用。
-
支持 JMX 监控和管理日志系统状态。
7)与 SLF4J 的集成:虽然这不是特有优势,但 Log4j2 提供了与 SLF4J(Simple Logging Facade for Java)的良好集成,使得从其他日志框架迁移更加平滑。
总的来说,Log4j2 的设计更现代化,强调高性能、易用性和灵活性,特别是在大规模分布式系统和高性能应用中表现突出。而 Logback 和 Log4j 1.x 虽有各自的优点,但在这些方面逐渐显得力不从心。至于 Java Util Logging (JUL),它是 Java 标准库的一部分,但功能相对基础,配置和扩展性不如 Log4j2 和 Logback 灵活。
2.2 Log4j2 的结构
Log4j2 的结构主要包括以下几个核心组件:
1)Logger: 这是开发者直接使用的接口,用于记录不同级别的日志信息(如 DEBUG, INFO, ERROR 等)。每个 Logger 都有一个名称,并且支持继承性,形成一个名为 Logger Hierarchy 的树状结构,根 Logger 的名称为'root'。
2)LoggerContext: 是日志系统的上下文环境,管理着一组 Logger 实例以及它们的配置。每个应用程序通常只有一个 LoggerContext,但它支持多个上下文以实现更细粒度的控制。
3)Configuration: 每个 LoggerContext 都关联一个有效的 Configuration,定义了日志的输出目的地(Appenders)、日志的过滤规则(Filters)、日志的格式化方式(Layouts)等。Configuration 可以通过配置文件(如 XML、JSON、properties)或编程方式动态加载。
4)Appender: 负责将日志事件发送到指定的目标,如控制台(Console)、文件(File)、数据库、网络 Socket 等。
5)Layout: 定义了日志信息的格式化方式,如模式字符串(Pattern String)决定了日期、时间、日志级别、线程名、日志信息等内容的排列和格式。
6)Filter: 可以在日志事件从 Logger 传递到 Appender 的过程中进行过滤,根据特定条件决定日志是否被输出。
7)Lookup: 提供动态值解析机制,如 ${ctx:variable}可以在日志中插入上下文变量的值。
那么,Log4j2 的日志是怎么将日志输出到文件/数据库/控制台等地方的?
2.3 Log4j2 日志输出流程
关键步骤源码分析:
1)1.1 主要是针对日志级别 Level 和指定的全局 Filter 组件进行过滤
2)ReliabilityStrategy 是 Log4j2 的日志可靠性策略实现,目前主要有以下四种:
-
AwaitCompletionReliabilityStrategy: 等待日志接收完成策略。这种策略主要是在应用关闭时,尽可能要等应用日志接收完成后再结束 Appender 的生命周期(这种策略只是说尽可能所有日志等待调用 Appender.append 方法完成,但在异步日志场景下,Appender.append 其实是落了 ringbuffer 或者其他队列里,实际上未持久化。因此该策略是尽可能保证接收完成而非处理完成)
-
AwaitUnconditionallyReliabilityStrategy: 无条件等待策略。这种策略会在 rootLogger 关闭时无条件等待一段时间,具体等待时间可以配置 log4j2.component.properties 文件的 log4j.waitMillisBeforeStopOldConfig 属性。
-
DefaultReliabilityStrategy: 默认策略。该策略不做任何等待。
-
LockingReliabilityStrategy: 锁等待策略。该策略当正在写入日志时,则会等待;否则即会停止等待。
3)1.2.1.3.1append 操作是将日志写入到对应的目的地,如 kafka、本地文件、邮件等。这里如果是异步日志,则会将日志追加到异步队列里,进而提高日志记录的性能。
4)1.2.1.3.1.1 调用 Layout encode 日志,是根据 log4j2.xml 中配置的 Layout 对日志进行格式化输出。
那么如果有一些个性化的日志输出需求,log4j2 能否帮我们实现?
2.4 如何实现日志脱敏
上面提到了 log4j2 的各种组件以及日志输出流程,log4j2 的强大很大程度上得益于其清晰且高度解耦的架构设计。例如其具有很强的扩展性,log4j2 的很多组件都可以自己定制插件,如:Appender、Filter、Layout 等。那么这里我结合我们实际业务中一个很常见的 case 去分析如何定制一个组件。
首先,作为一家强监管的金融公司,日志脱敏涉及数据保护和隐私安全等问题,非常重要。过去我们很多业务系统在实现业务脱敏时,很容易想到在打日志的入口统一封装一个格式化方法,造成日志输出在无形中把异步日志输出变成了同步输出(日志脱敏的耗时往往比日志集中持久化到磁盘耗时要高)。
那么如何优雅的实现日志脱敏的功能,既能实现其功能又可以保证日志的性能,是 log4j2 插件化的一个很重要的应用场景。前面我们提到日志输出流程中会使用 Layout encode 日志,而 PatternConverter 是 Layout 非常重要的组成部分。其通过定义一系列的占位符(如 %d、%m、 %t 等)帮助我们自定义格式输出日志对象,同时 PatternConverter 支持以高度可定制的插件集成到 Log4j2 框架中,因此我们可以借助其去定制脱敏组件。
话不多说,我们直接上日志脱敏 PatternConverter 插件源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.util.Arrays;import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.message.FormattedMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.MessageFormatMessage;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.message.StringFormattedMessage;
import org.apache.logging.log4j.util.PerformanceSensitive;/*** @author baichun* @version ShieldMessagePatternConverter.java, v 0.1 2024年04月09日 21:13 baichun*/
@Plugin(name = "ShieldPatternConverter",category = "Converter"
)
@ConverterKeys({"shield", "sd", "shieldMessage", "sm"})
@PerformanceSensitive({"allocation"})
public final class ShieldMessagePatternConverter extends LogEventPatternConverter {private final String[] options;private ShieldMessagePatternConverter(String[] options) {super("Shield", "shield");this.options = options == null ? null : (String[])Arrays.copyOf(options, options.length);}//必须要有newInstance方法,log4j2会调用该方法进行初始化public static ShieldMessagePatternConverter newInstance(String[] options) {return new ShieldMessagePatternConverter(options);}@Overridepublic void format(LogEvent logEvent, StringBuilder output) {Message message = logEvent.getMessage();String format = message.getFormat();if (isFormatMessage(message)) {//在这里格式化脱敏日志String msgInfo = ShieldUtils.format(format, message.getParameters());output.append(msgInfo);} else {output.append(message.getFormattedMessage());}}private boolean isFormatMessage(Message message) {return message instanceof ParameterizedMessage || message instanceof StringFormattedMessage|| message instanceof FormattedMessage || message instanceof MessageFormatMessage;}
}
定义好组件后,log4j2 即能够自动扫描识别到,不需要其他定义和配置。接下来看看如何使用。ConverterKeys 这个注解指定了在 log4j2.xml 中应如何使用该插件。以下是 log4j2.xml 应用示例:
<RollingFile name="TEST_APPENDER" fileName="test.log"
filePattern="test.log.%d{yyyy-MM-dd}"
append="true">
<!-- %sm即为脱敏组件 -->
<PatternLayout pattern="%d %sm%n" charset="UTF-8"/>
<TimeBasedTriggeringPolicy/>
<DefaultRolloverStrategy/>
</RollingFile>
2.5 Log4j2 的异步日志
异步日志原理概述
前面提到了 Log4j2 的高可扩展性,同时 Log4j2 的性能也是极高的,下面是 Log4j2 官方的 benchmark 数据,仅供参考:
Log4j2 之所以性能如此之高,其中一个很重要的原因就是其基于 Disrupter 的环形缓冲区的无锁化结构 Ringbuffer 设计。Disruptor 是英国外汇交易公司 LMAX 开发的一个高性能队列,基于 Disruptor 开发的系统单线程能支撑每秒 600 万订单。目前,包括 Apache Strom、Log4j2 在内的很多知名项目都应用了 Disruptor 来获取高性能。关于 Disruptor 的原理,这里不再赘述,大家可以自行查阅:https://lmax-exchange.github.io/disruptor/#_what_is_the_disruptor
Disrupter 组件构成:
Disrupter 性能测试结果
如何使用异步日志
log4j2 开启异步日志的方法主要有以下两种方式:
1)全局异步日志
-
通过 JVM 启动参数来全局启用异步日志功能。在启动应用程序时,向 JVM 传递以下系统属性:
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
-
在类路径(classpath)中添加一个名为 log4j2.component.properties 的文件,并包含以下内容(这个文件会在 Log4j2 初始化时被读取):
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
复制代码
这两种方式下,所有 Logger 都会自动使用异步处理。
2)混合异步日志
在 log4j2.xml 配置文件中,可以手动指定特定的 Logger 使用异步处理,通过将 <Root>或<Logger>元素替换为<AsyncRoot>或<AsyncLogger>。例如:
<Configuration status='WARN'>
<Appenders>... <!-- your appenders here -->
</Appenders>
<Loggers>
<AsyncRoot level='info' includeLocation='false'>
<AppenderRef ref='yourAppenderName'/>
</AsyncRoot>
<!-- 或者为特定logger配置 -->
<AsyncLogger name='com.example.MyClass' level='debug'>
<AppenderRef ref='yourAppenderName'/>
</AsyncLogger>
</Loggers>
</Configuration>
异步日志的潜在问题及解决方案
-
潜在问题:
-
日志丢失问题:如果机器发生意外重启、发布、掉电导致的 jvm 进程停止,停留在队列的未来得及输出到目的地的 LogEvent 可能会丢失
-
日志顺序问题:由于日志事件是在不同的线程中异步处理的,因此日志条目可能不会严格按照它们产生的顺序出现在日志文件中,这对于需要严格按时间顺序追踪日志的应用可能是个问题。
-
其他问题:如增加资源损耗、配置复杂度和调试复杂度等问题
-
解决方案:
-
对于日志丢失问题:
-
原生 Log4j2 有完整的生命周期管理,并监听了 jvm 关闭的事件。当 jvm 关闭时,Log4j2 会监听 Disrupter 队列中的 RingbufferLogEvent 数量,直到日志打印完(或超时)才释放关闭 Log4j2,jvm 才得以正常关闭。但是自然灾害或者机房掉电等不可抗力因素,无法避免丢失问题。
-
我们基于 Log4j2 定制的 AsyncAbleRollingFileAppender,其中有独立的 Disrupter,且不在 Log4j2 生命周期管理当中,存在日志丢失风险。可以采用类似方案解决:
try {LoggerContextFactory factory = LogManager.getFactory();if (!(factory instanceof Log4jContextFactory)) {return;}Log4jContextFactory log4jContextFactory = (Log4jContextFactory) factory;ShutdownCallbackRegistry registry = log4jContextFactory.getShutdownCallbackRegistry();if (!(registry instanceof DefaultShutdownCallbackRegistry)) {return;}DefaultShutdownCallbackRegistry defaultShutdownCallbackRegistry = (DefaultShutdownCallbackRegistry) registry;Field hooksField = DefaultShutdownCallbackRegistry.class.getDeclaredField("hooks");hooksField.setAccessible(true);Collection<Cancellable> hooks = (Collection<Cancellable>) hooksField.get(defaultShutdownCallbackRegistry);Collection<Cancellable> newHooks = new CopyOnWriteArrayList<>();//将对Appender的队列消费监听和卸载放在首要位置,避免log4j2关闭后再卸载AppendernewHooks.add(new Log4j2Cancellable(() -> {//负责监听AsyncAbleRollingFileAppender的队列消费情况,并在消费完成后关闭AsyncAbleRollingFileAppendernew AppenderUnInstaller(register).run();}));newHooks.addAll(hooks);hooksField.set(defaultShutdownCallbackRegistry, newHooks);} catch (NoSuchFieldException e) {// This catch statement is intentionally empty} catch (IllegalAccessException e) {// This catch statement is intentionally empty}
-
AsyncAbleRollingFileAppender 使用独立的 disrupter,且 RingBufferLogEvent 未及时清理对象,容易导致内存泄漏,异步日志场景请慎用。
-
对于日志顺序性问题:
-
异步线程池大小设置为 1,但是会影响日志打印的速度(现在的普遍做法)。
-
延迟打印
三、效果
4 月份的这一问题发生后,我们从原理出发,对理财的核心应用做了升级和优化,整体服务耗时上取得了不错的性能优化效果。
应用 rpc 耗时:
应用网关耗时:
但与此同时,我们也发现升级后,应用的 fgc 次数更多了,经过 heapdump 分析后,发现 AsyncAbleRollingFileAppender 内部实现的 RingBufferLogEvent 执行后,不会释放引用的 LogEvent,导致 Disrupter 一直持有已打印的 LogEvent 的引用关系,进而导致了内存泄漏。后来,我们采取主动释放对象引用(RingBufferLogEvent.setLogEvent(null))优化的方案,发布以后前后 fgc 对比如下:
GC 优化前:
GC 优化后:
四、建议
日志作为诊断问题、监控系统健康状况与优化服务效能不可或缺的一环,其重要性不言而喻。熟练掌握并有效利用如 Log4j2 这样的高性能日志框架以及注意一些打印日志的策略(如动静分离、合理分割、合理设置日志级别等),对于开发者而言至关重要:
1)动静分离 :在一些大日志输出场景中,即使是异步日志也会给系统带来性能风险。因此建议合理识别大日志中的动态数据和静态数据。静态数据定时输出,动态数据关联唯一静态标识输出,在降低性能风险的同时又满足监控分析的需要;
2)合理分割 :日志文件需要合理分割,并设置合理的保留策略,及时释放磁盘空间。
3)合理设置日志级别 :避免日志滥用,尤其是 debug 日志,既有利于日志定位问题的速度,又能提高性能。
相关文章:

异步日志:性能优化的金钥匙
一、背景 2024 年 4 月的一个宁静的夜晚,正当大家忙完一天的工作准备休息时,应急群里“咚咚咚”开始报警,提示我们余利宝业务的赎回接口成功率下降。 通过 Monitor 监控发现,该接口的耗时已经超过了网关配置的超时阈值(2s)&#…...

matlab仿真 模拟调制(上)
(内容源自详解MATLAB/SIMULINK 通信系统建模与仿真 刘学勇编著第五章内容,有兴趣的读者请阅读原书) 1.幅度调制 clear all ts0.0025; %信号抽样时间间隔 t0:ts:10-ts;%时间矢量 fs1/ts;%抽样频率 dffs/length(t); %fft的频率分…...

【数据结构】--- 堆的应用
个人主页:星纭-CSDN博客 系列文章专栏 :数据结构 踏上取经路,比抵达灵山更重要!一起努力一起进步! 一.堆排序 在前一个文章的学习中,我们使用数组的物理结构构造出了逻辑结构上的堆。那么堆到底有什么用呢&…...

0基础学会在亚马逊云科技AWS上利用SageMaker、PEFT和LoRA高效微调AI大语言模型(含具体教程和代码)
项目简介: 小李哥今天将继续介绍亚马逊云科技AWS云计算平台上的前沿前沿AI技术解决方案,帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS上的AI软甲开发最佳实践,并应用到自己的日常工作里。本次介绍的是如何在Amazon SageMaker上…...

护网HW面试——redis利用方式即复现
参考:https://xz.aliyun.com/t/13071 面试中经常会问到ssrf的打法,讲到ssrf那么就会讲到配合打内网的redis,本篇就介绍redis的打法。 未授权 原理: Redis默认情况下,会绑定在0.0.0.0:6379,如果没有采用相关…...
C++ //练习 15.8 给出静态类型和动态类型的定义。
C Primer(第5版) 练习 15.8 练习 15.8 给出静态类型和动态类型的定义。 环境:Linux Ubuntu(云服务器) 工具:vim 解释 静态类型:在编译时已知,是在变量声明时的类型或表达式生成的…...

阿里云ECS服务器安装jdk并运行jar包,访问成功详解
安装 OpenJDK 8 使用 yum 包管理器安装 OpenJDK 8 sudo yum install -y java-1.8.0-openjdk-devel 验证安装 安装完成后,验证 JDK 是否安装成功: java -version设置 JAVA_HOME 环境变量: 为了确保系统中的其他应用程序可以找到 JDK&…...
Windows系统上使用npm来安装和配置Yarn,在VSCode中使用
一、安装Yarn 1. 安装Node.js和npm 如果还没有安装Node.js和npm,可以从Node.js官方网站下载并安装最新版本的Node.js,npm会随Node.js一起安装。 2. 使用npm安装Yarn 打开命令提示符或PowerShell,运行以下命令来全局安装Yarn: …...

Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理
Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理 目录 Unity ColorSpace 之 【颜色空间】相关说明,以及【Linear】颜色校正 【Gamma】的简单整理 一、简单介绍 二、在Unity中设置颜色空间 三、Unity中的Gamma…...
JavaScript的学习(二)
今天继续学习JavaScript的第二天,还是打基础 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…...

【接口自动化_06课_Pytest+Excel+Allure完整框架集成】
一、logging在接口自动化里的应用 1、设置日志的配置,并收集日志文件 日志的设置需要在pytest.ini文件里设置。这个里面尽量不要有中文 2、debug日志的打印 pytest.ini文件的开关一定得是true才能在控制台打印日志 import allure import pytest from P06_PytestFr…...

Profibus协议转Profinet协议网关模块连接智能电表通讯案例
一、背景 在工业自动化领域,Profibus协议和Profinet协议是两种常见的工业通讯协议,而连接智能电表需要用到这两种协议之间的网关模块。本文将通过一个实际案例,详细介绍如何使用Profibus转Profinet模块(XD-PNPBM20)实…...

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(九)-无人机服务区分离
引言 本文是3GPP TR 22.829 V17.1.0技术报告,专注于无人机(UAV)在3GPP系统中的增强支持。文章提出了多个无人机应用场景,分析了相应的能力要求,并建议了新的服务级别要求和关键性能指标(KPIs)。…...

acrobat 中 PDF 复制时不能精确选中所选内容所在行的一种解决方法
现象:划取行的时候,自动扩展为多行 如果整段选中复制,粘贴后是乱码 解决步骤 识别完,保存 验证 可以按行复制了。 如果遇到仅使用 acrobat OCR 不能彻底解决的,更换其他自己熟悉的进行 OCR。...

安卓学习中遇到的问题【bug】
安卓学习中遇到的问题 1Gradle下载慢怎么办? Gradle下载慢怎么办? distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-7.5-bin.zip 2 Could not resolve all files for configuration ‘:classpath‘. > Could not resolv…...

【日常记录】【CSS】display:inline 的样式截断
文章目录 1. 案例2. css属性:box-decoration-break参考地址 1. 案例 现在有一篇文章,某些句子,是要被标记的,加一些css 让他突出一下 可以看到,在最后,断开了,那如若要让 断开哪里的样式 和 开始…...
数据库系统安全
数据库安全威胁 数据库作为信息系统中的核心组成部分,存储和管理着大量敏感和关键的数据,成为网络攻击者的主要目标之一。以下是常见的数据库安全威胁及其详细描述: 一、常见数据库安全威胁 SQL注入攻击(SQL Injectionÿ…...
Qt MV架构-代理模型
一、基本概念 代理模型可以将一个模型中的数据进行排序或者过滤,然后提供给视图进行显示。 Qt中提供了QSortFilterProxyModel作为标准的代理模型来完成模型中数据的排序和过滤。 要使用一个代理模型,则只需要为其设置源模型,然后再视图中使…...

WebSocket实现群聊功能、房间隔离
引用WebSocket相关依赖 <dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version></dependency><dependency><groupId>org.springframework</grou…...

顶顶通呼叫中心中间件实现随时启动和停止质检(mod_cti基于FreeSWITCH)
文章目录 前言联系我们拨号方案启动停止ASR执行FreeSWITCH 命令接口启动ASR接口停止ASR接口 通知配置cti.json配置质检结果写入数据库 前言 顶顶通呼叫中心中间件的实时质检功能是由两个模块组成:mod_asr 和 mod_qc。 mod_asr:负责调用ASR将用户们在通…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
【大厂机试题解法笔记】矩阵匹配
题目 从一个 N * M(N ≤ M)的矩阵中选出 N 个数,任意两个数字不能在同一行或同一列,求选出来的 N 个数中第 K 大的数字的最小值是多少。 输入描述 输入矩阵要求:1 ≤ K ≤ N ≤ M ≤ 150 输入格式 N M K N*M矩阵 输…...