最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang實(shí)現(xiàn)四層負(fù)載均衡的示例代碼

 更新時(shí)間:2023年07月02日 14:29:25   作者:藍(lán)胖子的編程夢  
做開發(fā)的同學(xué)應(yīng)該經(jīng)常聽到過負(fù)載均衡的概念,今天我們就來實(shí)現(xiàn)一個(gè)乞丐版的四層負(fù)載均衡,并用它對(duì)mysql進(jìn)行負(fù)載均衡測試,感興趣的可以了解一下

本文代碼已經(jīng)上傳到github

https://github.com/HobbyBear/codelearning/tree/master/layer4balance

為了知識(shí)的完整性,我們也科普下七層負(fù)載均衡的概念,我們先簡單了解下四層負(fù)載均衡和7層負(fù)載均衡的區(qū)別。

四層負(fù)載均衡和七層負(fù)載均衡

七層負(fù)載均衡

首先,我們來看下七層負(fù)載均衡,它一般是針對(duì)應(yīng)用層請(qǐng)求協(xié)議做請(qǐng)求轉(zhuǎn)發(fā),拿http請(qǐng)求舉例,有A,B兩臺(tái)服務(wù)器,如果采用輪詢的負(fù)載均衡策略,負(fù)載均衡器將第一個(gè)請(qǐng)求轉(zhuǎn)發(fā)給了A服務(wù)器,那么第二個(gè)請(qǐng)求到達(dá)時(shí),負(fù)載均衡器就會(huì)把請(qǐng)求轉(zhuǎn)發(fā)到B服務(wù)器。

在轉(zhuǎn)發(fā)時(shí),能夠在應(yīng)用協(xié)議層對(duì)請(qǐng)求做一些變動(dòng),拿http請(qǐng)求來說,可以對(duì)http的請(qǐng)求頭,http路徑做相應(yīng)的變動(dòng)。

四層負(fù)載均衡

再來看看四層負(fù)載均衡,它一般是指針對(duì)連接做的負(fù)載均衡,舉例說明下,有A,B兩臺(tái)服務(wù)器,同樣采取輪詢的策略,某個(gè)客戶端發(fā)起一個(gè)新的連接,經(jīng)過均衡器連接到了A服務(wù)器,現(xiàn)在又來一個(gè)客戶端同樣發(fā)起連接,經(jīng)過均衡器后,此時(shí)就該和B服務(wù)器建立連接了。而在同一個(gè)連接里是能夠發(fā)送多個(gè)請(qǐng)求的,這也是和七層負(fù)載均衡最本質(zhì)的區(qū)別,它是針對(duì)連接做的負(fù)載均衡。

實(shí)現(xiàn)四層負(fù)載均衡器

實(shí)現(xiàn)四層負(fù)載均衡策略的方式有很多,比較著名的四層負(fù)載均衡軟件就有l(wèi)vs,它是通過修改數(shù)據(jù)包的ip地址或者mac地址實(shí)現(xiàn)四層負(fù)載均衡,性能較好,工作模式有好幾種,具體的就不在本文展開了。

本文實(shí)現(xiàn)的四層負(fù)載均衡的原理和nginx四層負(fù)載類似 ,通過均衡器在客戶端和服務(wù)端之前都維護(hù)一個(gè)連接來達(dá)到讓 客戶端在同一個(gè)連接里發(fā)送的請(qǐng)求都會(huì)被服務(wù)端同一個(gè)連接所接收的目的。如下圖所示:

以后client1 通過連接A發(fā)的請(qǐng)求都會(huì)由連接B發(fā)往服務(wù)器,而client2通過連接C發(fā)送的請(qǐng)求,都將經(jīng)過連接D發(fā)往另一臺(tái)服務(wù)器。

實(shí)現(xiàn)邏輯

現(xiàn)在讓我們來實(shí)現(xiàn)下這部分的邏輯,我將會(huì)以輪詢的策略實(shí)現(xiàn)連接的負(fù)載均衡。

