主頁 > 後端開發 > 分布式追蹤系統原理看不懂,40張圖帶你親手實踐

分布式追蹤系統原理看不懂,40張圖帶你親手實踐

2020-09-10 18:10:36 後端開發

前言

在微服務架構中,一次請求往往涉及到多個模塊,多個中間件,多臺機器的相互協作才能完成,這一系列呼叫請求中,有些是串行的,有些是并行的,那么如何確定這個請求背后呼叫了哪些應用,哪些模塊,哪些節點及呼叫的先后順序?如何定位每個模塊的性能問題?本文將為你揭曉答案,

本文將會從以下幾個方面來闡述

  • 分布式追蹤系統原理及作用
  • SkyWalking的原理及架構設計
  • 我司在分布式呼叫鏈上的實踐

分布式追蹤系統的原理及作用

如何衡量一個介面的性能好壞,一般我們至少會關注以下三個指標

  • 介面的 RT 你怎么知道?
  • 是否有例外回應?
  • 主要慢在哪里?

單體架構

在初期,公司剛起步的時候,可能多會采用如下單體架構,對于單體架構我們該用什么方式來計算以上三個指標呢?

40張圖看懂分布式追蹤系統原理及實踐

 

最容易想到的顯然是用 AOP

40張圖看懂分布式追蹤系統原理及實踐

 

使用 AOP 在呼叫具體的業務邏輯前后分別列印一下時間即可計算出整體的呼叫時間,使用 AOP 來 catch 住例外也可知道是哪里的呼叫導致的例外,

微服務架構

在單體架構中由于所有的服務,組件都在一臺機器上,所以相對來說這些監控指標比較容易實作,不過隨著業務的快速發展,單體架構必然會朝微服務架構發展,如下

40張圖看懂分布式追蹤系統原理及實踐

 

如圖示:一個稍微復雜的微服務架構

如果有用戶反饋某個頁面很慢,我們知道這個頁面的請求呼叫鏈是 A -----> C -----> B -----> D,此時如何定位可能是哪個模塊引起的問題,每個服務 Service A,B,C,D 都有好幾臺機器,怎么知道某個請求呼叫了服務的具體哪臺機器呢?

40張圖看懂分布式追蹤系統原理及實踐

 

可以明顯看到,由于無法準確定位每個請求經過的確切路徑,在微服務這種架構下有以下幾個痛點

  1. 排查問題難度大,周期長
  2. 特定場景難復現
  3. 系統性能瓶頸分析較難

分布式呼叫鏈就是為了解決以上幾個問題而生,它主要的作用如下

  • 自動采取資料
  • 分析資料產生完整呼叫鏈:有了請求的完整呼叫鏈,問題有很大概率可復現
  • 資料可視化:每個組件的性能可視化,能幫助我們很好地定位系統的瓶頸,及時找出問題所在

通過分布式追蹤系統能很好地定位如下請求的每條具體請求鏈路,從而輕易地實作請求鏈路追蹤,每個模塊的性能瓶頸定位與分析,

40張圖看懂分布式追蹤系統原理及實踐

 


分布式呼叫鏈標準 - OpenTracing

知道了分布式呼叫鏈的作用,那我們來看下如何實作分布式呼叫鏈的實作及原理, 首先為了解決不同的分布式追蹤系統 API 不兼容的問題,誕生了 OpenTracing 規范,OpenTracing 是一個輕量級的標準化層,它位于應用程式/類別庫和追蹤或日志分析程式之間,

40張圖看懂分布式追蹤系統原理及實踐

 

這樣 OpenTracing 通過提供平臺無關,廠商無關的 API,使得開發人員能夠方便地添加追蹤系統的實作,

說到這大家是否想過 Java 中類似的實作?還記得 JDBC 吧,通過提供一套標準的介面讓各個廠商去實作,程式員即可面對介面編程,不用關心具體的實作,這里的介面其實就是標準,所以制定一套標準非常重要,可以實作組件的可插拔,

40張圖看懂分布式追蹤系統原理及實踐

 

