久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

PHP程序員為什么要學習GO語言

166次閱讀
沒有評論

共計 7873 個字符,預計需要花費 20 分鐘才能閱讀完成。

PHP 程序員為什么要學習 GO 語言,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

很多人將 GO 語言稱為 21 世紀的 C 語言,因為 GO 不僅擁有 C 的簡潔和性能,而且還很好的提供了 21 世紀互聯網環境下服務端開發的各種實用特性,讓開發者在語言級別就可以方便的得到自己想要的東西。

發展歷史

2007 年 9 月,Rob Pike 在 Google 分布式編譯平臺上進行 C ++ 編譯,在漫長的等待過程中,他和 Robert Griesemer 探討了程序設計語言的一些關鍵性問題,他們認為,簡化編程語言相比于在臃腫的語言上不斷增加新特性,會是更大的進步。隨后他們在編譯結束之前說服了身邊的 Ken Thompson,覺得有必要為此做一些事情。幾天后,他們發起了一個叫 Golang 的項目,將它作為自由時間的實驗項目。2008 年 5 月 Google 發現了 GO 語言的巨大潛力,得到了 Google 的全力支持,這些人開始全職投入 GO 語言的設計和開發。2009 年 11 月 GO 語言第一個版本發布。2012 年 3 月 第一個正式版本 Go1.0 發布。2015 年 8 月 go1.5 發布,這個版本被認為是歷史性的。完全移除 C 語言部分,使用 GO 編譯 GO,少量代碼使用匯編實現。另外,他們請來了內存管理方面的權威專家 Rick Hudson,對 GC 進行了重新設計,支持并發 GC,解決了一直以來廣為詬病的 GC 時延 (STW) 問題。并且在此后的版本中,又對 GC 做了更進一步的優化。到 go1.8 時,相同業務場景下的 GC 時延已經可以從 go1.1 的數秒,控制在 1ms 以內。GC 問題的解決,可以說 GO 語言在服務端開發方面,幾乎抹平了所有的弱點。

在 GO 語言的版本迭代過程中,語言特性基本上沒有太大的變化,基本上維持在 GO1.1 的基準上,并且官方承諾,新版本對老版本下開發的代碼完全兼容。事實上,GO 開發團隊在新增語言特性上顯得非常謹慎,而在穩定性、編譯速度、執行效率以及 GC 性能等方面進行了持續不斷的優化。

開發團隊

GO 語言的開發陣營可以說是空前強大,主要成員中不乏計算機軟件界的歷史性人物,對計算機軟件的發展影響深遠。Ken Thompson,來自貝爾實驗室,設計了 B 語言,創立了 Unix 操作系統(最初使用 B 語言實現),隨后在 Unix 開發過程中,又和 Dennis Ritchie 一同設計了 C 語言,繼而使用 C 語言重構了 Unix 操作系統。Dennis Ritchie 和 Ken Thompson 被稱為 Unix 和 C 語言之父,并在 1983 年共同被授以圖靈獎,以表彰他們對計算機軟件發展所作的杰出貢獻。Rob Pike,同樣來自貝爾實驗室,Unix 小組重要成員,發明了 Limbo 語言,并且和 Ken Thompson 共同設計了 UTF- 8 編碼,《Unix 編程環境》、《編程實踐》作者之一。

可以說,GO 語言背靠 Google 這棵大樹,又不乏牛人坐鎮,是名副其實的“牛二代”。

大名鼎鼎的 Docker,完全用 GO 實現,業界最為火爆的容器編排管理系統 kubernetes,完全用 GO 實現,之后的 Docker Swarm,完全用 GO 實現。除此之外,還有各種有名的項目如 etcd/consul/flannel 等等,均使用 GO 實現。有人說,GO 語言之所以出名,是趕上了云時代,但為什么不能換種說法,也是 GO 語言促使了云的發展?

除了云項目外,還有像今日頭條、UBER 這樣的公司,他們也使用 GO 語言對自己的業務進行了徹底的重構。

GO 語言關鍵特性

GO 語言之所以厲害,是因為它在服務端的開發中,總能抓住程序員的痛點,以最直接、簡單、高效、穩定的方式來解決問題。這里我們并不會深入討論 GO 語言的具體語法,只會將語言中關鍵的、對簡化編程具有重要意義的方面介紹給大家,跟隨大師們的腳步,體驗 GO 的設計哲學。

GO 語言的關鍵特性主要包括以下幾方面:

