Web服務 Web服務可以讓你在HTTP協議的基礎上通過XML或者JSON來交換信息。如果你想知道上海的天氣預報、中國石油的股價或者淘寶商家的一個商品信息,你可以編寫一段簡短的代碼,通過抓取這些信息然後通過標準的介面開放出來,就如同你調用一個本地函數並返回一個值。 Web服務背後的關鍵在於平臺的無關 ...
Web服務
Web服務可以讓你在HTTP協議的基礎上通過XML或者JSON來交換信息。如果你想知道上海的天氣預報、中國石油的股價或者淘寶商家的一個商品信息,你可以編寫一段簡短的代碼,通過抓取這些信息然後通過標準的介面開放出來,就如同你調用一個本地函數並返回一個值。
Web服務背後的關鍵在於平臺的無關性,你可以運行你的服務在Linux系統,可以與其他Windows的asp.net程式交互,同樣的,也可以通過同一個介面和運行在FreeBSD上面的JSP無障礙地通信。
目前主流的有如下幾種Web服務:REST、SOAP。
Socket編程
在很多底層網路應用開發者的眼裡一切編程都是Socket,話雖然有點誇張,但卻也幾乎如此了,現在的網路編程幾乎都是用Socket來編程。你想過這些情景麽?我們每天打開瀏覽器瀏覽網頁時,瀏覽器進程怎麼和Web伺服器進行通信的呢?當你用QQ聊天時,QQ進程怎麼和伺服器或者是你的好友所在的QQ進程進行通信的呢?當你打開PPstream觀看視頻時,PPstream進程如何與視頻伺服器進行通信的呢? 如此種種,都是靠Socket來進行通信的,以一斑窺全豹,可見Socket編程在現代編程中占據了多麼重要的地位,這一節我們將介紹Go語言中如何進行Socket編程。
什麼是Socket?
Socket起源於Unix,而Unix基本哲學之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。Socket就是該模式的一個實現,網路的Socket數據傳輸是一種特殊的I/O,Socket也是一種文件描述符。Socket也具有一個類似於打開文件的函數調用:Socket(),該函數返回一個整型的Socket描述符,隨後的連接建立、數據傳輸等操作都是通過該Socket實現的。
常用的Socket類型有兩種:流式Socket(SOCK_STREAM)和數據報式Socket(SOCK_DGRAM)。流式是一種面向連接的Socket,針對於面向連接的TCP服務應用;數據報式Socket是一種無連接的Socket,對應於無連接的UDP服務應用。
Socket如何通信
網路中的進程之間如何通過Socket通信呢?首要解決的問題是如何唯一標識一個進程,否則通信無從談起!在本地可以通過進程PID來唯一標識一個進程,但是在網路中這是行不通的。其實TCP/IP協議族已經幫我們解決了這個問題,網路層的“ip地址”可以唯一標識網路中的主機,而傳輸層的“協議+埠”可以唯一標識主機中的應用程式(進程)。這樣利用三元組(ip地址,協議,埠)就可以標識網路的進程了,網路中需要互相通信的進程,就可以利用這個標誌在他們之間進行交互
使用TCP/IP協議的應用程式通常採用應用編程介面:UNIX BSD的套接字(socket)和UNIX System V的TLI(已經被淘汰),來實現網路進程之間的通信。就目前而言,幾乎所有的應用程式都是採用socket,而現在又是網路時代,網路中進程通信是無處不在,這就是為什麼說“一切皆Socket”。
Socket基礎知識
通過上面的介紹我們知道Socket有兩種:TCP Socket和UDP Socket,TCP和UDP是協議,而要確定一個進程的需要三元組,需要IP地址和埠。
IPv4地址
目前的全球網際網路所採用的協議族是TCP/IP協議。IP是TCP/IP協議中網路層的協議,是TCP/IP協議族的核心協議。目前主要採用的IP協議的版本號是4(簡稱為IPv4),發展至今已經使用了30多年。
IPv4的地址位數為32位,也就是最多有2的32次方的網路設備可以聯到Internet上。近十年來由於互聯網的蓬勃發展,IP位址的需求量愈來愈大,使得IP位址的發放愈趨緊張,前一段時間,據報道IPV4的地址已經發放完畢,我們公司目前很多伺服器的IP都是一個寶貴的資源。
地址格式類似這樣:127.0.0.1 172.122.121.111
IPv6地址
IPv6是下一版本的互聯網協議,也可以說是下一代互聯網的協議,它是為瞭解決IPv4在實施過程中遇到的各種問題而被提出的,IPv6採用128位地址長度,幾乎可以不受限制地提供地址。按保守方法估算IPv6實際可分配的地址,整個地球的每平方米面積上仍可分配1000多個地址。在IPv6的設計過程中除了一勞永逸地解決了地址短缺問題以外,還考慮了在IPv4中解決不好的其它問題,主要有端到端IP連接、服務質量(QoS)、安全性、多播、移動性、即插即用等。
地址格式類似這樣:2002:c0e8:82e7:0:0:0:c0e8:82e7
這一節講了tcp編程,幸好我之前學習過一點tcp編程的知識,不然又是啥都不知道~~~~
WebSocket
WebSocket是HTML5的重要特性,它實現了基於瀏覽器的遠程socket,它使瀏覽器和伺服器可以進行全雙工通信,許多瀏覽器(Firefox、Google Chrome和Safari)都已對此做了支持。
在WebSocket出現之前,為了實現即時通信,採用的技術都是“輪詢”,即在特定的時間間隔內,由瀏覽器對伺服器發出HTTP Request,伺服器在收到請求後,返回最新的數據給瀏覽器刷新,“輪詢”使得瀏覽器需要對伺服器不斷發出請求,這樣會占用大量帶寬。
WebSocket採用了一些特殊的報頭,使得瀏覽器和伺服器只需要做一個握手的動作,就可以在瀏覽器和伺服器之間建立一條連接通道。且此連接會保持在活動狀態,你可以使用JavaScript來向連接寫入或從中接收數據,就像在使用一個常規的TCP Socket一樣。它解決了Web實時化的問題,相比傳統HTTP有如下好處:
- 一個Web客戶端只建立一個TCP連接
- Websocket服務端可以推送(push)數據到web客戶端.
- 有更加輕量級的頭,減少數據傳送量
WebSocket URL的起始輸入是ws://或是wss://(在SSL上
Go實現WebSocket
WebSocket分為客戶端和服務端,接下來我們將實現一個簡單的例子:用戶輸入信息,客戶端通過WebSocket將信息發送給伺服器端,伺服器端收到信息之後主動Push信息到客戶端,然後客戶端將輸出其收到的信息,客戶端的代碼如下:
<html>
<head></head>
<body>
<script type="text/javascript">
var sock = null;
var wsuri = "ws://127.0.0.1:1234";
window.onload = function() {
console.log("onload");
sock = new WebSocket(wsuri);
sock.onopen = function() {
console.log("connected to " + wsuri);
}
sock.onclose = function(e) {
console.log("connection closed (" + e.code + ")");
}
sock.onmessage = function(e) {
console.log("message received: " + e.data);
}
};
function send() {
var msg = document.getElementById('message').value;
sock.send(msg);
};
</script>
<h1>WebSocket Echo Test</h1>
<form>
<p>
Message: <input id="message" type="text" value="Hello, world!">
</p>
</form>
<button onclick="send();">Send Message</button>
</body>
</html>
可以看到客戶端JS,很容易的就通過WebSocket函數建立了一個與伺服器的連接sock,當握手成功後,會觸發WebSocket對象的onopen事件,告訴客戶端連接已經成功建立。客戶端一共綁定了四個事件。
1)onopen 建立連接後觸發
2)onmessage 收到消息後觸發
3)onerror 發生錯誤時觸發
4)onclose 關閉連接時觸發
我們伺服器端的實現如下:
package main
import (
"golang.org/x/net/websocket"
"fmt"
"log"
"net/http"
)
func Echo(ws *websocket.Conn) {
var err error
for {
var reply string
if err = websocket.Message.Receive(ws, &reply); err != nil {
fmt.Println("Can't receive")
break
}
fmt.Println("Received back from client: " + reply)
msg := "Received: " + reply
fmt.Println("Sending to client: " + msg)
if err = websocket.Message.Send(ws, msg); err != nil {
fmt.Println("Can't send")
break
}
}
}
func main() {
http.Handle("/", websocket.Handler(Echo))
if err := http.ListenAndServe(":1234", nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}
當客戶端將用戶輸入的信息Send之後,伺服器端通過Receive接收到了相應信息,然後通過Send發送了應答信息
目前隨著HTML5的發展,我想未來WebSocket會是Web開發的一個重點,我們需要儲備這方面的知識
REST
RESTful,是目前最為流行的一種互聯網軟體架構。因為它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多網站的採用。本小節我們將來學習它到底是一種什麼樣的架構?以及在Go裡面如何來實現它。
什麼是REST
REST(REpresentational State Transfer)這個概念,首次出現是在 2000年Roy Thomas Fielding(他是HTTP規範的主要編寫者之一)的博士論文中,它指的是一組架構約束條件和原則。滿足這些約束條件和原則的應用程式或設計就是RESTful的。
要理解什麼是REST,我們需要理解下麵幾個概念:
資源(Resources) REST是"表現層狀態轉化",其實它省略了主語。"表現層"其實指的是"資源"的"表現層"。
那麼什麼是資源呢?就是我們平常上網訪問的一張圖片、一個文檔、一個視頻等。這些資源我們通過URI來定位,也就是一個URI表示一個資源。表現層(Representation)
資源是做一個具體的實體信息,他可以有多種的展現方式。而把實體展現出來就是表現層,例如一個txt文本信息,他可以輸出成html、json、xml等格式,一個圖片他可以jpg、png等方式展現,這個就是表現層的意思。
URI確定一個資源,但是如何確定它的具體表現形式呢?應該在HTTP請求的頭信息中用Accept和Content-Type欄位指定,這兩個欄位才是對"表現層"的描述。
- 狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和伺服器的一個互動過程。在這個過程中,肯定涉及到數據和狀態的變化。而HTTP協議是無狀態的,那麼這些狀態肯定保存在伺服器端,所以如果客戶端想要通知伺服器端改變數據和狀態的變化,肯定要通過某種方式來通知它。
客戶端能通知伺服器端的手段,只能是HTTP協議。具體來說,就是HTTP協議裡面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源。
綜合上面的解釋,我們總結一下什麼是RESTful架構:
(1)每一個URI代表一種資源;
(2)客戶端和伺服器之間,傳遞這種資源的某種表現層;
(3)客戶端通過四個HTTP動詞,對伺服器端資源進行操作,實現"表現層狀態轉化"。
Web應用要滿足REST最重要的原則是:客戶端和伺服器之間的交互在請求之間是無狀態的,即從客戶端到伺服器的每個請求都必須包含理解請求所必需的信息。如果伺服器在請求之間的任何時間點重啟,客戶端不會得到通知。此外此請求可以由任何可用伺服器回答,這十分適合雲計算之類的環境。因為是無狀態的,所以客戶端可以緩存數據以改進性能。
另一個重要的REST原則是系統分層,這表示組件無法瞭解除了與它直接交互的層次以外的組件。通過將系統知識限制在單個層,可以限制整個系統的複雜性,從而促進了底層的獨立性。
當REST架構的約束條件作為一個整體應用時,將生成一個可以擴展到大量客戶端的應用程式。它還降低了客戶端和伺服器之間的交互延遲。統一界面簡化了整個系統架構,改進了子系統之間交互的可見性。REST簡化了客戶端和伺服器的實現,而且對於使用REST開發的應用程式更加容易擴展
RESTful的實現
Go沒有為REST提供直接支持,但是因為RESTful是基於HTTP協議實現的,所以我們可以利用net/http包來自己實現,當然需要針對REST做一些改造,REST是根據不同的method來處理相應的資源,目前已經存在的很多自稱是REST的應用,其實並沒有真正的實現REST
REST就是根據不同的method訪問同一個資源的時候實現不同的邏輯處理。
REST是一種架構風格,汲取了WWW的成功經驗:無狀態,以資源為中心,充分利用HTTP協議和URI協議,提供統一的介面定義,使得它作為一種設計Web服務的方法而變得流行。在某種意義上,通過強調URI和HTTP等早期Internet標準,REST是對大型應用程式伺服器時代之前的Web方式的回歸。目前Go對於REST的支持還是很簡單的,通過實現自定義的路由規則,我們就可以為不同的method實現不同的handle,這樣就實現了REST的架構。
RPC
前面幾個小節我們介紹瞭如何基於Socket和HTTP來編寫網路應用,通過學習我們瞭解了Socket和HTTP採用的是類似"信息交換"模式,即客戶端發送一條信息到服務端,然後(一般來說)伺服器端都會返回一定的信息以表示響應。客戶端和服務端之間約定了交互信息的格式,以便雙方都能夠解析交互所產生的信息。但是很多獨立的應用並沒有採用這種模式,而是採用類似常規的函數調用的方式來完成想要的功能。
RPC就是想實現函數調用模式的網路化。客戶端就像調用本地函數一樣,然後客戶端把這些參數打包之後通過網路傳遞到服務端,服務端解包到處理過程中執行,然後執行的結果反饋給客戶端。
RPC(Remote Procedure Call Protocol)——遠程過程調用協議,是一種通過網路從遠程電腦程式上請求服務,而不需要瞭解底層網路技術的協議。它假定某些傳輸協議的存在,如TCP或UDP,以便為通信程式之間攜帶信息數據。通過它可以使函數調用模式網路化。在OSI網路通信模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網路分散式多程式在內的應用程式更加容易。
go RPC
Go標準包中已經提供了對RPC的支持,而且支持三個級別的RPC:TCP、HTTP、JSONRPC。但Go的RPC包是獨一無二的RPC,它和傳統的RPC系統不同,它只支持Go開發的伺服器與客戶端之間的交互,因為在內部,它們採用了Gob來編碼。
Go已經提供了對RPC的良好支持,通過上面HTTP、TCP、JSON RPC的實現,我們就可以很方便的開發很多分散式的Web應用,我想作為讀者的你已經領會到這一點。但遺憾的是目前Go尚未提供對SOAP RPC的支持,欣慰的是現在已經有第三方的開源實現了。
可惜,rpc這我根本看不懂┭┮﹏┭┮,只是大概知道它是怎嘛做的
這一章我們介紹了目前流行的幾種主要的網路應用開發方式,第一小節介紹了網路編程中的基礎:Socket編程,因為現在網路正在朝雲的方向快速進化,作為這一技術演進的基石的的socket知識,作為開發者的你,是必須要掌握的。第二小節介紹了正愈發流行的HTML5中一個重要的特性WebSocket,通過它,伺服器可以實現主動的push消息,以簡化以前ajax輪詢的模式。第三小節介紹了REST編寫模式,這種模式特別適合來開髮網絡應用API,目前移動應用的快速發展,我覺得將來會是一個潮流。第四小節介紹了Go實現的RPC相關知識,對於上面四種開發方式,Go都已經提供了良好的支持,net包及其子包,是所有涉及到網路編程的工具的所在地
(〝▼皿▼)這一章一節比一節難,尤其是最後一節