并且這里還要考慮下實(shí)現(xiàn)數(shù)據(jù)復(fù)制的邏輯,我們需要在均衡器分別建立對(duì)客戶端和服務(wù)端的socket連接,并且將其中一個(gè)socket的數(shù)據(jù)轉(zhuǎn)移到另一個(gè)socket,如果每次都將某一個(gè)socket數(shù)據(jù)讀到用戶層,再寫到另一個(gè)socket就會(huì)導(dǎo)致一些沒有必要的拷貝。偽代碼如下:

var (
src net.Conn  // 一個(gè)socket 連接
dst net.Conn  // 一個(gè)socket連接
)
// ...
buf = make([]byte, size)    
nr, er := src.Read(buf)
nw, ew := dst.Write(buf[0:nr])

有沒有什么技術(shù)讓內(nèi)核自動(dòng)將某個(gè)socket的數(shù)據(jù)轉(zhuǎn)移到另一個(gè)socket,不用將數(shù)據(jù)拷貝到應(yīng)用層來,這正是零拷貝相關(guān)的技術(shù),關(guān)于零拷貝的技術(shù)原理我在之前這篇文章 有很詳細(xì)的介紹,內(nèi)核提供了一個(gè)splice的系統(tǒng)調(diào)用,專門用于socket連接間拷貝數(shù)據(jù),只需要調(diào)用時(shí)傳入對(duì)應(yīng)socket連接的文件描述符即可讓內(nèi)核自動(dòng)完成拷貝過程。

func?Splice(rfd?int,?roff?*int64,?wfd?int,?woff?*int64,?len?int,?flags?int)?(n?int64,?err?error)?

這個(gè)系統(tǒng)調(diào)用已經(jīng)被golang更深層次的封裝到了一個(gè)比較常用的方法io.Copy里,這個(gè)方法會(huì)自動(dòng)判斷reader和writer底層的類型,如果都是socket連接則會(huì)調(diào)用splice系統(tǒng)調(diào)用實(shí)現(xiàn)零拷貝。

func Copy(dst Writer, src Reader) (written int64, err error) {  
   return copyBuffer(dst, src, nil)  
}

接著我們看下均衡的代碼邏輯,運(yùn)行邏輯如下:

1, 監(jiān)聽到新連接,啟動(dòng)一個(gè)協(xié)程去處理連接。

2 , 在新協(xié)程里與通過輪詢的策略,選擇一個(gè)后端服務(wù)器并與之建立連接。

3, 啟動(dòng)兩個(gè)協(xié)程分別進(jìn)行io.Copy ,將客戶端的socket寫到服務(wù)端socket,將服務(wù)端socket返回的信息寫到客戶端socket。代碼如下:

type Server struct {  
   Li      net.Listener  
   Balance balancepolicy.Policy  
}  
func (s *Server) Run() {  
   for {  
      c, err := s.Li.Accept()  
      if err != nil {  
         log.Fatal(err)  
      }  
      go func(c net.Conn) {  
         remoteAddr := c.RemoteAddr()  
         backendIp := s.Balance.PickNode(remoteAddr.String())  
         serverConn, err := net.Dial("tcp", backendIp)  
         if err != nil {  
            log.Fatal(err)  
            c.Close()  
            return  
         }  
         fmt.Println("獲取到了新連接", remoteAddr, backendIp)  
         go func() {  
            _, err := io.Copy(serverConn, c)  
            if err != nil {  
               fmt.Println(err, 1)  
            }  
            c.Close()  
            serverConn.Close()  
            fmt.Println("結(jié)束1", err)  
         }()  
         go func() {  
            _, err := io.Copy(c, serverConn)  
            if err != nil {  
               fmt.Println(err, 2)  
            }  
            c.Close()  
            serverConn.Close()  
            fmt.Println("結(jié)束2", err)  
         }()  
      }(c)  
   }  
}

io.Copy 會(huì)不斷的拷貝源socket的數(shù)據(jù)到目的socket,直到連接關(guān)閉。

更好的方案

