日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

PD源码阅读系列:PD节点启动

發(fā)布時(shí)間:2023/12/16 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PD源码阅读系列:PD节点启动 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

李雷,神州數(shù)碼武漢云基地,目前在研究TiDB的PD模塊。

在TiDB生態(tài)中,PD作為調(diào)度模塊,負(fù)責(zé)整個(gè)集群的調(diào)度以及保存整個(gè)集群的云信息。這篇文章將從PD的啟動(dòng)作為入手點(diǎn),簡單剖析PD節(jié)點(diǎn)啟動(dòng)的步驟,了解PD啟動(dòng)的流程,學(xué)習(xí)PD讀取配置、啟動(dòng)日志和監(jiān)控、設(shè)置并啟動(dòng)PD節(jié)點(diǎn)服務(wù)并通過協(xié)程的方式監(jiān)聽退出命令等知識(shí)點(diǎn)。

PD簡介

Placement Driver (后續(xù)以 PD 簡稱) 是 TiDB 里面全局中心總控節(jié)點(diǎn),它負(fù)責(zé)整個(gè)集群的調(diào)度,負(fù)責(zé)全局 ID 的生成,以及全局時(shí)間戳 TSO 的生成等。PD 還保存著整個(gè)集群 TiKV 的元信息,負(fù)責(zé)給 client 提供路由功能。

在架構(gòu)上面,PD 所有的數(shù)據(jù)都是通過 TiKV 主動(dòng)上報(bào)獲知的。同時(shí),PD 對(duì)整個(gè) TiKV 集群的調(diào)度等操作,也只會(huì)在 TiKV 發(fā)送 heartbeat 命令的結(jié)果里面返回相關(guān)的命令,讓 TiKV 自行去處理,而不是主動(dòng)去給 TiKV 發(fā)命令。這樣設(shè)計(jì)上面就非常簡單,我們完全可以認(rèn)為 PD 是一個(gè)無狀態(tài)的服務(wù)(當(dāng)然,PD 仍然會(huì)將一些信息持久化到 etcd),所有的操作都是被動(dòng)觸發(fā),即使 PD 掛掉,新選出的 PD leader 也能立刻對(duì)外服務(wù),無需考慮任何之前的中間狀態(tài)。

Why PD?

根據(jù)上文,我們了解到PD節(jié)點(diǎn)的主要作用在于元數(shù)據(jù)的存儲(chǔ)以及TiKV節(jié)點(diǎn)的調(diào)度。那么我們不禁要問,為什么需要PD?

當(dāng)我們只有一個(gè)TiKV時(shí),那就根本不需要調(diào)度,因?yàn)閿?shù)據(jù)只可能存在于這一臺(tái)機(jī)器上,各種客戶端也只可能與這一個(gè)TiKV節(jié)點(diǎn)進(jìn)行交互。在分布式存儲(chǔ)領(lǐng)域,這種情況不可能一直持續(xù)下去,因?yàn)閿?shù)據(jù)的增量一定會(huì)超過這臺(tái)機(jī)器的存儲(chǔ)極限。到時(shí)我們必須將部分?jǐn)?shù)據(jù)遷移到其他機(jī)器上去。

了解過TiKV的同學(xué)們都知道TiKV使用range的方式將數(shù)據(jù)進(jìn)行切分。我們使用Region來表示一個(gè)數(shù)據(jù)range。每個(gè)Region都有多個(gè)副本Peer。通常為了數(shù)據(jù)可靠性,我們至少使用三個(gè)副本。

最開始系統(tǒng)初始化的時(shí)候,我們只有一個(gè)Region。當(dāng)數(shù)據(jù)量持續(xù)增大而超過Region設(shè)置的最大Size(64MB)閾值時(shí),Region就會(huì)分裂,生成兩個(gè)新的Region。Region是調(diào)度TiKV的基本單位。當(dāng)我們新增一個(gè)TiKV的時(shí)候,PD就會(huì)將原來TiKV中的一些Region調(diào)度到這個(gè)新增的TiKV中去。這樣就能保證整個(gè)數(shù)據(jù)均衡的分布在TiKV集群上面。因?yàn)橐粋€(gè)Region通常是64MB,將一個(gè)Region從一個(gè)TiKV移動(dòng)到另一個(gè)TiKV的過程中,數(shù)據(jù)量變更其實(shí)不大。所以可以直接使用Region的數(shù)量來大概的做數(shù)據(jù)的平衡。

