為 Java 程式設計師準備的 Go 入門 PPT
這是 Google 的 Go 團隊技術主管經理 Sameer Ajmani 分享的 PPT,為 Java 程式設計師快速入門 Go 而準備的。
視訊
這個 PPT 是 2015年4月23日在 NYJavaSIG 中使用的。
主要內容
1. Go 是什麼,誰在使用 Go?
2. 比較 Go 和 Java
3. 程式碼示例
4. 併發
5. 工具
Go 是什麼?
“Go 是開源的程式語言,可以很簡單的構建簡單,可靠和高效的軟體。”
Go 的歷史
從 2007 後半年開始設計
- Robert Griesemer, Rob Pike 和 Ken Thompson.
- Ian Lance Taylor 和 Russ Cox
從 2009 年開始開源,有一個非常活躍的社群。
Go 語言穩定版本 Go 1 是在 2012 年早期釋出的。
為什麼有 Go?
Go 是解決 Google 規模的一個解決方案。
系統規模
- 規劃的規模為 10⁶⁺ 臺機器
- 每天在幾千臺機器上作業
- 在系統中與其他作業進行協作,互動
- 同一時間進行大量工作
解決方案:對併發的支援非常強大
第二個問題:工程規模
在 2011 年
- 跨 40+ 辦公室的 5000+ 名開發者
- 每分鐘有 20+ 修改
- 每個月修改 50% 的程式碼基礎庫
- 每天執行 5千萬的測試用例
- 單個程式碼樹
解決方案:為大型程式碼基礎庫二設計的語言
誰在 Google 使用 Go?
大量的專案,幾千位 Go 程式設計師,百萬行的 Go 程式碼。
公開的例子:
- 移動裝置的 Chrome SPDY 代理
- Chrome, ChromeOS, Android SDK, Earth 等等的下載伺服器
- YouTube Vitess MySQL 均衡器
主要任務是網路伺服器,但是這是通用的語言。
除了 Google 還有誰在使用 Go?
Apcera, Bitbucket, bitly, Canonical, CloudFlare, Core OS, Digital Ocean, Docker, Dropbox, Facebook, Getty Images, GitHub, Heroku, Iron.io, Kubernetes, Medium, MongoDB services, Mozilla services, New York Times, pool.ntp.org, Secret, SmugMug, SoundCloud, Stripe, Square, Thomson Reuters, Tumblr, …
比較 Go 和 Java
Go 和 Java 有很多共同之處
- C 系列 (強型別,括號)
- 靜態型別
- 垃圾收集
- 記憶體安全 (nil 引用,執行時邊界檢查)
- 變數總是初始化 (zero/nil/false)
- 方法
- 介面
- 型別斷言 (例項)
- 反射
Go 與 Java 的不同之處
- 程式碼程式直接編譯成機器碼,沒有 VM
- 靜態連結二進位制
- 記憶體佈局控制
- 函式值和詞法閉包
- 內建字串 (UTF-8)
- 內建泛型對映和陣列/片段
- 內建併發
Go 特意去掉了大量的特性
- 沒有類
- 沒有構造器
- 沒有繼承
- 沒有 final
- 沒有異常
- 沒有註解
- 沒有自定義泛型
為什麼 Go 要省去那些特性?
程式碼清晰明瞭是首要的
當檢視程式碼時,可以很清晰的知道程式將會做什麼
當編寫程式碼的時候,也可以很清晰的讓程式做你想做的
有時候這意味著編寫出一個迴圈而不是呼叫一個模糊的函式。
(不要變的太枯燥)
詳細的設計背景請看:
- Less is exponentially more (Pike, 2012)
- Go at Google: Language Design in the Service of Software Engineering (Pike, 2012)
示例
Java程式猿對Go應該很眼熟
Main.java
public class Main { public static void main(String[] args) { System.out.println("Hello, world!"); } }
hello.go
package main import "fmt" func main() { fmt.Println("Hello, 世界!") }
Hello, web server(你好,web服務)
package main
import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/hello", handleHello) fmt.Println("serving on http://localhost:7777/hello") log.Fatal(http.ListenAndServe("localhost:7777", nil)) } func handleHello(w http.ResponseWriter, req *http.Request) { log.Println("serving", req.URL) fmt.Fprintln(w, "Hello, 世界!") }
(訪問許可權)型別根據變數名來宣告。
公共變數名首字大寫,私有變數首字母小寫。
示例:Google搜尋前端
func main() { http.HandleFunc("/search", handleSearch) fmt.Println("serving on http://localhost:8080/search") log.Fatal(http.ListenAndServe("localhost:8080", nil)) } // handleSearch handles URLs like "/search?q=golang" by running a // Google search for "golang" and writing the results as HTML to w. func handleSearch(w http.ResponseWriter, req *http.Request) {
請求驗證
func handleSearch(w http.ResponseWriter, req *http.Request) { log.Println("serving", req.URL) // Check the search query. query := req.FormValue("q") if query == "" { http.Error(w, `missing "q" URL parameter`, http.StatusBadRequest) return }
FormValueis 是 *http.Request 的一個方法:
package http type Request struct {...} func (r *Request) FormValue(key string) string {...}
query := req.FormValue(“q”)初始化變數query,其變數型別是右邊表示式的結果,這裡是string型別.
取搜尋結果
// Run the Google search. start := time.Now() results, err := Search(query) elapsed := time.Since(start) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return }
Search方法有兩個返回值,分別為結果results和錯誤error.
func Search(query string) ([]Result, error) {...}
當error的值為nil時,results有效。
type error interface { Error() string // a useful human-readable error message }
Error型別可能包含額外的資訊,可通過斷言訪問。
渲染搜尋結果
// Render the results. type templateData struct { Results []Result Elapsed time.Duration } if err := resultsTemplate.Execute(w, templateData{ Results: results, Elapsed: elapsed, }); err != nil { log.Print(err) return }
結果results使用Template.Execute生成HTML,並存入一個io.Writer:
type Writer interface { Write(p []byte) (n int, err error) }
http.ResponseWriter實現了io.Writer介面。
Go變數操作HTML模板
// A Result contains the title and URL of a search result. type Result struct { Title, URL string } var resultsTemplate = template.Must(template.New("results").Parse(` <html> <head/> <body> <ol> {{range .Results}} <li>{{.Title}} - <a href="{{.URL}}">{{.URL}}</a></li> {{end}} </ol> <p>{{len .Results}} results in {{.Elapsed}}</p> </body> </html> `))
請求Google搜尋API
func Search(query string) ([]Result, error) { // Prepare the Google Search API request. u, err := url.Parse("https://ajax.googleapis.com/ajax/services/search/web?v=1.0") if err != nil { return nil, err } q := u.Query() q.Set("q", query) u.RawQuery = q.Encode() // Issue the HTTP request and handle the response. resp, err := http.Get(u.String()) if err != nil { return nil, err } defer resp.Body.Close()
defer宣告使resp.Body.Close執行在Search方法返回時。
解析返回的JSON資料到Go struct型別
developers.google.com/web-search/docs/#fonje
var jsonResponse struct { ResponseData struct { Results []struct { TitleNoFormatting, URL string } } } if err := json.NewDecoder(resp.Body).Decode(&jsonResponse); err != nil { return nil, err } // Extract the Results from jsonResponse and return them. var results []Result for _, r := range jsonResponse.ResponseData.Results { results = append(results, Result{Title: r.TitleNoFormatting, URL: r.URL}) } return results, nil }
這就是它的前端
所有引用的包都來自標準庫:
import ( "encoding/json" "fmt" "html/template" "log" "net/http" "net/url" "time" )
Go伺服器規模:每一個請求都執行在自己的goroutine裡。
讓我們談談併發。
通訊順序程式(Hoare,1978)
併發程式作為獨立程式,通過資訊交流的順序執行。
順序執行很容易理解,非同步則不是。
“不要為共亨記憶體通訊,為通訊共享記憶體。”
Go原理: goroutines, channels, 和 select宣告.
Goroutines
Goroutines 就像輕量級執行緒。
它們通過小棧(tiny stacks)和按需調整執行。
Go 程式可以擁有成千上萬個(goroutines)例項
使用go宣告啟動一個goroutines:
go f(args)
Go執行時把goroutines放進OS執行緒裡。
不要使用執行緒堵塞goroutines。
Channels
Channels被定義是為了與goroutines之間通訊。
c := make(chan string) // goroutine 1 c <- "hello!" // goroutine 2 s := <-c fmt.Println(s) // "hello!"
Select
select宣告一個語句塊來判斷執行。
select { case n := <-in: fmt.Println("received", n) case out <- v: fmt.Println("sent", v) }
只有條件成立的case塊會執行。
示例:Google搜尋(後端)
問: Google搜尋能做些什麼?
答: 提出一個問題,它可以返回一個搜尋結果的頁面(和一些廣告)。
問: 我們怎麼得到這些搜尋結果?
答: 傳送一個問題到網頁搜尋、圖片搜尋、YouTube(視訊)、地圖、新聞,稍等然後檢索出結果。
我們該怎麼實現它?
Google搜尋 : 一個假的框架
We can simulate a Search function with a random timeout up to 100ms.
我們要模擬一個搜尋函式,讓它隨機超時0到100毫秒。
var ( Web = fakeSearch("web") Image = fakeSearch("image") Video = fakeSearch("video") ) type Search func(query string) Result func fakeSearch(kind string) Search { return func(query string) Result { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) return Result(fmt.Sprintf("%s result for %q/n", kind, query)) } }
Google搜尋: 測試框架
func main() { start := time.Now() results := Google("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed) }
Google搜尋 (順序)
Google函式獲取一個查詢,然後返回一個的結果集 (不一定是字串).
Google按順序呼叫Web(網頁)、Image(圖片)、Video(視訊)並將返回加入到結果集中。
func Google(query string) (results []Result) { results = append(results, Web(query)) results = append(results, Image(query)) results = append(results, Video(query)) return }
Google搜尋(並行)
同時執行 Web,、Image、 和Video搜尋,並等待所有結果。
func方法是在query和c的地方關閉的。
func Google(query string) (results []Result) { c := make(chan Result) go func() { c <- Web(query) }() go func() { c <- Image(query) }() go func() { c <- Video(query) }() for i := 0; i < 3; i++ { result := <-c results = append(results, result) } return }
Google搜尋 (超時)
等待慢的伺服器。
沒有鎖,沒有條件變數,沒有返回值。
c := make(chan Result, 3) go func() { c <- Web(query) }() go func() { c <- Image(query) }() go func() { c <- Video(query) }() timeout := time.After(80 * time.Millisecond) for i := 0; i < 3; i++ { select { case result := <-c: results = append(results, result) case <-timeout: fmt.Println("timed out") return } } return
防止超時
問: 如何防止丟掉慢的服務的結果?
答: 複製這個服務,然後傳送請求到多個複製的服務,並使用第一個響應的結果。
func First(query string, replicas ...Search) Result { c := make(chan Result, len(replicas)) searchReplica := func(i int) { c <- replicas[i](query) } for i := range replicas { go searchReplica(i) } return <-c }
使用First函式
func main() { start := time.Now() result := First("golang", fakeSearch("replica 1"), fakeSearch("replica 2")) elapsed := time.Since(start) fmt.Println(result) fmt.Println(elapsed) }
Google搜尋 (複製)
使用複製的服務以減少多餘延遲。
c := make(chan Result, 3) go func() { c <- First(query, Web1, Web2) }() go func() { c <- First(query, Image1, Image2) }() go func() { c <- First(query, Video1, Video2) }() timeout := time.After(80 * time.Millisecond) for i := 0; i < 3; i++ { select { case result := <-c: results = append(results, result) case <-timeout: fmt.Println("timed out") return } } return
其他
沒有鎖,沒有條件變數,沒有呼叫。
總結
經過一些簡單轉換,我們使用 Go 的併發原語來轉換一個
- 慢
- 順序性的
- 故障敏感的
程式為一個
- 快
- 併發
- 可複用的
- 健壯的
工具
Go 有很多強大的工具
- gofmt 和 goimports
- The go tool
- godoc
- IDE 和編輯器支援
這語言就是為工具鏈設計的。
gofmt 和 goimports
Gofmt 可以自動格式化程式碼,沒有選項。
Goimports 基於你的工作空間更新匯入宣告
大部分人可以安全的使用這些工具。
The go tool
The go tool 可以在一個傳統目錄佈局中用原始碼構建 Go 程式。不需要 Makefiles 或者其他配置。
匹配這些工具及其依賴,然後進行構建,安裝:
% go get golang.org/x/tools/cmd/present
執行:
% present
godoc
為世界上所有的開源 Go 程式碼生成文件:
IDE 和編輯器支援
Eclipse, IntelliJ, emacs, vim 等等:
- gofmt
- goimports
- godoclookups
- code completion
- code navigation
但是沒有 “Go IDE”.
Go 工具無處不在。
Go 的下一步計劃
Go 路線線上檢視
大量的學習資料
完美的社群
相關文章
- [譯] 為 JavaScript 程式設計師準備的 Flutter 指南JavaScript程式設計師Flutter
- Java入門基礎學習,成為一個Java程式設計師的必備知識Java程式設計師
- 好程式設計師Java培訓分享如何快速入門Java程式設計程式設計師Java
- ??想快速進入人工智慧領域的Java程式設計師?你準備好了嗎?人工智慧Java程式設計師
- Java程式設計師必備技能Java程式設計師
- 好程式設計師Java培訓分享如何快速入門Java程式設計師Java
- 來自Java程式設計師的Python新手入門小結Java程式設計師Python
- java Swing程式設計入門Java程式設計
- Go語言程式設計快速入門Go程式設計
- Java程式設計師必備的工具和框架Java程式設計師框架
- Java程式設計師必備的Intellij外掛Java程式設計師IntelliJ
- 好程式設計師Java培訓分享SpringBoot入門篇程式設計師JavaSpring Boot
- 好程式設計師Java分享MySQL之SQL入門(一)程式設計師JavaMySql
- 入門全棧Java程式設計師——課程介紹全棧Java程式設計師
- Java程式設計師工資為什麼這麼高?想要入門Java怎麼辦?Java程式設計師
- Java程式設計師的工資標準是多少Java程式設計師
- 程式設計師如何準備面試中的演算法程式設計師面試演算法
- JAVA NIO程式設計入門(二)Java程式設計
- JAVA NIO 程式設計入門(三)Java程式設計
- JAVA NIO程式設計入門(一)Java程式設計
- Java入門程式設計師必看:給陣列進行排序Java程式設計師陣列排序
- Go Web 程式設計入門--路由器GoWeb程式設計路由器
- Go Web 程式設計入門--應用 ORMGoWeb程式設計ORM
- Java11新特性,Java程式設計師必備Java程式設計師
- 程式設計師程式設計入門一定知道!程式設計師需要學什麼?程式設計師
- 程式設計師跳槽時,如何高效地準備面試?程式設計師面試
- 學習程式設計前的準備程式設計
- 好程式設計師Java培訓分享零基礎快速入門Java程式設計師Java
- 程式設計師必備裝備!程式設計師
- java程式設計師入門先學什麼開發者工具Java程式設計師
- 好程式設計師Java學習路線分享Redis快速入門程式設計師JavaRedis
- 程式設計師的入門門檻真的那麼低嗎?程式設計師
- Java入門之基礎程式設計Java程式設計
- Java程式設計師必備的一些流程圖Java程式設計師流程圖
- 3 年 Java 程式設計師應該具備的技能!Java程式設計師
- 如何成為更好的Java程式設計師?- javarevisitedJava程式設計師
- reveal.js - 程式設計師的PPT製作神器JS程式設計師
- 小程式入門簡介,你所需要準備的
- 對一名Java程式設計師而言 面試前要準備哪些內容Java程式設計師面試