接下來我們來看 OpenTracing 的資料模型,主要有以下三個

  • Trace:一個完整請求鏈路
  • Span:一次呼叫程序(需要有開始時間和結束時間)
  • SpanContext:Trace 的全域背景關系資訊, 如里面有traceId

理解這三個概念非常重要,為了讓大家更好地理解這三個概念,我特意畫了一張圖

40張圖看懂分布式追蹤系統原理及實踐

 

如圖示,一次下單的完整請求完整就是一個 Trace, 顯然對于這個請求來說,必須要有一個全域標識來標識這一個請求,每一次呼叫就稱為一個 Span,每一次呼叫都要帶上全域的 TraceId, 這樣才可把全域 TraceId 與每個呼叫關聯起來,這個 TraceId 就是通過 SpanContext 傳輸的,既然要傳輸顯然都要遵循協議來呼叫,如圖示,我們把傳輸協議比作車,把 SpanContext 比作貨,把 Span 比作路應該會更好理解一些,

理解了這三個概念,接下來我看看分布式追蹤系統如何采集統一圖中的微服務呼叫鏈

40張圖看懂分布式追蹤系統原理及實踐

 

我們可以看到底層有一個 Collector 一直在默默無聞地收集資料,那么每一次呼叫 Collector 會收集哪些資訊呢,

  1. 全域 trace_id:這是顯然的,這樣才能把每一個子呼叫與最初的請求關聯起來
  2. span_id: 圖中的 0,1,1.1,2,這樣就能標識是哪一個呼叫
  3. parent_span_id:比如 b 呼叫 d 的 span_id 是 1.1,那么它的 parent_span_id 即為 a 呼叫 b 的 span_id 即 1,這樣才能把兩個緊鄰的呼叫關聯起來,

有了這些資訊,Collector 收集的每次呼叫的資訊如下

40張圖看懂分布式追蹤系統原理及實踐

 

根據這些圖表資訊顯然可以據此來畫出呼叫鏈的可視化視圖如下

40張圖看懂分布式追蹤系統原理及實踐

 

于是一個完整的分布式追蹤系統就實作了,

以上實作看起來確實簡單,但有以下幾個問題需要我們仔細思考一下

  1. 怎么自動采集 span 資料:自動采集,對業務代碼無侵入
  2. 如何跨行程傳遞 context
  3. traceId 如何保證全域唯一
  4. 請求量這么多采集會不會影響性能

接下我來看看 SkyWalking 是如何解決以上四個問題的

SkyWalking的原理及架構設計

怎么自動采集 span 資料

SkyWalking 采用了插件化 + javaagent 的形式來實作了 span 資料的自動采集,這樣可以做到對代碼的 無侵入性,插件化意味著可插拔,擴展性好(后文會介紹如何定義自己的插件)

40張圖看懂分布式追蹤系統原理及實踐

 


如何跨行程傳遞 context

我們知道資料一般分為 header 和 body, 就像 http 有 header 和 body, RocketMQ 也有 MessageHeader,Message Body, body 一般放著業務資料,所以不宜在 body 中傳遞 context,應該在 header 中傳遞 context,如圖示

40張圖看懂分布式追蹤系統原理及實踐

 

dubbo 中的 attachment 就相當于 header ,所以我們把 context 放在 attachment 中,這樣就解決了 context 的傳遞問題,

40張圖看懂分布式追蹤系統原理及實踐

 

小提示:這里的傳遞 context 流程均是在 dubbo plugin 處理的,業務無感知,這個 plugin 是怎么實作的呢,下文會分析

traceId 如何保證全域唯一

要保證全域唯一 ,我們可以采用分布式或者本地生成的 ID,使用分布式話需要有一個發號器,每次請求都要先請求一下發號器,會有一次網路呼叫的開銷,所以 SkyWalking 最終采用了本地生成 ID 的方式,它采用了大名鼎鼎的 snowflow 演算法,性能很高,

40張圖看懂分布式追蹤系統原理及實踐

 


圖示: snowflake 演算法生成的 id

不過 snowflake 演算法有一個眾所周知的問題:時間回撥,這個問題可能會導致生成的 id 重復,那么 SkyWalking 是如何解決時間回撥問題的呢,

