go語言規(guī)范文檔,go語言代碼規(guī)范

golang配制高性能sql.DB

有很多教程是關(guān)于Go的sql.DB類型和如何使用它來執(zhí)行SQL數(shù)據(jù)庫查詢的。但大多數(shù)內(nèi)容都沒有講述 SetMaxOpenConns() , SetMaxIdleConns() 和 SetConnMaxLifetime()方法, 您可以使用它們來配置sql.DB的行為并改變其性能。

創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比貢井網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式貢井網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋貢井地區(qū)。費(fèi)用合理售后完善,10多年實(shí)體公司更值得信賴。

轉(zhuǎn)自:

整理:go語言中文文檔:

在本文我將詳細(xì)解釋這些設(shè)置的作用,并說明它們所能產(chǎn)生的(積極和消極)影響。

一個(gè)sql.DB對(duì)象就是一個(gè)數(shù)據(jù)庫連接池,它包含“正在用”和“空閑的”連接。一個(gè)正在用的連接指的是,你正用它來執(zhí)行數(shù)據(jù)庫任務(wù),例如執(zhí)行SQL語句或行查詢。當(dāng)任務(wù)完成連接就是空閑的。

當(dāng)您創(chuàng)建sql.DB執(zhí)行數(shù)據(jù)庫任務(wù)時(shí),它將首先檢查連接池中是否有可用的空閑連接。如果有可用的連接,那么Go將重用現(xiàn)有連接,并在執(zhí)行任務(wù)期間將其標(biāo)記為正在使用。如果池中沒有空閑連接,而您需要一個(gè)空閑連接,那么Go將創(chuàng)建一個(gè)新的連接。

默認(rèn)情況下,在同一時(shí)間打開連接的數(shù)量是沒有限制(包含使用中+空閑)。但你可以通過SetMaxOpenConns()方法實(shí)現(xiàn)自定義限制,如下所示:

在這個(gè)示例代碼中,連接池現(xiàn)在有5個(gè)并發(fā)打開的連接數(shù)。如果所有5個(gè)連接都已經(jīng)被標(biāo)記為正在使用,并且需要另一個(gè)新的連接,那么應(yīng)用程序?qū)⒈黄鹊却?,直?個(gè)連接中的一個(gè)被釋放并變?yōu)榭臻e。

為了說明更改MaxOpenConns的影響,我運(yùn)行了一個(gè)基準(zhǔn)測(cè)試,將最大打開連接數(shù)設(shè)置為1、2、5、10和無限?;鶞?zhǔn)測(cè)試在PostgreSQL數(shù)據(jù)庫上執(zhí)行并行的INSERT語句,您可以在這里找到代碼。測(cè)試結(jié)果:

對(duì)于這個(gè)基準(zhǔn)測(cè)試,我們可以看到,允許打開的連接越多,在數(shù)據(jù)庫上執(zhí)行INSERT操作所花費(fèi)的時(shí)間就越少(打開的連接數(shù)為1時(shí),執(zhí)行速度3129633ns/op,而無限連接:531030ns/op——大約快了6倍)。這是因?yàn)樵试S打開的連接越多,可以并發(fā)執(zhí)行的數(shù)據(jù)庫查詢就越多。

默認(rèn)情況下,sql.DB允許連接池中最多保留2個(gè)空閑連接。你可以通過SetMaxIdleConns()方法改變它,如下所示:

從理論上講,允許池中有更多的空閑連接將提高性能,因?yàn)檫@樣就不太可能從頭開始建立新連接——因此有助于提升數(shù)據(jù)庫性能。

讓我們來看看相同的基準(zhǔn)測(cè)試,最大空閑連接設(shè)置為none, 1,2,5和10:

當(dāng)MaxIdleConns設(shè)置為none時(shí),必須為每個(gè)INSERT從頭創(chuàng)建一個(gè)新的連接,我們可以從基準(zhǔn)測(cè)試中看到,平均運(yùn)行時(shí)和內(nèi)存使用量相對(duì)較高。

