Go語言Web基礎--Go如何使得Web工作 - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不(bù)管是(shì)網站,軟件還是(shì)小程序,都要(yào / yāo)直接或間接能爲(wéi / wèi)您産生價值,我們在(zài)追求其視覺表現的(de)同時(shí),更側重于(yú)功能的(de)便捷,營銷的(de)便利,運營的(de)高效,讓網站成爲(wéi / wèi)營銷工具,讓軟件能切實提升企業内部管理水平和(hé / huò)效率。優秀的(de)程序爲(wéi / wèi)後期升級提供便捷的(de)支持!

您當前位置>首頁 » 新聞資訊 » 網站建設 >

Go語言Web基礎--Go如何使得Web工作

發表時(shí)間:2018-7-22

發布人(rén):融晨科技

浏覽次數:92

Go搭建一個(gè)Web服務器

前面小節已經介紹了(le/liǎo)Web是(shì)基于(yú)http協議的(de)一個(gè)服務,Go語言裏面提供了(le/liǎo)一個(gè)完善的(de)net/http包,通過http包可以(yǐ)很方便的(de)就(jiù)搭建起來(lái)一個(gè)可以(yǐ)運行的(de)Web服務。同時(shí)使用這(zhè)個(gè)包能很簡單地(dì / de)對Web的(de)路由,靜态文件,模版,cookie等數據進行設置和(hé / huò)操作。

http包建立Web服務器

package main

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

func sayhelloName(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()  //解析參數,默認是(shì)不(bù)會解析的(de)
    fmt.Println(r.Form)  //這(zhè)些信息是(shì)輸出(chū)到(dào)服務器端的(de)打印信息
    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 astaxie!") //這(zhè)個(gè)寫入到(dào)w的(de)是(shì)輸出(chū)到(dào)客戶端的(de)
}

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

上(shàng)面這(zhè)個(gè)代碼,我們build之(zhī)後,然後執行web.exe,這(zhè)個(gè)時(shí)候其實已經在(zài)9090端口監聽http鏈接請求了(le/liǎo)。

在(zài)浏覽器輸入http://localhost:9090

可以(yǐ)看到(dào)浏覽器頁面輸出(chū)了(le/liǎo)Hello astaxie!

可以(yǐ)換一個(gè)地(dì / de)址試試:http://localhost:9090/?url_long=111&url_long=222

看看浏覽器輸出(chū)的(de)是(shì)什麽,服務器輸出(chū)的(de)是(shì)什麽?

在(zài)服務器端輸出(chū)的(de)信息如下:

我們看到(dào)上(shàng)面的(de)代碼,要(yào / yāo)編寫一個(gè)Web服務器很簡單,隻要(yào / yāo)調用http包的(de)兩個(gè)函數就(jiù)可以(yǐ)了(le/liǎo)。

如果你以(yǐ)前是(shì)PHP程序員,那你也(yě)許就(jiù)會問,我們的(de)nginx、apache服務器不(bù)需要(yào / yāo)嗎?Go就(jiù)是(shì)不(bù)需要(yào / yāo)這(zhè)些,因爲(wéi / wèi)他(tā)直接就(jiù)監聽tcp端口了(le/liǎo),做了(le/liǎo)nginx做的(de)事情,然後sayhelloName這(zhè)個(gè)其實就(jiù)是(shì)我們寫的(de)邏輯函數了(le/liǎo),跟php裏面的(de)控制層(controller)函數類似。

如果你以(yǐ)前是(shì)Python程序員,那麽你一定聽說(shuō)過tornado,這(zhè)個(gè)代碼和(hé / huò)他(tā)是(shì)不(bù)是(shì)很像,對,沒錯,Go就(jiù)是(shì)擁有類似Python這(zhè)樣動态語言的(de)特性,寫Web應用很方便。

如果你以(yǐ)前是(shì)Ruby程序員,會發現和(hé / huò)ROR的(de)/script/server啓動有點類似。

我們看到(dào)Go通過簡單的(de)幾行代碼就(jiù)已經運行起來(lái)一個(gè)Web服務了(le/liǎo),而(ér)且這(zhè)個(gè)Web服務内部有支持高并發的(de)特性,我将會在(zài)接下來(lái)的(de)兩個(gè)小節裏面詳細的(de)講解一下Go是(shì)如何實現Web高并發的(de)。


Go如何使得Web工作

前面介紹了(le/liǎo)如何通過Go搭建一個(gè)Web服務,我們可以(yǐ)看到(dào)簡單應用一個(gè)net/http包就(jiù)方便的(de)搭建起來(lái)了(le/liǎo)。那麽Go在(zài)底層到(dào)底是(shì)怎麽做的(de)呢?萬變不(bù)離其宗,Go的(de)Web服務工作也(yě)離不(bù)開我們前面介紹的(de)Web工作方式。

web工作方式的(de)幾個(gè)概念