可以看到上述方案中維護(hù)一個(gè)客戶端的連接將會(huì)啟動(dòng)3個(gè)協(xié)程,當(dāng)連接量上去后,均衡器很可能成為瓶頸,有沒有辦法減少下協(xié)程的數(shù)量,可以直接采用epoll的方式監(jiān)聽連接的讀寫,以及關(guān)閉事件(這樣能在一個(gè)協(xié)程里處理多個(gè)連接),當(dāng)連接可讀時(shí),直接使用splice系統(tǒng)調(diào)用對(duì)數(shù)據(jù)進(jìn)行拷貝直到返回syscall.EAGAIN 就停止,因?yàn)榉祷豷yscall.EAGAIN 說明連接緩沖區(qū)內(nèi)的數(shù)據(jù)暫時(shí)被讀取完了,繼續(xù)下一次epoll wait的監(jiān)聽循環(huán)。這樣能極大的減少協(xié)程數(shù)量。不過實(shí)現(xiàn)我就不準(zhǔn)備再繼續(xù)展開了,后續(xù)有空再補(bǔ)充下這部分。對(duì)epoll的使用有興趣的同學(xué)也可以看看我之前一篇用epoll實(shí)現(xiàn)類似redis的網(wǎng)絡(luò)模型框架這篇文章

測試負(fù)載均衡代碼

現(xiàn)在讓我們來測試下負(fù)載均衡的代碼,我會(huì)用docker-compose去啟動(dòng)兩個(gè)mysql,然后本地啟動(dòng)我們負(fù)載均衡器的代碼,之后用兩個(gè)mysql客戶端去連接負(fù)載均衡器,看下是不是mysql客戶端連接到了不同的mysql服務(wù)器。

docker-compose的配置文件如下:

version: '3'  
services:  
  mysql1:  
    restart: always  
    image: amd64/mysql:latest  
    container_name: mysql1  
    environment:  
      - "MYSQL_ROOT_PASSWORD=1234567"  
      - "MYSQL_DATABASE=test"  
    ports:  
      - "3306:3306"  
  mysql2:  
    restart: always  
    image: amd64/mysql:latest  
    container_name: mysql2  
    environment:  
      - "MYSQL_ROOT_PASSWORD=1234567"  
      - "MYSQL_DATABASE=test2"  
    ports:  
      - "3307:3306"

為了能驗(yàn)證不同客戶端的確連上了不同的mysql服務(wù)器,我在mysql1上創(chuàng)建了test數(shù)據(jù)庫,在mysql2上創(chuàng)建了test2數(shù)據(jù)庫。到時(shí)候連上不同服務(wù)器數(shù)據(jù)庫是不一樣的。

均衡服務(wù)器監(jiān)聽5555端口啟動(dòng)

s := &proxy.Server{}  
li, err := net.Listen("tcp", ":5555")  
if err != nil {  
   log.Fatal(err)  
}  
s.Li = li  
s.Balance = balancepolicy.NewRoundRobin()  
s.Balance.AddNode("127.0.0.1:3306", "mysql1")  
s.Balance.AddNode("127.0.0.1:3307", "mysql2")  
s.Run()

之后用mysql客戶端去連接均衡服務(wù)器

## client1
mysql -h 127.0.0.1 -u root  -P 5555  -D test  -p1234567

## client2
mysql -h 127.0.0.1 -u root  -P 5555  -D test2  -p1234567

發(fā)現(xiàn)兩個(gè)mysql客戶端的確連接到了不同服務(wù)器,并且能正常執(zhí)行命令,over。