只允許保留和重用一個(gè)空閑連接對(duì)基準(zhǔn)測(cè)試影響特別明顯——它將平均運(yùn)行時(shí)間減少了大約8倍,內(nèi)存使用量減少了大約20倍。繼續(xù)增加空閑連接池的大小會(huì)使性能變得更好,盡管改進(jìn)并不明顯。

那么,您應(yīng)該維護(hù)一個(gè)大的空閑連接池嗎?答案取決于應(yīng)用程序。重要的是要意識(shí)到保持空閑連接是有代價(jià)的—它占用了可以用于應(yīng)用程序和數(shù)據(jù)庫的內(nèi)存。

還有一種可能是,如果一個(gè)連接空閑時(shí)間太長,那么它可能會(huì)變得不可用。例如,MySQL的wait_timeout設(shè)置將自動(dòng)關(guān)閉任何8小時(shí)(默認(rèn))內(nèi)未使用的連接。

當(dāng)發(fā)生這種情況時(shí),sql.DB會(huì)優(yōu)雅地處理它。壞連接將自動(dòng)重試兩次,然后放棄,此時(shí)Go將該連接從連接池中刪除,并創(chuàng)建一個(gè)新的連接。因此,將MaxIdleConns設(shè)置得太大可能會(huì)導(dǎo)致連接變得不可用,與空閑連接池更小(使用更頻繁的連接更少)相比,會(huì)占有更多的資源。所以,如果你很可能很快就會(huì)再次使用,你只需保持一個(gè)空閑的連接。

最后要指出的是,MaxIdleConns應(yīng)該總是小于或等于MaxOpenConns。Go強(qiáng)制執(zhí)行此操作,并在必要時(shí)自動(dòng)減少M(fèi)axIdleConns。

現(xiàn)在讓我們看看SetConnMaxLifetime()方法,它設(shè)置連接可重用的最大時(shí)間長度。如果您的SQL數(shù)據(jù)庫也實(shí)現(xiàn)了最大連接生命周期,或者—例如—您希望方便地在負(fù)載均衡器后交換數(shù)據(jù)庫,那么這將非常有用。

你可以這樣使用它:

在這個(gè)例子中,所有的連接都將在創(chuàng)建后1小時(shí)“過期”,并且在過期后無法重用。但注意:

從理論上講,ConnMaxLifetime越短,連接過期的頻率就越高——因此,需要從頭創(chuàng)建連接的頻率就越高。為了說明這一點(diǎn),我運(yùn)行了將ConnMaxLifetime設(shè)置為100ms、200ms、500ms、1000ms和無限(永遠(yuǎn)重用)的基準(zhǔn)測(cè)試,默認(rèn)設(shè)置為無限打開連接和2個(gè)空閑連接。這些時(shí)間段顯然比您在大多數(shù)應(yīng)用程序中使用的時(shí)間要短得多,但它們有助于很好地說明行為。

在這些特定的基準(zhǔn)測(cè)試中,我們可以看到,與無限生存期相比,在100ms生存期時(shí)內(nèi)存使用量增加了3倍以上,而且每個(gè)INSERT的平均運(yùn)行時(shí)也稍微長一些。

如果您在代碼中設(shè)置了ConnMaxLifetime,那么一定要記住連接將過期(隨后重新創(chuàng)建)的頻率。例如,如果您總共有100個(gè)連接,而ConnMaxLifetime為1分鐘,那么您的應(yīng)用程序可能每秒鐘殺死和重新創(chuàng)建1.67個(gè)連接(平均值)。您不希望這個(gè)頻率太大,最終會(huì)阻礙性能,而不是提高性能。

最后,如果不說明超過數(shù)據(jù)庫連接數(shù)量的硬限制將會(huì)發(fā)生什么,那么本文就不完整了。 為了說明這一點(diǎn),我將修改postgresql.conf文件,這樣總共只允許5個(gè)連接(默認(rèn)是100個(gè))…

