mirror of https://github.com/fatedier/frp
				
				
				
			frpc: support health check
							parent
							
								
									cc6486addb
								
							
						
					
					
						commit
						698219b621
					
				| 
						 | 
				
			
			@ -255,6 +255,7 @@ func (ctl *Control) login() (err error) {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// connectServer return a new connection to frps
 | 
			
		||||
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
 | 
			
		||||
	if g.GlbClientCfg.TcpMux {
 | 
			
		||||
		stream, errRet := ctl.session.OpenStream()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										123
									
								
								client/health.go
								
								
								
								
							
							
						
						
									
										123
									
								
								client/health.go
								
								
								
								
							| 
						 | 
				
			
			@ -15,18 +15,133 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/fatedier/frp/models/config"
 | 
			
		||||
	"context"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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{
 | 
			
		||||
		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() {
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,6 @@ const (
 | 
			
		|||
	ProxyStatusWaitStart    = "wait start"
 | 
			
		||||
	ProxyStatusRunning      = "running"
 | 
			
		||||
	ProxyStatusCheckFailed  = "check failed"
 | 
			
		||||
	ProxyStatusCheckSuccess = "check success"
 | 
			
		||||
	ProxyStatusClosed       = "closed"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -381,6 +381,8 @@ func (cfg *LocalSvrConf) checkForCli() (err error) {
 | 
			
		|||
// Health check info
 | 
			
		||||
type HealthCheckConf struct {
 | 
			
		||||
	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"`
 | 
			
		||||
	HealthCheckUrl       string `json:"health_check_url"`
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue