前言
之前接觸的代理是sofa-mosn這種牡直,它c(diǎn)lient端連接到mosn后,mosn將數(shù)據(jù)包轉(zhuǎn)發(fā)到后端祭饭。其中
1)mosn到后端服務(wù)器有個連接池惫皱,連接是可以復(fù)用的
2)mosn會對數(shù)據(jù)包進(jìn)行解包
3)支持請求數(shù)據(jù)(不是連接請求)的負(fù)載均衡
tcp負(fù)載均衡
最近接觸到TCP層的負(fù)載均衡,Proxy只能實(shí)現(xiàn)連接的負(fù)載均衡眷唉,即后端有兩個機(jī)器予颤,第一個連接會將所有數(shù)據(jù)發(fā)送到S1,第二個連接會轉(zhuǎn)發(fā)到S2冬阳。相當(dāng)于client連接proxy后蛤虐,proxy會創(chuàng)建一個到server的connection。
為什么不能復(fù)用連接肝陪?
1)proxy不知道client發(fā)送的數(shù)據(jù)內(nèi)容驳庭,也不知道client什么時候發(fā)送完。如果proxy到server的連接不是獨(dú)享的氯窍,會導(dǎo)致包錯亂饲常。
實(shí)現(xiàn)
下面以https://github.com/yyyar/gobetween
為例解釋一下。
server.go
# client到proxy的連接
clientConn := ctx.Conn
# 負(fù)載均衡選取后端節(jié)點(diǎn)狼讨,拿到的是服務(wù)器的信息贝淤,不是連接
backend, err := this.scheduler.TakeBackend(ctx)
# 創(chuàng)建proxy到server的連接
if this.cfg.BackendsTls != nil {
backendConn, err = tls.DialWithDialer(&net.Dialer{
Timeout: utils.ParseDurationOrDefault(*this.cfg.BackendConnectionTimeout, 0),
}, "tcp", backend.Address(), this.backendsTlsConfg)
} else {
backendConn, err = net.DialTimeout("tcp", backend.Address(), utils.ParseDurationOrDefault(*this.cfg.BackendConnectionTimeout, 0))
}
# server返回給proxy的數(shù)據(jù) 轉(zhuǎn)發(fā)給 client
cs := proxy(clientConn, backendConn, utils.ParseDurationOrDefault(*this.cfg.BackendIdleTimeout, 0))
# proxy收到client發(fā)送過來的數(shù)據(jù) 轉(zhuǎn)發(fā)給server
bs := proxy(backendConn, clientConn, utils.ParseDurationOrDefault(*this.cfg.ClientIdleTimeout, 0))
具體的proxy過程見proxy.go
func Copy(to io.Writer, from io.Reader, ch chan<- core.ReadWriteCount) error {
for {
# 讀數(shù)據(jù)
readN, readErr := from.Read(buf)
if readN > 0 {
#寫數(shù)據(jù)
writeN, writeErr := to.Write(buf[0:readN])
if writeN > 0 {
ch <- core.ReadWriteCount{CountRead: uint(readN), CountWrite: uint(writeN)}
}
}
}
}
參考
https://github.com/darkhelmet/balance
https://kasvith.me/posts/lets-create-a-simple-lb-go/