然后在無限連接的情況下重新運(yùn)行基準(zhǔn)測(cè)試……

一旦達(dá)到5個(gè)連接的硬限制,數(shù)據(jù)庫驅(qū)動(dòng)程序(pq)立即返回一個(gè)太多客戶端連接的錯(cuò)誤消息,而無法完成INSERT。為了防止這個(gè)錯(cuò)誤,我們需要將sql.DB中打開連接的最大總數(shù)(正在使用的+空閑的)設(shè)置為低于5。像這樣:

現(xiàn)在,sql.DB在任何時(shí)候最多只能創(chuàng)建3個(gè)連接,基準(zhǔn)測(cè)試運(yùn)行時(shí)應(yīng)該不會(huì)出現(xiàn)任何錯(cuò)誤。但是這樣做需要注意:當(dāng)達(dá)到開放連接數(shù)限制,并且所有連接都在使用時(shí),應(yīng)用程序需要執(zhí)行的任何新的數(shù)據(jù)庫任務(wù)都將被迫等待,直到連接標(biāo)記為空閑。例如,在web應(yīng)用程序的上下文中,用戶的HTTP請(qǐng)求看起來會(huì)“掛起”,甚至在等待數(shù)據(jù)庫任務(wù)運(yùn)行時(shí)可能會(huì)超時(shí)。

為了減輕這種情況,你應(yīng)該始終在一個(gè)上下文中傳遞。在調(diào)用數(shù)據(jù)庫時(shí),啟用上下文的方法(如ExecContext()),使用固定的、快速的超時(shí)上下文對(duì)象。

總結(jié)

1、根據(jù)經(jīng)驗(yàn),應(yīng)該顯式設(shè)置MaxOpenConns值。這應(yīng)該小于數(shù)據(jù)庫和基礎(chǔ)設(shè)施對(duì)連接數(shù)量的硬性限制。

2、一般來說,更高的MaxOpenConns和MaxIdleConns值將帶來更好的性能。但你應(yīng)該注意到效果是遞減的,連接池空閑連接太多(連接沒有被重用,最終會(huì)變壞)實(shí)際上會(huì)導(dǎo)致性能下降。

3、為了降低上面第2點(diǎn)帶來的風(fēng)險(xiǎn),您可能需要設(shè)置一個(gè)相對(duì)較短的ConnMaxLifetime。但你也不希望它太短,導(dǎo)致連接被殺死或不必要地頻繁重建。

4、MaxIdleConns應(yīng)該總是小于或等于MaxOpenConns。

對(duì)于中小型web應(yīng)用程序,我通常使用以下設(shè)置作為起點(diǎn),然后根據(jù)實(shí)際吞吐量水平的負(fù)載測(cè)試結(jié)果進(jìn)行優(yōu)化。

golang elasticsearch 文檔操作(CRUD) --- 2022-04-02

本節(jié)主要介紹go語言對(duì)Elasticsearch文檔的基礎(chǔ)操作:創(chuàng)建、查詢、更新、刪除。

為了方便演示文檔的CRUD操作,我們先定義索引的struct結(jié)構(gòu)

根據(jù)文檔ID,查詢文檔

通過多個(gè)Id批量查詢文檔,對(duì)應(yīng)ES的multi get

根據(jù)id更新文檔

支持批量更新文檔內(nèi)容

提示: 復(fù)雜查詢條件,請(qǐng)參考 go es查詢用法

GO語言(二十五):管理依賴項(xiàng)(上)-

當(dāng)您的代碼使用外部包時(shí),這些包(作為模塊分發(fā))成為依賴項(xiàng)。隨著時(shí)間的推移,您可能需要升級(jí)或更換它們。Go 提供了依賴管理工具,可幫助您在合并外部依賴項(xiàng)時(shí)確保 Go 應(yīng)用程序的安全。

