Golang伺服器的網路層實現

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

原文:Golang伺服器的網路層實現

由於最近有接觸到一些長已連線的服務器實現,對網路模型有所學習。對基於C/C++的網路模型實現和基於GoLang的實現對比下來,發現Golang的網路模型編程難度大大降低,這得益於Golang的goroutine,可以在編程的時候肆無忌憚的建立並發"線程",當伺服器能為每一個用戶端都開啟若干"線程"的話,編程變的簡單很多。

傳統語言的網路層處理

服務需要同時服務N個用戶端,所以傳統的編程方式是採用IO複用,這樣在一個線程中對N個通訊端進行事件捕獲,當讀寫事件產生後再真正read()或者write(),這樣才能提高吞吐:

中:

  • 綠色線程為接受用戶端TCP連結的線程,使用阻塞的調用socket.accept(),當有新的串連到來後,將socket對象conn加入IO複用隊列。

  • 紫色線程為IO複用的阻塞調用,通常採用epoll等系統調用實現IO複用。當IO複用隊列中的任意socket有資料到來,或者寫緩衝區空閑時可觸發epoll調用的返回,否則阻塞epoll調用。資料的實際發送和接收都在紫色線程中完成。所以為了提高吞吐,對某個socket的readwrite都應該使用非阻塞的模式,這樣才能最大限度的提高系統吞吐。例如,假設正在對某個socket調用阻塞的write,當資料沒有完全發送完成前,write將無法返回,從而阻止了整個epoll進入下一個迴圈,如果這個時候其他的socket有讀就緒的話,將無法第一時間響應。所以非阻塞的讀寫將在某個fd讀寫較慢的時候,立刻返回,而不會一直等到讀寫結束。這樣才能提高吞吐。然而,採用非阻讀寫將大大提高編程難度。

  • 紫色線程負責將資料進行解碼並放入隊列中,等待背景工作執行緒處理;背景工作執行緒有資料要發送時,也將資料放入發送隊列,並通過某種機制通知紫色線程對應的socket有資料要寫,進而使得資料在紫色線程中寫入socket。

這種模型的編程難度主要體現在:

  1. 線程少(也不能太多),導致一個線程需要處理多個描述符,從而存在對描述符狀態的維護問題。甚至,業務層面的會話等都需要小心維護

  2. 非阻塞IO調用,使描述符的狀態更為複雜

  3. 隊列的同步處理

不得不說,能用C或C++來寫伺服器的是真大神!

Golang的goroutine

Golang是一門比較新的語言,正在快速的發展。Golang從語言層面支援一種叫協程的輕量級執行緒模式,稱為goroutine。當我們建立協程時,實際並不會建立作業系統的線程,Golang會使用現有的線程來調度協程。也就是說,從程式員的角度,協程是並發執行的,好像線程一下,而從作業系統的角度來看,程式可能只有幾個線程在運行。在同一個應用程式中,協程可以有成千上萬個!所以可以有成千上萬個並發任務,而這些任務的調度又十分輕量,比線程調度輕量的多的多。所以從程式員的角度,使用Golang就可以在一個應用程式中同時開啟成千上萬個並發任務。簡直逆天!

在Golang中使用go關鍵字來開啟一個goroutine

func main() {    log.Println("Hello, world")    netListen, err := net.Listen("tcp", "localhost:4000")    if err != nil {        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())        os.Exit(1)    }    defer netListen.Close()    log.Println("Waiting for clients")    for {        conn, err := netListen.Accept()        if err != nil {            continue        }        log.Println(conn.RemoteAddr().String(), " tcp connect success")        go handleConnection(conn)    }}func handleConnection(conn net.Conn) {    ...}

Golang的channel

除了對並發的支援外,Golang中有一種叫channel的並發同步機制。channel類似隊列,是goroutine安全的。所以結合goroutinechannel可以輕而易舉實現並發編程。

Golang如何?網路層

通過參考多個Golang的開來源程式,筆者得出的結論是:肆無忌憚的用goroutine吧。於是一個Golang版的網路模型大致是這樣的:

是單個用戶端已連線的服務器模組結構,同樣的一個顏色代表一個協程:

  • 綠色goroutine依然是接受TCP連結

  • 當完成握手accept返回conn對象後,使用一個單獨的goroutine阻塞讀(紫色),使用一個單獨的goroutine阻塞寫(紅色)

  • 讀到的資料通過解碼後放入讀channel,並由藍色的goroutine來處理

  • 需要寫資料時,藍色的goroutine將資料寫入寫channel,從而觸發紅色的goroutine編碼並寫入conn

可以看到,針對一個用戶端,服務端至少有3個goroutine在單獨為這個用戶端服務。如果從線程的角度來看,簡直是浪費啊,然而這就是協程的好處。這個模型很容易理解,因為跟人們的正常思維方式是一致的。並且都是阻塞的調用,所以無需維護狀態。

再來看看多個用戶端的情況:

在多個用戶端之間,雖然用了相同的顏色表示goroutine,但實際上他們都是獨立的goroutine,可以想象goroutine的數量將是驚人的。然而,根本不用擔心!這樣的應用程式可能真正的線程只有幾個而已。

相關文章

聯繫我們

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