用Go實現非同步Web開發

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

不知道大家還記得不記得大約一年前,我的一個白日夢《關於Web編程非同步模型的白日夢》,然後這個白日夢我又連續做了好幾天《Web編程非同步模型的PHP原生實現》、《Web編程非同步模型的 Gearman 實現(殘)》。

當時怎麼也沒相通,還死皮白賴的粘在PHP的非同步實現上不肯放手。好吧,實現是繁瑣的,應用是成功的,代碼是容易寫的,環境是要搭建的……

昨晚睡覺前突然覺得自己應該真正用Go實現一下非同步Web,哪怕是個例子也好啊。於是,邊吃飯,邊敲了一票代碼搞了一個很簡單的demo,分享給大家吧。在這裡下載完整的代碼:webdemo。

包含以下檔案:

  • async.go——非同步Web
  • data.go——類比資料
  • Makefile——不說了,你懂……
  • page.go——頁面
  • sync.go——同步Web
  • timer.go——記錄執行時間的日誌
  • webdemo.go——主檔案

編譯並運行

make run

通過瀏覽器分別訪問同步方式的http://127.0.0.1:8888/sync,和非同步方式的http://127.0.0.1:8888/async。在控制台會輸出請求處理的時間長度,實際上即便不看統計時間長度,兩者之間的速度差異很直觀的能體會出來。

mikespook@mikespook-desktop:~/Desktop/webdemo$ make run8g -o _go_.8 timer.go data.go page.go async.go sync.go rm -f _obj/webdemo.agopack grc _obj/webdemo.a _go_.8 cp _obj/webdemo.a "/home/mikespook/bin/go/pkg/linux_386/webdemo.a"8g webdemo.go8l -o webdemo webdemo.8./webdemo2011/03/25 15:06:35 async:2286230002011/03/25 15:06:57 sync:20024992000

核心思路很簡單,我在類比資料請求的代碼裡用了 time.Sleep(),讓每次資料請求都延遲 0.2 秒(一個最佳化得不太好的,很大的資料庫表的一次很爛的查詢所用時間):

// data.gofunc GetContents(key string) string {    time.Sleep(SLEEP) // 延遲    return fmt.Sprintf("%s. The quick brown fox jumps over the lazy dog.", key)}

對於同步資料請求來說,必須等上一次的資料請求完畢才能發起下一次資料請求(原始的PHP即是如此),那麼如果100個0.2秒的資料請求,則最終耗時一定大於 20 秒。
下面是請求100次資料的處理:

// webdemo.go 同步資料請求func syncHandler(w http.ResponseWriter, r *http.Request) {    timer := webdemo.NewTimer("sync")    defer timer.End()    page := webdemo.NewSyncPage()    for i := 0; i < 100; i ++ {        key := strconv.Itoa(i)        page.SetContents(key)    }    page.Render(w)}

下面是擷取資料到頁面,並渲染頁面輸出到http.ResonseWriter的方法:

// sync.gofunc (page *SyncPage) SetContents(key string) {    page.contents[key] = GetContents(key)}func (page *SyncPage) Render(w http.ResponseWriter) {    lines := ""    for i := 0; i < len(page.contents); i++ {        key := strconv.Itoa(i)        lines += fmt.Sprintf(TEMPLATE_LINE, page.contents[key])    }    block := fmt.Sprintf(TEMPLATE_BLOCK, lines)    fmt.Fprintf(w, TEMPLATE_PAGE, block)}

對於非同步來說,總時間長度略大於單條資料請求時間長度。下面是非同步請求的 handler 代碼,其實跟同步並無區別。

// webdemo.go 非同步Handlerfunc asyncHandler(w http.ResponseWriter, r *http.Request) {    timer := webdemo.NewTimer("async")    defer timer.End()    page := webdemo.NewAsyncPage()    page.CountOut = 100    for i:= 0; i < page.CountOut; i++ {        key := strconv.Itoa(i)        page.SetContents(key)    }    page.Render(w)}

為了實現非同步資料擷取,在 async.go 的代碼中,使用 go 語言強大的 channel 來實現。所以在 AsyncPage 結構中定義了一個 contents 是 chan。

// async.go// 頁面內容type AsyncContents struct {    key, value string}// 頁面type AsyncPage struct {    contents chan AsyncContents    timeout chan bool    CountOut int    page Page}

在非同步頁面的 SetContents 方法中,用 go 關鍵字建立一個 goroutines 向 chan 中輸入內容。同時建立另一個 goroutines 作為 timeout(本例中不會出現 timeout 的情況,不過實際環境中這是必要的)。

// async.gofunc (page *AsyncPage) SetContents(key string) {    // 非同步資料擷取    go func() {        page.contents <- AsyncContents{key, GetContents(key)}    }()    // 設定針對頁面的逾時    go func() {        time.Sleep(TIMEOUT)        page.timeout <- true    }()}

頁面的渲染也跟同步方式不同,通過 select 將資料從chan 中取出,並渲染到模板。

// async.gofunc (page *AsyncPage) Render(w http.ResponseWriter) {    lines := ""    LOOP: for i := 0; i < page.CountOut; i++{        select {            case line := <-page.contents:                lines = fmt.Sprintf("%s" + TEMPLATE_LINE, lines, line.value)                // 每擷取一個資料,就去掉一個逾時                go func() {<-page.timeout}()            case <-page.timeout:                lines = fmt.Sprintf("%s" + TEMPLATE_LINE, lines, "Time Out")                break LOOP        }    }    block := fmt.Sprintf(TEMPLATE_BLOCK, lines)    fmt.Fprintf(w, TEMPLATE_PAGE, block)}

為了示範非同步結構,我並沒有使用模板來渲染頁面。不過用模板來渲染也差不多:非同步擷取模板上叫做%VARn的變數,然後將資料集合渲染至模板上……

當資料的擷取有依賴關係的時候情況會比較複雜,不過如果把 web 頁面當作一個樹(DOM Tree?)的話,讓非同步資料從分葉節點開始擷取,逐層上推是個不錯的辦法。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.