本主題介紹如何執(zhí)行任務(wù)以管理您在代碼中承擔(dān)的依賴項(xiàng)。您可以使用 Go 工具執(zhí)行其中的大部分操作。本主題還介紹了如何執(zhí)行其他一些您可能會(huì)覺得有用的依賴相關(guān)任務(wù)。

您可以通過 Go 工具獲取和使用有用的包。在 pkg.go.dev 上,您可以搜索您可能覺得有用的包,然后使用go命令將這些包導(dǎo)入您自己的代碼中以調(diào)用它們的功能。

下面列出了最常見的依賴項(xiàng)管理步驟。

在 Go 中,您將依賴項(xiàng)作為包含您導(dǎo)入的包的模塊來管理。此過程由以下機(jī)構(gòu)支持:

您可以搜索pkg.go.dev以查找具有您可能覺得有用的功能的軟件包。

找到要在代碼中使用的包后,在頁面頂部找到包路徑,然后單擊復(fù)制路徑按鈕將路徑復(fù)制到剪貼板。在您自己的代碼中,將路徑粘貼到導(dǎo)入語句中,如下例所示:

在您的代碼導(dǎo)入包后,啟用依賴項(xiàng)跟蹤并獲取包的代碼進(jìn)行編譯。

要跟蹤和管理您添加的依賴項(xiàng),您首先要將代碼放入其自己的模塊中。這會(huì)在源代碼樹的根目錄創(chuàng)建一個(gè) go.mod 文件。您添加的依賴項(xiàng)將列在該文件中。

要將您的代碼添加到它自己的模塊中,請(qǐng)使用 go mod init命令。例如,從命令行切換到代碼的根目錄,然后按照以下示例運(yùn)行命令:

該go mod init命令的參數(shù)是您的模塊的模塊路徑。如果可能,模塊路徑應(yīng)該是源代碼的存儲(chǔ)庫位置。

如果一開始您不知道模塊的最終存儲(chǔ)庫位置,請(qǐng)使用安全的替代品。這可能是您擁有的域的名稱或您控制的另一個(gè)名稱(例如您的公司名稱),以及來自模塊名稱或源目錄的路徑。

當(dāng)您使用 Go 工具管理依賴項(xiàng)時(shí),這些工具會(huì)更新 go.mod 文件,以便它維護(hù)您的依賴項(xiàng)的當(dāng)前列表。

添加依賴項(xiàng)時(shí),Go 工具還會(huì)創(chuàng)建一個(gè) go.sum 文件,其中包含您所依賴的模塊的校驗(yàn)和。Go 使用它來驗(yàn)證下載的模塊文件的完整性,特別是對(duì)于在您的項(xiàng)目上工作的其他開發(fā)人員。

在代碼中包含存儲(chǔ)庫中的 go.mod 和 go.sum 文件。

當(dāng)您運(yùn)行g(shù)o mod init創(chuàng)建用于跟蹤依賴項(xiàng)的模塊時(shí),您指定一個(gè)模塊路徑作為模塊的名稱。模塊路徑成為模塊中包的導(dǎo)入路徑前綴。一定要指定一個(gè)不會(huì)與其他模塊的模塊路徑?jīng)_突的模塊路徑。

至少,一個(gè)模塊路徑只需要表明它的來源,例如公司或作者或所有者名稱。但是路徑也可能更能描述模塊是什么或做什么。

模塊路徑通常采用以下形式:

1、Go 工具可以在其中找到模塊源代碼的存儲(chǔ)庫的位置。

例如,它可能是github.com/ /.

如果您認(rèn)為您可能會(huì)發(fā)布模塊供其他人使用,請(qǐng)使用此最佳實(shí)踐。

2、一個(gè)你控制的名字。

如果您不使用存儲(chǔ)庫名稱,請(qǐng)務(wù)必選擇一個(gè)您確信不會(huì)被其他人使用的前綴。一個(gè)不錯(cuò)的選擇是您公司的名稱。避免使用常用術(shù)語,例如widgets、utilities或 app。

