Go開發(fā)關(guān)鍵技術(shù)指南:Engineering

Engineering

我覺得Go在工程上良好的支持,是Go能夠在服務(wù)器領(lǐng)域有一席之地的重要原因。這里說的工程友好包括:

  • gofmt保證代碼的基本一致,增加可讀性玄坦,避免在爭論不清楚的地方爭論。
  • 原生支持的profiling绘沉,為性能調(diào)優(yōu)和死鎖問題提供了強(qiáng)大的工具支持。
  • utest和coverage豺总,持續(xù)集成车伞,為項(xiàng)目的質(zhì)量提供了良好的支撐。
  • example和注釋喻喳,讓接口定義更友好合理另玖,讓庫的質(zhì)量更高。

GOFMT規(guī)范編碼

這幾天朋友圈霸屏的文章是碼農(nóng)因?yàn)榇a不規(guī)范問題打擊同事表伦,雖然實(shí)際上槍擊案可能不是因?yàn)榇a規(guī)范谦去,但可以看出大家對(duì)于代碼規(guī)范問題能引發(fā)槍擊是毫不懷疑的。這些年在不同的公司碼代碼蹦哼,和不同的人一起碼代碼鳄哭,每個(gè)地方總有人喜歡糾結(jié)于if ()中是否應(yīng)該有空格,甚至還大開懟戒纲熏。Go語言從來不會(huì)有這種爭論妆丘,因?yàn)橛?code>gofmt,語言的工具鏈支持了格式化代碼局劲,避免大家在代碼風(fēng)格上白費(fèi)口舌勺拣。

比如,下面的代碼看著真是揪心鱼填,任何語言都可以寫出類似的一坨代碼:

package main
import (
    "fmt"
    "strings"
)
func foo()[]string {
    return []string{"gofmt","pprof","cover"}}

func main() {
    if v:=foo();len(v)>0{fmt.Println("Hello",strings.Join(v,", "))}
}

如果有幾萬行代碼都是這樣药有,是不是有扣動(dòng)扳機(jī)的沖動(dòng)?如果我們執(zhí)行下gofmt -w t.go之后苹丸,就變成下面的樣子:

package main

import (
    "fmt"
    "strings"
)

func foo() []string {
    return []string{"gofmt", "pprof", "cover"}
}

func main() {
    if v := foo(); len(v) > 0 {
        fmt.Println("Hello", strings.Join(v, ", "))
    }
}

是不是心情舒服多了愤惰?gofmt只能解決基本的代碼風(fēng)格問題苇经,雖然這個(gè)已經(jīng)節(jié)約了不少口舌和唾沫,我想特別強(qiáng)調(diào)幾點(diǎn):

  • 有些IDE會(huì)在保存時(shí)自動(dòng)gofmt羊苟,如果沒有手動(dòng)運(yùn)行下命令gofmt -w .塑陵,可以將當(dāng)前目錄和子目錄下的所有文件都格式化一遍,也很容易的是不是蜡励。
  • gofmt不識(shí)別空行令花,因?yàn)榭招惺怯幸饬x的,因?yàn)榭招杏幸饬x所以gofmt不知道如何處理凉倚,而這正是很多同學(xué)經(jīng)常犯的問題兼都。
  • gofmt有時(shí)候會(huì)因?yàn)閷?duì)齊問題,導(dǎo)致額外的不必要的修改稽寒,這不會(huì)有什么問題扮碧,但是會(huì)干擾CR從而影響CR的質(zhì)量。

先看空行問題杏糙,不能隨便使用空行慎王,因?yàn)榭招杏幸饬x。不能在不該空行的地方用空行宏侍,不能在該有空行的地方不用空行赖淤,比如下面的例子:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    f, err := os.Open(os.Args[1])

    if err != nil {

        fmt.Println("show file err %v", err)
        os.Exit(-1)
    }
    defer f.Close()
    io.Copy(os.Stdout, f)
}