并發與協程基于消息傳遞的通信方式豐富實用的內置數據類型函數多返回值 defer 機制反射 (reflect) 高性能 HTTP Server 工程管理編程規范

在當今這個多核時代,并發編程的意義不言而喻。當然,很多語言都支持多線程、多進程編程,但遺憾的是,實現和控制起來并不是那么令人感覺輕松和愉悅。Golang 不同的是,語言級別支持協程 (goroutine) 并發 (協程又稱微線程,比線程更輕量、開銷更小,性能更高),操作起來非常簡單,語言級別提供關鍵字(go) 用于啟動協程,并且在同一臺機器上可以啟動成千上萬個協程。

對比 JAVA 的多線程和 GO 的協程實現,明顯更直接、簡單。這就是 GO 的魅力所在,以簡單、高效的方式解決問題,關鍵字 go,或許就是 GO 語言最重要的標志。

基于消息傳遞的通信方式

在異步的并發編程過程中,只能方便、快速的啟動協程還不夠。協程之間的消息通信,也是非常重要的一環,否則,各個協程就會成為脫韁的野馬而無法控制。在 GO 語言中,使用基于消息傳遞的通信方式 (而不是大多數語言所使用的基于共享內存的通信方式) 進行協程間通信,并且將消息管道 (channel) 作為基本的數據類型,使用類型關鍵字 (chan) 進行定義,并發操作時線程安全。這點在語言的實現上,也具有革命性。可見,GO 語言本身并非簡單得沒有底線,恰恰他們會將最實用、最有利于解決問題的能力,以最簡單、直接的形式提供給用戶。

Channel 并不僅僅只是用于簡單的消息通信,還可以引申出很多非常實用,而實現起來又非常方便的功能。比如,實現 TCP 連接池、限流等等,而這些在其它語言中實現起來并不輕松,但 GO 語言可以輕易做到。

GO 語言作為編譯型語言,在數據類型上也支持得非常全面,除了傳統的整型、浮點型、字符型、數組、結構等類型外。從實用性上考慮,也對字符串類型、切片類型 (可變長數組)、字典類型、復數類型、錯誤類型、管道類型、甚至任意類型(Interface{}) 進行了原生支持,并且用起來非常方便。比如字符串、切片類型,操作簡便性幾乎和 python 類似。

另外,將錯誤類型 (error) 作為基本的數據類型,并且在語言級別不再支持 try…catch 的用法,這應該算是一個非常大膽的革命性創舉,也難怪很多人吐槽 GO 語言不倫不類。但是跳出傳統的觀念,GO 的開發者認為在編程過程中,要保證程序的健壯性和穩定性,對異常的精確化處理是非常重要的,只有在每一個邏輯處理完成后,明確的告知上層調用,是否有異常,并由上層調用明確、及時的對異常進行處理,這樣才可以高程度的保證程序的健壯性和穩定性。雖然這樣做會在編程過程中出現大量的對 error 結果的判斷,但是這無疑也增強了開發者對異常處理的警惕度。而實踐證明,只要嚴格按 GO 推薦的風格編碼,想寫出不健壯的代碼,都很難。當然,前提是你不排斥它,認可它。

在語言中支持函數多返回值,并不是什么新鮮事,Python 就是其中之一。允許函數返回多個值,在某些場景下,可以有效的簡化編程。GO 語言推薦的編程風格,是函數返回的最后一個參數為 error 類型(只要邏輯體中可能出現異常),這樣,在語言級別支持多返回值,就很有必要了。

Defer 延遲處理機制

在 GO 語言中,提供關鍵字 defer,可以通過該關鍵字指定需要延遲執行的邏輯體,即在函數體 return 前或出現 panic 時執行。這種機制非常適合善后邏輯處理,比如可以盡早避免可能出現的資源泄漏問題。

可以說,defer 是繼 goroutine 和 channel 之后的另一個非常重要、實用的語言特性,對 defer 的引入,在很大程度上可以簡化編程,并且在語言描述上顯得更為自然,極大的增強了代碼的可讀性。

