異步日志:性能優化的金鑰匙

異步日志:性能優化的金鑰匙

一、背景

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>

異步日志的潛在問題及解決方案

  • 潛在問題:

  1. 日志丟失問題:如果機器發生意外重啟、發布、掉電導致的 jvm 進程停止,停留在隊列的未來得及輸出到目的地的 LogEvent 可能會丟失

  2. 日志順序問題:由于日志事件是在不同的線程中異步處理的,因此日志條目可能不會嚴格按照它們產生的順序出現在日志文件中,這對于需要嚴格按時間順序追蹤日志的應用可能是個問題。

  3. 其他問題:如增加資源損耗、配置復雜度和調試復雜度等問題

  • 解決方案:

  1. 對于日志丟失問題:

  • 原生 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. 對于日志順序性問題:

  • 異步線程池大小設置為 1,但是會影響日志打印的速度(現在的普遍做法)。

  • 延遲打印

三、效果

4 月份的這一問題發生后,我們從原理出發,對理財的核心應用做了升級和優化,整體服務耗時上取得了不錯的性能優化效果。

應用 rpc 耗時:

應用網關耗時:

但與此同時,我們也發現升級后,應用的 fgc 次數更多了,經過 heapdump 分析后,發現 AsyncAbleRollingFileAppender 內部實現的 RingBufferLogEvent 執行后,不會釋放引用的 LogEvent,導致 Disrupter 一直持有已打印的 LogEvent 的引用關系,進而導致了內存泄漏。后來,我們采取主動釋放對象引用(RingBufferLogEvent.setLogEvent(null))優化的方案,發布以后前后 fgc 對比如下:

GC 優化前:

GC 優化后:

四、建議

日志作為診斷問題、監控系統健康狀況與優化服務效能不可或缺的一環,其重要性不言而喻。熟練掌握并有效利用如 Log4j2 這樣的高性能日志框架以及注意一些打印日志的策略(如動靜分離、合理分割、合理設置日志級別等),對于開發者而言至關重要:

1)動靜分離 :在一些大日志輸出場景中,即使是異步日志也會給系統帶來性能風險。因此建議合理識別大日志中的動態數據和靜態數據。靜態數據定時輸出,動態數據關聯唯一靜態標識輸出,在降低性能風險的同時又滿足監控分析的需要;

2)合理分割 :日志文件需要合理分割,并設置合理的保留策略,及時釋放磁盤空間。

3)合理設置日志級別 :避免日志濫用,尤其是 debug 日志,既有利于日志定位問題的速度,又能提高性能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44232.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44232.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44232.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Cloud Alibaba整合Seata實戰

Spring Cloud Alibaba整合Seata實戰 1.啟動Seata Server 1.1 環境準備 1&#xff09;指定nacos作為配置中心和注冊中心 修改registry.conf文件 注意&#xff1a;客戶端配置registry.conf使用nacos時也要注意group要和seata server中的group一致&#xff0c;默認group是&quo…

我的PHP8編譯日志

編譯命令在arm和x86架構上是一樣的&#xff0c;如果缺少依賴庫&#xff0c;按需要安裝&#xff1a; 登錄后復制 yuminstall libcurl libcurl-devel yum install openssl openssl-devel yum install pcre2 pcre2-devel yum install libxml2 libxml2-devel 1.2.3.4. 配置和編譯&…

Linux--生產消費模型

線程系列&#xff1a; Linux–線程的認識(一) Linux–線程的分離、線程庫的地址關系的理解、線程的簡單封裝&#xff08;二&#xff09; 線程的互斥&#xff1a;臨界資源只能在同一時間被一個線程使用 生產消費模型 生產消費模型是多線程編程和分布式系統中的一個經典概念&…

我們水冷使制動電阻功率密度成倍增加-水冷電阻設計工廠

先進陶瓷 我們后來發現工業應用中對占用空間最小的水冷電阻器的工業需求&#xff0c;推出了適用于中壓工業應用的水冷電阻器。它的特點是兩塊由具有特殊性能的先進陶瓷制成的板。 使用工業電驅動裝置的一個重要好處是&#xff0c;可靠的再生和動態制動系統可以補充或取代傳統…

Llama2 訓練指南

Llama2 是一個基于 Python 的機器學習框架&#xff0c;旨在幫助開發者快速構建和部署機器學習模型。下面是 Llama2 訓練指南&#xff0c;旨在幫助您了解如何使用 Llama2 訓練模型。 概述 Llama2 提供了多種方式來訓練模型&#xff0c;包括使用 Keras 和 TensorFlow。下面是在…

Laravel :如何將Excel文件導入數據庫

文章目錄 一、前提二、使用2.1、新建一個導入文件2.2、新建一個控制器和方法,調用導入文件2.3、 新建一個頁面&#xff0c;支持文件上傳 一、前提 想要將excel內容入庫&#xff0c;laravel有擴展可以使用,常用的擴展是maatwebsite/excel&#xff0c;安裝步驟參考上一篇&#x…

力扣 202快樂數

快樂數這題有兩個關鍵 一個是求n的 各個位上平方和 另一個是判斷是否為快樂數的依據是是否在哈希表中找到已經出現過的數 1求各個位上平方和方法 定義sum sum N除以十取余的平方和 n/10 循環終止條件是n0 2查找一個數是否出現&#xff0c;用哈希表unordered_set &…