上面我們對(duì)TiKV數(shù)據(jù)的調(diào)度做了簡單的介紹,但是實(shí)際的情況要比這個(gè)復(fù)雜很多。我們不僅要考慮數(shù)據(jù)的均衡,也要考慮計(jì)算的均衡。這樣才能保證整個(gè)TiKV集群更快更好的對(duì)外提供服務(wù)。因?yàn)門iKV使用的是Raft一致性算法。Raft有一個(gè)強(qiáng)約束就是為了保證線性一致性。所有的讀寫都必須通過Leader發(fā)起。假設(shè)現(xiàn)在有三個(gè)TiKV,如果幾乎所有的Leader都集中在某一個(gè)TiKV上,那么會(huì)造成這個(gè)TiKV成為性能瓶頸,最好的做法是Leader也能夠均衡地分布在不同的TiKV上,這樣整個(gè)系統(tǒng)都能對(duì)外提供服務(wù)。

總的來說,在分布式存儲(chǔ)TiKV中,調(diào)度任務(wù)及其重要。這關(guān)乎系統(tǒng)向外提供服務(wù)的質(zhì)量。我們必須同時(shí)考慮存儲(chǔ)Storage和計(jì)算Leader等資源。所以我們得出一個(gè)觀點(diǎn),分布式存儲(chǔ)系統(tǒng)是必須要有一個(gè)調(diào)度模塊的。那么,調(diào)度模塊的實(shí)現(xiàn)形式是什么樣的?今天我們都知道了在TIDB生態(tài)中,有PD作為TiKV集群的調(diào)度模塊。那么為什么需要單獨(dú)拿出來作為一個(gè)項(xiàng)目?我認(rèn)為這樣做的最大好處就是降低耦合。TiDB生態(tài)中,TiDB server負(fù)責(zé)查詢,TiKV負(fù)責(zé)存儲(chǔ),PD則負(fù)責(zé)TiKV調(diào)度。如果將調(diào)度模塊寫在TiDB或者TiKV里,當(dāng)TiDB或TiKV擴(kuò)展節(jié)點(diǎn)時(shí),PD也會(huì)跟著1:1地?cái)U(kuò)展。這將會(huì)造成一定的性能浪費(fèi),因?yàn)槲覀儗?shí)際上并不一定需要與TIDB或TiKV節(jié)點(diǎn)數(shù)一樣多的PD模塊。另外也可以說這是遵守了軟件設(shè)計(jì)原則中的單一職責(zé)原則。

PD相關(guān)技術(shù)

  • Go:PD完全由Go開發(fā)。Go語言簡單易用,天生支持高并發(fā)。PD源碼體積很小,不到5M,但是性能相當(dāng)不錯(cuò)。
  • Etcd:分布式系統(tǒng)中最關(guān)鍵的分布式可靠鍵值存儲(chǔ)。PD將Region meta信息持久化在etcd,以保證切換 PD Leader 節(jié)點(diǎn)后能快速繼續(xù)提供 Region 路由服務(wù)。
  • Raft:Etcd實(shí)現(xiàn)數(shù)據(jù)可靠性靠的是分布式一致性算法Raft。
  • Prometheus:PD集成Prometheus來達(dá)到指標(biāo)監(jiān)控的目的。每個(gè)PD啟動(dòng)時(shí)都會(huì)配置Prometheus,將系統(tǒng)運(yùn)行的指標(biāo)傳給Prometheus。
  • Zap Logger:Go系統(tǒng)庫自帶的日志包存在一定的性能與功能缺乏。PD集成了由 Ubder 開源的高性能日志框架Zap Logger來提高PD的性能。
  • TOML:PD配置文件書寫語法,由前GitHub CEO, Tom Preston-Werner,于2013年創(chuàng)建。其目標(biāo)是成為一個(gè)小規(guī)模的易于使用的語義化配置文件格式。

PD本地編譯運(yùn)行

PD代碼開源,可以從github獲取:

https://github.com/tikv/pd