上面的例子看起來就相當(dāng)?shù)钠孑猓?code>if和os.Open之間沒有任何原因需要個(gè)空行,結(jié)果來了個(gè)空行谅河;而deferio.Copy之間應(yīng)該有個(gè)空行卻沒有個(gè)空行咱旱。空行是非常好的體現(xiàn)了邏輯關(guān)聯(lián)的方式绷耍,所以空行不能隨意吐限,非常嚴(yán)重影響可讀性,要么就是一坨東西看得很費(fèi)勁褂始,要么就是突然看到兩個(gè)緊密的邏輯身首異處诸典,真的讓人很詫異。上面的代碼可以改成這樣崎苗,是不是看起來很舒服了:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    f, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println("show file err %v", err)
        os.Exit(-1)
    }
    defer f.Close()

    io.Copy(os.Stdout, f)
}

再看gofmt的對(duì)齊問題搂赋,一般出現(xiàn)在一些結(jié)構(gòu)體有長短不一的字段,比如統(tǒng)計(jì)信息益缠,比如下面的代碼:

package main

type NetworkStat struct {
    IncomingBytes int `json:"ib"`
    OutgoingBytes int `json:"ob"`
}

func main() {
}

如果新增字段比較長脑奠,會(huì)導(dǎo)致之前的字段也會(huì)增加空白對(duì)齊,看起來整個(gè)結(jié)構(gòu)體都改變了:

package main

type NetworkStat struct {
    IncomingBytes          int `json:"ib"`
    OutgoingBytes          int `json:"ob"`
    IncomingPacketsPerHour int `json:"ipp"`
    DropKiloRateLastMinute int `json:"dkrlm"`
}

func main() {
}

比較好的解決辦法就是用注釋幅慌,添加注釋后就不會(huì)強(qiáng)制對(duì)齊了宋欺。

Profile性能調(diào)優(yōu)

性能調(diào)優(yōu)是一個(gè)工程問題,關(guān)鍵是測量后優(yōu)化,而不是盲目優(yōu)化齿诞。Go提供了大量的測量程序的工具和機(jī)制酸休,包括Profiling Go Programs, Introducing HTTP Tracing,我們也在性能優(yōu)化時(shí)使用過Go的Profiling祷杈,原生支持是非常便捷的斑司。

對(duì)于多線程同步可能出現(xiàn)的死鎖和競爭問題,Go提供了一系列工具鏈但汞,比如Introducing the Go Race Detector, Data Race Detector宿刮,不過打開race后有明顯的性能損耗,不應(yīng)該在負(fù)載較高的線上服務(wù)器打開私蕾,會(huì)造成明顯的性能瓶頸僵缺。

推薦服務(wù)器開啟http profiling,偵聽在本機(jī)可以避免安全問題踩叭,需要profiling時(shí)去機(jī)器上把profile數(shù)據(jù)拿到后磕潮,拿到線下分析原因。實(shí)例代碼如下:

package main

import (
    "net/http"
    _ "net/http/pprof"
    "time"
)

func main() {
    go http.ListenAndServe("127.0.0.1:6060", nil)

    for {
        b := make([]byte, 4096)
        for i := 0; i < len(b); i++ {
            b[i] = b[i] + 0xf
        }
        time.Sleep(time.Nanosecond)
    }
}

編譯成二進(jìn)制后啟動(dòng)go mod init private.me && go build . && ./private.me容贝,在瀏覽器訪問頁面可以看到各種性能數(shù)據(jù)的導(dǎo)航:http://localhost:6060/debug/pprof/

例如分析CPU的性能瓶頸自脯,可以執(zhí)行go tool pprof private.me http://localhost:6060/debug/pprof/profile,默認(rèn)是分析30秒內(nèi)的性能數(shù)據(jù)斤富,進(jìn)入pprof后執(zhí)行top可以看到CPU使用最高的函數(shù):

