使用Go Hijack和jQuery輕鬆實現非同步推送服務

來源:互聯網
上載者:User

 

首先要說明的是,這裡實現的非同步推送服務採用的是Long Polling方式,並不是Comet。

如果想用Comet來實現的話,可以參考這個開源項目:http://cometd.org/。不過其中的服務端實現只有Java版和Python版。如果要用Go來做後端的話需要自己實現Bayeux協議。

關於非同步推送服務的解決方案的資料有很多,在這裡就不在贅述了。當然,當前最先進的兩個方案就是Long Polling和Comet。

1. 預備知識

1.1 Go語言

關於Go語言,其實要說的很多。但是為了不跑題,請大家移步到這裡:http://code.google.com/p/golang-china/。另外,Go語言的官網地址是:http://golang.org/。

1.2 Hijack

Hijack其實是一個單詞,雖然有很多人把它和電影《泰坦尼克號》中Rose的召喚聯絡到一起。Hijack被譯為劫持,在“處理HTTP請求”的這個上下文中,就意味著可以讓我們“劫持”或者說“保持”)HTTP請求連結,做一些其他動作比如根據需要修改HTTP響應的內容),然後再在之後的某個時間將響應“推送”回去。說到這,我想這就與Long Polling的運作方式很相似了。

Go語言的Hijack介面非常簡單,我們在官方的文檔網站上可以找到說明:http://godoc.org/net/http#Hijacker。本文中的核心代碼也是來自於此文檔。

1.3 jQuery

jQuery作為當今最流行的Javascript開發架構,我想基本上每一個做過Web開發的人都會知道,所以在這裡我就不多說了。如果你不知道,可以看這裡:http://jquery.com/。

2. 實戰

2.1 需求

在本案例中,我需要做一個能即時查看當前授權碼的頁面,而且我不想手動重新整理頁面。另外,我還想記錄一下從頁面開啟到當前時刻授權碼改變過多少次。因為授權碼在被使用後會自動變更一次,所以授權的變更次數就等於使用授權碼服務的人數。

2.2 編寫服務端代碼

之前說了,我們使用Go語言來編寫後端代碼。我們要使用Go語言的官方http庫。

其中,我們需要用http.HandleFunc來註冊針對某個url的要求處理常式。如下:

 
  1. http.HandleFunc("/auth_code", getAuthCodeForAdmin) //向http伺服器註冊一個對指定url進行處理的函數。 

在函數getAuthCodeForAdmin的簽名中,有兩個參數——http.Request對象指標和http.ResponseWriter對象。http.Request對象指標用來擷取請求資訊,http.ResponseWriter對象用來寫入響應。

如果要使用Go的Hijack方式來處理HTTP請求,就需要先import其官方的http包:

 
  1. import ( "net/http" ) 

之後,我們在處理函數getAuthCodeForAdmin中先將http.ResponseWriter對象顯式轉換為http.Hijacker介面:

 
  1. hj, ok := w.(http.Hijacker) 

傳回值中賦給“ok”變數的值代錶轉換是否成功,如果不成功就說明http.ResponseWriter對象未實現http.Hijacker介面。

如果轉換成功,我們就可以調用http.Hijacker介面的Hijack方法來擷取連線物件及其讀寫緩衝對象了:

 
  1. conn, bufrw, err := hj.Hijack() 

傳回值中,“conn”代表連線物件,“bufrw”代表該串連的讀寫緩衝對象。

如果傳回值“err”等於nil就說明擷取成功,我就可以繼續下面的事情了。但首先需要在函數推出前關閉串連,不論函數是否執行成功以及是否有錯誤發生:

 
  1. defer conn.Close() 

使用defer關鍵字意味著,讓程式執行流程退出該函數前先執行緊隨其後的語句或函數。這樣就保證了資源的及時釋放。