源碼閱讀需要在本地編譯運(yùn)行PD源碼。首先需要準(zhǔn)備PD所需環(huán)境。我本地運(yùn)行的是Win10 系統(tǒng),安裝了如下依賴:go 1.14.7 + cmake3 + mingw64,使用intellij idea本地編譯運(yùn)行。

這里需要注意的是,我一開始安裝的 go 版本為1.15。結(jié)果每次本地編譯都會(huì)報(bào)類似于內(nèi)存泄漏等問題。解決方法是降低 go 的版本。我降到1.14版本后即可正常編譯運(yùn)行PD server。

還有另外一點(diǎn)是PD源碼有個(gè)ui模塊中文件 embedded_assets_rewriter 可能會(huì)報(bào)錯(cuò),報(bào)錯(cuò)原因是未識(shí)別的變量。我在相關(guān)論壇提問也沒得到回應(yīng),于是只能選擇注釋掉未聲明的變量并將相關(guān)方法返回nil。處理完這些問題就能跑起PD server來了。

PD源碼閱讀

今天將解讀pd源碼的開始部分:啟動(dòng)一個(gè)pd server。

閱讀從根目錄下的cmd/pd-server/main.go開始,由此展開。

一、讀取配置

PD的配置信息有三個(gè)來源。分別是Config對(duì)象默認(rèn)配置,外部配置文件和命令行參數(shù)。它們的優(yōu)先級(jí)分別是命令行參數(shù) > 外部配置文件 > 默認(rèn)。下面第一塊代碼就是讀取配置的兩行代碼。config.NewConfig()獲取到系統(tǒng)的默認(rèn)配置。系統(tǒng)默認(rèn)配置文件在/conf/config.toml里。在Config 的結(jié)構(gòu)體中,可以利用第三方包 http://github.com/BurntSushi/toml 直接讀取 toml 格式的配置文件中的值。下面的第二段代碼就是config結(jié)構(gòu)體中使用 toml 工具包讀取 toml 格式的配置文件中的值來設(shè)置屬性的默認(rèn)值的部分代碼。通過 toml:"配置文件中屬性名"的形式獲取到配置的值。從而設(shè)置為該屬性的默認(rèn)值。Parse 方法讀取命令行參數(shù)并將參數(shù)設(shè)置到config對(duì)象中去。

讀取配置

cfg := config.NewConfig()err := cfg.Parse(os.Args[1:])

Config結(jié)構(gòu)體部分代碼

type Config struct {flagSet *flag.FlagSetVersion bool `json:"-"`ConfigCheck bool `json:"-"`ClientUrls string`toml:"client-urls" json:"client-urls"`PeerUrls string`toml:"peer-urls" json:"peer-urls"`AdvertiseClientUrlsstring `toml:"advertise-client-urls"json:"advertise-client-urls"`AdvertisePeerUrls string`toml:"advertise-peer-urls" json:"advertise-peer-urls"`}

創(chuàng)建默認(rèn)配置對(duì)象cfg時(shí),NewConfig 方法內(nèi)部還將利用 flagSet 對(duì)象對(duì)cfg各個(gè)屬性做屬性說明。對(duì)于bool類型的屬性將調(diào)用flagSet的BoolVar方法對(duì)其進(jìn)行說明。具體過程會(huì)聲明該變量的簡稱,值以及用處。同理 StringVar 就是對(duì) string 類型的變量做說明的。

下面的示例代碼就展示了 BoolVar 和 StringVar 的內(nèi)部邏輯以及使用這些方法對(duì)config對(duì)象的屬性做說明的過程。我們可以看到使用 StringVar 對(duì)屬性 configFile 做了說明。其簡稱為 config 。它的值默認(rèn)為 "" 。它的用處就是作為配置文件。同理,BoolVar也對(duì)bool類型的屬性 ConfigCheck 做了說明。說明它是檢查配置文件的合規(guī)性的。

New Config()

cfg := &Config{}cfg.flagSet =flag.NewFlagSet("pd", flag.ContinueOnError)fs := cfg.flagSetfs.StringVar(&cfg.configFile,"config", "", "config file")fs.BoolVar(&cfg.ConfigCheck,"config-check", false, "check config file validity and exit")func (f *FlagSet) BoolVar(p *bool, namestring, value bool, usage string) {f.Var(newBoolValue(value, p), name, usage)}func (f *FlagSet) StringVar(p *string,name string, value string, usage string) {f.Var(newStringValue(value, p), name, usage)}