Mosh|SQL教程第四彈(未完)

SQL有很多自帶的內聚的函數&#xff08;MAX、MIN、AVG、SUM、COUNT&#xff09; 一、聚合函數&#xff08;Aggregate Functions&#xff09; 這里的括號可以寫列名也可以寫表達式,下面是一個練習&#xff1a; 二、GROUP BY子句 統計2019-07-01以后每個客戶的總銷售額 注意這…

result.h

#ifndef ASYNCIO_RESULT_H #define ASYNCIO_RESULT_H#include <asyncio/exception.h> #include <variant> #include <optional> namespace ASYNCIO_NS {// 結果類封裝&#xff08;不是協程函數的返回類型&#xff09; template<typename T> struct Res…

Javaweb11-Filter過濾器

Filter過濾器 1.Filter的基本概念&#xff1a; 在Java Servlet中&#xff0c;Filter接口是用來處理HttpServletRequest和HttpServletResponse的對象的過濾器。主要用途是在請求到達Servlet之前或者響應離開Servlet之前對請求或響應進行預處理或后處理。 2.Filter常見的API F…

探展2024世界人工智能大會之合合信息掃描黑科技~

文章目錄 ?? 前言?? AIGC古籍修復文化遺產煥新?? 高效的文檔圖像處理解決方案?? AIGC掃描黑科技一鍵全搞定?? 行業級的大模型加速器?? 結語 ?? 前言 大家好&#xff0c;我是 哈哥&#xff08;哈哥撩編程&#xff09; &#xff0c;這次非常榮幸受邀作為專業觀眾參…

【常用知識點-Java】讀取Properties文件

Author&#xff1a;趙志乾 Date&#xff1a;2024-07-11 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 簡介 .properties文件是一種簡單的文本文件&#xff0c;用于存儲鍵值對&#xff0c;其每個鍵值對占一行&#xff0c;且鍵和值之間用…

力扣題解( 最長遞增子序列的個數)

673. 最長遞增子序列的個數 給定一個未排序的整數數組 nums &#xff0c; 返回最長遞增子序列的個數 。 注意 這個數列必須是 嚴格 遞增的。 思路&#xff1a; 用一個maxlen表示當前最長遞增數組的長度&#xff0c;maxcount表示最大長度。當進下標為i的元素時&#xff0c;將…

重磅!新公司法正式實施,這些變化你必須知道! ?

新公司法來了&#xff01;企業設立和經營必知的關鍵變動 &#x1f3db;?&#x1f680; 大家好&#xff0c;我是貓頭虎&#xff0c;科技自媒體博主。今天我們來聊聊一件大事——新公司法的實施&#xff0c;這對企業設立和經營帶來了哪些重大影響&#xff1f;跟著我&#xff0c…

【DDIM】DENOISING DIFFUSION IMPLICIT MODELS【論文精讀】【視頻講解】【公式推導】

論文&#xff1a;DENOISING DIFFUSION IMPLICIT MODELS&#xff08;https://arxiv.org/abs/2010.02502&#xff09; B站視頻鏈接 DDIM論文精講視頻 去噪擴散隱模型的論文精讀&#xff0c;涉及本文的大部分公式逐步推導。總計3小時的詳細論文講解。 講解詳細對應文檔 DDIM視頻…

聊聊mysql

記錄那些坑 本文會持續更新&#xff0c;陸續更新有關mysql技術內幕、實戰優化、面試技巧。 文章目錄 前言索引BTree之聚集索引BTree之輔助索引BTree之聯合索引BTree之覆蓋索引 使用到的工具1、py_innodb_page_info工具2、hexdump工具 總結 前言 重中之重的MySql數據庫 mysql…

模擬人機猜數游戲

設計目的 1、加深學生對該課程基礎知識和基本理論的理解和掌握&#xff0c;培養學生綜合運用所學知識獨立 分析和解決問題的能力; 2、培養學生在計算機軟硬件開發、理論計算、查閱資料等方面的能力&#xff0c;使學生逐步樹立正 確的設計思想; 3、加強理論聯系實際&#xff0c…

邦芒支招:職場高效溝通的6個秘訣

??俗話說得好“良言一句三冬暖&#xff0c;惡語傷人六月寒。”無論在什么單位&#xff0c;社會上竟是形形色色的人等&#xff0c;人過一百&#xff0c;形形色色。每個人都想得到他人賞識、揮灑才華、爭得提升。但是&#xff0c;要和陌生的人融洽相處、溝通合作&#xff0c;是…

Android 藍牙語音通話調試

首先要清楚藍牙語音通話屬于藍牙得哪一個協議 1、HEADSET 耳機和免提模式,用于藍牙耳機 2、A2DP (advanced audio distribution profile)高級音頻及立體聲規范,包括A2DP SINK和A2DP SOURCE 3、HEALTH 健康設備規范,和一些健康設備進行通信 4、OPP (object push profi…

概率論期末速成(知識點+例題)

考試范圍 一&#xff1a; 事件關系運算性質全概率公式、貝葉斯公式古典概型 二&#xff1a; 離散分布律連續密度函數性質 -> 解決三個問題&#xff08;求待定系數、求概率、求密度函數&#xff09;分布函數 -> 解決三個問題常用分布&#xff08;最后一節課的那幾個分…