Go 保證以下字符串不會(huì)在包名稱中使用。

1、test– 您可以將test用作模塊路徑前綴以便代碼用于在另一個(gè)模塊中本地測(cè)試功能進(jìn)行測(cè)試。

使用test作為模塊路徑前綴是測(cè)試的一部分。例如,您的測(cè)試本身可能會(huì)運(yùn)行g(shù)o mod init test,然后以某種特定方式設(shè)置該模塊,以便使用 Go 源代碼分析工具進(jìn)行測(cè)試。

2、example– 在某些 Go 文檔中用作模塊路徑前綴,例如在創(chuàng)建模塊以跟蹤依賴關(guān)系的教程中。

請(qǐng)注意,Go 文檔還用于example.com說明示例何時(shí)可能是已發(fā)布的模塊。

【golang詳解】go語言GMP(GPM)原理和調(diào)度

Goroutine調(diào)度是一個(gè)很復(fù)雜的機(jī)制,下面嘗試用簡單的語言描述一下Goroutine調(diào)度機(jī)制,想要對(duì)其有更深入的了解可以去研讀一下源碼。

首先介紹一下GMP什么意思:

G ----------- goroutine: 即Go協(xié)程,每個(gè)go關(guān)鍵字都會(huì)創(chuàng)建一個(gè)協(xié)程。

M ---------- thread內(nèi)核級(jí)線程,所有的G都要放在M上才能運(yùn)行。

P ----------- processor處理器,調(diào)度G到M上,其維護(hù)了一個(gè)隊(duì)列,存儲(chǔ)了所有需要它來調(diào)度的G。

Goroutine 調(diào)度器P和 OS 調(diào)度器是通過 M 結(jié)合起來的,每個(gè) M 都代表了 1 個(gè)內(nèi)核線程,OS 調(diào)度器負(fù)責(zé)把內(nèi)核線程分配到 CPU 的核上執(zhí)行

模型圖:

避免頻繁的創(chuàng)建、銷毀線程,而是對(duì)線程的復(fù)用。

1)work stealing機(jī)制

當(dāng)本線程無可運(yùn)行的G時(shí),嘗試從其他線程綁定的P偷取G,而不是銷毀線程。

2)hand off機(jī)制

當(dāng)本線程M0因?yàn)镚0進(jìn)行系統(tǒng)調(diào)用阻塞時(shí),線程釋放綁定的P,把P轉(zhuǎn)移給其他空閑的線程執(zhí)行。進(jìn)而某個(gè)空閑的M1獲取P,繼續(xù)執(zhí)行P隊(duì)列中剩下的G。而M0由于陷入系統(tǒng)調(diào)用而進(jìn)被阻塞,M1接替M0的工作,只要P不空閑,就可以保證充分利用CPU。M1的來源有可能是M的緩存池,也可能是新建的。當(dāng)G0系統(tǒng)調(diào)用結(jié)束后,根據(jù)M0是否能獲取到P,將會(huì)將G0做不同的處理:

如果有空閑的P,則獲取一個(gè)P,繼續(xù)執(zhí)行G0。

如果沒有空閑的P,則將G0放入全局隊(duì)列,等待被其他的P調(diào)度。然后M0將進(jìn)入緩存池睡眠。

如下圖

GOMAXPROCS設(shè)置P的數(shù)量,最多有GOMAXPROCS個(gè)線程分布在多個(gè)CPU上同時(shí)運(yùn)行

在Go中一個(gè)goroutine最多占用CPU 10ms,防止其他goroutine被餓死。

具體可以去看另一篇文章

【Golang詳解】go語言調(diào)度機(jī)制 搶占式調(diào)度

當(dāng)創(chuàng)建一個(gè)新的G之后優(yōu)先加入本地隊(duì)列,如果本地隊(duì)列滿了,會(huì)將本地隊(duì)列的G移動(dòng)到全局隊(duì)列里面,當(dāng)M執(zhí)行work stealing從其他P偷不到G時(shí),它可以從全局G隊(duì)列獲取G。

