如何实现四层负载均衡
时间:2023-07-02 02:18:28来源:哔哩哔哩

四层负载均衡和七层负载均衡

七层负载均衡

首先,我们来看下七层负载均衡,它一般是针对应用层请求协议做请求转发,拿http请求举例,有A,B两台服务器,如果采用轮询的负载均衡策略,负载均衡器将第一个请求转发给了A服务器,那么第二个请求到达时,负载均衡器就会把请求转发到B服务器。

在转发时,能够在应用协议层对请求做一些变动,拿http请求来说,可以对http的请求头,http路径做相应的变动。

四层负载均衡

再来看看四层负载均衡,它一般是指针对连接做的负载均衡,举例说明下,有A,B两台服务器,同样采取轮询的策略,某个客户端发起一个新的连接,经过均衡器连接到了A服务器,现在又来一个客户端同样发起连接,经过均衡器后,此时就该和B服务器建立连接了。而在同一个连接里是能够发送多个请求的,这也是和七层负载均衡最本质的区别,它是针对连接做的负载均衡。


(资料图)

实现四层负载均衡器

实现四层负载均衡策略的方式有很多,比较著名的四层负载均衡软件就有lvs,它是通过修改数据包的ip地址或者mac地址实现四层负载均衡,性能较好,工作模式有好几种,具体的就不在本文展开了。

本文实现的四层负载均衡的原理和nginx四层负载类似,通过均衡器在客户端和服务端之前都维护一个连接来达到让 客户端在同一个连接里发送的请求都会被服务端同一个连接所接收的目的。如下图所示:

以后client1 通过连接A发的请求都会由连接B发往服务器,而client2通过连接C发送的请求,都将经过连接D发往另一台服务器。

实现逻辑

现在让我们来实现下这部分的逻辑,我将会以轮询的策略实现连接的负载均衡。

并且这里还要考虑下实现数据复制的逻辑,我们需要在均衡器分别建立对客户端和服务端的socket连接,并且将其中一个socket的数据转移到另一个socket,如果每次都将某一个socket数据读到用户层,再写到另一个socket就会导致一些没有必要的拷贝。伪代码如下:

var (src  // 一个socket 连接dst  // 一个socket连接)// ...buf = make([]byte, size)    nr, er := (buf)nw, ew := (buf[0:nr])

有没有什么技术让内核自动将某个socket的数据转移到另一个socket,不用将数据拷贝到应用层来,这正是零拷贝相关的技术,关于零拷贝的技术原理我在之前这篇文章 有很详细的介绍,内核提供了一个splice的系统调用,专门用于socket连接间拷贝数据,只需要调用时传入对应socket连接的文件描述符即可让内核自动完成拷贝过程。

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

这个系统调用已经被golang更深层次的封装到了一个比较常用的方法里,这个方法会自动判断reader和writer底层的类型,如果都是socket连接则会调用splice系统调用实现零拷贝。

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

接着我们看下均衡的代码逻辑,运行逻辑如下:

1, 监听到新连接,启动一个协程去处理连接。

2 , 在新协程里与通过轮询的策略,选择一个后端服务器并与之建立连接。

3, 启动两个协程分别进行 ,将客户端的socket写到服务端socket,将服务端socket返回的信息写到客户端socket。代码如下:

type Server struct {     Li           Balance  }    func (s *Server) Run() {     for {        c, err := ()        if err != nil {           (err)        }        go func(c ) {           remoteAddr := ()           backendIp := (())           serverConn, err := ("tcp", backendIp)           if err != nil {              (err)              ()              return           }           ("获取到了新连接", remoteAddr, backendIp)           go func() {              _, err := (serverConn, c)              if err != nil {                 (err, 1)              }              ()              ()              ("结束1", err)           }()           go func() {              _, err := (c, serverConn)              if err != nil {                 (err, 2)              }              ()              ()              ("结束2", err)           }()        }(c)     }    }

会不断的拷贝源socket的数据到目的socket,直到连接关闭。

更好的方案

可以看到上述方案中维护一个客户端的连接将会启动3个协程,当连接量上去后,均衡器很可能成为瓶颈,有没有办法减少下协程的数量,可以直接采用epoll的方式监听连接的读写,以及关闭事件(这样能在一个协程里处理多个连接),当连接可读时,直接使用splice系统调用对数据进行拷贝直到返回 就停止,因为返回 说明连接缓冲区内的数据暂时被读取完了,继续下一次epoll wait的监听循环。这样能极大的减少协程数量。不过实现我就不准备再继续展开了,后续有空再补充下这部分。对epoll的使用有兴趣的同学也可以看看我之前一篇用epoll实现类似redis的网络模型框架这篇文章

测试负载均衡代码

现在让我们来测试下负载均衡的代码,我会用docker-compose去启动两个mysql,然后本地启动我们负载均衡器的代码,之后用两个mysql客户端去连接负载均衡器,看下是不是mysql客户端连接到了不同的mysql服务器。

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"

为了能验证不同客户端的确连上了不同的mysql服务器,我在mysql1上创建了test数据库,在mysql2上创建了test2数据库。到时候连上不同服务器数据库是不一样的。

均衡服务器监听5555端口启动

s := &{}  li, err := ("tcp", ":5555")  if err != nil {     (err)  }   = li   = ()  ("", "mysql1")  ("", "mysql2")  ()

之后用mysql客户端去连接均衡服务器

## client1mysql -h -u root  -P 5555  -D test  -p1234567## client2mysql -h -u root  -P 5555  -D test2  -p1234567

发现两个mysql客户端的确连接到了不同服务器,并且能正常执行命令,over。

标签:

最新
  • 如何实现四层负载均衡

    四层负载均衡和七层负载均衡七层负载均衡首先,我们来看下七层负载均衡

  • 2023年国家医保药品目录调整工作方案正式公布 当前热闻

    为了进一步提高参保人员用药保障水平,规范医保用药管理,建立管用高效

  • AI时代已至 车企如何拥抱技术革新? 世界百事通

    6月27日,“2023亚马逊云科技中国峰会”在上海召开。围绕“因构建而可

  • 消息!米醋洗脸有什么功效?米醋洗脸多久洗一次?

    米醋洗脸有什么功效?用米醋洗脸,对于部分人来说是比较不错的,但是

  • 天天观速讯丨支付宝网商银行能当银行卡用吗?支付宝网商银行转账有没有手续费?

    支付宝网商银行能当银行卡用吗?用户在网商银行开立账户之后也是可以

  • 邻水县:重点项目齐头并进 “三大会战”全面打响

    近日,位于广安市邻水县丰禾镇的明月山西麓旅游环线建设现场,10余名工

  • 优质市场监管服务护航海河教育园高质量发展 当前关注

    原标题:优质市场监管服务护航海河教育园高质量发展工人日报-中工网记

  • 理财产品的风险有哪些?理财产品的风险等级分为哪些 最新资讯

    理财产品的风险有哪些市场风险:市场风险是指由于市场波动或不确定

  • 天天动态:光模块被指业绩不及预期,龙头剑桥科技跌停,还能相信“光”吗?

    LightCounting在报告中指出,2023年第一季度,光学收发器及相关产品的

  • 我国浅层常压页岩气开采取得突破

    6月26日,中国石化部署在重庆武隆地区的风险探井——坪地1HF井,自投产

  • 普京:一年内向“瓦格纳”提供超860亿卢布!

    24小时财经资讯平台,依托新锐财经日报《每日经济新闻》(NationalBusin

  • 今日热搜:胡润发布中国产业互联网30强 华为、宝信软件、工业富联等上榜

    上证报中国证券网讯(记者邱德坤)6月27日,胡润研究院发布《2023胡润

  • 信阳市人民医院2023年义诊活动走进高庙安置小区-环球要闻

    为提高居民健康意识和疾病预防能力,6月25日上午,信阳市人民医院医疗

  • 每日观察!长白山保护开发区上榜“2023中国避暑旅游优选地”

    长白山保护开发区上榜“2023中国避暑旅游优选地”记者在长白山管委会旅

  • 焦点热文:融资租赁提前还贷需要支付的费用?如何提前还贷?

    融资租赁提前还贷需要支付的费用1 提前还贷违约金如果我们在合同规

  • 当前快讯:男子挥手拦公交车求助众人合力抬车救出受伤老人

    原标题:公交车行驶途中,一男子拦车求助众人合力抬车救出受伤老人核心

  • 旅游
    • 记者:米兰向费内巴切发送授权信,希望对方授权自己与居勒尔谈判 今日热讯

    • 海口市应急管理局:专家研判近期海南发生破坏性地震可能性不大 今亮点

    • 这份心理调适指南,送给高考后的你们_天天新动态

    • 当前热议!利拉德直播听《Miami》!经纪人:只是巧合!