接下來,我們先觀測新的授權碼的出現,當其出現後我們就使用串連讀寫緩衝對象bufrw返回給http用戶端。從觀測到返回給http用戶端的時間並不確定,也許時間會很長,這也從側面體現了Long Polling中的Long。看下面的代碼:

 
  1. nacChan := make(chan string) 
  2. triggerFunc := func(newAuthCode string) { 
  3.     nacChan <- newAuthCode 
  4. triggerId := fmt.Sprintf("long-polling|%s|%s|%d", loginName, groupName, time.Now().UnixNano()) 
  5. request.AddNewAuthCodeTrigger(triggerId, triggerFunc) 
  6. defer request.DelNewAuthCodeTrigger(triggerId) 

這段代碼其中包含的東西很多,我們不需要全搞明白,只要知道這是為新授權碼產生時間註冊一個觸發器就行了。

當新授權碼被產生後,充當觸發器的函數triggerFunc會被調用。它會向名為nacChan的Channel中添加一個元素。注意,這個Channel是字串類型的,並且是阻塞式。阻塞式意味著擷取元素的語句會一直阻塞,直到該Channel被添入元素。另外,當Channel中已有一個元素時,添加元素的語句也會被阻塞。我們在這裡只用到了阻塞式Channel的前一個特性。元素擷取語句是這樣寫的:

 
  1. newAuthCode := <-nacChan 

擷取到新授權碼後,程式會立即把它“push”給http用戶端。

 
  1. done := pushResponse(bufrw, newAuthCode) 

函數pushResponse的完整定義如下:

 
  1. func pushResponse(bufrw *bufio.ReadWriter, authCode string) bool { 
  2.     _, err := bufrw.Write([]byte(authCode)) if err == nil { 
  3.         err = bufrw.Flush() 
  4.     } if err != nil { 
  5.         go_lib.LogErrorf("PushAuthCodeError: %s\n", err) 
  6.         return false  
  7.     }  
  8.     return true  

其中用到了很多“net/http”以外的包,關於它們的說明可以到Go文檔網站http://godoc.org/中尋找。另外,“go_lib”是我為了自己開發方便而寫的一個函數庫,源碼在這裡:https://github.com/hyper-carrot/go_lib,有興趣的讀者可以查看。

至此,基於Long Polling的非同步推送服務的服務端就完成了。函數getAuthCodeForAdmin的完整代碼可以參看:https://github.com/hyper-carrot/hypermind/blob/master/server.go#L244。

2.2 編寫用戶端代碼

相應的用戶端代碼相當簡單,如下:

 
  1. $(document).ready(function() { 
  2.     url = "/auth_code" count = 0 function poll_auth_code() { 
  3.         $.ajax({ url: url, success: function(data) { if (count == 0) { 
  4.                 $("#initial").text(data); 
  5.                 url += "?type=lp" } 
  6.             $("#current").text(data); 
  7.             $("#count").text(count); 
  8.             count++ 
  9.         }, dataType: "text", complete: poll_auth_code, timeout: (1000 * 60 * 10) }); 
  10.     } 
  11.     poll_auth_code() 
  12.  }); 

它整體採用了一種基於timeout的迴圈機制,邏輯相當簡單,我在這就不多說了,源碼在此:https://github.com/hyper-carrot/hypermind/blob/master/web/page/admin_auth_code.gtpl#L15。

部分頁面的快照如:

650) this.width=650;" src="http://www.bkjia.com/uploads/allimg/131228/11595511Y-0.png" border="0" alt="" data-pinit="registered" />

3. 結束語

怎麼樣?很簡單吧?基於這些代碼,我們還可以實現更複雜一些、更有趣的非同步推送功能。

在這之後,我試著用Go Hijack和jQuery實現基於Comet的非同步推送,但是由於未找到jQuery中對HTTP請求響應內容細粒度的處理方法,暫時放棄了。Long Polling對於我這裡的需求來講是夠用了。

另外再提一點。對比基於Long Polling方式Comet方式非同步推送服務,它們各有利弊。簡單來說,前者會更多的消耗請求處理資源,後者會更多的消耗伺服器連接埠資源。個人感覺,在大量推送請求的情境下,還是Long Polling方式更好一些,因為它會比Comet方式更加及時的釋放資源。但是,基於Comet方式的非同步推送服務在“push”速度上佔有優勢,也大大降低了漏掉推送訊息的可能性。當然,我們可以通過在基於Long Polling方式的用戶端代碼中設定足夠長的timeout時間來模仿Comet方式。

最後,我認為實現Comet方式的最佳方式是WebSocket。所以讓我們摒棄掉低版本的網路瀏覽器吧!

 

本文出自 “Hyper Mind” 部落格,請務必保留此出處http://freej.blog.51cto.com/235241/1137594

相關文章

聯繫我們

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