Sevice Computing:閱讀:《Golang web 應用開發》

這次作業是閱讀《Golang web 應用開發》,後來發現這次也是要寫博客作業的,所以期末抽空又讀了一下然後總結一下web章節和自己的想法。整本書在GitHub上面開源的,閱讀鏈接如下:

《Golang web 應用開發》閱讀鏈接

爲什麼是Golang?

這個部分書中其實沒有寫到,但是整章看下來其實我們可以感覺到,Golang開發Web應用是很方便的,同時又是相當的靈活。Golang 上手簡單, 而且在語法上的要求相對不那麼嚴格。同時Golang又有非常豐富的第三方庫, 官方已經有提供net/http包爲搭建http服務器做準備。使用這個包能很簡單地對web的路由,靜態文件,模版,cookie等數據進行設置。對於業務沒那麼複雜的項目,作爲簡單的web server, 寫一些API 的後端是不錯的選擇。下面是對閱讀內容重點的提煉和總結分析。

Web工作方式

我們在上網的過程其實就是web在工作。一個Web服務器也被稱爲HTTP服務器,它通過HTTP協議與客戶端通信。這個客戶端通常指的是Web瀏覽器(其實手機端客戶端內部也是瀏覽器實現的)。對於普通的上網過程,系統其實是這樣做的:

瀏覽器本身是一個客戶端,當你輸入URL的時候,首先瀏覽器會去請求DNS服務器,通過DNS獲取相應的域名對應的IP,然後通過IP地址找到IP對應的服務器後,要求建立TCP連接,等瀏覽器發送完HTTP
Request(請求)包後,服務器接收到請求包之後纔開始處理請求包,服務器調用自身服務,返回HTTP
Response(響應)包;客戶端收到來自服務器的響應後開始渲染這個Response包裏的主體(body),等收到全部的內容隨後斷開與該服務器之間的TCP連接。客戶機與服務器之間的通信是非持久連接的,也就是當服務器發送了應答後就與客戶機斷開連接,等待下一次請求。

URL和DNS解析

URL(Uniform Resource Locator)是“統一資源定位符”的英文縮寫,用於描述一個網絡上的資源, 基本格式如下

scheme://host[:port#]/path/…/[?query-string][#anchor]
scheme :指定底層使用的協議(例如:http, https, ftp)
host :HTTP服務器的IP地址或者域名
port# :HTTP服務器的默認端口是80,這種情況下端口號可以省略。如果使用了別的端口,必須指明,例如http://www.cnblogs.com:8080/
path : 訪問資源的路徑
query-string :發送給http服務器的數據
anchor : 錨

DNS(Domain Name System)是“域名系統”的英文縮寫,是一種組織成域層次結構的計算機和網絡服務命名系統,它用於TCP/IP網絡,它從事將主機名或域名轉換爲實際IP地址的工作。DNS就是這樣的一位“翻譯官”,它的基本工作原理可用下圖來表示。
在這裏插入圖片描述
通過上面的步驟,我們最後獲取的是IP地址,也就是瀏覽器最後發起請求的時候是基於IP來和服務器做信息交互的。

HTTP協議

HTTP協議是Web工作的核心,所以要了解清楚Web的工作方式就需要詳細的瞭解清楚HTTP是怎麼樣工作的。

HTTP是一種讓Web服務器與瀏覽器(客戶端)通過Internet發送與接收數據的協議,它建立在TCP協議之上,一般採用TCP的80端口。它是一個請求、響應協議–客戶端發出一個請求,服務器響應這個請求。在HTTP中,客戶端總是通過建立一個連接與發送一個HTTP請求來發起一個事務。服務器不能主動去與客戶端聯繫,也不能給客戶端發出一個回調連接。客戶端與服務器端都可以提前中斷一個連接。例如,當瀏覽器下載一個文件時,你可以通過點擊“停止”鍵來中斷文件的下載,關閉與服務器的HTTP連接。

