智能合約調(diào)用是實(shí)現(xiàn)一個(gè) DApp 的關(guān)鍵,一個(gè)完整的 DApp 包括前端、后端、智能合約及區(qū)塊 鏈系統(tǒng),智能合約的調(diào)用是連接區(qū)塊鏈與前后端的關(guān)鍵。
網(wǎng)站制作、建網(wǎng)站找專(zhuān)業(yè)網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站:定制網(wǎng)站、模板網(wǎng)站、仿站、重慶小程序開(kāi)發(fā)公司、軟件開(kāi)發(fā)、重慶APP開(kāi)發(fā)公司等。做網(wǎng)站價(jià)格咨詢(xún)創(chuàng)新互聯(lián)建站:服務(wù)完善、10余年建站、值得信賴(lài)!網(wǎng)站制作電話(huà):18980820575
我們先來(lái)了解一下智能合約調(diào)用的基礎(chǔ)原理。智能合約運(yùn)行在以太坊節(jié)點(diǎn)的 EVM 中。因此要 想調(diào)用合約必須要訪(fǎng)問(wèn)某個(gè)節(jié)點(diǎn)。
以后端程序?yàn)槔蠖朔?wù)若想連接節(jié)點(diǎn)有兩種可能,一種是雙 方在同一主機(jī),此時(shí)后端連接節(jié)點(diǎn)可以采用 本地 IPC(Inter-Process Communication,進(jìn) 程間通信)機(jī)制,也可以采用 RPC(Remote Procedure Call,遠(yuǎn)程過(guò)程調(diào)用)機(jī)制;另 一種情況是雙方不在同一臺(tái)主機(jī),此時(shí)只能采用 RPC 機(jī)制進(jìn)行通信。
提到 RPC, 讀者應(yīng)該對(duì) Geth 啟動(dòng)參數(shù)有點(diǎn)印象,Geth 啟動(dòng)時(shí)可以選擇開(kāi)啟 RPC 服務(wù),對(duì)應(yīng)的 默認(rèn)服務(wù)端口是 8545。。
接著,我們來(lái)了解一下智能合約運(yùn)行的過(guò)程。
智能合約的運(yùn)行過(guò)程是后端服務(wù)連接某節(jié)點(diǎn),將 智能合約的調(diào)用(交易)發(fā)送給節(jié)點(diǎn),節(jié)點(diǎn)在驗(yàn)證了交易的合法性后進(jìn)行全網(wǎng)廣播,被礦工打包到 區(qū)塊中代表此交易得到確認(rèn),至此交易才算完成。
就像數(shù)據(jù)庫(kù)一樣,每個(gè)區(qū)塊鏈平臺(tái)都會(huì)提供主流 開(kāi)發(fā)語(yǔ)言的 SDK(Software Development Kit,軟件開(kāi)發(fā)工具包),由于 Geth 本身就是用 Go 語(yǔ)言 編寫(xiě)的,因此若想使用 Go 語(yǔ)言連接節(jié)點(diǎn)、發(fā)交易,直接在工程內(nèi)導(dǎo)入 go-ethereum(Geth 源碼) 包就可以了,剩下的問(wèn)題就是流程和 API 的事情了。
總結(jié)一下,智能合約被調(diào)用的兩個(gè)關(guān)鍵點(diǎn)是節(jié)點(diǎn)和 SDK。
由于 IPC 要求后端與節(jié)點(diǎn)必須在同一主機(jī),所以很多時(shí)候開(kāi)發(fā)者都會(huì)采用 RPC 模式。除了 RPC,以太坊也為開(kāi)發(fā)者提供了 json- rpc 接口,本文就不展開(kāi)討論了。
接下來(lái)介紹如何使用 Go 語(yǔ)言,借助 go-ethereum 源碼庫(kù)來(lái)實(shí)現(xiàn)智能合約的調(diào)用。這是有固定 步驟的,我們先來(lái)說(shuō)一下總體步驟,以下面的合約為例。
步驟 01:編譯合約,獲取合約 ABI(Application Binary Interface,應(yīng)用二進(jìn)制接口)。 單擊【ABI】按鈕拷貝合約 ABI 信息,將其粘貼到文件 calldemo.abi 中(可使用 Go 語(yǔ)言IDE 創(chuàng)建該文件,文件名可自定義,后綴最好使用 abi)。
最好能將 calldemo.abi 單獨(dú)保存在一個(gè)目錄下,輸入“l(fā)s”命令只能看到 calldemo.abi 文件,參 考效果如下:
步驟 02:獲得合約地址。注意要將合約部署到 Geth 節(jié)點(diǎn)。因此 Environment 選擇為 Web3 Provider。
在【Environment】選項(xiàng)框中選擇“Web3 Provider”,然后單擊【Deploy】按鈕。
部署后,獲得合約地址為:0xa09209c28AEf59a4653b905792a9a910E78E7407。
步驟 03:利用 abigen 工具(Geth 工具包內(nèi)的可執(zhí)行程序)編譯智能合約為 Go 代碼。abigen 工具的作用是將 abi 文件轉(zhuǎn)換為 Go 代碼,命令如下:
其中各參數(shù)的含義如下。 (1)abi:是指定傳入的 abi 文件。 (2)type:是指定輸出文件中的基本結(jié)構(gòu)類(lèi)型。 (3)pkg:指定輸出文件 package 名稱(chēng)。 (4)out:指定輸出文件名。 執(zhí)行后,將在代碼目錄下看到 funcdemo.go 文件,讀者可以打開(kāi)該文件欣賞一下,注意不要修改它。
步驟 04:創(chuàng)建 main.go,填入如下代碼。 注意代碼中 HexToAddress 函數(shù)內(nèi)要傳入該合約部署后的地址,此地址在步驟 01 中獲得。
步驟 04:設(shè)置 go mod,以便工程自動(dòng)識(shí)別。
前面有所提及,若要使用 Go 語(yǔ)言調(diào)用智能合約,需要下載 go-ethereum 工程,可以使用下面 的指令:
該指令會(huì)自動(dòng)將 go-ethereum 下載到“$GOPATH/src/github.com/ethereum/go-ethereum”,這樣還算 不錯(cuò)。不過(guò),Go 語(yǔ)言自 1.11 版本后,增加了 module 管理工程的模式。只要設(shè)置好了 go mod,下載 依賴(lài)工程的事情就不必關(guān)心了。
接下來(lái)設(shè)置 module 生效和 GOPROXY,命令如下:
在項(xiàng)目工程內(nèi),執(zhí)行初始化,calldemo 可以自定義名稱(chēng)。
步驟 05:運(yùn)行代碼。執(zhí)行代碼,將看到下面的效果,以及最終輸出的 2020。
上述輸出信息中,可以看到 Go 語(yǔ)言會(huì)自動(dòng)下載依賴(lài)文件,這就是 go mod 的神奇之處。看到 2020,相信讀者也知道運(yùn)行結(jié)果是正確的了。
本文是對(duì) Gopher 2017 中一個(gè)非常好的 Talk?: [Understanding Channel](GopherCon 2017: Kavya Joshi - Understanding Channels) 的學(xué)習(xí)筆記,希望能夠通過(guò)對(duì) channel 的關(guān)鍵特性的理解,進(jìn)一步掌握其用法細(xì)節(jié)以及 Golang 語(yǔ)言設(shè)計(jì)哲學(xué)的管窺蠡測(cè)。
channel 是可以讓一個(gè) goroutine 發(fā)送特定值到另一個(gè) gouroutine 的通信機(jī)制。
原生的 channel 是沒(méi)有緩存的(unbuffered channel),可以用于 goroutine 之間實(shí)現(xiàn)同步。
關(guān)閉后不能再寫(xiě)入,可以讀取直到 channel 中再?zèng)]有數(shù)據(jù),并返回元素類(lèi)型的零值。
gopl/ch3/netcat3
首先從 channel 是怎么被創(chuàng)建的開(kāi)始:
在 heap 上分配一個(gè) hchan 類(lèi)型的對(duì)象,并將其初始化,然后返回一個(gè)指向這個(gè) hchan 對(duì)象的指針。
理解了 channel 的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),現(xiàn)在轉(zhuǎn)到 channel 的兩個(gè)最基本方法: sends 和 receivces ,看一下以上的特性是如何體現(xiàn)在 sends 和 receives 中的:
假設(shè)發(fā)送方先啟動(dòng),執(zhí)行 ch - task0 :
如此為 channel 帶來(lái)了 goroutine-safe 的特性。
在這樣的模型里, sender goroutine - channel - receiver goroutine 之間, hchan 是唯一的共享內(nèi)存,而這個(gè)唯一的共享內(nèi)存又通過(guò) mutex 來(lái)確保 goroutine-safe ,所有在隊(duì)列中的內(nèi)容都只是副本。
這便是著名的 golang 并發(fā)原則的體現(xiàn):
發(fā)送方 goroutine 會(huì)阻塞,暫停,并在收到 receive 后才恢復(fù)。
goroutine 是一種 用戶(hù)態(tài)線(xiàn)程 , 由 Go runtime 創(chuàng)建并管理,而不是操作系統(tǒng),比起操作系統(tǒng)線(xiàn)程來(lái)說(shuō),goroutine更加輕量。
Go runtime scheduler 負(fù)責(zé)將 goroutine 調(diào)度到操作系統(tǒng)線(xiàn)程上。
runtime scheduler 怎么將 goroutine 調(diào)度到操作系統(tǒng)線(xiàn)程上?
當(dāng)阻塞發(fā)生時(shí),一次 goroutine 上下文切換的全過(guò)程:
然而,被阻塞的 goroutine 怎么恢復(fù)過(guò)來(lái)?
阻塞發(fā)生時(shí),調(diào)用 runtime sheduler 執(zhí)行 gopark 之前,G1 會(huì)創(chuàng)建一個(gè) sudog ,并將它存放在 hchan 的 sendq 中。 sudog 中便記錄了即將被阻塞的 goroutine G1 ,以及它要發(fā)送的數(shù)據(jù)元素 task4 等等。
接收方 將通過(guò)這個(gè) sudog 來(lái)恢復(fù) G1
接收方 G2 接收數(shù)據(jù), 并發(fā)出一個(gè) receivce ,將 G1 置為 runnable :
同樣的, 接收方 G2 會(huì)被阻塞,G2 會(huì)創(chuàng)建 sudoq ,存放在 recvq ,基本過(guò)程和發(fā)送方阻塞一樣。
不同的是,發(fā)送方 G1如何恢復(fù)接收方 G2,這是一個(gè)非常神奇的實(shí)現(xiàn)。
理論上可以將 task 入隊(duì),然后恢復(fù) G2, 但恢復(fù) G2后,G2會(huì)做什么呢?
G2會(huì)將隊(duì)列中的 task 復(fù)制出來(lái),放到自己的 memory 中,基于這個(gè)思路,G1在這個(gè)時(shí)候,直接將 task 寫(xiě)到 G2的 stack memory 中!
這是違反常規(guī)的操作,理論上 goroutine 之間的 stack 是相互獨(dú)立的,只有在運(yùn)行時(shí)可以執(zhí)行這樣的操作。
這么做純粹是出于性能優(yōu)化的考慮,原來(lái)的步驟是:
優(yōu)化后,相當(dāng)于減少了 G2 獲取鎖并且執(zhí)行 memcopy 的性能消耗。
channel 設(shè)計(jì)背后的思想可以理解為 simplicity 和 performance 之間權(quán)衡抉擇,具體如下:
queue with a lock prefered to lock-free implementation:
比起完全 lock-free 的實(shí)現(xiàn),使用鎖的隊(duì)列實(shí)現(xiàn)更簡(jiǎn)單,容易實(shí)現(xiàn)
———文章來(lái)源 YamiOdymel/PHP-to-Golang
PHP和模塊之間的關(guān)系令人感到煩躁,假設(shè)你要讀取 yaml 檔案,你需要有一個(gè) yaml 的模塊,為此,你還需要將其編譯然后將編譯后的模塊擺放至指定位置,之后換了一臺(tái)伺服器你還要重新編譯,這點(diǎn)到現(xiàn)在還是沒(méi)有改善;順帶一提之后出了PHP 7效能確實(shí)提升了許多(比Python 3快了些),但PHP仍令我感到臃腫,我覺(jué)得是時(shí)候
(轉(zhuǎn)行)了。
PHP 和Golang 的效能我想毋庸置疑是后者比較快(而且是以倍數(shù)來(lái)算),也許有的人會(huì)認(rèn)為兩種不應(yīng)該被放在一起比較,但Golang 本身就是偏向Web 開(kāi)發(fā)的,所以這也是為什么我考慮轉(zhuǎn)用Golang 的原因,起初我的考慮有幾個(gè):Node.js 和Rust 還有最終被選定的Golang;先談?wù)凬ode.js 吧。
Node.js的效能可以說(shuō)是快上PHP 3.5倍至6倍左右 ,而且撰寫(xiě)的語(yǔ)言還是JavaScript,蒸蚌,如此一來(lái)就不需要學(xué)習(xí)新語(yǔ)言了!搭配Babel更可以說(shuō)是萬(wàn)能,不過(guò)那跟「跳跳虎」一樣的Async邏輯還有那恐怖的Callback Hell,有人認(rèn)為前者是種優(yōu)點(diǎn),這點(diǎn)我不否認(rèn),但是對(duì)學(xué)習(xí)PHP的我來(lái)說(shuō)太過(guò)于"Mind Fuck",至于后者的Callback Hell雖然有Promise,但是那又是另一個(gè)「Then Hell」的故事了。相較于Golang之下,Node.js似乎就沒(méi)有那么吸引我了。你確實(shí)可以用Node.js寫(xiě)出很多東西,不過(guò)那V8引擎的效能仍然有限,而且要學(xué)習(xí)新的事物,不就應(yīng)該是「全新」的嗎;)?
題外話(huà): 為什么Node.js不適合大型和商業(yè)專(zhuān)案?
在拋棄改用Node.js 之后我曾經(jīng)花了一天的時(shí)間嘗試Rust 和Iron 框架,嗯??Rust 太強(qiáng)大了,強(qiáng)大到讓我覺(jué)得Rust 不應(yīng)該用在這里,這想法也許很蠢,但Rust 讓我覺(jué)得適合更應(yīng)該拿來(lái)用在系統(tǒng)或者是部分底層的地方,而不應(yīng)該是網(wǎng)路服務(wù)。
Golang是我最終的選擇,主要在于我花了一天的時(shí)間來(lái)研究的時(shí)候意外地發(fā)現(xiàn)Golang夭壽簡(jiǎn)潔( 關(guān)鍵字只有25個(gè) ),相較之下Rust太過(guò)于「強(qiáng)大」令我怯步;而且Golang帶有許多工具,例如 go fmt 會(huì)自動(dòng)幫你整理程式碼、 go doc 會(huì)自動(dòng)幫你生產(chǎn)文件、 go test 可以自動(dòng)單元測(cè)試并生產(chǎn)覆蓋率報(bào)表、也有 go get 套件管理工具(雖然沒(méi)有版本功能),不過(guò)都很實(shí)用,而且也不需要加上分號(hào)( ; ),真要說(shuō)不好的地方??大概就是強(qiáng)迫你花括號(hào)不能換行放吧(沒(méi)錯(cuò),我就是花括號(hào)會(huì)換行放的人)。
當(dāng)我在撰寫(xiě)這份文件的時(shí)候 我會(huì)先假設(shè)你有一定的基礎(chǔ) ,你可以先閱讀下列的手冊(cè),他們都很不錯(cuò)。
你能夠在PHP 里面想建立一個(gè)變數(shù)的時(shí)候就直接建立,夭壽贊,是嗎?
蒸蚌!那么Golang 呢?在Golang 中變數(shù)分為幾類(lèi):「新定義」、「預(yù)先定義」、「自動(dòng)新定義」、「覆蓋」。讓我們來(lái)看看范例:
在PHP中你會(huì)很常用到 echo 來(lái)顯示文字,像這樣。
然而在Golang中你會(huì)需要 fmt 套件,關(guān)于「什么是套件」的說(shuō)明你可以在文章下述了解。
這很簡(jiǎn)單,而且兩個(gè)語(yǔ)言的用法相差甚少,下面這是PHP:
只是Golang 稍微聒噪了一點(diǎn),你必須在函式后面宣告他最后會(huì)回傳什么資料型別。
在PHP 中你要回傳多個(gè)資料你就會(huì)用上陣列,然后將資料放入陣列里面,像這樣。
然而在Golang 中你可以不必用到一個(gè)陣列,函式可以一次回傳多個(gè)值:
兩個(gè)語(yǔ)言的撰寫(xiě)方式不盡相同。
主要是PHP 的陣列能做太多事情了,所以在PHP 里面要儲(chǔ)存什么用陣列就好了。
在Golang里??沒(méi)有這么萬(wàn)能的東西,首先要先了解Golang中有這些型態(tài): array , slice , map , interface ,
你他媽的我到底看了三洨,首先你要知道Golang是個(gè)強(qiáng)型別語(yǔ)言,意思是你的陣列中 只能有一種型態(tài) ,什么意思?當(dāng)你決定這個(gè)陣列是用來(lái)擺放字串資料的時(shí)候,你就只能在里面放字串。沒(méi)有數(shù)值、沒(méi)有布林值,就像你沒(méi)有女朋友一樣。
先撇開(kāi)PHP 的「萬(wàn)能陣列」不管,Golang 中的陣列既單純卻又十分腦殘,在定義一個(gè)陣列的時(shí)候,你必須給他一個(gè)長(zhǎng)度還有其內(nèi)容存放的資料型態(tài),你的陣列內(nèi)容不一定要填滿(mǎn)其長(zhǎng)度,但是你的陣列內(nèi)容不能超過(guò)你當(dāng)初定義的長(zhǎng)度。
切片??這聽(tīng)起來(lái)也許很奇怪,但是你確實(shí)可以「切」他,讓我們先談?wù)劇盖衅贡绕稹戈嚵小挂迷谀睦铮骸改悴挥枚x其最大長(zhǎng)度,而且你可以直接賦予值」,沒(méi)了。
我們剛才有提到你可以「切」他,記得嗎?這有點(diǎn)像是PHP中的 array_slice() ,但是Golang直接讓Slice「內(nèi)建」了這個(gè)用法,其用法是: slice[開(kāi)始:結(jié)束] 。
在PHP中倒是沒(méi)有那么方便,在下列PHP范例中你需要不斷地使用 array_slice() 。
你可以把「映照」看成是一個(gè)有鍵名和鍵值的陣列,但是記住:「你需要事先定義其鍵名、鍵值的資料型態(tài)」,這仍限制你沒(méi)辦法在映照中存放多種不同型態(tài)的資料。
在Golang里可就沒(méi)這么簡(jiǎn)單了,你需要先用 make() 宣告 map 。
也許你不喜歡「接口」這個(gè)詞,但用「介面」我怕會(huì)誤導(dǎo)大眾,所以,是的,接下來(lái)我會(huì)繼續(xù)稱(chēng)其為「接口」。還記得你可以在PHP 的關(guān)聯(lián)陣列里面存放任何型態(tài)的資料嗎,像下面這樣?
現(xiàn)在你有福了!正因?yàn)镚olang中的 interface{} 可以接受任何內(nèi)容,所以你可以把它拿來(lái)存放任何型態(tài)的資料。
有時(shí)候你也許會(huì)有個(gè)不定值的變數(shù),在PHP 里你可以直接將一個(gè)變數(shù)定義成字串、數(shù)值、空值、就像你那變心的女友一樣隨時(shí)都在變。
在Golang中你必須給予變數(shù)一個(gè)指定的資料型別,不過(guò)還記得剛才提到的:「Golang中有個(gè) interface{} 能夠 存放任何事物 」嗎( 雖然也不是真的任何事物啦?? )?
當(dāng)我們程式中不需要繼續(xù)使用到某個(gè)資源或是發(fā)生錯(cuò)誤的時(shí)候,我們索性會(huì)將其關(guān)閉或是拋棄來(lái)節(jié)省資源開(kāi)銷(xiāo),例如PHP 里的讀取檔案:
在Golang中,你可以使用 defer 來(lái)在函式結(jié)束的時(shí)候自動(dòng)執(zhí)行某些程式(其執(zhí)行方向?yàn)榉聪?。所以你就不需要在函式最后面結(jié)束最前面的資源。
defer 可以被稱(chēng)為「推遲執(zhí)行」,實(shí)際上就是在函式結(jié)束后會(huì)「反序」執(zhí)行的東西,例如你按照了這樣的順序定義 defer : A-B-C-D ,那么執(zhí)行的順序其實(shí)會(huì)是 D-C-B-A ,這用在程式結(jié)束時(shí)還蠻有用的,讓我們看看Golang如何改善上述范例。
這東西很邪惡,不是嗎?又不是在寫(xiě)B(tài)ASIC,不過(guò)也許有時(shí)候你會(huì)在PHP 用上呢。但是拜托,不要。
Golang中僅有 for 一種回圈但卻能夠達(dá)成 foreach 、 while 、 for 多種用法。普通 for 回圈寫(xiě)法在兩個(gè)語(yǔ)言中都十分相近。
在Golang請(qǐng)記得:如果你的 i 先前并不存在,那么你就需要定義它,所以下面這個(gè)范例你會(huì)看見(jiàn) i := 0 。
在PHP里, foreach() 能夠直接給你值和鍵名,用起來(lái)十分簡(jiǎn)單。
Golang里面雖然僅有 for() 但卻可以使用 range 達(dá)成和PHP一樣的 foreach 方式。
一個(gè) while(條件) 回圈在PHP里面可以不斷地執(zhí)行區(qū)塊中的程式,直到 條件 為 false 為止。
在Golang里也有相同的做法,但仍是透過(guò) for 回圈,請(qǐng)注意這個(gè) for 回圈并沒(méi)有任何的分號(hào)( ; ),而且一個(gè)沒(méi)有條件的 for 回圈會(huì)一直被執(zhí)行。
PHP中有 do .. while() 回圈可以先做區(qū)塊中的動(dòng)作。
在Golang中則沒(méi)有相關(guān)函式,但是你可以透過(guò)一個(gè)無(wú)止盡的 for 回圈加上條件式來(lái)讓他結(jié)束回圈。
要是你真的希望完全符合像是PHP那樣的設(shè)計(jì)方式,或者你可以在Golang中使用很邪惡的 goto 。
在PHP中我們可以透過(guò) date() 像這樣取得目前的日期。
在Golang就稍微有趣點(diǎn)了,因?yàn)镚olang中并不是以 Y-m-d 這種格式做為定義,而是 1 、 2 、 3 ,這令你需要去翻閱文件,才能夠知道 1 的定義是代表什么。
俗話(huà)說(shuō):「爆炸就是藝術(shù)」,可愛(ài)的PHP用詞真的很大膽,像是: explode() (爆炸)、 die() (死掉),回歸正傳,如果你想在PHP里面將字串切割成陣列,你可以這么做。
簡(jiǎn)單的就讓一個(gè)字串給「爆炸」了,那么Golang 呢?
對(duì)了,記得引用 strings 套件。
這真的是很常用到的功能,就像物件一樣有著鍵名和鍵值,在PHP 里面你很簡(jiǎn)單的就能靠陣列(Array)辦到。
真是太棒了,那么Golang呢?用 map 是差不多啦。如果有必要的話(huà),你可以稍微復(fù)習(xí)一下先前提到的「多資料儲(chǔ)存型態(tài)-Stores」。
你很常會(huì)在PHP里面用 isset() 檢查一個(gè)索引是否存在,不是嗎?
在Golang里面很簡(jiǎn)單的能夠這樣辦到(僅適用于 map )。
指針(有時(shí)也做參照)是一個(gè)像是「變數(shù)別名」的方法,這種方法讓你不用整天覆蓋舊的變數(shù),讓我們假設(shè) A = 1; B = A; 這個(gè)時(shí)候 B 會(huì)復(fù)制一份 A 且兩者不相干,倘若你希望修改 B 的時(shí)候?qū)嶋H上也會(huì)修改到 A 的值,就會(huì)需要指針。
指針比起復(fù)制一個(gè)變數(shù),他會(huì)建立一個(gè)指向到某個(gè)變數(shù)的記憶體位置,這也就是為什么你改變指針,實(shí)際上是在改變某個(gè)變數(shù)。
在Golang你需要用上 * 還有 符號(hào)。
有些時(shí)候你會(huì)回傳一個(gè)陣列,這個(gè)陣列里面可能有資料還有錯(cuò)誤代號(hào),而你會(huì)用條件式判斷錯(cuò)誤代號(hào)是否非空值。
在Golang中函式可以一次回傳多個(gè)值。為此,你不需要真的回傳一個(gè)陣列,不過(guò)要注意的是你將會(huì)回傳一個(gè)屬于 error 資料型態(tài)的錯(cuò)誤,所以你需要引用 errors 套件來(lái)幫助你做這件事。
該注意的是Golang沒(méi)有 try .. catch ,因?yàn)?Golang推薦這種錯(cuò)誤處理方式 ,你應(yīng)該在每一次執(zhí)行可能會(huì)發(fā)生錯(cuò)誤的程式時(shí)就處理錯(cuò)誤,而非后來(lái)用 try 到處包覆你的程式。
在 if 條件式里宣告變數(shù)會(huì)讓你只能在 if 內(nèi)部使用這個(gè)變數(shù),而不會(huì)污染到全域范圍。
也許你在PHP中更常用的會(huì)是 try .. catch ,在大型商業(yè)邏輯時(shí)經(jīng)常看見(jiàn)如此地用法,實(shí)際上這種用法令人感到聒噪(因?yàn)槟銜?huì)需要一堆 try 區(qū)塊):
Golang中并沒(méi)有 try .. catch ,實(shí)際上Golang也 不鼓勵(lì)這種行為 (Golang推薦逐一處理錯(cuò)誤的方式),倘若你真想辦倒像是捕捉異常這樣的方式,你確實(shí)可以使用Golang中另類(lèi)處理錯(cuò)誤的方式(可以的話(huà)盡量避免使用這種方式): panic() , recover() , defer 。
你可以把 panic() 當(dāng)作是 throw (丟出錯(cuò)誤),而這跟PHP的 exit() 有87%像,一但你執(zhí)行了 panic() 你的程式就會(huì)宣告而終,但是別擔(dān)心,因?yàn)槌淌浇Y(jié)束的時(shí)候會(huì)呼叫 defer ,所以我們接下來(lái)要在 defer 停止 panic() 。
關(guān)于 defer 上述已經(jīng)有提到了,他是一個(gè)反向執(zhí)行的宣告,會(huì)在函式結(jié)束后被執(zhí)行,當(dāng)你呼叫了 panic() 結(jié)束程式的時(shí)候,也就會(huì)開(kāi)始執(zhí)行 defer ,所以我們要在 defer 內(nèi)使用 recover() 讓程式不再繼續(xù)進(jìn)行結(jié)束動(dòng)作,這就像是捕捉異常。
recover() 可以看作 catch (捕捉),我們要在 defer 里面用 recover() 解決 panic() ,如此一來(lái)程式就會(huì)回歸正常而不會(huì)被結(jié)束。
還記得在PHP里要引用一堆檔案的日子嗎?到處可見(jiàn)的 require() 或是 include() ?到了Golang這些都不見(jiàn)了,取而代之的是「套件(Package)」。現(xiàn)在讓我們來(lái)用PHP解釋一下。
這看起來(lái)很正常對(duì)吧?但假設(shè)你有一堆檔案,這馬上就成了 Include Hell ,讓我們看看Golang怎么透過(guò)「套件」解決這個(gè)問(wèn)題。
「 蛤???殺小??? 」你可能如此地說(shuō)道。是的, main.go 中除了引用 fmt 套件( 為了要輸出結(jié)果用的套件 )之外完全沒(méi)有引用到 a.go 。
「 蛤???殺小?????? 」你仿佛回到了幾秒鐘前的自己。
既然沒(méi)有引用其他檔案,為什么 main.go 可以輸出 foo 呢?注意到了嗎, 兩者都是屬于 main 套件 ,因此 他們共享同一個(gè)區(qū)域 ,所以接下來(lái)要介紹的是什么叫做「套件」。
套件是每一個(gè) .go 檔案都必須聲明在Golang原始碼中最開(kāi)端的東西,像下面這樣:
這意味著目前的檔案是屬于 main 套件( 你也可以依照你的喜好命名 ),那么要如何讓同個(gè)套件之間的函式溝通呢?
接著是Golang;注意!你不需要引用任何檔案,因?yàn)橄铝袃蓚€(gè)檔案同屬一個(gè)套件。
一個(gè)由「套件」所掌握的世界,比起PHP的 include() 和 require() 還要好太多了,對(duì)嗎?
在Golang 中沒(méi)有引用單獨(dú)檔案的方式,你必須匯入一整個(gè)套件,而且你要記住:「一定你匯入了,你就一定要使用它」,像下面這樣。
假如你不希望使用你匯入的套件,你只是為了要觸發(fā)那個(gè)套件的 main() 函式而引用的話(huà)??,那么你可以在前面加上一個(gè)底線(xiàn)( _ )。
如果你的套件出現(xiàn)了名稱(chēng)沖突,你可以在套件來(lái)源前面給他一個(gè)新的名稱(chēng)。
現(xiàn)在你知道可以匯入套件了,那么什么是「匯出」?同個(gè)套件內(nèi)的函式還有共享變數(shù)確實(shí)可以直接用,但那 并不表示可以給其他套件使用 ,其方法取決于 函式/變數(shù)的「開(kāi)頭大小寫(xiě)」 。
是的。 Golang依照一個(gè)函式/變數(shù)的開(kāi)頭大小寫(xiě)決定這個(gè)東西是否可供「匯出」 。
這用在區(qū)別函式的時(shí)候格外有用,因?yàn)樾?xiě)開(kāi)頭的任何事物都是不供匯出的,反之,大寫(xiě)開(kāi)頭的任何事物都是用來(lái)匯出供其他套件使用的。
一開(kāi)始可能會(huì)覺(jué)得這是什么奇異的規(guī)定,但寫(xiě)久之后,你就能發(fā)現(xiàn)比起JavaScript和Python以「底線(xiàn)為開(kāi)頭的命名方式」還要來(lái)得更好;比起成天宣告 public 、 private 、 protected 還要來(lái)得更快。
在Golang 中沒(méi)有類(lèi)別,但有所謂的「建構(gòu)體(Struct)」和「接口(Interface)」,這就能夠滿(mǎn)足幾乎所有的需求了,這也是為什么我認(rèn)為Golang 很簡(jiǎn)潔卻又很強(qiáng)大的原因。
讓我們先用PHP 建立一個(gè)類(lèi)別,然后看看Golang 怎么解決這個(gè)問(wèn)題。
雖然Golang沒(méi)有類(lèi)別,但是「建構(gòu)體(Struct)」就十分地堪用了,首先你要知道在Golang中「類(lèi)別」的成員還有方法都是在「類(lèi)別」外面所定義的,這跟PHP在類(lèi)別內(nèi)定義的方式有所不同,在Golang中還有一點(diǎn),那就是他們沒(méi)有 public 、 private 、 protected 的種類(lèi)。
在PHP中,當(dāng)有一個(gè)類(lèi)別被 new 的時(shí)候會(huì)自動(dòng)執(zhí)行該類(lèi)別內(nèi)的建構(gòu)子( __construct() ),通常你會(huì)用這個(gè)來(lái)初始化一些類(lèi)別內(nèi)部的值。
但是在Golang 里因?yàn)闆](méi)有類(lèi)別,也就沒(méi)有建構(gòu)子,不巧的是建構(gòu)體本身也不帶有建構(gòu)子的特性,這個(gè)時(shí)候你只能自己在外部建立一個(gè)建構(gòu)用函式。
讓我們假設(shè)你有兩個(gè)類(lèi)別,你會(huì)把其中一個(gè)類(lèi)別傳入到另一個(gè)類(lèi)別里面使用,廢話(huà)不多說(shuō)!先上個(gè)PHP 范例(為了簡(jiǎn)短篇幅我省去了換行)。
在Golang中你也有相同的用法,但是請(qǐng)記得:「 任何東西都是在「類(lèi)別」外完成建構(gòu)的 」。
在PHP 中沒(méi)有相關(guān)的范例,這部分會(huì)以剛才「嵌入」章節(jié)中的Golang 范例作為解說(shuō)對(duì)象。
你可以看見(jiàn)Golang在進(jìn)行 Foo 嵌入 Bar 的時(shí)候,會(huì)自動(dòng)將 Foo 的成員暴露在 Bar 底下,那么假設(shè)「雙方之間有相同的成員名稱(chēng)」呢?
這個(gè)時(shí)候被嵌入的成員就會(huì)被「遮蔽」,下面是個(gè)實(shí)際范例,還有你如何解決遮蔽問(wèn)題:
雖然都是呼叫同一個(gè)函式,但是這個(gè)函式可以針對(duì)不同的資料來(lái)源做出不同的舉動(dòng),這就是多形。你也能夠把這看作是:「訊息的意義由接收者定義,而不是傳送者」。
目前PHP 中沒(méi)有真正的「多形」,不過(guò)你仍可以做出同樣的東西。
嗯??那么Golang呢?實(shí)際上更簡(jiǎn)單而且更有條理了,在Golang中有 interface 可以幫忙完成這個(gè)工作。
如果你對(duì)Interface還不熟悉,可以試著查看「 解釋Golang中的Interface到底是什么 」文章。
謝謝你看到這里,可惜這篇文章卻沒(méi)有說(shuō)出Golang 最重要的賣(mài)點(diǎn):「Goroutine」和「Channel」
不知道你有沒(méi)有聽(tīng)過(guò)這么一句:在使用 map 時(shí)盡量不要在 big map 中保存指針。好吧,你現(xiàn)在已經(jīng)聽(tīng)過(guò)了:)為什么呢?原因在于 Go 語(yǔ)言的垃圾回收器會(huì)掃描標(biāo)記 map 中的所有元素,GC 開(kāi)銷(xiāo)相當(dāng)大,直接GG。
這兩天在《Mastering Go》中看到 GC 這一章節(jié)里面對(duì)比 map 和 slice 在垃圾回收中的效率對(duì)比,書(shū)中只給出結(jié)論沒(méi)有說(shuō)明理由,這我是不能忍的,于是有了這篇學(xué)習(xí)筆記。扯那么多,Show Your Code
這是一個(gè)簡(jiǎn)單的測(cè)試程序,保存字符串的 map 和 保存整形的 map GC 的效率相差幾十倍,是不是有同學(xué)會(huì)說(shuō)明明保存的是 string 哪有指針?這個(gè)要說(shuō)到 Go 語(yǔ)言中 string 的底層實(shí)現(xiàn)了,源碼在 src/runtime/string.go里,可以看到 string 其實(shí)包含一個(gè)指向數(shù)據(jù)的指針和一個(gè)長(zhǎng)度字段。注意這里的是否包含指針,包括底層的實(shí)現(xiàn)。
Go 語(yǔ)言的 GC 會(huì)遞歸遍歷并標(biāo)記所有可觸達(dá)的對(duì)象,標(biāo)記完成之后將所有沒(méi)有引用的對(duì)象進(jìn)行清理。掃描到指針就會(huì)往下接著尋找,一直到結(jié)束。
Go 語(yǔ)言中 map 是基于 數(shù)組和鏈表 的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,通過(guò) 優(yōu)化的拉鏈法 解決哈希沖突,每個(gè) bucket 可以保存 8 對(duì)鍵值,在 8 個(gè)鍵值對(duì)數(shù)據(jù)后面有一個(gè) overflow 指針,因?yàn)橥爸凶疃嘀荒苎b 8 個(gè)鍵值對(duì),如果有多余的鍵值對(duì)落到了當(dāng)前桶,那么就需要再構(gòu)建一個(gè)桶(稱(chēng)為溢出桶),通過(guò) overflow 指針鏈接起來(lái)。
因?yàn)?overflow 指針的緣故,所以無(wú)論 map 保存的是什么,GC 的時(shí)候就會(huì)把所有的 bmap 掃描一遍,帶來(lái)巨大的 GC 開(kāi)銷(xiāo)。官方 issues 就有關(guān)于這個(gè)問(wèn)題的討論, runtime: Large maps cause significant GC pauses #9477
無(wú)腦機(jī)翻如下:
如果我們有一個(gè)map [k] v,其中k和v都不包含指針,并且我們想提高掃描性能,則可以執(zhí)行以下操作。
將“ allOverflow [] unsafe.Pointer”添加到 hmap 并將所有溢出存儲(chǔ)桶存儲(chǔ)在其中。 然后將 bmap 標(biāo)記為noScan。 這將使掃描非常快,因?yàn)槲覀儾粫?huì)掃描任何用戶(hù)數(shù)據(jù)。
實(shí)際上,它將有些復(fù)雜,因?yàn)槲覀冃枰獜腶llOverflow中刪除舊的溢出桶。 而且它還會(huì)增加 hmap 的大小,因此也可能需要重新整理數(shù)據(jù)。
最終官方在 hmap 中增加了 overflow 相關(guān)字段完成了上面的優(yōu)化,這是具體的 commit 地址。
下面看下具體是如何實(shí)現(xiàn)的,源碼基于 go1.15,src/cmd/compile/internal/gc/reflect.go 中
通過(guò)注釋可以看出,如果 map 中保存的鍵值都不包含指針(通過(guò) Haspointers 判斷),就使用一個(gè) uintptr 類(lèi)型代替 bucket 的指針用于溢出桶 overflow 字段,uintptr 類(lèi)型在 GO 語(yǔ)言中就是個(gè)大小可以保存得下指針的整數(shù),不是指針,就相當(dāng)于實(shí)現(xiàn)了 將 bmap 標(biāo)記為 noScan, GC 的時(shí)候就不會(huì)遍歷完整個(gè) map 了。隨著不斷的學(xué)習(xí),愈發(fā)感慨 GO 語(yǔ)言中很多模塊設(shè)計(jì)得太精妙了。
差不多說(shuō)清楚了,能力有限,有不對(duì)的地方歡迎留言討論,源碼位置還是問(wèn)的群里大佬 _
上一節(jié)中,我們?yōu)槊總€(gè)連接都創(chuàng)建了一個(gè)goroutine來(lái)讀取其中的消息,現(xiàn)在我們將這個(gè)讀取消息的方法實(shí)現(xiàn)一下。
我們?cè)赼pplication目錄下新建controllers目錄,并在其中創(chuàng)建一個(gè)MessageController.go文件。
首先我們新建一個(gè)MessageController的結(jié)構(gòu)體,內(nèi)容如下
這個(gè)結(jié)構(gòu)體包括兩個(gè)內(nèi)容,一個(gè)是我們將連接放在數(shù)組之后,返回的索引,另一個(gè)是連接本身.
這個(gè)是具體的方法。
我們首先設(shè)置了一下讀消息的大小、超時(shí)時(shí)間以及超時(shí)后需要的操作。
超時(shí)時(shí)間如果設(shè)置為0,那么就是永不超時(shí)。之前在這里直接寫(xiě)0,被告知需要傳一個(gè)time.Time類(lèi)型的數(shù)據(jù)。最終谷歌后才得到了這個(gè)值time.Time{}為"0001-01-01 00:00:00 +0000 UTC"。
我們將用戶(hù)手法消息的內(nèi)容定義為一個(gè)結(jié)構(gòu)體,然后將用戶(hù)的訂閱信息的json通過(guò)json.unmarshal轉(zhuǎn)換成這個(gè)結(jié)構(gòu)體。
之后的switch操作與我們?cè)赟woole中的操作基本雷同,在查詢(xún)到login之后,調(diào)用service中 的login方法來(lái)進(jìn)行注冊(cè)。
下一節(jié)中我們?cè)俳榻B具體的注冊(cè)邏輯。
《Go語(yǔ)言學(xué)習(xí)筆記》(雨痕)電子書(shū)網(wǎng)盤(pán)下載免費(fèi)在線(xiàn)閱讀
鏈接:
提取碼:qyzq ?
書(shū)名:Go語(yǔ)言學(xué)習(xí)筆記
豆瓣評(píng)分:8.1
作者:?雨痕
出版社:?電子工業(yè)出版社
出品方:?博文視點(diǎn)
出版年:?2016-6
頁(yè)數(shù):?468
內(nèi)容簡(jiǎn)介
作為時(shí)下流行的一種系統(tǒng)編程語(yǔ)言,Go 簡(jiǎn)單易學(xué),性能很好,且支持各類(lèi)主流平臺(tái)。已有大量項(xiàng)目采用 Go 編寫(xiě),這其中就包括 Docker 等明星作品,其開(kāi)發(fā)和執(zhí)行效率早已被證明。本書(shū)經(jīng)四年多逐步完善,內(nèi)容覆蓋了語(yǔ)言、運(yùn)行時(shí)、性能優(yōu)化、工具鏈等各層面知識(shí)。且內(nèi)容經(jīng)大量讀者反饋和校對(duì),沒(méi)有明顯的缺陷和錯(cuò)誤。上卷細(xì)致解析了語(yǔ)言規(guī)范相關(guān)細(xì)節(jié),便于讀者深入理解語(yǔ)言相關(guān)功能的使用方法和注意事項(xiàng)。下卷則對(duì)運(yùn)行時(shí)源碼做出深度剖析,引導(dǎo)讀者透徹了解語(yǔ)言功能背后的支持環(huán)境和運(yùn)行體系,諸如內(nèi)存分配、垃圾回收和并發(fā)調(diào)度等。本書(shū)不適合編程初學(xué)入門(mén),可供有實(shí)際編程經(jīng)驗(yàn)或正在使用Go 工作的人群參考。
作者簡(jiǎn)介??
自 1996 年從事計(jì)算機(jī)軟件開(kāi)發(fā)工作以來(lái),已 20 春秋。期間供職于北大方正、西單電子商務(wù)、九城數(shù)碼、知乎等公司。主要從事核心開(kāi)發(fā)、架構(gòu)設(shè)計(jì),以及部分管理工作。
網(wǎng)站名稱(chēng):go語(yǔ)言筆記01 go語(yǔ)言筆記 mybase
鏈接地址:http://vcdvsql.cn/article42/hejdec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動(dòng)網(wǎng)站建設(shè)、App開(kāi)發(fā)、品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站維護(hù)、網(wǎng)站建設(shè)、虛擬主機(jī)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)