40張圖看懂分布式追蹤系統原理及實踐

 

每生成一個 id,都會記錄一下生成 id 的時間(lastTimestamp),如果發現當前時間比上一次生成 id 的時間(lastTimestamp)還小,那說明發生了時間回撥,此時會生成一個亂數來作為 traceId,這里可能就有同學要較真了,可能會覺得生成的這個亂數也會和已生成的全域 id 重復,是否再加一層校驗會好點,

這里要說一下系統設計上的方案取舍問題了,首先如果針對產生的這個亂數作唯一性校驗無疑會多一層呼叫,會有一定的性能損耗,但其實時間回撥發生的概率很小(發生之后由于機器時間紊亂,業務會受到很大影響,所以機器時間的調整必然要慎之又慎),再加上生成的亂數重合的概率也很小,綜合考慮這里確實沒有必要再加一層全域唯一性校驗,對于技術方案的選型,一定要避免過度設計,過猶不及,

請求量這么多,全部采集會不會影響性能?

如果對每個請求呼叫都采集,那毫無疑問資料量會非常大,但反過來想一下,是否真的有必要對每個請求都采集呢,其實沒有必要,我們可以設定采樣頻率,只采樣部分資料,SkyWalking 默認設定了 3 秒采樣 3 次,其余請求不采樣,如圖示

40張圖看懂分布式追蹤系統原理及實踐

 

這樣的采樣頻率其實足夠我們分析組件的性能了,按 3 秒采樣 3 次這樣的頻率來采樣資料會有啥問題呢,理想情況下,每個服務呼叫都在同一個時間點(如下圖示)這樣的話每次都在同一時間點采樣確實沒問題

40張圖看懂分布式追蹤系統原理及實踐

 

但在生產上,每次服務呼叫基本不可能都在同一時間點呼叫,因為期間有網路呼叫延時等,實際呼叫情況很可能是下圖這樣

40張圖看懂分布式追蹤系統原理及實踐

 

這樣的話就會導致某些呼叫在服務 A 上被采樣了,在服務 B,C 上不被采樣,也就沒法分析呼叫鏈的性能,那么 SkyWalking 是如何解決的呢,

它是這樣解決的:如果上游有攜帶 Context 過來(說明上游采樣了),則下游強制采集資料,這樣可以保證鏈路完整,

SkyWalking 的基礎架構

SkyWalking 的基礎如下架構,可以說幾乎所有的的分布式呼叫都是由以下幾個組件組成的

40張圖看懂分布式追蹤系統原理及實踐

 

首先當然是節點資料的定時采樣,采樣后將資料定時上報,將其存盤到 ES, MySQL 等持久化層,有了資料自然而然可根據資料做可視化分析,


SkyWalking 的性能如何

接下來大家肯定比較關心 SkyWalking 的性能,那我們來看下官方的測評資料

40張圖看懂分布式追蹤系統原理及實踐

 

圖中藍色代表未使用 SkyWalking 的表現,橙色代表使用了 SkyWalking 的表現,以上是在 TPS 為 5000 的情況下測出的資料,可以看出,不論是 CPU,記憶體,還是回應時間,使用 SkyWalking 帶來的性能損耗幾乎可以忽略不計,

接下來我們再來看 SkyWalking 與另一款業界比較知名的分布式追蹤工具 Zipkin, Pinpoint 的對比(在采樣率為 1 秒 1 個,執行緒數 500,請求總數為 5000 的情況下做的對比),可以看到在關鍵的回應時間上, Zipkin(117ms),PinPoint(201ms)遠遜色于 SkyWalking(22ms)!

40張圖看懂分布式追蹤系統原理及實踐

 

從性能損耗這個指標上看,SkyWalking 完勝!

再看下另一個指標:對代碼的侵入性如何,ZipKin 是需要在應用程式中埋點的,對代碼的侵入強,而 SkyWalking 采用 javaagent + 插件化這種修改位元組碼的方式可以做到對代碼無任何侵入,除了性能和對代碼的侵入性上 SkyWaking 表現不錯外,它還有以下優勢幾個優勢

  • 對多語言的支持,組件豐富:目前其支持 Java, .Net Core, PHP, NodeJS, Golang, LUA 語言,組件上也支持dubbo, mysql 等常見組件,大部分能滿足我們的需求,
  • 擴展性:對于不滿足的插件,我們按照 SkyWalking 的規則手動寫一個即可,新實作的插件對代碼無入侵,

