97 lines
2.6 KiB
Go
97 lines
2.6 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"app-deploy-platform/backend/internal/model"
|
|
)
|
|
|
|
func (m *Manager) StartHealthProbe(ctx context.Context) {
|
|
ticker := time.NewTicker(m.cfg.HealthcheckIntervalDuration())
|
|
defer ticker.Stop()
|
|
|
|
m.probeInstances(ctx)
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
m.probeInstances(ctx)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *Manager) probeInstances(ctx context.Context) {
|
|
var instances []model.ServiceInstance
|
|
if err := m.db.WithContext(ctx).Preload("Host").Find(&instances).Error; err != nil {
|
|
return
|
|
}
|
|
|
|
client := &http.Client{Timeout: 3 * time.Second}
|
|
now := time.Now()
|
|
|
|
for _, instance := range instances {
|
|
healthStatus := probeURL(ctx, client, buildURL(instance.Host.PrivateIP, instance.Port, instance.HealthPath))
|
|
readyStatus := probeURL(ctx, client, buildURL(instance.Host.PrivateIP, instance.Port, instance.ReadyPath))
|
|
|
|
updates := map[string]any{
|
|
"health_status": healthStatus,
|
|
"ready_status": readyStatus,
|
|
"last_health_checked_at": &now,
|
|
"updated_at": now,
|
|
}
|
|
|
|
if err := m.db.WithContext(ctx).Model(&model.ServiceInstance{}).
|
|
Where("id = ?", instance.ID).
|
|
Updates(updates).Error; err != nil {
|
|
continue
|
|
}
|
|
|
|
hostUpdates := map[string]any{"updated_at": now}
|
|
if healthStatus == model.StatusUp || readyStatus == model.StatusUp {
|
|
hostUpdates["last_seen_at"] = &now
|
|
}
|
|
_ = m.db.WithContext(ctx).Model(&model.Host{}).Where("id = ?", instance.HostID).Updates(hostUpdates).Error
|
|
|
|
if m.redis != nil {
|
|
cacheValue := fmt.Sprintf(`{"health":"%s","ready":"%s","checked_at":"%s"}`, healthStatus, readyStatus, now.Format(time.RFC3339))
|
|
_ = m.redis.Set(ctx, fmt.Sprintf("instance:%d:health", instance.ID), cacheValue, m.cfg.HealthcheckIntervalDuration()*2).Err()
|
|
}
|
|
}
|
|
}
|
|
|
|
func buildURL(host string, port int, path string) string {
|
|
if path == "" {
|
|
path = "/health"
|
|
}
|
|
return fmt.Sprintf("http://%s:%d%s", host, port, path)
|
|
}
|
|
|
|
func probeURL(ctx context.Context, client *http.Client, url string) string {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return model.StatusUnknown
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return model.StatusDown
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusBadRequest {
|
|
return model.StatusUp
|
|
}
|
|
return model.StatusDown
|
|
}
|
|
|
|
func updateDeploymentStatus(tx *gorm.DB, deploymentID uint, values map[string]any) error {
|
|
return tx.Model(&model.Deployment{}).Where("id = ?", deploymentID).Updates(values).Error
|
|
}
|