協(xié)程經(jīng)歷過程

我們創(chuàng)建一個(gè)協(xié)程 go func()經(jīng)歷過程如下圖:

說明:

這里有兩個(gè)存儲(chǔ)G的隊(duì)列,一個(gè)是局部調(diào)度器P的本地隊(duì)列、一個(gè)是全局G隊(duì)列。新創(chuàng)建的G會(huì)先保存在P的本地隊(duì)列中,如果P的本地隊(duì)列已經(jīng)滿了就會(huì)保存在全局的隊(duì)列中;處理器本地隊(duì)列是一個(gè)使用數(shù)組構(gòu)成的環(huán)形鏈表,它最多可以存儲(chǔ) 256 個(gè)待執(zhí)行任務(wù)。

G只能運(yùn)行在M中,一個(gè)M必須持有一個(gè)P,M與P是1:1的關(guān)系。M會(huì)從P的本地隊(duì)列彈出一個(gè)可執(zhí)行狀態(tài)的G來執(zhí)行,如果P的本地隊(duì)列為空,就會(huì)想其他的MP組合偷取一個(gè)可執(zhí)行的G來執(zhí)行;

一個(gè)M調(diào)度G執(zhí)行的過程是一個(gè)循環(huán)機(jī)制;會(huì)一直從本地隊(duì)列或全局隊(duì)列中獲取G

上面說到P的個(gè)數(shù)默認(rèn)等于CPU核數(shù),每個(gè)M必須持有一個(gè)P才可以執(zhí)行G,一般情況下M的個(gè)數(shù)會(huì)略大于P的個(gè)數(shù),這多出來的M將會(huì)在G產(chǎn)生系統(tǒng)調(diào)用時(shí)發(fā)揮作用。類似線程池,Go也提供一個(gè)M的池子,需要時(shí)從池子中獲取,用完放回池子,不夠用時(shí)就再創(chuàng)建一個(gè)。

work-stealing調(diào)度算法:當(dāng)M執(zhí)行完了當(dāng)前P的本地隊(duì)列隊(duì)列里的所有G后,P也不會(huì)就這么在那躺尸啥都不干,它會(huì)先嘗試從全局隊(duì)列隊(duì)列尋找G來執(zhí)行,如果全局隊(duì)列為空,它會(huì)隨機(jī)挑選另外一個(gè)P,從它的隊(duì)列里中拿走一半的G到自己的隊(duì)列中執(zhí)行。

如果一切正常,調(diào)度器會(huì)以上述的那種方式順暢地運(yùn)行,但這個(gè)世界沒這么美好,總有意外發(fā)生,以下分析goroutine在兩種例外情況下的行為。

Go runtime會(huì)在下面的goroutine被阻塞的情況下運(yùn)行另外一個(gè)goroutine:

用戶態(tài)阻塞/喚醒

當(dāng)goroutine因?yàn)閏hannel操作或者network I/O而阻塞時(shí)(實(shí)際上golang已經(jīng)用netpoller實(shí)現(xiàn)了goroutine網(wǎng)絡(luò)I/O阻塞不會(huì)導(dǎo)致M被阻塞,僅阻塞G,這里僅僅是舉個(gè)栗子),對(duì)應(yīng)的G會(huì)被放置到某個(gè)wait隊(duì)列(如channel的waitq),該G的狀態(tài)由_Gruning變?yōu)開Gwaitting,而M會(huì)跳過該G嘗試獲取并執(zhí)行下一個(gè)G,如果此時(shí)沒有可運(yùn)行的G供M運(yùn)行,那么M將解綁P,并進(jìn)入sleep狀態(tài);當(dāng)阻塞的G被另一端的G2喚醒時(shí)(比如channel的可讀/寫通知),G被標(biāo)記為,嘗試加入G2所在P的runnext(runnext是線程下一個(gè)需要執(zhí)行的 Goroutine。), 然后再是P的本地隊(duì)列和全局隊(duì)列。