我司在分布式呼叫鏈上的實踐

SkyWalking 在我司的應用架構

由勺ò干知 SkyWalking 有很多優點,那么是不是我們用了它的全部組件了呢,其實不然,來看下其在我司的應用架構

40張圖看懂分布式追蹤系統原理及實踐

 

從圖中可以看出我們只采用了 SkyWalking 的 agent 來進行采樣,放棄了另外的「資料上報及分析」,「資料存盤」,「資料可視化」三大組件,那為啥不直接采用 SkyWalking 的整套解決方案呢,因為在接入 SkyWalking 之前我們的 Marvin 監控生態體系已經相對比較完善了,如果把其整個替換成 SkyWalking,一來沒有必要,Marvin 在大多數場景下都能滿足我們的需求,二來系統替換成本高,三來如果重新接入用戶學習成本很高,

這也給我們一個啟示:任何產品搶占先機很重要,后續產品的替換成本會很高,搶占先機,也就是搶占了用戶的心智,這就像微信雖然 UI,功能上制作精良,但在國外照樣干不過 Whatsapp 一樣,因為先機已經沒了,

從另一方面來看,對架構來說,沒有最好的,只有最合適的,結合當前業務場景去平衡折中才是架構設計的本質

我司對 SkyWalking 作了哪些改造和實踐

我司主要作了以下改造和實踐

  1. 預發環境由于除錯需要強制采樣
  2. 實作更細粒度的采樣?
  3. 日志中嵌入traceId
  4. 自研實作了 SkyWalking 插件

預發環境由于除錯需要強制采樣

從上文分析可知 Collector 是在后臺定時采樣的,這不挺好的嗎,為啥要實作強制采樣呢,還是為了排查定位問題,有時線上出現問題,我們希望在預發上能重現,希望能看到這個請求的完整呼叫鏈,所以在預發上實作強制采樣很有必要,所以我們對 Skywalking 的 dubbo 插件進行了改造,實作強制采樣

我們在請求的 Cookie 上帶上一個類似 force_flag = true 這樣的鍵值對來表示我們希望強制采樣,在網關收到這個 Cookie 后,就會在 dubbo 的 attachment 里帶上force_flag = true 這個鍵值對,然后 skywalking 的 dubbo 插件就可以據此來判斷是否是強制采樣了,如果有這個值即強制采樣,如果沒有這個值,則走正常的定時采樣,

40張圖看懂分布式追蹤系統原理及實踐

 

實作更細粒度的采樣?

哈叫更細粒度的采樣,先來看下 skywalking 默認的采樣方式 ,即統一采樣

40張圖看懂分布式追蹤系統原理及實踐

 

我們知道這種方式默認是 3 秒采樣前 3 次,其他請求都丟棄,這樣的話有個問題,假設在這臺機器上在 3 秒內有多個 dubbo,mysql,redis 呼叫,但在如果前三次都是 dubbo 呼叫的話,其他像 mysql, redis 等呼叫就采樣不到了,所以我們對 skywalking 進行了改造,實作了分組采樣,如下

40張圖看懂分布式追蹤系統原理及實踐

 

就是說 3 秒內進行 3 次 redis, dubbo, mysql 等的采樣,也就避免了此問題

日志中如何嵌入traceId?

輸出日志中嵌入 traceId 便于我們排查問題,所以打出出 traceId 非常有必要,該怎么在日志中嵌入 traceId 呢?我們用的是 log4j,這里就要了解一下 log4j 的插件機制了,log4j 允許我們自定義插件來輸出日志的格式,首先我們需要定義日志的格式,在自定義的日志格式中嵌入 %traceId, 作為占位符,如下

40張圖看懂分布式追蹤系統原理及實踐

 

然后我們再實作一個 log4j 的插件,如下