(pprof) top
Showing nodes accounting for 42.41s, 99.14% of 42.78s total
Dropped 27 nodes (cum <= 0.21s)
Showing top 10 nodes out of 22
      flat  flat%   sum%        cum   cum%
    27.20s 63.58% 63.58%     27.20s 63.58%  runtime.pthread_cond_signal
    13.07s 30.55% 94.13%     13.08s 30.58%  runtime.pthread_cond_wait
     1.93s  4.51% 98.64%      1.93s  4.51%  runtime.usleep
     0.15s  0.35% 98.99%      0.22s  0.51%  main.main

除了top冤今,還可以輸入web命令看調(diào)用圖,還可以用go-torch看火焰圖等茂缚。

UTest和Coverage

當(dāng)然工程化少不了UTest和覆蓋率,關(guān)于覆蓋Go也提供了原生支持The cover story屋谭,一般會(huì)有專門的CISE集成測試環(huán)境脚囊。集成測試之所以重要,是因?yàn)殡S著代碼規(guī)模的增長桐磁,有效的覆蓋能顯著的降低引入問題的可能性悔耘。

什么是有效的覆蓋?一般多少覆蓋率比較合適我擂?80%覆蓋夠好了嗎衬以?90%覆蓋一定比30%覆蓋好嗎?我覺得可不一定了校摩,參考Testivus On Test Coverage看峻。對(duì)于UTest和覆蓋,我覺得重點(diǎn)在于:

  • UTest和覆蓋率一定要有衙吩,哪怕是0.1%也必須要有互妓,為什么呢?因?yàn)槌霈F(xiàn)故障時(shí)讓老板心里好受點(diǎn)啊,能用數(shù)據(jù)衡量出來裸奔的代碼有多少冯勉。
  • 核心代碼和業(yè)務(wù)代碼一定要分離澈蚌,強(qiáng)調(diào)核心代碼的覆蓋率才有意義,比如整體覆蓋了80%灼狰,核心代碼占5%宛瞄,核心代碼覆蓋率為10%,那么這個(gè)覆蓋就不怎么有效了交胚。
  • 除了關(guān)鍵正常邏輯份汗,更應(yīng)該重視異常邏輯,異常邏輯一般不會(huì)執(zhí)行到承绸,而一旦藏有bug可能就會(huì)造成問題裸影。有可能有些罕見的代碼無法覆蓋到,那么這部分邏輯代碼军熏,CR時(shí)需要特別人工Review轩猩。

分離核心代碼是關(guān)鍵〉磁欤可以將核心代碼分離到單獨(dú)的package均践,對(duì)這個(gè)package要求更高的覆蓋率,比如我們要求98%的覆蓋(實(shí)際上做到了99.14%的覆蓋)摩幔。對(duì)于應(yīng)用的代碼彤委,具備可測性是非常關(guān)鍵的,舉個(gè)我自己的例子或衡,go-oryx這部分代碼是判斷哪些url是代理焦影,就不具備可測性,下面是主要的邏輯:

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if o := r.Header.Get("Origin"); len(o) > 0 {
            w.Header().Set("Access-Control-Allow-Origin", "*")
        }

        if proxyUrls == nil {
            ......
            fs.ServeHTTP(w, r)
            return
        }

        for _, proxyUrl := range proxyUrls {
            srcPath, proxyPath := r.URL.Path, proxyUrl.Path
            ......
            if proxy, ok := proxies[proxyUrl.Path]; ok {
                p.ServeHTTP(w, r)
                return
            }
        }

        fs.ServeHTTP(w, r)
    })

可以看得出來封断,關(guān)鍵需要測試的核心代碼斯辰,在于后面如何判斷URL符合定義的規(guī)范,這部分應(yīng)該被定義成函數(shù)坡疼,這樣就可以單獨(dú)測試了:

func shouldProxyURL(srcPath, proxyPath string) bool {
    if !strings.HasSuffix(srcPath, "/") {
        // /api to /api/
        // /api.js to /api.js/
        // /api/100 to /api/100/
        srcPath += "/"
    }

    if !strings.HasSuffix(proxyPath, "/") {
        // /api/ to /api/
        // to match /api/ or /api/100
        // and not match /api.js/
        proxyPath += "/"
    }

    return strings.HasPrefix(srcPath, proxyPath)
}