到此這篇關(guān)于Golang實(shí)現(xiàn)四層負(fù)載均衡的示例代碼的文章就介紹到這了,更多相關(guān)Golang四層負(fù)載均衡內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言中uintptr和unsafe.Pointer的區(qū)別的實(shí)現(xiàn)小結(jié)

    Go語言中uintptr和unsafe.Pointer的區(qū)別的實(shí)現(xiàn)小結(jié)

    Go語言中的uintptr和unsafe.Pointer都是用于底層內(nèi)存操作的類型,本文就來介紹一下uintptr和unsafe.Pointer的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-10-10
  • Go語言中make和new的區(qū)別及說明

    Go語言中make和new的區(qū)別及說明

    這篇文章主要介紹了Go語言中make和new的區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-07-07
  • Golang并發(fā)編程之GMP模型詳解

    Golang并發(fā)編程之GMP模型詳解

    傳統(tǒng)的并發(fā)編程模型是基于線程和共享內(nèi)存的同步訪問控制的,共享數(shù)據(jù)受鎖的保護(hù),線程將爭奪這些鎖以訪問數(shù)據(jù)。本文將介紹Go并發(fā)編程中的GMP模型,感興趣的可以了解一下
    2023-03-03
  • go語言中http超時(shí)引發(fā)的事故解決

    go語言中http超時(shí)引發(fā)的事故解決

    我們使用的是golang標(biāo)準(zhǔn)庫的http client,對(duì)于一些http請(qǐng)求,我們?cè)谔幚淼臅r(shí)候,會(huì)考慮加上超時(shí)時(shí)間,如果超時(shí)可能會(huì)引起報(bào)錯(cuò),本文就記一次超時(shí)引發(fā)的事故
    2021-06-06
  • 使用Singleflight實(shí)現(xiàn)Golang代碼優(yōu)化

    使用Singleflight實(shí)現(xiàn)Golang代碼優(yōu)化

    有許多方法可以優(yōu)化代碼以提高效率,減少運(yùn)行進(jìn)程就是其中之一,本文我們就來學(xué)習(xí)一下如何通過使用一個(gè)Go包Singleflight來減少重復(fù)進(jìn)程,從而優(yōu)化Go代碼吧
    2023-09-09
  • Go panic和recover函數(shù)使用細(xì)節(jié)深入探究

    Go panic和recover函數(shù)使用細(xì)節(jié)深入探究

    這篇文章主要為大家介紹了Go?的panic和recover函數(shù)使用細(xì)節(jié)深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go 語言中的 Struct Tag 的用法詳解

    Go 語言中的 Struct Tag 的用法詳解

    在 Go 語言中,結(jié)構(gòu)體字段標(biāo)簽(Struct Tag) 是一種用于給字段添加元信息(metadata)的機(jī)制,常用于序列化(如 JSON、XML)、ORM 映射、驗(yàn)證等場景,本文給大家介紹Go 語言中的 Struct Tag 的用法,感興趣的朋友一起看看吧
    2025-05-05
  • 詳解go語言是如何實(shí)現(xiàn)協(xié)程的

    詳解go語言是如何實(shí)現(xiàn)協(xié)程的

    go語言的精華就在于協(xié)程的設(shè)計(jì),只有理解協(xié)程的設(shè)計(jì)思想和工作機(jī)制,才能確保我們能夠完全的利用協(xié)程編寫強(qiáng)大的并發(fā)程序,所以本文將給大家介紹了go語言是如何實(shí)現(xiàn)協(xié)程的,文中有詳細(xì)的代碼講解,需要的朋友可以參考下
    2024-04-04
  • go語言通過zlib壓縮數(shù)據(jù)的方法

    go語言通過zlib壓縮數(shù)據(jù)的方法

    這篇文章主要介紹了go語言通過zlib壓縮數(shù)據(jù)的方法,實(shí)例分析了Go語言中zlib的使用技巧,需要的朋友可以參考下
    2015-03-03
  • sublime3+Golang+代碼補(bǔ)全的實(shí)現(xiàn)

    sublime3+Golang+代碼補(bǔ)全的實(shí)現(xiàn)

    本文主要介紹了sublime3+Golang+代碼補(bǔ)全的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評(píng)論

唐山市| 哈密市| 微博| 玉龙| 贵阳市| 乐陵市| 井研县| 江口县| 颍上县| 郴州市| 永善县| 开封县| 红桥区| 峨边| 涟源市| 宁南县| 张家川| 东宁县| 海城市| 凉城县| 榕江县| 阳山县| 嵊泗县| 九龙城区| 河津市| 镇雄县| 山东省| 美姑县| 本溪市| 和田市| 清远市| 房山区| 梁山县| 沁水县| 武汉市| 泉州市| 南投市| 扬中市| 石棉县| 山阳县| 北安市|