系統(tǒng)調(diào)用阻塞

當(dāng)M執(zhí)行某一個(gè)G時(shí)候如果發(fā)生了阻塞操作,M會(huì)阻塞,如果當(dāng)前有一些G在執(zhí)行,調(diào)度器會(huì)把這個(gè)線程M從P中摘除,然后再創(chuàng)建一個(gè)新的操作系統(tǒng)的線程(如果有空閑的線程可用就復(fù)用空閑線程)來服務(wù)于這個(gè)P。當(dāng)M系統(tǒng)調(diào)用結(jié)束時(shí)候,這個(gè)G會(huì)嘗試獲取一個(gè)空閑的P執(zhí)行,并放入到這個(gè)P的本地隊(duì)列。如果獲取不到P,那么這個(gè)線程M變成休眠狀態(tài), 加入到空閑線程中,然后這個(gè)G會(huì)被放入全局隊(duì)列中。

隊(duì)列輪轉(zhuǎn)

可見每個(gè)P維護(hù)著一個(gè)包含G的隊(duì)列,不考慮G進(jìn)入系統(tǒng)調(diào)用或IO操作的情況下,P周期性的將G調(diào)度到M中執(zhí)行,執(zhí)行一小段時(shí)間,將上下文保存下來,然后將G放到隊(duì)列尾部,然后從隊(duì)列中重新取出一個(gè)G進(jìn)行調(diào)度。

除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。

除了每個(gè)P維護(hù)的G隊(duì)列以外,還有一個(gè)全局的隊(duì)列,每個(gè)P會(huì)周期性地查看全局隊(duì)列中是否有G待運(yùn)行并將其調(diào)度到M中執(zhí)行,全局隊(duì)列中G的來源,主要有從系統(tǒng)調(diào)用中恢復(fù)的G。之所以P會(huì)周期性地查看全局隊(duì)列,也是為了防止全局隊(duì)列中的G被餓死。

M0

M0是啟動(dòng)程序后的編號(hào)為0的主線程,這個(gè)M對(duì)應(yīng)的實(shí)例會(huì)在全局變量rutime.m0中,不需要在heap上分配,M0負(fù)責(zé)執(zhí)行初始化操作和啟動(dòng)第一個(gè)G,在之后M0就和其他的M一樣了

G0

G0是每次啟動(dòng)一個(gè)M都會(huì)第一個(gè)創(chuàng)建的goroutine,G0僅用于負(fù)責(zé)調(diào)度G,G0不指向任何可執(zhí)行的函數(shù),每個(gè)M都會(huì)有一個(gè)自己的G0,在調(diào)度或系統(tǒng)調(diào)用時(shí)會(huì)使用G0的棧空間,全局變量的G0是M0的G0

一個(gè)G由于調(diào)度被中斷,此后如何恢復(fù)?

中斷的時(shí)候?qū)⒓拇嫫骼锏臈P畔?,保存到自己的G對(duì)象里面。當(dāng)再次輪到自己執(zhí)行時(shí),將自己保存的棧信息復(fù)制到寄存器里面,這樣就接著上次之后運(yùn)行了。

我這里只是根據(jù)自己的理解進(jìn)行了簡單的介紹,想要詳細(xì)了解有關(guān)GMP的底層原理可以去看Go調(diào)度器 G-P-M 模型的設(shè)計(jì)者的文檔或直接看源碼

參考: ()

()

go語言實(shí)現(xiàn)一個(gè)簡單的簡單網(wǎng)關(guān)

網(wǎng)關(guān)=反向代理+負(fù)載均衡+各種策略,技術(shù)實(shí)現(xiàn)也有多種多樣,有基于 nginx 使用 lua 的實(shí)現(xiàn),比如 openresty、kong;也有基于 zuul 的通用網(wǎng)關(guān);還有就是 golang 的網(wǎng)關(guān),比如 tyk。

