frpc: support health check

pull/968/head
fatedier 6 years ago
parent cc6486addb
commit 082447f517

@ -15,18 +15,133 @@
package client package client
import ( import (
"github.com/fatedier/frp/models/config" "context"
"net"
"net/http"
"time"
) )
type HealthCheckMonitor struct { type HealthCheckMonitor struct {
cfg config.HealthCheckConf checkType string
interval time.Duration
timeout time.Duration
maxFailedTimes int
// For tcp
addr string
// For http
url string
failedTimes uint64
statusOK bool
statusNormalFn func()
statusFailedFn func()
ctx context.Context
cancel context.CancelFunc
} }
func NewHealthCheckMonitor(cfg *config.HealthCheckConf) *HealthCheckMonitor { func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string,
statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor {
if intervalS <= 0 {
intervalS = 10
}
if timeoutS <= 0 {
timeoutS = 3
}
if maxFailedTimes <= 0 {
maxFailedTimes = 1
}
ctx, cancel := context.WithCancel(context.Background())
return &HealthCheckMonitor{ return &HealthCheckMonitor{
cfg: *cfg, checkType: checkType,
interval: time.Duration(intervalS) * time.Second,
timeout: time.Duration(timeoutS) * time.Second,
maxFailedTimes: maxFailedTimes,
addr: addr,
url: url,
statusOK: false,
statusNormalFn: statusNormalFn,
statusFailedFn: statusFailedFn,
ctx: ctx,
cancel: cancel,
} }
} }
func (monitor *HealthCheckMonitor) Start() { func (monitor *HealthCheckMonitor) Start() {
go monitor.checkWorker()
}
func (monitor *HealthCheckMonitor) Stop() {
monitor.cancel()
}
func (monitor *HealthCheckMonitor) checkWorker() {
for {
ctx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout))
ok := monitor.doCheck(ctx)
// check if this monitor has been closed
select {
case <-ctx.Done():
cancel()
return
default:
cancel()
}
if ok {
if !monitor.statusOK && monitor.statusNormalFn != nil {
monitor.statusOK = true
monitor.statusNormalFn()
}
} else {
monitor.failedTimes++
if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
monitor.statusOK = false
monitor.statusFailedFn()
}
}
time.Sleep(monitor.interval)
}
}
func (monitor *HealthCheckMonitor) doCheck(ctx context.Context) bool {
switch monitor.checkType {
case "tcp":
return monitor.doTcpCheck(ctx)
case "http":
return monitor.doHttpCheck(ctx)
default:
return false
}
}
func (monitor *HealthCheckMonitor) doTcpCheck(ctx context.Context) bool {
var d net.Dialer
conn, err := d.DialContext(ctx, "tcp", monitor.addr)
if err != nil {
return false
}
conn.Close()
return true
}
func (monitor *HealthCheckMonitor) doHttpCheck(ctx context.Context) bool {
req, err := http.NewRequest("GET", monitor.url, nil)
if err != nil {
return false
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return false
}
if resp.StatusCode/100 != 2 {
return false
}
return true
} }

@ -381,6 +381,8 @@ func (cfg *LocalSvrConf) checkForCli() (err error) {
// Health check info // Health check info
type HealthCheckConf struct { type HealthCheckConf struct {
HealthCheckType string `json:"health_check_type"` // tcp | http HealthCheckType string `json:"health_check_type"` // tcp | http
HealthCheckTimeout int `json:"health_check_timeout"`
HealthCheckMaxFailed int `json:"health_check_max_failed"`
HealthCheckIntervalS int `json:"health_check_interval_s"` HealthCheckIntervalS int `json:"health_check_interval_s"`
HealthCheckUrl string `json:"health_check_url"` HealthCheckUrl string `json:"health_check_url"`

Loading…
Cancel
Save