@@ -80,6 +80,7 @@ type Master struct {
8080 notifyChannel chan * InstanceEvent // 事件通知通道
8181 tcpingSem chan struct {} // TCPing并发控制
8282 startTime time.Time // 启动时间
83+ backupDone chan struct {} // 备份停止信号
8384}
8485
8586// Instance 实例信息
@@ -314,6 +315,7 @@ func NewMaster(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
314315 notifyChannel : make (chan * InstanceEvent , semaphoreLimit ),
315316 tcpingSem : make (chan struct {}, tcpingSemLimit ),
316317 startTime : time .Now (),
318+ backupDone : make (chan struct {}),
317319 }
318320 master .tunnelTCPAddr = host
319321
@@ -323,6 +325,9 @@ func NewMaster(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
323325 // 启动事件分发器
324326 go master .startEventDispatcher ()
325327
328+ // 启动定期备份
329+ go master .startPeriodicBackup ()
330+
326331 return master
327332}
328333
@@ -516,6 +521,9 @@ func (m *Master) Shutdown(ctx context.Context) error {
516521
517522 wg .Wait ()
518523
524+ // 关闭定期备份
525+ close (m .backupDone )
526+
519527 // 关闭事件通知通道,停止事件分发器
520528 close (m .notifyChannel )
521529
@@ -535,6 +543,11 @@ func (m *Master) Shutdown(ctx context.Context) error {
535543
536544// saveState 保存实例状态到文件
537545func (m * Master ) saveState () error {
546+ return m .saveStateToPath (m .statePath )
547+ }
548+
549+ // saveStateToPath 保存实例状态到指定路径
550+ func (m * Master ) saveStateToPath (filePath string ) error {
538551 if ! m .stateMu .TryLock () {
539552 return nil
540553 }
@@ -553,20 +566,20 @@ func (m *Master) saveState() error {
553566 // 如果没有实例,直接返回
554567 if len (persistentData ) == 0 {
555568 // 如果状态文件存在,删除它
556- if _ , err := os .Stat (m . statePath ); err == nil {
557- return os .Remove (m . statePath )
569+ if _ , err := os .Stat (filePath ); err == nil {
570+ return os .Remove (filePath )
558571 }
559572 return nil
560573 }
561574
562575 // 确保目录存在
563- if err := os .MkdirAll (filepath .Dir (m . statePath ), 0755 ); err != nil {
576+ if err := os .MkdirAll (filepath .Dir (filePath ), 0755 ); err != nil {
564577 m .logger .Error ("Create state dir failed: %v" , err )
565578 return err
566579 }
567580
568581 // 创建临时文件
569- tempFile , err := os .CreateTemp (filepath .Dir (m . statePath ), "np-*.tmp" )
582+ tempFile , err := os .CreateTemp (filepath .Dir (filePath ), "np-*.tmp" )
570583 if err != nil {
571584 m .logger .Error ("Create temp failed: %v" , err )
572585 return err
@@ -597,7 +610,7 @@ func (m *Master) saveState() error {
597610 }
598611
599612 // 原子地替换文件
600- if err := os .Rename (tempPath , m . statePath ); err != nil {
613+ if err := os .Rename (tempPath , filePath ); err != nil {
601614 m .logger .Error ("Rename temp failed: %v" , err )
602615 removeTemp ()
603616 return err
@@ -606,6 +619,25 @@ func (m *Master) saveState() error {
606619 return nil
607620}
608621
622+ // startPeriodicBackup 启动定期备份
623+ func (m * Master ) startPeriodicBackup () {
624+ for {
625+ select {
626+ case <- time .After (ReloadInterval ):
627+ // 固定备份文件名
628+ backupPath := fmt .Sprintf ("%s.backup" , m .statePath )
629+
630+ if err := m .saveStateToPath (backupPath ); err != nil {
631+ m .logger .Error ("Backup state failed: %v" , err )
632+ } else {
633+ m .logger .Info ("State backup saved: %v" , backupPath )
634+ }
635+ case <- m .backupDone :
636+ return
637+ }
638+ }
639+ }
640+
609641// loadState 从文件加载实例状态
610642func (m * Master ) loadState () {
611643 // 检查文件是否存在
0 commit comments