[优化] 接口优化, 心跳请求返回规则摘要

This commit is contained in:
ryan
2026-03-13 11:22:37 +08:00
parent b33923d5f7
commit 3fb4cec99c
23 changed files with 300 additions and 177 deletions
+37 -33
View File
@@ -14,13 +14,13 @@ import (
type HeartbeatService interface {
Register(ctx context.Context, payload protocol.NodePayload) (*protocol.RegisterNodeResponse, error)
Heartbeat(ctx context.Context, payload protocol.NodePayload) (*protocol.AgentSettings, error)
Heartbeat(ctx context.Context, payload protocol.NodePayload) (*protocol.HeartbeatResult, error)
SetToken(token string)
}
type SyncService interface {
SyncOnStartup(ctx context.Context) error
SyncOnce(ctx context.Context) error
SyncOnStartup(ctx context.Context, target *protocol.ActiveConfigMeta) error
SyncOnce(ctx context.Context, target *protocol.ActiveConfigMeta) error
}
type Updater interface {
@@ -61,20 +61,24 @@ func (r *Runner) Run(ctx context.Context) error {
}
log.Printf("agent runner started: node_id=%s node=%s ip=%s", nodeID, r.Config.NodeName, r.Config.NodeIP)
if r.hasAgentToken() {
if err = r.SyncService.SyncOnStartup(ctx); err != nil {
r.recordSyncError(err)
log.Printf("agent startup sync failed: %v", err)
} else {
log.Printf("agent startup sync completed")
}
r.refreshOpenrestyHealth(ctx)
settings, hbErr := r.HeartbeatService.Heartbeat(ctx, r.nodePayload(nodeID))
heartbeatResult, hbErr := r.HeartbeatService.Heartbeat(ctx, r.nodePayload(nodeID))
if hbErr != nil {
log.Printf("agent startup heartbeat failed: %v", hbErr)
} else {
if heartbeatResult == nil {
heartbeatResult = &protocol.HeartbeatResult{}
}
log.Printf("agent startup heartbeat succeeded: node_id=%s", nodeID)
r.applySettings(settings)
r.applySettings(heartbeatResult.AgentSettings)
if err = r.SyncService.SyncOnStartup(ctx, heartbeatResult.ActiveConfig); err != nil {
r.recordSyncError(err)
log.Printf("agent startup sync failed: %v", err)
} else {
log.Printf("agent startup sync completed")
}
r.tryRestartOpenresty(ctx)
r.tryAutoUpdate(ctx)
}
} else if err = r.tryRegister(ctx, &nodeID); err != nil {
log.Printf("agent initial discovery register failed: %v", err)
@@ -82,8 +86,6 @@ func (r *Runner) Run(ctx context.Context) error {
heartbeatTicker := time.NewTicker(r.Config.HeartbeatInterval.Duration())
defer heartbeatTicker.Stop()
syncTicker := time.NewTicker(r.Config.SyncInterval.Duration())
defer syncTicker.Stop()
for {
select {
@@ -98,25 +100,23 @@ func (r *Runner) Run(ctx context.Context) error {
continue
}
r.refreshOpenrestyHealth(ctx)
settings, hbErr := r.HeartbeatService.Heartbeat(ctx, r.nodePayload(nodeID))
heartbeatResult, hbErr := r.HeartbeatService.Heartbeat(ctx, r.nodePayload(nodeID))
if hbErr != nil {
log.Printf("agent heartbeat failed: %v", hbErr)
} else {
if changed := r.applySettings(settings); changed {
if heartbeatResult == nil {
heartbeatResult = &protocol.HeartbeatResult{}
}
if changed := r.applySettings(heartbeatResult.AgentSettings); changed {
heartbeatTicker.Reset(r.Config.HeartbeatInterval.Duration())
syncTicker.Reset(r.Config.SyncInterval.Duration())
}
if err = r.SyncService.SyncOnce(ctx, heartbeatResult.ActiveConfig); err != nil {
r.recordSyncError(err)
log.Printf("agent sync failed: %v", err)
}
r.tryRestartOpenresty(ctx)
r.tryAutoUpdate(ctx)
}
case <-syncTicker.C:
if !r.hasAgentToken() {
continue
}
if err = r.SyncService.SyncOnce(ctx); err != nil {
r.recordSyncError(err)
log.Printf("agent sync failed: %v", err)
}
}
}
}
@@ -138,14 +138,6 @@ func (r *Runner) applySettings(settings *protocol.AgentSettings) bool {
changed = true
}
}
if settings.SyncInterval > 0 {
newInterval := config.MillisecondDuration(time.Duration(settings.SyncInterval) * time.Millisecond)
if newInterval != r.Config.SyncInterval {
log.Printf("agent sync interval updated: %s -> %s", r.Config.SyncInterval, newInterval)
r.Config.SyncInterval = newInterval
changed = true
}
}
r.autoUpdate = settings.AutoUpdate
r.updateNow = settings.UpdateNow
r.updateRepo = strings.TrimSpace(settings.UpdateRepo)
@@ -226,12 +218,24 @@ func (r *Runner) tryRegister(ctx context.Context, nodeID *string) error {
r.HeartbeatService.SetToken(response.AgentToken)
*nodeID = response.NodeID
log.Printf("agent discovery registration succeeded: node_id=%s", response.NodeID)
if err = r.SyncService.SyncOnStartup(ctx); err != nil {
r.refreshOpenrestyHealth(ctx)
heartbeatResult, heartbeatErr := r.HeartbeatService.Heartbeat(ctx, r.nodePayload(*nodeID))
if heartbeatErr != nil {
log.Printf("agent post-register heartbeat failed: %v", heartbeatErr)
return nil
}
if heartbeatResult == nil {
heartbeatResult = &protocol.HeartbeatResult{}
}
r.applySettings(heartbeatResult.AgentSettings)
if err = r.SyncService.SyncOnStartup(ctx, heartbeatResult.ActiveConfig); err != nil {
r.recordSyncError(err)
log.Printf("agent post-register startup sync failed: %v", err)
} else {
log.Printf("agent post-register startup sync completed")
}
r.tryRestartOpenresty(ctx)
r.tryAutoUpdate(ctx)
return nil
}
+16 -14
View File
@@ -21,7 +21,7 @@ type fakeHeartbeatService struct {
registerErr error
registerResp *protocol.RegisterNodeResponse
heartbeatErrs []error
heartbeatSettings []*protocol.AgentSettings
heartbeatResults []*protocol.HeartbeatResult
heartbeatPayloads []protocol.NodePayload
onHeartbeat func(int)
lastToken string
@@ -34,7 +34,7 @@ func (f *fakeHeartbeatService) Register(ctx context.Context, payload protocol.No
return f.registerResp, f.registerErr
}
func (f *fakeHeartbeatService) Heartbeat(ctx context.Context, payload protocol.NodePayload) (*protocol.AgentSettings, error) {
func (f *fakeHeartbeatService) Heartbeat(ctx context.Context, payload protocol.NodePayload) (*protocol.HeartbeatResult, error) {
f.mu.Lock()
f.heartbeatCalls++
callIndex := f.heartbeatCalls
@@ -43,16 +43,16 @@ func (f *fakeHeartbeatService) Heartbeat(ctx context.Context, payload protocol.N
if len(f.heartbeatErrs) >= callIndex {
err = f.heartbeatErrs[callIndex-1]
}
var settings *protocol.AgentSettings
if len(f.heartbeatSettings) >= callIndex {
settings = f.heartbeatSettings[callIndex-1]
var result *protocol.HeartbeatResult
if len(f.heartbeatResults) >= callIndex {
result = f.heartbeatResults[callIndex-1]
}
onHeartbeat := f.onHeartbeat
f.mu.Unlock()
if onHeartbeat != nil {
onHeartbeat(callIndex)
}
return settings, err
return result, err
}
func (f *fakeHeartbeatService) SetToken(token string) {
@@ -94,14 +94,14 @@ func (f *fakeRuntimeManager) Restart(ctx context.Context) error {
return f.restartErr
}
func (f *fakeSyncService) SyncOnStartup(ctx context.Context) error {
func (f *fakeSyncService) SyncOnStartup(ctx context.Context, target *protocol.ActiveConfigMeta) error {
f.mu.Lock()
defer f.mu.Unlock()
f.startupCalls++
return f.startupErr
}
func (f *fakeSyncService) SyncOnce(ctx context.Context) error {
func (f *fakeSyncService) SyncOnce(ctx context.Context, target *protocol.ActiveConfigMeta) error {
f.mu.Lock()
f.syncOnceCalls++
callIndex := f.syncOnceCalls
@@ -119,6 +119,7 @@ func TestRunnerKeepsHeartbeatWhenStartupSyncFails(t *testing.T) {
stateStore := state.NewStore(filepath.Join(t.TempDir(), "state.json"))
heartbeatService := &fakeHeartbeatService{
heartbeatResults: []*protocol.HeartbeatResult{{}},
onHeartbeat: func(callCount int) {
if callCount >= 2 {
cancel()
@@ -136,7 +137,6 @@ func TestRunnerKeepsHeartbeatWhenStartupSyncFails(t *testing.T) {
AgentVersion: config.AgentVersion,
NginxVersion: "1.27.1.2",
HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond),
SyncInterval: config.MillisecondDuration(20 * time.Millisecond),
},
StateStore: stateStore,
HeartbeatService: heartbeatService,
@@ -170,6 +170,9 @@ func TestRunnerDoesNotExitOnHeartbeatOrSyncError(t *testing.T) {
heartbeatService := &fakeHeartbeatService{
registerErr: errors.New("register timeout"),
heartbeatErrs: []error{errors.New("heartbeat timeout")},
heartbeatResults: []*protocol.HeartbeatResult{
{},
},
}
syncService := &fakeSyncService{
syncOnceErr: errors.New("openresty reload failed"),
@@ -187,7 +190,6 @@ func TestRunnerDoesNotExitOnHeartbeatOrSyncError(t *testing.T) {
AgentVersion: config.AgentVersion,
NginxVersion: "1.27.1.2",
HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond),
SyncInterval: config.MillisecondDuration(10 * time.Millisecond),
},
StateStore: stateStore,
HeartbeatService: heartbeatService,
@@ -225,7 +227,9 @@ func TestRunnerReportsOpenrestyHealthAndExecutesRestart(t *testing.T) {
t.Fatalf("failed to seed state: %v", err)
}
heartbeatService := &fakeHeartbeatService{
heartbeatSettings: []*protocol.AgentSettings{{RestartOpenrestyNow: true}},
heartbeatResults: []*protocol.HeartbeatResult{{
AgentSettings: &protocol.AgentSettings{RestartOpenrestyNow: true},
}},
onHeartbeat: func(callCount int) {
if callCount >= 1 {
cancel()
@@ -244,7 +248,6 @@ func TestRunnerReportsOpenrestyHealthAndExecutesRestart(t *testing.T) {
AgentVersion: config.AgentVersion,
NginxVersion: "1.27.1.2",
HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond),
SyncInterval: config.MillisecondDuration(100 * time.Millisecond),
},
StateStore: stateStore,
HeartbeatService: heartbeatService,
@@ -289,6 +292,7 @@ func TestRunnerDiscoveryRegisterUpdatesTokenAndNodeID(t *testing.T) {
AgentToken: "agent-token-issued",
Name: "edge-01",
},
heartbeatResults: []*protocol.HeartbeatResult{{}},
onHeartbeat: func(callCount int) {
if callCount >= 1 {
cancel()
@@ -313,7 +317,6 @@ func TestRunnerDiscoveryRegisterUpdatesTokenAndNodeID(t *testing.T) {
AgentVersion: config.AgentVersion,
NginxVersion: "1.27.1.2",
HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond),
SyncInterval: config.MillisecondDuration(20 * time.Millisecond),
},
StateStore: stateStore,
HeartbeatService: heartbeatService,
@@ -323,7 +326,6 @@ func TestRunnerDiscoveryRegisterUpdatesTokenAndNodeID(t *testing.T) {
runner.Config.AgentVersion = config.AgentVersion
runner.Config.NginxVersion = "1.27.1.2"
runner.Config.HeartbeatInterval = config.MillisecondDuration(10 * time.Millisecond)
runner.Config.SyncInterval = config.MillisecondDuration(20 * time.Millisecond)
err = runner.Run(ctx)
if !errors.Is(err, context.Canceled) {