func run(ctx context.Context) error {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        ......
        for _, proxyUrl := range proxyUrls {
            if !shouldProxyURL(r.URL.Path, proxyUrl.Path) {
                continue
            }

代碼參考go-oryx: Extract and test URL proxy彬呻,覆蓋率請(qǐng)看gocover: For go-oryx coverage,這樣的代碼可測性就會(huì)比較好柄瑰,也能在有限的精力下盡量讓覆蓋率有效闸氮。

Note: 可見,單元測試和覆蓋率教沾,并不是測試的事情蒲跨,而是代碼本身應(yīng)該提高的代碼“可測試性”。

另外授翻,對(duì)于Go的測試還有幾點(diǎn)值得說明:

  • helper:測試時(shí)如果調(diào)用某個(gè)函數(shù)财骨,出錯(cuò)時(shí)總是打印那個(gè)共用的函數(shù)的行數(shù)镐作,而不是測試的函數(shù)。比如test_helper.go隆箩,如果compare不調(diào)用t.Helper()该贾,那么錯(cuò)誤顯示是hello_test.go:26: Returned: [Hello, world!], Expected: [BROKEN!],調(diào)用t.Helper()之后是hello_test.go:18: Returned: [Hello, world!], Expected: [BROKEN!]`捌臊,實(shí)際上應(yīng)該是18行的case有問題杨蛋,而不是26行這個(gè)compare函數(shù)的問題。
  • benchmark:測試時(shí)還可以帶Benchmark的理澎,參數(shù)不是testing.T而是testing.B逞力,執(zhí)行時(shí)會(huì)動(dòng)態(tài)調(diào)整一些參數(shù),比如testing.B.N糠爬,還有并行執(zhí)行的testing.PB. RunParallel寇荧,參考Benchamrk
  • main: 測試也是有個(gè)main函數(shù)的执隧,參考TestMain揩抡,可以做一些全局的初始化和處理。
  • doc.go: 整個(gè)包的文檔描述镀琉,一般是在package http前面加說明峦嗤,比如http doc的使用例子。

對(duì)于Helper還有一種思路屋摔,就是用帶堆棧的error烁设,參考前面關(guān)于errors的說明,不僅能將所有堆棧的行數(shù)給出來钓试,而且可以帶上每一層的信息装黑。

注意如果package只暴露了interface,比如go-oryx-lib: aac通過NewADTS() (ADTS, error)返回的是接口ADTS弓熏,無法給ADTS的函數(shù)加Example恋谭;因此我們專門暴露了一個(gè)ADTSImpl的結(jié)構(gòu)體,而New函數(shù)返回的還是接口硝烂,這種做法不是最好的,讓用戶有點(diǎn)無所適從铜幽,不知道該用ADTS還是ADTSImpl滞谢。所以一種可選的辦法,就是在包里面有個(gè)doc.go放說明除抛,例如net/http/doc.go文件狮杨,就是在package http前面加說明,比如http doc的使用例子到忽。

注釋和Example

注釋和Example是非常容易被忽視的橄教,我覺得應(yīng)該注意的地方包括:

  • 項(xiàng)目的README.md和Wiki清寇,這實(shí)際上就是新人指南,因?yàn)樾氯巳绻芏敲淳秃苋菀琢私膺@個(gè)項(xiàng)目的大概情況护蝶,很多項(xiàng)目都沒有這個(gè)华烟。如果沒有README,那么就需要看文件持灰,該看哪個(gè)文件盔夜?這就讓人很抓狂了。
  • 關(guān)鍵代碼沒有注釋堤魁,比如庫的API喂链,關(guān)鍵的函數(shù),不好懂的代碼段落妥泉。如果看標(biāo)準(zhǔn)庫椭微,絕大部分可以調(diào)用的API都有很好的注釋,沒有注釋怎么調(diào)用呢盲链?只能看代碼實(shí)現(xiàn)了蝇率,如果每次調(diào)用都要看一遍實(shí)現(xiàn),真的很難受了匈仗。
  • 庫沒有Example瓢剿,庫是一種要求很高的包,就是給別人使用的包悠轩,比如標(biāo)準(zhǔn)庫间狂。絕大部分的標(biāo)準(zhǔn)庫的包,都有Example火架,因?yàn)闆]有Example很難設(shè)計(jì)出合理的API鉴象。

先看關(guān)鍵代碼的注釋,有些注釋完全是代碼的重復(fù)何鸡,沒有任何存在的意義纺弊,唯一的存在就是提高代碼的“注釋率”,這又有什么用呢骡男,比如下面代碼:

wsconn *Conn //ws connection

// The RPC call.
type rpcCall struct {

// Setup logger.
if err := SetupLogger(......); err != nil {

// Wait for os signal
server.WaitForSignals(

如果注釋能通過函數(shù)名看出來(比較好的函數(shù)名要能看出來它的職責(zé))淆游,那么就不需要寫重復(fù)的注釋,注釋要說明一些從代碼中看不出來的東西隔盛,比如標(biāo)準(zhǔn)庫的函數(shù)的注釋:

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {

// ParseInt interprets a string s in the given base (0, 2 to 36) and
// bit size (0 to 64) and returns the corresponding value i.
//
// If base == 0, the base is implied by the string's prefix:
// base 2 for "0b", base 8 for "0" or "0o", base 16 for "0x",
// and base 10 otherwise. Also, for base == 0 only, underscore
// characters are permitted per the Go integer literal syntax.
// If base is below 0, is 1, or is above 36, an error is returned.
//
// The bitSize argument specifies the integer type
// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64
// correspond to int, int8, int16, int32, and int64.
// If bitSize is below 0 or above 64, an error is returned.
//
// The errors that ParseInt returns have concrete type *NumError
// and include err.Num = s. If s is empty or contains invalid
// digits, err.Err = ErrSyntax and the returned value is 0;
// if the value corresponding to s cannot be represented by a
// signed integer of the given size, err.Err = ErrRange and the
// returned value is the maximum magnitude integer of the
// appropriate bitSize and sign.
func ParseInt(s string, base int, bitSize int) (i int64, err error) {

標(biāo)準(zhǔn)庫做得很好的是犹菱,會(huì)把參數(shù)名稱寫到注釋中(而不是用@param這種方式),而且會(huì)說明大量的背景信息吮炕,這些信息是從函數(shù)名和參數(shù)看不到的重要信息腊脱。

咱們?cè)倏碋xample,一種特殊的test龙亲,可能不會(huì)執(zhí)行陕凹,它的主要作用是為了推演接口是否合理悍抑,當(dāng)然也就提供了如何使用庫的例子,這就要求Example必須覆蓋到庫的主要使用場景杜耙。舉個(gè)例子搜骡,有個(gè)庫需要方式SSRF攻擊,也就是檢查HTTP Redirect時(shí)的URL規(guī)則泥技,最初我們是這樣提供這個(gè)庫的:

func NewHttpClientNoRedirect() *http.Client {

看起來也沒有問題浆兰,提供一種特殊的http.Client,如果發(fā)現(xiàn)有Redirect就返回錯(cuò)誤珊豹,那么它的Example就會(huì)是這樣:

func ExampleNoRedirectClient() {
    url := "http://xxx/yyy"

    client := ssrf.NewHttpClientNoRedirect()
    Req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        fmt.Println("failed to create request")
        return
    }

    resp, err := client.Do(Req)
    fmt.Printf("status :%v", resp.Status)
}

這時(shí)候就會(huì)出現(xiàn)問題簸呈,我們總是返回了一個(gè)新的http.Client,如果用戶自己有了自己定義的http.Client怎么辦店茶?實(shí)際上我們只是設(shè)置了http.Client.CheckRedirect這個(gè)回調(diào)函數(shù)蜕便。如果我們先寫Example,更好的Example會(huì)是這樣:

func ExampleNoRedirectClient() {
    client := http.Client{}

    //Must specify checkRedirect attribute to NewFuncNoRedirect
    client.CheckRedirect = ssrf.NewFuncNoRedirect()

    Req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        fmt.Println("failed to create request")
        return
    }

    resp, err := client.Do(Req)
}

那么我們自然知道應(yīng)該如何提供接口了贩幻。

其他工程化

最近得知WebRTC有4GB的代碼轿腺,包括它自己的以及依賴的代碼,就算去掉一般的測試文件和文檔丛楚,也有2GB的代碼W蹇恰!趣些!編譯起來真的是非常的耗時(shí)間仿荆,而Go對(duì)于編譯速度的優(yōu)化,據(jù)說是在Google有過驗(yàn)證的坏平,具體我們還沒有到這個(gè)規(guī)模拢操。具體可以參考Why so fast?,主要是編譯器本身比GCC快(5X)舶替,以及Go的依賴管理做的比較好令境。

Go的內(nèi)存和異常處理也做得很好,比如不會(huì)出現(xiàn)野指針顾瞪,雖然有空指針問題可以用recover來隔離異常的影響舔庶。而C或C++服務(wù)器,目前還沒有見過沒有內(nèi)存問題的陈醒,上線后就是各種的野指針滿天飛惕橙,總有因?yàn)橐爸羔樃闼赖臅r(shí)候,只是或多或少罷了孵延。

按照Go的版本發(fā)布節(jié)奏吕漂,6個(gè)月就發(fā)一個(gè)版本亲配,基本上這么多版本都很穩(wěn)定尘应,Go1.11的代碼一共有166萬行Go代碼惶凝,還有12萬行匯編代碼,其中單元測試代碼有32萬行(占17.9%)犬钢,使用實(shí)例Example有1.3萬行苍鲜。Go對(duì)于核心API是全部覆蓋的,提交有沒有導(dǎo)致API不符合要求都有單元測試保證玷犹,Go有多個(gè)集成測試環(huán)境混滔,每個(gè)平臺(tái)是否測試通過也能看到,這一整套機(jī)制讓Go項(xiàng)目雖然越來越龐大歹颓,但是整體研發(fā)效率卻很高坯屿。

Links

由于簡書限制了文章字?jǐn)?shù),只好分成不同章節(jié):

  • Overview 為何Go有時(shí)候也叫Golang?為何要選擇Go作為服務(wù)器開發(fā)的語言巍扛?是沖動(dòng)领跛?還是騷動(dòng)?Go的重要里程碑和事件撤奸,當(dāng)年吹的那些牛逼吠昭,都實(shí)現(xiàn)了哪些?
  • Could Not Recover 君可知胧瓜,有什么panic是無法recover的矢棚?包括超過系統(tǒng)線程限制,以及map的競爭寫府喳。當(dāng)然一般都能recover蒲肋,比如Slice越界、nil指針劫拢、除零肉津、寫關(guān)閉的chan等。
  • Errors 為什么Go2的草稿3個(gè)有2個(gè)是關(guān)于錯(cuò)誤處理的舱沧?好的錯(cuò)誤處理應(yīng)該怎么做妹沙?錯(cuò)誤和異常機(jī)制的差別是什么?錯(cuò)誤處理和日志如何配合熟吏?
  • Logger 為什么標(biāo)準(zhǔn)庫的Logger是完全不夠用的距糖?怎么做日志切割和輪轉(zhuǎn)?怎么在混成一坨的服務(wù)器日志中找到某個(gè)連接的日志牵寺?甚至連接中的流的日志悍引?怎么做到簡潔又夠用?
  • Interfaces 什么是面向?qū)ο蟮腟OLID原則帽氓?為何Go更符合SOLID趣斤?為何接口組合比繼承多態(tài)更具有正交性?Go類型系統(tǒng)如何做到looser, organic, decoupled, independent, and therefore scalable黎休?一般軟件中如果出現(xiàn)數(shù)學(xué)浓领,要么真的牛逼要么裝逼玉凯。正交性這個(gè)數(shù)學(xué)概念在Go中頻繁出現(xiàn),是神仙還是妖怪联贩?為何接口設(shè)計(jì)要考慮正交性漫仆?
  • Modules 如何避免依賴地獄(Dependency Hell)?小小的版本號(hào)為何會(huì)帶來大災(zāi)難泪幌?Go為什么推出了GOPATH盲厌、Vendor還要搞module和vgo?新建了16個(gè)倉庫做測試祸泪,碰到了9個(gè)坑吗浩,搞清楚了gopath和vendor如何遷移,以及vgo with vendor如何使用(畢竟生產(chǎn)環(huán)境不能每次都去外網(wǎng)下載)没隘。
  • Concurrency & Control 服務(wù)器中的并發(fā)處理難在哪里拓萌?為什么說Go并發(fā)處理優(yōu)勢占領(lǐng)了云計(jì)算開發(fā)語言市場?什么是C10K升略、C10M問題微王?如何管理goroutine的取消、超時(shí)和關(guān)聯(lián)取消品嚣?為何Go1.7專門將context放到了標(biāo)準(zhǔn)庫炕倘?context如何使用,以及問題在哪里翰撑?
  • Engineering Go在工程化上的優(yōu)勢是什么罩旋?為什么說Go是一門面向工程的語言?覆蓋率要到多少比較合適眶诈?什么叫代碼可測性涨醋?為什么良好的庫必須先寫Example?
  • Go2 Transition Go2會(huì)像Python3不兼容Python2那樣作嗎逝撬?C和C++的語言演進(jìn)可以有什么不同的收獲浴骂?Go2怎么思考語言升級(jí)的問題?
  • SRS & Others Go在流媒體服務(wù)器中的使用宪潮。Go的GC靠譜嗎溯警?Twitter說相當(dāng)?shù)目孔V,有圖有真相狡相。為何Go的聲明語法是那樣梯轻?C的又是怎樣?是拍的大腿尽棕,還是拍的腦袋喳挑?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末伊诵,一起剝皮案震驚了整個(gè)濱河市媚朦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌日戈,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孙乖,死亡現(xiàn)場離奇詭異浙炼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)唯袄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門弯屈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恋拷,你說我怎么就攤上這事资厉。” “怎么了蔬顾?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵宴偿,是天一觀的道長。 經(jīng)常有香客問我诀豁,道長窄刘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任舷胜,我火速辦了婚禮娩践,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烹骨。我一直安慰自己翻伺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布沮焕。 她就那樣靜靜地躺著吨岭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪峦树。 梳的紋絲不亂的頭發(fā)上未妹,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音空入,去河邊找鬼络它。 笑死,一個(gè)胖子當(dāng)著我的面吹牛歪赢,可吹牛的內(nèi)容都是我干的化戳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼点楼!你這毒婦竟也來了扫尖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤掠廓,失蹤者是張志新(化名)和其女友劉穎换怖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蟀瞧,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沉颂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悦污。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铸屉。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖切端,靈堂內(nèi)的尸體忽然破棺而出彻坛,到底是詐尸還是另有隱情,我是刑警寧澤踏枣,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布昌屉,位于F島的核電站,受9級(jí)特大地震影響茵瀑,放射性物質(zhì)發(fā)生泄漏怠益。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一瘾婿、第九天 我趴在偏房一處隱蔽的房頂上張望蜻牢。 院中可真熱鬧,春花似錦偏陪、人聲如沸抢呆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抱虐。三九已至,卻和暖如春饥脑,著一層夾襖步出監(jiān)牢的瞬間恳邀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工灶轰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谣沸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓笋颤,卻偏偏與公主長得像乳附,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容