130 lines
3.2 KiB
Go
130 lines
3.2 KiB
Go
package agent
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Heartbeat struct {
|
|
cfg Config
|
|
nodeID string
|
|
client *http.Client
|
|
}
|
|
|
|
type HeartbeatPayload struct {
|
|
NodeID string `json:"node_id"`
|
|
CPUPct float64 `json:"cpu_percent"`
|
|
MemPct float64 `json:"memory_percent"`
|
|
BandwidthMbps float64 `json:"bandwidth_mbps"`
|
|
ActiveConnections int `json:"active_connections"`
|
|
HlsReaders int `json:"hls_readers"`
|
|
NetworkInMbps float64 `json:"network_in_mbps"`
|
|
NetworkOutMbps float64 `json:"network_out_mbps"`
|
|
ViewerCount int `json:"viewer_count"`
|
|
FrameDrops int `json:"frame_drop_indicators"`
|
|
PacketLoss int `json:"packet_loss_indicators"`
|
|
MediaAvailable bool `json:"media_metrics_available"`
|
|
}
|
|
|
|
type RegisterPayload struct {
|
|
NodeID string `json:"node_id"`
|
|
Name string `json:"name"`
|
|
Host string `json:"host"`
|
|
HlsPort int `json:"hls_port"`
|
|
ApiPort int `json:"api_port"`
|
|
Weight int `json:"weight"`
|
|
Subnet string `json:"subnet"`
|
|
}
|
|
|
|
func NewHeartbeat(cfg Config, nodeID string) *Heartbeat {
|
|
return &Heartbeat{
|
|
cfg: cfg,
|
|
nodeID: nodeID,
|
|
client: &http.Client{Timeout: 10 * time.Second},
|
|
}
|
|
}
|
|
|
|
func (h *Heartbeat) Register() error {
|
|
payload := RegisterPayload{
|
|
NodeID: h.nodeID,
|
|
Name: h.cfg.NodeName,
|
|
Host: h.cfg.OriginURL,
|
|
HlsPort: h.cfg.HlsPort,
|
|
ApiPort: h.cfg.ApiPort,
|
|
Weight: h.cfg.Weight,
|
|
Subnet: h.cfg.Subnet,
|
|
}
|
|
return h.post("/api/nodes/register", payload)
|
|
}
|
|
|
|
func (h *Heartbeat) Discover() error {
|
|
if h.cfg.DiscoveryURL == "" {
|
|
return nil
|
|
}
|
|
resp, err := h.client.Get(h.cfg.DiscoveryURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode >= 400 {
|
|
return fmt.Errorf("discovery status %d", resp.StatusCode)
|
|
}
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
host := strings.TrimSpace(string(body))
|
|
if host == "" {
|
|
return fmt.Errorf("discovery returned an empty address")
|
|
}
|
|
if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
|
|
host = "http://" + host
|
|
}
|
|
h.cfg.OriginURL = strings.TrimRight(host, "/")
|
|
return nil
|
|
}
|
|
|
|
func (h *Heartbeat) OriginURL() string {
|
|
return h.cfg.OriginURL
|
|
}
|
|
|
|
func (h *Heartbeat) Send(m Metrics) error {
|
|
payload := HeartbeatPayload{
|
|
NodeID: h.nodeID,
|
|
CPUPct: m.CPU,
|
|
MemPct: m.Memory,
|
|
BandwidthMbps: m.Bandwidth,
|
|
ActiveConnections: m.ActiveConns,
|
|
HlsReaders: m.HlsReaders,
|
|
NetworkInMbps: m.NetworkIn,
|
|
NetworkOutMbps: m.NetworkOut,
|
|
ViewerCount: m.ViewerCount,
|
|
FrameDrops: m.FrameDrops,
|
|
PacketLoss: m.PacketLoss,
|
|
MediaAvailable: m.MediaAvailable,
|
|
}
|
|
return h.post("/api/nodes/heartbeat", payload)
|
|
}
|
|
|
|
func (h *Heartbeat) post(path string, payload interface{}) error {
|
|
body, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
url := h.cfg.OriginURL + path
|
|
resp, err := h.client.Post(url, "application/json", bytes.NewReader(body))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode >= 400 {
|
|
return fmt.Errorf("status %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|