這篇文章主要是講如何基于 golang 實(shí)現(xiàn)一個(gè)簡單的網(wǎng)關(guān)。

轉(zhuǎn)自: troy.wang/docs/golang/posts/golang-gateway/

整理:go語言鐘文文檔:

啟動(dòng)兩個(gè)后端 web 服務(wù)(代碼)

這里使用命令行工具進(jìn)行測(cè)試

具體代碼

直接使用基礎(chǔ)庫 httputil 提供的NewSingleHostReverseProxy即可,返回的reverseProxy對(duì)象實(shí)現(xiàn)了serveHttp方法,因此可以直接作為 handler。

具體代碼

director中定義回調(diào)函數(shù),入?yún)?http.Request,決定如何構(gòu)造向后端的請(qǐng)求,比如 host 是否向后傳遞,是否進(jìn)行 url 重寫,對(duì)于 header 的處理,后端 target 的選擇等,都可以在這里完成。

director在這里具體做了:

modifyResponse中定義回調(diào)函數(shù),入?yún)?http.Response,用于修改響應(yīng)的信息,比如響應(yīng)的 Body,響應(yīng)的 Header 等信息。

最終依舊是返回一個(gè)ReverseProxy,然后將這個(gè)對(duì)象作為 handler 傳入即可。

參考 2.2 中的NewSingleHostReverseProxy,只需要實(shí)現(xiàn)一個(gè)類似的、支持多 targets 的方法即可,具體實(shí)現(xiàn)見后面。

作為一個(gè)網(wǎng)關(guān)服務(wù),在上面 2.3 的基礎(chǔ)上,需要支持必要的負(fù)載均衡策略,比如:

隨便 random 一個(gè)整數(shù)作為索引,然后取對(duì)應(yīng)的地址即可,實(shí)現(xiàn)比較簡單。

具體代碼

使用curIndex進(jìn)行累加計(jì)數(shù),一旦超過 rss 數(shù)組的長度,則重置。

具體代碼

輪詢帶權(quán)重,如果使用計(jì)數(shù)遞減的方式,如果權(quán)重是5,1,1那么后端 rs 依次為a,a,a,a,a,b,c,a,a,a,a…,其中 a 后端會(huì)瞬間壓力過大;參考 nginx 內(nèi)部的加權(quán)輪詢,或者應(yīng)該稱之為平滑加權(quán)輪詢,思路是:

后端真實(shí)節(jié)點(diǎn)包含三個(gè)權(quán)重:

操作步驟:

具體代碼

一致性 hash 算法,主要是用于分布式 cache 熱點(diǎn)/命中問題;這里用于基于某 key 的 hash 值,路由到固定后端,但是只能是基本滿足流量綁定,一旦后端目標(biāo)節(jié)點(diǎn)故障,會(huì)自動(dòng)平移到環(huán)上最近的那么個(gè)節(jié)點(diǎn)。

實(shí)現(xiàn):

具體代碼

每一種不同的負(fù)載均衡算法,只需要實(shí)現(xiàn)添加以及獲取的接口即可。

然后使用工廠方法,根據(jù)傳入的參數(shù),決定使用哪種負(fù)載均衡策略。

具體代碼

作為網(wǎng)關(guān),中間件必不可少,這類包括請(qǐng)求響應(yīng)的模式,一般稱作洋蔥模式,每一層都是中間件,一層層進(jìn)去,然后一層層出來。

中間件的實(shí)現(xiàn)一般有兩種,一種是使用數(shù)組,然后配合 index 計(jì)數(shù);一種是鏈?zhǔn)秸{(diào)用。

具體代碼

分享標(biāo)題:go語言規(guī)范文檔,go語言代碼規(guī)范
瀏覽地址:http://muchs.cn/article26/phgscg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、全網(wǎng)營銷推廣、定制開發(fā)網(wǎng)站收錄、ChatGPT、網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)