這是 Google 的 Go 團隊技術主管經理 Sameer Ajmani 分享的 PPT,爲 Java 程序員快速入門 Go 而準備的。javascript
這個 PPT 是 2015年4月23日在 NYJavaSIG 中使用的。css
前往 YouTube 觀看視頻
html
1. Go 是什麼,誰在使用 Go?
2. 比較 Go 和 Java
3. 代碼示例
4. 併發
5. 工具前端
「Go 是開源的編程語言,能夠很簡單的構建簡單,可靠和高效的軟件。」java
golang.orgnginx
從 2007 後半年開始設計git
從 2009 年開始開源,有一個很是活躍的社區。程序員
Go 語言穩定版本 Go 1 是在 2012 年早期發佈的。golang
Go 是解決 Google 規模的一個解決方案。web
解決方案:對併發的支持很是強大
在 2011 年
解決方案:爲大型代碼基礎庫二設計的語言
大量的項目,幾千位 Go 程序員,百萬行的 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, …
代碼清晰明瞭是首要的
當查看代碼時,能夠很清晰的知道程序將會作什麼
當編寫代碼的時候,也能夠很清晰的讓程序作你想作的
有時候這意味着編寫出一個循環而不是調用一個模糊的函數。
(不要變的太枯燥)
詳細的設計背景請看:
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, 世界!") }
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, 世界!") }
(訪問權限)類型根據變量名來聲明。
公共變量名首字大寫,私有變量首字母小寫。
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接口。
// 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> `))
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方法返回時。
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裏。
讓咱們談談併發。
併發程序做爲獨立進程,經過信息交流的順序執行。
順序執行很容易理解,異步則不是。
「不要爲共亨內存通訊,爲通訊共享內存。」
Go原理: goroutines, channels, 和 select聲明.
Goroutines 就像輕量級線程。
它們經過小棧(tiny stacks)和按需調整運行。
Go 程序能夠擁有成千上萬個(goroutines)實例
使用go聲明啓動一個goroutines:
go f(args)
Go運行時把goroutines放進OS線程裏。
不要使用線程堵塞goroutines。
Channels被定義是爲了與goroutines之間通訊。
c := make(chan string)
// goroutine 1 c <- "hello!" // goroutine 2 s := <-c fmt.Println(s) // "hello!"
select聲明一個語句塊來判斷執行。
select {
case n := <-in: fmt.Println("received", n) case out <- v: fmt.Println("sent", v) }
只有條件成立的case塊會運行。
問: Google搜索能作些什麼?
答: 提出一個問題,它能夠返回一個搜索結果的頁面(和一些廣告)。
問: 咱們怎麼獲得這些搜索結果?
答: 發送一個問題到網頁搜索、圖片搜索、YouTube(視頻)、地圖、新聞,稍等而後檢索出結果。
咱們該怎麼實現它?
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)) } }
func main() { start := time.Now() results := Google("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed) }
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 }
同時執行 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 }
等待慢的服務器。
沒有鎖,沒有條件變量,沒有返回值。
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 }
func main() { start := time.Now() result := First("golang", fakeSearch("replica 1"), fakeSearch("replica 2")) elapsed := time.Since(start) fmt.Println(result) fmt.Println(elapsed) }
使用複製的服務以減小多餘延遲。
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 的併發原語來轉換一個
程序爲一個
這語言就是爲工具鏈設計的。
Gofmt 能夠自動格式化代碼,沒有選項。
Goimports 基於你的工做空間更新導入聲明
大部分人能夠安全的使用這些工具。
The go tool 能夠在一個傳統目錄佈局中用源代碼構建 Go 程序。不須要 Makefiles 或者其餘配置。
匹配這些工具及其依賴,而後進行構建,安裝:
% go get golang.org/x/tools/cmd/present
運行:
% present
爲世界上全部的開源 Go 代碼生成文檔:
Eclipse, IntelliJ, emacs, vim 等等:
可是沒有 「Go IDE」.
Go 工具無處不在。
Go 路線在線查看
大量的學習資料
完美的社區