以上是默認(rèn)配置的一些處理操作。接下來講講獲取外部配置文件和命令行中的配置信息。

PD 在啟動(dòng)時(shí)可以攜帶外部的配置文件對(duì) PD 的屬性做配置。具體操作是用命令行啟動(dòng) PD 時(shí),使用命令行參數(shù) --config 指明外部配置文件的位置。例如 --config "/usr/local/config.toml" 將指定 PD 啟動(dòng)時(shí)讀取本機(jī)文件目錄 /usr/local/config.toml 的配置文件。

接著我們?cè)诖a層面看一下這個(gè)過程:

首先在 main 方法中獲取命令行參數(shù)信息。這一步驟是通過 go 的os包支持的。通過 os.Args獲取命令行參數(shù)數(shù)組。然后傳入到 config 對(duì)象的 Parse 方法中。

接著在 Parse 方法中,調(diào)用 flagSet 的 Parse 方法將命令行參數(shù)都設(shè)置到config對(duì)象對(duì)應(yīng)的屬性上。在隨后的代碼中將判斷 config 對(duì)象中 configFile 屬性是否非空。因?yàn)檫@個(gè)屬性默認(rèn)是空字符串,只有設(shè)置了值,才能進(jìn)行下一步讀取指定路徑的配置文件。當(dāng)它的值非空時(shí)將調(diào)用 configFromFile 方法讀取指定目錄的配置文件,讀取的結(jié)果放到 toml.MetaData 對(duì)象中。然后將這個(gè)對(duì)象傳入到 config 對(duì)象的Adjust 方法中用于調(diào)整 config 的各個(gè)屬性值。

PD 的配置文件描述全面的資料可以參考:

PD 配置文件描述

命令行參數(shù)描述可以參考:

PD 配置參數(shù)

讀取完配置后,Parse 方法將返回err對(duì)象以幫助判斷Parse過程是否成功。err 如果是 nil,則說明Parse是沒有問題的。如果是ErrHelp,則說明輸入命令行的是-h或者是-help。輸入這個(gè)命令說明我只是想查看pd啟動(dòng)時(shí)可以攜帶哪些配置參數(shù)而不是直接啟動(dòng)pd。所以在這個(gè)case下將調(diào)用 exit 方法退出啟動(dòng)程序。除此之外,其他情況就是parse過程錯(cuò)誤,輸出錯(cuò)誤提示信息。

Parse結(jié)果檢查

switch errors.Cause(err) { case nil: case flag.ErrHelp:exit(0) default:log.Fatal("parse cmd flags error", errs.ZapError(errs.ErrParseFlags)) }

二、啟動(dòng)logger服務(wù)并打印PD Server的信息和警告信息

PD使用zap Logger替代go原生的log組件以此來提高整體運(yùn)行的性能。我們都知道go原生的logger使用起來十分簡單。我們通過設(shè)置任何io.writer作為日志記錄輸出并向其發(fā)送要寫入的日志就行。但是簡單歸簡單,原生logger也有很多不足的地方。例如:僅限基本日志級(jí)別、只有一個(gè)Print選項(xiàng)、Fatal日志通過調(diào)用os.Exit(1)來結(jié)束程序、Panic日志在寫入日志消息之后拋出一個(gè)panic、不提供日志切割的能力、缺乏日志格式化能力等。綜合這些原因,pd使用uber開源的日志框架zap logger來替換原生的logger。zap logger有兩個(gè)優(yōu)點(diǎn)。其一是提供了結(jié)構(gòu)化日志記錄和printf風(fēng)格的日志記錄。其二是它非常的快。關(guān)于zap logger高性能的設(shè)計(jì)思路可以參考它家github地址:

https://github.com/uber-go/zap#performance

下方代碼就是PD創(chuàng)建zap logger來替換原生logger的過程:

首先調(diào)用cfg對(duì)象的 SetupLogger 方法設(shè)置cfg的logger和logProps屬性。在SetupLogger 方法內(nèi)部,使用PingCAP自家的log包里的初始化方法 InitLogger獲得zap.logger 和ZapProperties對(duì)象并將二者分別賦給cfg的 logger 和 logProps屬性。接著使用 ReplaceGlobals替換全局的logger。然后刷新緩存,最后使用 InitLogger 初始化zap logger。

logger組件設(shè)置啟動(dòng)好之后,打印PD信息和警告。

啟動(dòng)logger:

err = cfg.SetupLogger() if err == nil {log.ReplaceGlobals(cfg.GetZapLogger(), cfg.GetZapLogProperties()) } else {log.Fatal("initialize logger error", errs.ZapError(err)) } // Flushing any buffered log entries defer log.Sync()// The old logger err = logutil.InitLogger(&cfg.Log) if err != nil {log.Fatal("initialize logger error", errs.ZapError(err)) }server.LogPDInfo()for _, msg := range cfg.WarningMsgs {log.Warn(msg) }

三、Prometheus監(jiān)控

在 main 方法中調(diào)用 EnableHandlingTimeHistogram 。在 PD 啟動(dòng)時(shí),會(huì)初始化一個(gè)默認(rèn)的 ServerMetrics 對(duì)象來記錄 PD server服務(wù)運(yùn)行的指標(biāo)。默認(rèn)不開啟 Histogram metrics 這個(gè)指標(biāo)監(jiān)控。因?yàn)檫@個(gè)指標(biāo)監(jiān)控耗費(fèi)性能較高。在源碼的注釋中也說明,開啟 Histogram metrics 監(jiān)控可能會(huì)耗費(fèi)較大性能。如果機(jī)器性能有限,那么可以選擇不開啟。

接著就會(huì)調(diào)用 Push 方法將指標(biāo)發(fā)送到 Prometheus 的推送網(wǎng)關(guān)上。具體推送方法是 prometheusPushClinet。在該方法內(nèi)首先構(gòu)造推送者對(duì)象pusher。pusher的構(gòu)造使用了建造者模式。首先使用推送的地址和任務(wù)初始化pusher,添加了為其添加了收集器以及分組標(biāo)簽。

Prometheus監(jiān)控:

grpcprometheus.EnableHandlingTimeHistogram()metricutil.Push(&cfg.Metric) Gatherer(prometheus.DefaultGatherer).Grouping("instance", instanceName())for {err := pusher.Push()if err != nil {log.Error("could not push metrics to Prometheus Pushgateway", errs.ZapError(errs.ErrPrometheusPushMetrics, err))}time.Sleep(interval)} }

四、動(dòng)態(tài)添加節(jié)點(diǎn)

PD使用 PrepareJoinCluster 方法將當(dāng)前節(jié)點(diǎn) Join指定的集群當(dāng)中去并且在Join成功后持久化Join配置,當(dāng)PD節(jié)點(diǎn)宕機(jī)后重啟時(shí),讀取本地配置就能快速重新加入集群。

下面簡單聊聊從PD節(jié)點(diǎn)首次加入到一個(gè)集群以及PD停機(jī)再次加入集群的情況。

當(dāng)PD節(jié)點(diǎn)首次Join某集群時(shí),我們進(jìn)入PrepareJoinCluster 方法,攜帶的參數(shù)時(shí)cfg,也就是PD的配置對(duì)象。當(dāng)我們想Join某個(gè)集群時(shí),首先保證目標(biāo)集群能夠正常工作。在啟動(dòng)PD節(jié)點(diǎn)時(shí)。命令行攜帶參數(shù)--join="target-urls",target-urls就是目標(biāo)集群里任意PD的advertise-clinet-url。PD啟動(dòng)時(shí)通過os.Args讀取這些額外參數(shù)并設(shè)置到cfg對(duì)象中去。首先要做基本的差錯(cuò)檢測(cè),排除Join信息錯(cuò)誤的情況。然后嘗試讀取本地保存的Join信息。我們是第一次Join到一個(gè)陌生的集群,這些信息以及目錄還沒有創(chuàng)建。接下來將創(chuàng)建一個(gè)etcd的client,創(chuàng)建時(shí)傳入Join信息、TLS憑證配置、超時(shí)限制等信息。下一步,ListEtcdMember 方法列出目標(biāo)集群所有的etcd成員。隨后判斷當(dāng)前PD節(jié)點(diǎn)是否與集群中的節(jié)點(diǎn)重名。重名則無法加入集群,直接退出。如果滿足條件名字不沖突。隨后使用 AddEtcdMenber方法嘗試加入集群。結(jié)果將返回到類型為*clientv3.MenberAddResponse的對(duì)象中。隨后再次調(diào)用 ListEtcdMenber 獲取最新的etcd集群成員信息并對(duì)集群情況進(jìn)行驗(yàn)證,并將最新的集群信息更新到cfg對(duì)象中。最后將節(jié)點(diǎn)配置信息保存到本地。