Golang 作為強類型的編譯型語言,靈活性上自然不如解析型語言。比如像 PHP,弱類型,并且可以直接對一個字符串變量的內容進行 new 操作,而在編譯型語言中,這顯然不太可能。但是,Golang 提供了 Any 類型 (interface{}) 和強大的類型反射 (reflect) 能力,二者相結合,開發的靈活性上已經很接近解析型語言。在邏輯的動態調用方面,實現起來仍然非常簡單。既然如此,那么像 PHP 這種解析型語言相比于 GO,優勢在那里呢? 就我個人而言,寫了近 10 年的 PHP,實現過開發框架、基礎類庫以及各種公共組件,雖然執行性能不足,但是開發效率有余; 而當遇上 Golang,這些優勢似乎不那么明顯了。

作為出現在互聯網時代的服務端語言,面向用戶服務的能力必不可少。GO 在語言級別自帶 HTTP/TCP/UDP 高性能服務器,基于協程并發,為業務開發提供最直接有效的能力支持。要在 GO 語言中實現一個高性能的 HTTP Server,只需要幾行代碼即可完成,非常簡單。

在 GO 語言中,有一套標準的工程管理規范,只要按照這個規范進行項目開發,之后的事情 (比如包管理、編譯等等) 都將變得非常的簡單。

在 GO 項目下,存在兩個關鍵目錄,一個是 src 目錄,用于存放所有的.go 源碼文件; 一個是 bin 目錄,用于存在編譯后的二進制文件。在 src 目錄下,除了 main 主包所在的目錄外,其它所有的目錄名稱與直接目錄下所對應的包名保持對應,否則編譯無法通過。這樣,GO 編譯器就可以從 main 包所在的目錄開始,完全使用目錄結構和包名來推導工程結構以及構建順序,避免像 C ++ 一樣,引入一個額外的 Makefile 文件。

在 GO 的編譯過程中,我們唯一要做的就是將 GO 項目路徑賦值給一個叫 GOPATH 的環境變量,讓編譯器知道將要編譯的 GO 項目所在的位置。然后進入 bin 目錄下,執行 go build {主包所在的目錄名},即可秒級完成工程編譯。編譯后的二進制文件,可以推到同類 OS 上直接運行,沒有任何環境依賴。

GO 語言的編程規范強制集成在語言中,比如明確規定花括號擺放位置,強制要求一行一句,不允許導入沒有使用的包,不允許定義沒有使用的變量,提供 gofmt 工具強制格式化代碼等等。奇怪的是,這些也引起了很多程序員的不滿,有人發表 GO 語言的 XX 條罪狀,里面就不乏對編程規范的指責。要知道,從工程管理的角度,任何一個開發團隊都會對特定語言制定特定的編程規范,特別像 Google 這樣的公司,更是如此。GO 的設計者們認為,與其將規范寫在文檔里,還不如強制集成在語言里,這樣更直接,更有利用團隊協作和工程管理。

API 快速開發框架實踐

編程語言是一個工具,它會告訴我們能做什么,而怎么做會更好,同樣值得去探討。這部分會介紹用 GO 語言實現的一個開發框架,以及幾個公共組件。當然,框架和公共組件,其它語言也完全可以實現,而這里所關注的是成本問題。除此之外,拋開 GO 語言本身不說,我們也希望可以讓大家從介紹的幾個組件中,得到一些解決問題的思路,那就是通過某種方式,去解決一個面上的問題,而非一味的寫代碼,最終卻只是解決點上的問題。如果你認可這種方式,相信下面的內容也許會影響你之后的項目開發方式,從根本上提高開發效率。

我們為什么選擇 GO 語言

選擇 GO 語言,主要是基于兩方面的考慮

執行性能 縮短 API 的響應時長,解決批量請求訪問超時的問題。在 Uwork 的業務場景下,一次 API 批量請求,往往會涉及對另外接口服務的多次調用,而在之前的 PHP 實現模式下,要做到并行調用是非常困難的,串行處理卻不能從根本上提高處理性能。而 GO 語言不一樣,通過協程可以方便的實現 API 的并行處理,達到處理效率的最大化。依賴 Golang 的高性能 HTTP Server,提升系統吞吐能力,由 PHP 的數百級別提升到數千里甚至過萬級別。開發效率 GO 語言使用起來簡單、代碼描述效率高、編碼規范統一、上手快。通過少量的代碼,即可實現框架的標準化,并以統一的規范快速構建 API 業務邏輯。能快速的構建各種通用組件和公共類庫,進一步提升開發效率,實現特定場景下的功能量產。

