Go 語言初步

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

這幾天認真玩起了 Go。所謂認真玩,就是拿 Go 寫點程式,前後大約兩千行吧。

據說 Go 的最佳開發平台是 Mac OS ,我沒有。其次應該是 Linux 。Windows 版還沒全部搞定,但是也可以用了。如果你用 google 搜尋,很容易去到一個叫 go-windows 的開源項目上。千萬別上當,這是個廢棄的項目。如果你用這個,很多庫都沒有,而且文法也是老的。我在 Windows 下甚至不能正確連結自己寫的多個 package 。活躍的 Windows 版是 gomingw ,對於 Windows 使用者,裝一個 mingw32 以後就可以開始玩了。

就三天來實戰經曆,我喜歡上這門新語言有如下原因:

mix-in 的介面風格。非常接近於我在用 C 時慣用的物件導向風格。有文法上的支援要舒服多了。以平坦的方式編寫函數,沒有層次。而後用 interface 把需要的功能彙總在一起。沒有繼承層次,只有組合功能。

強型別系統。使得犯錯誤的機會大大降低。正確通過編譯,幾乎就沒有什麼 bug 了。而編寫程式又有點使用 lua 這種動態語言的感覺,總之,寫起來很舒服。

內建的 string / slice 類型,以及 gc 。這是我覺得現代編程必須的東西。手工管理未必有更高的效率,但一定有更多的出錯機會。至少,我一直主張有一個方便的 string 不變數的基本類型的(參見這一篇)。

defer 是個有趣使用的東西,用它來實現 RAII 比 C++ 利用棧上對象的解構函式的 trick 方案讓人塌實多了。go 在語言設計上是很吝嗇新的關鍵字的。但多出一個關鍵字 defer ,並用內建函數 panic / recover 來解決許多看似應該用 exception 解決的問題要漂亮的多。

zero 初始化。我一直覺得 C++ 的建構函式特別多餘。按我用 C 的慣例,一切資料結構都應該用 0 初始化。所以 C 裡有 calloc 這個函數。go 把這點貫徹了。不會再有未定義的資料。

包系統特別的好。而且嚴格定義了包的初始化過程,即 init 函數。在我自己的 C 語言構建的項目中,實現了幾乎一樣的機制,甚至也叫 init 。但是有語言層面的支援就是好。對,只有 init 沒有 exit 。正合我意。

goroutine 是個相當有用的設計。8 年前,我給 C 實現了 coroutine 庫,並用在項目裡,並堅信,程式就應該這麼寫。但是沒有語言級的支援,用起來還是很麻煩。goroutine 不僅簡化了許多商務邏輯的編寫,而且天生就是為並發編程而生的。select/chan 可能是唯一正確的並發編程的模型。Erlang 還是太小眾了,而 Go 可以延用 Erlang 的模型,卻有著純正的 C 語言血統,我想會被更多人接受的。雖然 Go 依然可以用共用狀態加鎖的方案,但不推薦使用。chan 用習慣了,還是相當方便的。