以(yǐ)下均是(shì)服務器端的(de)幾個(gè)概念

  • Request:用戶請求的(de)信息,用來(lái)解析用戶的(de)請求信息,包括post、get、cookie、url等信息
  • Response:服務器需要(yào / yāo)反饋給客戶端的(de)信息
  • Conn:用戶的(de)每次請求鏈接
  • Handler:處理請求和(hé / huò)生成返回信息的(de)處理邏輯

分析http包運行機制

如下圖所示,是(shì)Go實現Web服務的(de)工作模式的(de)流程圖

http包執行流程:

  1. 創建Listen Socket, 監聽指定的(de)端口, 等待客戶端請求到(dào)來(lái)。

  2. Listen Socket接受客戶端的(de)請求, 得到(dào)Client Socket, 接下來(lái)通過Client Socket與客戶端通信。

  3. 處理客戶端的(de)請求, 首先從Client Socket讀取HTTP請求的(de)協議頭, 如果是(shì)POST方法, 還可能要(yào / yāo)讀取客戶端提交的(de)數據, 然後交給相應的(de)handler處理請求, handler處理完畢準備好客戶端需要(yào / yāo)的(de)數據, 通過Client Socket寫給客戶端。

這(zhè)整個(gè)的(de)過程裏面我們隻要(yào / yāo)了(le/liǎo)解清楚下面三個(gè)問題,也(yě)就(jiù)知道(dào)Go是(shì)如何讓Web運行起來(lái)了(le/liǎo)

  • 如何監聽端口?
  • 如何接收客戶端請求?
  • 如何分配handler?

Go是(shì)通過一個(gè)函數ListenAndServe來(lái)處理這(zhè)些事情的(de),這(zhè)個(gè)底層其實這(zhè)樣處理的(de):初始化一個(gè)server對象,然後調用了(le/liǎo)net.Listen("tcp", addr),也(yě)就(jiù)是(shì)底層用TCP協議搭建了(le/liǎo)一個(gè)服務,然後監控我們設置的(de)端口。

下面代碼來(lái)自Go的(de)http包的(de)源碼,通過下面的(de)代碼我們可以(yǐ)看到(dào)整個(gè)的(de)http處理過程:

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep>監控之(zhī)後如何接收客戶端的(de)請求呢?上(shàng)面代碼執行監控端口之(zhī)後,調用了(le/liǎo)srv.Serve(net.Listener)函數,這(zhè)個(gè)函數就(jiù)是(shì)處理接收客戶端的(de)請求信息。這(zhè)個(gè)函數裏面起了(le/liǎo)一個(gè)for{},首先通過Listener接收請求,其次創建一個(gè)Conn,最後單獨開了(le/liǎo)一個(gè)goroutine,把這(zhè)個(gè)請求的(de)數據當做參數扔給這(zhè)個(gè)conn去服務:go c.serve()。這(zhè)個(gè)就(jiù)是(shì)高并發體現了(le/liǎo),用戶的(de)每一次請求都是(shì)在(zài)一個(gè)新的(de)goroutine去服務,相互不(bù)影響。 

那麽如何具體分配到(dào)相應的(de)函數來(lái)處理請求呢?conn首先會解析request:c.readRequest(),然後獲取相應的(de)handler:handler := c.server.Handler,也(yě)就(jiù)是(shì)我們剛才在(zài)調用函數ListenAndServe時(shí)候的(de)第二個(gè)參數,我們前面例子(zǐ)傳遞的(de)是(shì)nil,也(yě)就(jiù)是(shì)爲(wéi / wèi)空,那麽默認獲取handler = DefaultServeMux,那麽這(zhè)個(gè)變量用來(lái)做什麽的(de)呢?對,這(zhè)個(gè)變量就(jiù)是(shì)一個(gè)路由器,它用來(lái)匹配url跳轉到(dào)其相應的(de)handle函數,那麽這(zhè)個(gè)我們有設置過嗎?有,我們調用的(de)代碼裏面第一句不(bù)是(shì)調用了(le/liǎo)http.HandleFunc("/", sayhelloName)嘛。這(zhè)個(gè)作用就(jiù)是(shì)注冊了(le/liǎo)請求/的(de)路由規則,當請求uri爲(wéi / wèi)"/",路由就(jiù)會轉到(dào)函數sayhelloName,DefaultServeMux會調用ServeHTTP方法,這(zhè)個(gè)方法内部其實就(jiù)是(shì)調用sayhelloName本身,最後通過寫入response的(de)信息反饋到(dào)客戶端。

詳細的(de)整個(gè)流程如下圖所示:

至此我們的(de)三個(gè)問題已經全部得到(dào)了(le/liǎo)解答,你現在(zài)對于(yú)Go如何讓Web跑起來(lái)的(de)是(shì)否已經基本了(le/liǎo)解呢?


End

相關案例查看更多