很多人在學習一門新語言或開啟一個新項目時,都會習慣性的是網上找一個認為合適的開源框架來開始自己的項目開發之旅。這樣并沒有什么不好,但是個人覺得,了解它內部的實現對我們會更有幫助。或許大家已經注意到了,所說的 MVC 框架,其本質上就是對請求路徑進行解析,然后根據請求路徑段,路由到相應的控制器 (C) 上,再由控制器進一步調用數據邏輯(M),拿到數據后,渲染視圖(V),返回用戶。在整個過程中,核心點在于邏輯的動態調用。

不過,對 API 框架的實現相對于 WEB 頁面框架的實現,會更簡單,因為它并不涉及視圖的渲染,只需要將數據結果以協議的方式返回給用戶即可。

使用 GO 語言實現一套完整的 MVC 開發框架,是非常容易的,集成 HTTP Server 的同時,整個框架的核心代碼不會超過 300 行,從這里可以實際感受到 GO 的語言描述效率之高(如果有興趣,可以參考 Uwork 開源項目 seine)。

也有人說,在 GO 語言中,就沒有框架可言,言外之意是說,引入一個重型的開源框架,必要性并不大,相反還可能把簡單的東西復雜化。

在實際項目開發過程中,只有高效的開發語言還不夠,要想進一步將開發效率擴大化,不斷的沉淀公共基礎庫是必不可少的,以便將通用的基礎邏輯進一步抽象和復用。

除此之外,通用組件能力是實現功能量產的根本,對開發效率會是質的提升。組件化的開發模式會幫忙我們將問題的解決能力從一個點上提升到一個面上。以下會重點介紹幾個通用組件的實現,有了它們的存在,才能真正的解放程序員的生產力。而這些強有力的公共組件在 Golang 中實現起來并不復雜。同時,結合 Golang 的并發處理能力,相比于 PHP 的版本實現,執行效率也會有質的提升。這是組件能力和語言效率的完美結合。

通用列表組件用于所有可能的二維數據源 (如 MySQL/MongoDB/ES 等等) 的數據查詢場景,從一個面上解決了數據查詢問題。在 Uwork 項目開發中,被大量使用,實現數據查詢接口和頁面查詢列表的量產開發。它以一個 JSON 配置文件為中心,來實現對通用數據源的查詢,并將查詢結果以 API 或頁面的形式自動返回給用戶。整個過程中幾乎沒有代碼開發,而唯一要做的只是以一種統一的規范編寫配置文件(而不是代碼),真正實現了對數據查詢需求的功能量產。

以上是通用列表組件的構建過程,要實現這樣一個功能強大的通用組件,是不是會給人一種可望而不可及的感覺? 其實并非如此,只要理清了它的整個過程,將構建思路融入 Golang 中,并不是一件復雜的事情。在我們的項目中,整個組件的實現,只用了不到 700 行 Go 代碼,就解決了一系列的數據查詢問題。另外,通過 Golang 的并發特性,實現字段處理器的并行執行,進一步的提高了組件的執行效率。可以說,通用列表和 Golang 的融合,是性能和效率的完美結合。

通用表單組件主要用于對數據庫的增、刪、改場景。該組件在 Uwork 的項目開發中,也有廣泛的應用,與通用列表類似,以一個 JSON 配置文件為中心,來完成對數據表數據的增、刪、改操作。特別是近期完成的部件級 SDB 管理平臺,通過通用表單實現了對整個系統的數據維護,通過高度抽象化,做到了業務的無代碼化生產。

以上是通用表單的完整構建過程,而對于這個一個組件的實現,我們用了不到 1000 行的 GO 代碼,就解決了對數據表數據維護整個面上的問題。

GO 語言本身支持協程并發,協程非常輕量,可以快速啟動成千上萬個協程工作單元。如果對協程任務的數量控制不當,最后的結果很可能適得其反,從而對外部或本身的服務造成不必要的壓力。協程池可以在一定程度上控制執行單元的數量,保證執行的安全性。而在 Golang 中要實現這樣一個協程池,是非常簡單的,只需要對 channel 和 goroutine 稍加封裝,就可以完成,整個構建過程不到 80 行代碼。

在 API 開發過程中,數據校驗永遠是必不可或缺的一個環節。如果只是簡單的數據校驗,幾行代碼也許就完成了,可是當遇上復雜的數據校驗時,很可能幾百行的代碼量也未必能完成,特別是遇到遞歸類型的數據校驗,那簡直就是一個噩夢。