{ 要不要獨立佔一行的信仰之爭終於結束了。還記得前段時間有位同學來 email 指責我開源的代碼沒有章法。程式寫的太亂。他的理由就是,我的 { 都沒有獨佔一行。好了,爭論可以結束了。在 Go 裡,如果你把 { 從 if/for 語言的行末去掉,放在下一行。編譯器是不會讓你通過的。(除非你再加一個 ; )我很欣慰 ;)

我發現我花了四年時間錘鍊自己用 C 語言構建系統的能力,試圖找到一個規範,可以更好的編寫軟體。結果發現只是對 Go 的模仿。缺乏語言層面的支援,只能是一個拙劣的模仿。

對於有 C 基礎的同學,比如我,學習 Go 毫不費力。按這篇文章的指引即可。Rob Pike 的三日教程 PPT ,我心急,用了一個下午就看完了,並且做完了練習。

不過實戰編寫程式還是需要反覆查閱文檔的。學習一門新語言,就是在學習它的各種慣用法和庫。而不是去類比熟悉的語言。我在編寫代碼的時候,時刻問自己,在 Go 裡,通常用什麼手法來處理這個問題。接下來就是不斷的查詢文檔了。從這個意義上講,學習新東西還是很累的。好在 Go 的各種設計都非常切合我的本意,所以自然是越寫越舒服了。

至於把變數類型申明都放在後面,按 Sean 同學的話說,有種真氣逆行的感覺。對我來說倒是小問題,幾個小時就習慣了。反而 C 語言那種亦前亦後的方式才是奇怪呢。

說一下我的練手項目。我用 Go 重新實現了處理多已連線的服務器。當然,現在的設計方案和幾年前寫 blog 時的方案有略微的不同。

需求是這樣的:

這個服務會監停一個連接埠,允許外部多個串連的接入,並可以把這個串連上的資料包匯總發到後端的一個串連上。簡單的說,就是一個 N 對 1 的資料處理器。把 N 個 TCP 資料流合成一個資料流。

一個服務的處理上限是 64K 的串連,使用 2 位元組的 id 號區分不同的外部串連。我定義了簡單的協議,每個資料片段有 3 位元組的資料頭。分別是資料長度一位元組和 2 位元組的串連 id 號。

這個服務僅僅做資料流的合并,而不規定資料邏輯上的分包。對內的資料管道上看起來的資料流就是這樣的:

len id_lo id_hi content ... len id_lo id_hi content ...  len id_lo id_hi content ... 

處理合并起來的資料流非常簡單,只需要通過一個 IO 管道 (可以是 socket 也可以的 stdio/stdout ,對於 Go 來說,甚至可以是一個 in-memory Pipe )這方便後端的程式不再考慮多串連的問題。

後端服務需要可以控制串連伺服器。最基本的功能就是可以強制斷開某個外部串連。並且可以獲得新的外部串連接入或離開的訊號。

更進一步,應該由後端伺服器來控制串連伺服器對外監聽連接埠的開啟與關閉,以及外部串連的上限等。

為了簡化設計,我選擇在一個特殊的內部串連(0號串連)上收發內部的控制指令。並且使用 \r\n 分割的文本協議。

用 Go 來實現這個服務非常簡潔。全部我只使用了 240 行左右的 Go 代碼。所有的網路連接都使用獨立的 goroutine 來控制。每個都以阻塞方式處理 socket 。主迴圈僅僅使用一個 select ,這類似 Erlang 的事件驅動模型。

對於控制指令流,建立一個 in-memory Pipe 即可。在對內的資料流上,過濾到 id 為 0 的資料包,轉寄到這個 Pipe 上。使用 bufio 把 Pipe 轉換成一個 bufio.Reader 介面,就可以方便的使用 ReadString 方法去讀以斷行符號分割的文本行,進而排發到解析指令的 goroutine 中,把結構化指令利用 chan 發進訊息 select 迴圈。整個只需要不到 10 行代碼。

大多數 goroutine 內部都是一個 for 迴圈,結束條件是和它通訊的 chan 被外部關閉。

需要稍微考慮效能的地方是給外部串連的資料包加上三位元組的頭轉寄到內部通道上。這裡如果每個包都用 make 建立一個新的 array slice 會有一些內部管理上的開銷。我的做法是每次申請 16k 的 array ,再建立一個 slice 去組包,如果這個 16k 的 array 沒用完,就會順著用下去。如 Go 的教程中所言,slice 的建立是很廉價的,想來也是如此。它只是對 array 的部分引用。

Go 的引用和值分得很清楚,這使它更像 C 而不是 Java ,卻又提供了 C++ 提供不了的安全性。

用 Go 寫網路程式,真是非常舒心。socket 和 file 在 interface 上的統一,暗合 Unix 之道。程式嘛,就是處理輸入,產生輸出。Reader 和 Writer 介面讓人愉快。


原文出自:http://blog.codingnow.com/2010/11/go_prime.html


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.