40張圖看懂分布式追蹤系統原理及實踐

 

首先 log4j 的插件要定義一個類,這個類要繼承 LogEventPatternConverter 這個類,并且用標準 Plugin 將其自身宣告為 Plugin,通過 @ConverterKeys 這個注解指定了要替換的占位符,然后在 format 方法里將其替換掉,這樣在日志中就會出現我們想要的 TraceId ,如下

40張圖看懂分布式追蹤系統原理及實踐

 

我司自研了哪些 skywalking 插件

SkyWalking 實作了很多插件,不過未提供 memcached 和 druid 的插件,所以我們根據其規范自研了這兩者的插件

40張圖看懂分布式追蹤系統原理及實踐

 

插件如何實作呢,可以看到它主要由三個部分組成

  1. 插件定義類: 指定插件的定義類,最侄訓根據這里的定義類打包生成 plugin
  2. Instrumentation: 指定切面,切點,要對哪個類的哪個方法進行增強
  3. Interceptor,指定步驟 2 重要在方法的前置,后置還是例外中寫增強邏輯

可能大家看了還是不懂,那我們以 dubbo plugin 來簡單講解一下,我們知道在 dubbo 服務中,每個請求從 netty 接收到訊息,遞交給業務執行緒池處理開始,到真正呼叫到業務方法結束,中間經過了十幾個 Filter 的處理

40張圖看懂分布式追蹤系統原理及實踐

 

而 MonitorFilter 可以攔截所有客戶端發出請求或者服務端處理請求,所以我們可以對 MonitorFilter 作增強,在其呼叫 invoke 方法前,將全域 traceId 注入到其 Invocation 的 attachment 中,這樣就可以確保在請求到達真正的業務邏輯前就已經存在全域 traceId,

所以顯然我們需要在插件中指定我們要增強的類(MonitorFilter),對其方法(invoke)做增強,要對這個方法做哪些增強呢,這就是攔截器(Inteceptor)要做的事,來看看 Dubbo 插件中的 instrumentation(DubboInstrumentation)

40張圖看懂分布式追蹤系統原理及實踐

 

我們再看看下代碼中描寫的攔截器(Inteceptor)干了什么事,以下列出關鍵步驟

40張圖看懂分布式追蹤系統原理及實踐

 

首先 beforeMethod 代表在執行 MonitorFilter 的 invoke 方法前會呼叫這里的方法,與之對應的是 afterMethod,代表在執行 invoke 方法后作增強邏輯,

其次我們從第 2,3點可以看到,不管是 consumer 還是 provider, 都對其全域 ID 作了相應處理,這樣確保到達真正的業務層的時候保證有了此全域 traceid,定義好 Instrumentation 和 Interceptor 后,最后一步就是在 skywalking.def 里指定定義的類

// skywalking-plugin.def 檔案
dubbo=org.apache.skywalking.apm.plugin.asf.dubbo.DubboInstrumentation

這樣打包出來的插件就會對 MonitorFilter 的 invoke 方法進行增強,在 invoke 方法執行前對期 attachment 作注入全域 traceId 等操作,這一切都是靜默的,對代碼無侵入的,

總結

本文由淺入深地介紹了分布式追蹤系統的原理,相信大家對其作用及作業機制有了比較深的理解,特別需要注意的是,引入某項技巧,一定要結合現有的技術架構作出最合理的選擇,就像 SkyWalking 有四個模塊,我司只采用其 agent 采樣功能一樣,沒有最好的技術,只有最合適的技術,通過此文,相信大家應該對 SkyWalking 的實作機制有了比較清晰的認識,文中只是介紹了一下 SkyWalking 的插件實作方式,不過其畢竟是工業級軟體,要了解其博大精深,還要多讀原始碼哦,

更多面試學習資料已經整理到我的git倉庫中,有需要的朋友自取:https://gitee.com/biwangsheng/personal.git

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/3011.html

標籤:Java

上一篇:JAVA 去除物體中型別為string的屬性值中的空格

下一篇:起飛!這份技術點拉滿的ELk+Lucene筆記,可能價值百萬

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more