當(dāng)PD停機(jī)再次重啟時(shí),直接讀取本地文件獲取集群信息并加入到集群中去。

Join節(jié)點(diǎn):

err = join.PrepareJoinCluster(cfg) if err != nil {log.Fatal("join meet error", errs.ZapError(err)) }

五、創(chuàng)建并運(yùn)行PD Server

這一步驟主要做兩件事情。第一個(gè)就是創(chuàng)建PD Server并運(yùn)行。第二就是監(jiān)聽退出信號(hào)。

首先使用 CreateServer 方法創(chuàng)建Server對(duì)象并且傳入所需要的參數(shù):上下文對(duì)象ctx、配置cfg、服務(wù)數(shù)組servcieBuilders。接著調(diào)用server的Run方法啟動(dòng)Server。在Run方法內(nèi),首先會(huì)通過協(xié)程開啟監(jiān)控。隨后開啟etcd和Server服務(wù)。最后通過Server的startServerLoop方法使得服務(wù)處于不斷運(yùn)行的狀態(tài)而不退出。

另外一個(gè)部分就是監(jiān)聽退出信號(hào)。通過監(jiān)聽四種信號(hào)來判斷是否要中止服務(wù)。這四種信號(hào)及含義如下表所示。監(jiān)聽程序通過協(xié)程的方式監(jiān)聽退出信號(hào),一旦監(jiān)聽到退出信號(hào),調(diào)用cancle方法即會(huì)向ctx對(duì)象的Done通道發(fā)送消息。Done通道一旦接收到消息運(yùn)行Server的線程就會(huì)退出。接著就會(huì)打印退出信息返回退出碼。

信號(hào)值動(dòng)作說明
SIGHUP1Term終端控制進(jìn)程結(jié)束(終端連接斷開)
SIGHINT2Term用戶發(fā)送INTR字符(Ctrl+C)觸發(fā)
SIGTERM15Term結(jié)束程序(可以被捕獲、阻塞或忽略)
SIGQUIT3Core用戶發(fā)送QUIT字符(Ctrl+/)觸發(fā)

創(chuàng)建 PD Server:

ctx, cancel := context.WithCancel(context.Background()) serviceBuilders := []server.HandlerBuilder{api.NewHandler, swaggerserver.NewHandler, autoscaling.NewHandler} serviceBuilders = append(serviceBuilders, dashboard.GetServiceBuilders()...) svr, err := server.CreateServer(ctx, cfg, serviceBuilders...) if err != nil {log.Fatal("create server failed", errs.ZapError(err)) }

總的來說,PD節(jié)點(diǎn)的啟動(dòng)會(huì)經(jīng)歷讀取配置、設(shè)置logger、啟動(dòng)prometheus監(jiān)控、join集群、啟動(dòng)server、監(jiān)聽退出命令后退出等步驟。

我們今天主要了解了PD節(jié)點(diǎn)啟動(dòng)的基本步驟,也了解到PD對(duì)zap logger和Prometheus等中間件的集成使用。最后學(xué)習(xí)了使用協(xié)程監(jiān)聽退出命令。

整個(gè)PD的啟動(dòng)流程用下面流程圖表示一下:

?本篇文章只是對(duì)PD節(jié)點(diǎn)啟動(dòng)做的一個(gè)粗略的解讀,有些地方可能存在錯(cuò)誤希望有真知灼見的大神能不吝賜教,指出我的問題,多多交流。

總結(jié)

以上是生活随笔為你收集整理的PD源码阅读系列:PD节点启动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。