(1)优化重连和心跳检测

This commit is contained in:
Hurricanezwf 2016-02-05 14:18:26 +08:00
parent af6fc61537
commit 5d6f37aa82
5 changed files with 104 additions and 48 deletions

2
.gitignore vendored
View File

@ -26,3 +26,5 @@ _testmain.go
# Self # Self
bin/ bin/
# Cache
*.swp

View File

@ -15,8 +15,7 @@ const (
heartbeatDuration = 2 //心跳检测时间间隔,单位秒 heartbeatDuration = 2 //心跳检测时间间隔,单位秒
) )
// client与server之间连接的保护锁 var isHeartBeatContinue bool = true
var connProtect sync.Mutex
func ControlProcess(cli *models.ProxyClient, wait *sync.WaitGroup) { func ControlProcess(cli *models.ProxyClient, wait *sync.WaitGroup) {
defer wait.Done() defer wait.Done()
@ -28,13 +27,11 @@ func ControlProcess(cli *models.ProxyClient, wait *sync.WaitGroup) {
} }
defer c.Close() defer c.Close()
go startHeartBeat(c)
for { for {
// ignore response content now // ignore response content now
_, err := c.ReadLine() _, err := c.ReadLine()
if err == io.EOF { if err == io.EOF {
connProtect.Lock() // 除了这里,其他地方禁止对连接进行任何操作 isHeartBeatContinue = false
log.Debug("ProxyName [%s], server close this control conn", cli.Name) log.Debug("ProxyName [%s], server close this control conn", cli.Name)
var sleepTime time.Duration = 1 var sleepTime time.Duration = 1
for { for {
@ -51,7 +48,6 @@ func ControlProcess(cli *models.ProxyClient, wait *sync.WaitGroup) {
} }
time.Sleep(sleepTime * time.Second) time.Sleep(sleepTime * time.Second)
} }
connProtect.Unlock()
continue continue
} else if err != nil { } else if err != nil {
log.Warn("ProxyName [%s], read from server error, %v", cli.Name, err) log.Warn("ProxyName [%s], read from server error, %v", cli.Name, err)
@ -104,6 +100,8 @@ func loginToServer(cli *models.ProxyClient) (connection *conn.Conn) {
} }
connection = c connection = c
go startHeartBeat(connection)
log.Debug("ProxyName [%s], connect to server[%s:%d] success!", cli.Name, ServerAddr, ServerPort)
} }
if connection == nil { if connection == nil {
@ -114,14 +112,17 @@ func loginToServer(cli *models.ProxyClient) (connection *conn.Conn) {
} }
func startHeartBeat(con *conn.Conn) { func startHeartBeat(con *conn.Conn) {
isHeartBeatContinue = true
for { for {
time.Sleep(heartbeatDuration * time.Second) time.Sleep(heartbeatDuration * time.Second)
if isHeartBeatContinue { // 把isHeartBeatContinue放在这里是为了防止SIGPIPE
connProtect.Lock()
err := con.Write("\r\n") err := con.Write("\r\n")
connProtect.Unlock() //log.Debug("send heart beat to server!")
if err != nil { if err != nil {
log.Error("Send hearbeat to server failed! Err:%s", err.Error()) log.Error("Send hearbeat to server failed! Err:%s", err.Error())
} }
} else {
break
}
} }
} }

View File

@ -1,12 +1,14 @@
package main package main
import ( import (
"fmt"
"encoding/json" "encoding/json"
"fmt"
"io"
"time"
"frp/pkg/utils/log"
"frp/pkg/utils/conn"
"frp/pkg/models" "frp/pkg/models"
"frp/pkg/utils/conn"
"frp/pkg/utils/log"
) )
func ProcessControlConn(l *conn.Listener) { func ProcessControlConn(l *conn.Listener) {
@ -19,6 +21,8 @@ func ProcessControlConn(l *conn.Listener) {
// control connection from every client and server // control connection from every client and server
func controlWorker(c *conn.Conn) { func controlWorker(c *conn.Conn) {
defer c.Close()
// the first message is from client to server // the first message is from client to server
// if error, close connection // if error, close connection
res, err := c.ReadLine() res, err := c.ReadLine()
@ -47,13 +51,14 @@ func controlWorker(c *conn.Conn) {
err = c.Write(string(buf) + "\n") err = c.Write(string(buf) + "\n")
if err != nil { if err != nil {
log.Warn("Write error, %v", err) log.Warn("Write error, %v", err)
time.Sleep(1 * time.Second)
return
} }
} else { } else {
// work conn, just return // work conn, just return
return return
} }
defer c.Close()
// others is from server to client // others is from server to client
server, ok := ProxyServers[clientCtlReq.ProxyName] server, ok := ProxyServers[clientCtlReq.ProxyName]
if !ok { if !ok {
@ -61,10 +66,16 @@ func controlWorker(c *conn.Conn) {
return return
} }
// read control msg from client
go readControlMsgFromClient(server, c)
serverCtlReq := &models.ClientCtlReq{} serverCtlReq := &models.ClientCtlReq{}
serverCtlReq.Type = models.WorkConn serverCtlReq.Type = models.WorkConn
for { for {
server.WaitUserConn() _, isStop := server.WaitUserConn()
if isStop {
break
}
buf, _ := json.Marshal(serverCtlReq) buf, _ := json.Marshal(serverCtlReq)
err = c.Write(string(buf) + "\n") err = c.Write(string(buf) + "\n")
if err != nil { if err != nil {
@ -76,6 +87,7 @@ func controlWorker(c *conn.Conn) {
log.Debug("ProxyName [%s], write to client to add work conn success", server.Name) log.Debug("ProxyName [%s], write to client to add work conn success", server.Name)
} }
log.Error("ProxyName [%s], I'm dead!", server.Name)
return return
} }
@ -132,3 +144,32 @@ func checkProxy(req *models.ClientCtlReq, c *conn.Conn) (succ bool, msg string,
succ = true succ = true
return return
} }
func readControlMsgFromClient(server *models.ProxyServer, c *conn.Conn) {
isContinueRead := true
f := func() {
isContinueRead = false
server.StopWaitUserConn()
}
timer := time.AfterFunc(10*time.Second, f)
defer timer.Stop()
for isContinueRead {
content, err := c.ReadLine()
//log.Debug("Receive msg from client! content:%s", content)
if err != nil {
if err == io.EOF {
log.Warn("Server detect client[%s] is dead!", server.Name)
server.StopWaitUserConn()
break
}
log.Error("ProxyName [%s], read error:%s", server.Name, err.Error())
continue
}
if content == "\r\n" {
log.Debug("receive hearbeat:%s", content)
timer.Reset(10 * time.Second)
}
}
}

View File

@ -1,8 +1,8 @@
package models package models
import ( import (
"sync"
"container/list" "container/list"
"sync"
"frp/pkg/utils/conn" "frp/pkg/utils/conn"
"frp/pkg/utils/log" "frp/pkg/utils/log"
@ -22,6 +22,7 @@ type ProxyServer struct {
Status int64 Status int64
Listener *conn.Listener // accept new connection from remote users Listener *conn.Listener // accept new connection from remote users
CtlMsgChan chan int64 // every time accept a new user conn, put "1" to the channel CtlMsgChan chan int64 // every time accept a new user conn, put "1" to the channel
StopBlockChan chan int64 // put any number to the channel, if you want to stop wait user conn
CliConnChan chan *conn.Conn // get client conns from control goroutine CliConnChan chan *conn.Conn // get client conns from control goroutine
UserConnList *list.List // store user conns UserConnList *list.List // store user conns
Mutex sync.Mutex Mutex sync.Mutex
@ -30,6 +31,7 @@ type ProxyServer struct {
func (p *ProxyServer) Init() { func (p *ProxyServer) Init() {
p.Status = Idle p.Status = Idle
p.CtlMsgChan = make(chan int64) p.CtlMsgChan = make(chan int64)
p.StopBlockChan = make(chan int64)
p.CliConnChan = make(chan *conn.Conn) p.CliConnChan = make(chan *conn.Conn)
p.UserConnList = list.New() p.UserConnList = list.New()
} }
@ -110,7 +112,15 @@ func (p *ProxyServer) Close() {
p.Unlock() p.Unlock()
} }
func (p *ProxyServer) WaitUserConn() (res int64) { func (p *ProxyServer) WaitUserConn() (res int64, isStop bool) {
res = <-p.CtlMsgChan select {
return case res = <-p.CtlMsgChan:
return res, false
case <-p.StopBlockChan:
return 0, true
}
}
func (p *ProxyServer) StopWaitUserConn() {
p.StopBlockChan <- 1
} }

View File

@ -1,11 +1,11 @@
package conn package conn
import ( import (
"fmt"
"net"
"bufio" "bufio"
"sync" "fmt"
"io" "io"
"net"
"sync"
"frp/pkg/utils/log" "frp/pkg/utils/log"
) )
@ -59,8 +59,10 @@ func (c *Conn) Write(content string) (err error) {
} }
func (c *Conn) Close() { func (c *Conn) Close() {
if c.TcpConn != nil { // ZWF:我觉得应该加一个非空保护
c.TcpConn.Close() c.TcpConn.Close()
} }
}
func Listen(bindAddr string, bindPort int64) (l *Listener, err error) { func Listen(bindAddr string, bindPort int64) (l *Listener, err error) {
tcpAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", bindAddr, bindPort)) tcpAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", bindAddr, bindPort))