HTTP協議是無狀態的,同一個客戶端的這次請求和上次請求是沒有對應關係的,對HTTP服務器來說,它並不知道這兩個請求是否來自同一個客戶端。爲了解決這個問題, Web程序引入了Cookie機制來維護連接的可持續狀態。

Go搭建一個Web服務器

Go語言裏面提供了一個完善的net/http包,通過http包可以很方便的搭建起來一個可以運行的Web服務。同時使用這個包能很簡單地對Web的路由,靜態文件,模版,cookie等數據進行設置和操作。

下面我們就可以使用go語言來搭建一個web服務器了。在這本書中給出了一個案例代碼如下,我們build之後,然後執行web.exe,這個時候其實已經在9090端口監聽http鏈接請求了。

在瀏覽器輸入http://localhost:9090可以看到瀏覽器頁面的輸出了。

package main

import (
	"fmt"
	"net/http"
	"strings"
	"log"
)

func sayhelloName(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()  //解析參數,默認是不會解析的
	fmt.Println(r.Form)  //這些信息是輸出到服務器端的打印信息
	fmt.Println("path", r.URL.Path)
	fmt.Println("scheme", r.URL.Scheme)
	fmt.Println(r.Form["url_long"])
	for k, v := range r.Form {
		fmt.Println("key:", k)
		fmt.Println("val:", strings.Join(v, ""))
	}
	fmt.Fprintf(w, "Hello Jessicazzw!") //這個寫入到w的是輸出到客戶端的
}

func main() {
	http.HandleFunc("/", sayhelloName) //設置訪問的路由
	err := http.ListenAndServe(":9090", nil) //設置監聽的端口
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

Go如何使得Web工作

http包的執行流程大致可以分成以下三步:

  1. 創建Listen Socket, 監聽指定的端口, 等待客戶端請求到來。
  2. Listen Socket接受客戶端的請求, 得到Client Socket, 接下來通過Client Socket與客戶端通信。
  3. 處理客戶端的請求, 首先從Client Socket讀取HTTP請求的協議頭, 如果是POST方法, 還可能要讀取客戶端提交的數據, 然後交給相應的handler處理請求, handler處理完畢準備好客戶端需要的數據, 通過Client Socket寫給客戶端。
    在這裏插入圖片描述
    下面代碼來自Go的http包的源碼,通過下面的代碼我們可以看到整個的http處理過程:
func (srv *Server) Serve(l net.Listener) error {
	defer l.Close()
	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		rw, e := l.Accept()
		if e != nil {
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return e
		}
		tempDelay = 0
		c, err := srv.newConn(rw)
		if err != nil {
			continue
		}
		go c.serve()
	}
}

Go是通過一個函數ListenAndServe來處理這些事情的,這個底層其實這樣處理的:初始化一個server對象,然後調用了net.Listen(“tcp”, addr),也就是底層用TCP協議搭建了一個服務,然後監控我們設置的端口。

執行監控端口之後,調用了srv.Serve(net.Listener)函數,這個函數就是處理接收客戶端的請求信息。這個函數裏面起了一個for{},首先通過Listener接收請求,其次創建一個Conn,最後單獨開了一個goroutine,把這個請求的數據當做參數扔給這個conn去服務:go c.serve()。這個就是高併發體現了,用戶的每一次請求都是在一個新的goroutine去服務,相互不影響。

conn首先會解析request:c.readRequest(),然後獲取相應的handler:handler := c.server.Handler,也就是我們剛纔在調用函數ListenAndServe時候的第二個參數,我們前面例子傳遞的是nil,也就是爲空,那麼默認獲取handler = DefaultServeMux,那麼這個變量用來做什麼的呢?對,這個變量就是一個路由器,它用來匹配url跳轉到其相應的handle函數,那麼這個我們有設置過嗎?有,我們調用的代碼裏面第一句不是調用了http.HandleFunc("/", sayhelloName)嘛。這個作用就是註冊了請求/的路由規則,當請求uri爲"/",路由就會轉到函數sayhelloName,DefaultServeMux會調用ServeHTTP方法,這個方法內部其實就是調用sayhelloName本身,最後通過寫入response的信息反饋到客戶端。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章