數據校驗組件,可以通過一種數據模板的配置方式,使用特定的邏輯來完成通用校驗,開發者只需要配置好相應的數據模板,進行簡單的調用,即可完成整個校驗過程。而對于這樣一個通用性的數據校驗組件,在 GO 語言中只用了不到 700 行的代碼量就完成了整個構建。

小結

在實際項目開發過程中,對開發效率提升最大的,無疑是符合系統業務場景的公共組件能力,這點也正好應證了 Rob Pike 那句話(Less is lessor Less is more),真正的高效率開發,是配置化的,并不需要寫太多的代碼,甚至根本就不需要寫代碼,即可完成邏輯實現,而這種方式對于后期的維護成本也是最優的,因為做到了高度的統一。

GO 的語言描述效率毋庸置疑,對上述所有公共組件的實現,均未超過 1000 行代碼,就解決了某個面上的問題。

(以上的部分代碼已經在 Uwork 開源項目 seine 中提供)

性能評測

壓力測試環境說明:

服務運行機器:單臺空閑 B6,24 核 CPU、64G 內存。PHP API 環境:Nginx+PHP-FPM,CI 框架。其中 Nginx 啟動 10 個子進程,每個子進程最大接收 1024 個連接,php-fpm 使用 static 模式,啟動 2000 個常駐子進程。Golang API 環境:使用 go1.8.6 編譯,直接拉起 Golang API Server 進程 (HttpServer),不考慮調優。客戶發起請求測試程序:使用 Golang 編寫,協程并發,運行在獨立的另外一臺空閑 B6 上,24 核 CPU,64G 內存,依次在 1 -2000 個不同級別(并發數步長為 50) 的并發上分別請求 20000 次。

壓力測試結果對比

在 Golang API 框架中,當并發數 50 時,處理 QPS 在 6.5w/ s 附近波動。表現穩定,壓力測試過程無報錯。

Nginx+php-fpm,只在 index.php 中輸出 exit(ok),當并發數 50 時,處理 QPS 在 1w/ s 附近波動。表現穩定,壓力測試過程無報錯。

Nginx+php-fpm+CI 框架中,邏輯執行到具體業務邏輯點,輸出 exit(ok),當并發數 50 時,處理 QPS 在 750/ s 附近波動。并且表現不穩定,壓力測試過程中隨著并發數的增大,錯誤量隨之增加。

通過壓力測試可以發現,Golang 和 PHP 在執行性能上,并沒有什么可比性; 而使用 Golang 實現的 HTTP API 框架,空載時單機性能 QPS 達到 6.5w/s,還是非常令人滿意的。

開發過程中需要注意的點

以下是在實際開發過程中遇到的一些問題,僅供參考:

異常處理統一使用 error,不要使用 panic/recover 來模擬 throw…catch,最初我是這么做的,后來發現這完全是自以為是的做法。原生的 error 過于簡單,而在實際的 API 開發過程中,不同的異常情況需要附帶不同的返回碼,基于此,有必要對 error 再進行一層封裝。任何協程邏輯執行體,邏輯最開始處必須要有 defer recover()異常恢復處理,否則 goroutine 內出現的 panic,將導致整個進程宕掉,需要避免部分邏輯 BUG 造成全局影響。在 Golang 中,變量 (chan 類型除外) 的操作是非線程安全的,也包括像 int 這樣的基本類型,因此并發操作全局變量時一定要考慮加鎖,特別是對 map 的并發操作。所有對 map 鍵值的獲取,都應該判斷存在性,最好是對同類操作進行統一封裝,避免出現不必要的運行時異常。定義 slice 數據類型時,盡量預設長度,避免內部出現不必要的數據重組。

看完上述內容,你們掌握 PHP 程序員為什么要學習 GO 語言的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注丸趣 TV 行業資訊頻道,感謝各位的閱讀!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-28發表,共計7873字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 阆中市| 佛坪县| 开远市| 铜陵市| 广宁县| 宁海县| 永康市| 和龙市| 凤阳县| 南皮县| 栾川县| 吕梁市| 黑水县| 揭阳市| 峨眉山市| 恩施市| 武山县| 桃江县| 中牟县| 佛冈县| 库尔勒市| 逊克县| 罗定市| 马鞍山市| 格尔木市| 玉溪市| 辽宁省| 康马县| 修武县| 阜新| 信阳市| 全椒县| 繁昌县| 密山市| 高尔夫| 元谋县| 奉新县| 芷江| 五峰| 满洲里市| 永春县|