mirror of
https://github.com/bolucat/Archive.git
synced 2025-10-09 01:50:34 +08:00
135 lines
3.4 KiB
Go
135 lines
3.4 KiB
Go
package metrics
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"net/url"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Ehco1996/ehco/internal/config"
|
|
"github.com/go-ping/ping"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func (pg *PingGroup) newPinger(addr string) (*ping.Pinger, error) {
|
|
pinger := ping.New(addr)
|
|
if err := pinger.Resolve(); err != nil {
|
|
pg.logger.Error("failed to resolve pinger", zap.String("addr", addr), zap.Error(err))
|
|
return nil, err
|
|
}
|
|
pinger.Interval = pingInterval
|
|
pinger.Timeout = time.Duration(math.MaxInt64)
|
|
pinger.RecordRtts = false
|
|
if runtime.GOOS != "darwin" {
|
|
pinger.SetPrivileged(true)
|
|
}
|
|
return pinger, nil
|
|
}
|
|
|
|
type PingGroup struct {
|
|
logger *zap.Logger
|
|
|
|
// k: addr
|
|
Pingers map[string]*ping.Pinger
|
|
|
|
// k: addr v:relay rule label joined by ","
|
|
PingerLabels map[string]string
|
|
}
|
|
|
|
func extractHost(input string) (string, error) {
|
|
// Check if the input string has a scheme, if not, add "http://"
|
|
if !strings.Contains(input, "://") {
|
|
input = "http://" + input
|
|
}
|
|
// Parse the URL
|
|
u, err := url.Parse(input)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return u.Hostname(), nil
|
|
}
|
|
|
|
func NewPingGroup(cfg *config.Config) *PingGroup {
|
|
logger := zap.L().Named("pinger")
|
|
|
|
pg := &PingGroup{
|
|
logger: logger,
|
|
Pingers: make(map[string]*ping.Pinger),
|
|
PingerLabels: map[string]string{},
|
|
}
|
|
|
|
// parse addr from rule
|
|
for _, relayCfg := range cfg.RelayConfigs {
|
|
// NOTE for (https/ws/wss)://xxx.com -> xxx.com
|
|
for _, remote := range relayCfg.TCPRemotes {
|
|
addr, err := extractHost(remote)
|
|
if err != nil {
|
|
pg.logger.Error("try parse host error", zap.Error(err))
|
|
}
|
|
if _, ok := pg.Pingers[addr]; ok {
|
|
// append rule label when remote host is same
|
|
pg.PingerLabels[addr] += fmt.Sprintf(",%s", relayCfg.Label)
|
|
continue
|
|
}
|
|
if pinger, err := pg.newPinger(addr); err != nil {
|
|
pg.logger.Error("new pinger meet error", zap.Error(err))
|
|
} else {
|
|
pg.Pingers[pinger.Addr()] = pinger
|
|
pg.PingerLabels[addr] = relayCfg.Label
|
|
}
|
|
}
|
|
}
|
|
|
|
// update metrics
|
|
for addr, pinger := range pg.Pingers {
|
|
pinger.OnRecv = func(pkt *ping.Packet) {
|
|
PingResponseDurationSeconds.WithLabelValues(
|
|
pkt.IPAddr.String(), pkt.Addr, pg.PingerLabels[addr]).Observe(pkt.Rtt.Seconds())
|
|
pg.logger.Sugar().Infof("%d bytes from %s icmp_seq=%d time=%v ttl=%v",
|
|
pkt.Nbytes, pkt.Addr, pkt.Seq, pkt.Rtt, pkt.Ttl)
|
|
}
|
|
pinger.OnDuplicateRecv = func(pkt *ping.Packet) {
|
|
pg.logger.Sugar().Infof("%d bytes from %s icmp_seq=%d time=%v ttl=%v (DUP!)",
|
|
pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt, pkt.Ttl)
|
|
}
|
|
}
|
|
return pg
|
|
}
|
|
|
|
func (pg *PingGroup) Describe(ch chan<- *prometheus.Desc) {
|
|
ch <- PingRequestTotal
|
|
}
|
|
|
|
func (pg *PingGroup) Collect(ch chan<- prometheus.Metric) {
|
|
for addr, pinger := range pg.Pingers {
|
|
stats := pinger.Statistics()
|
|
ch <- prometheus.MustNewConstMetric(
|
|
PingRequestTotal,
|
|
prometheus.CounterValue,
|
|
float64(stats.PacketsSent),
|
|
stats.IPAddr.String(),
|
|
stats.Addr,
|
|
pg.PingerLabels[addr],
|
|
)
|
|
}
|
|
}
|
|
|
|
func (pg *PingGroup) Run() {
|
|
if len(pg.Pingers) <= 0 {
|
|
return
|
|
}
|
|
pg.logger.Sugar().Infof("Start Ping Group now total pinger: %d", len(pg.Pingers))
|
|
splay := time.Duration(pingInterval.Nanoseconds() / int64(len(pg.Pingers)))
|
|
for addr, pinger := range pg.Pingers {
|
|
go func() {
|
|
if err := pinger.Run(); err != nil {
|
|
pg.logger.Error("Starting pinger meet err", zap.String("addr", addr), zap.Error(err))
|
|
}
|
|
}()
|
|
time.Sleep(splay)
|
|
}
|
|
}
|