diff --git a/service/ssmapi/cache.go b/service/ssmapi/cache.go index 03cc8c3f..f942265d 100644 --- a/service/ssmapi/cache.go +++ b/service/ssmapi/cache.go @@ -49,6 +49,9 @@ func (s *Service) loadCache() error { os.RemoveAll(basePath) return err } + s.cacheMutex.Lock() + s.lastSavedCache = cacheBinary + s.cacheMutex.Unlock() return nil } @@ -56,16 +59,30 @@ func (s *Service) saveCache() error { if s.cachePath == "" { return nil } + cacheBinary, err := s.encodeCache() + if err != nil { + return err + } + s.cacheMutex.Lock() + defer s.cacheMutex.Unlock() + if bytes.Equal(s.lastSavedCache, cacheBinary) { + return nil + } + return s.writeCache(cacheBinary) +} + +func (s *Service) writeCache(cacheBinary []byte) error { basePath := filemanager.BasePath(s.ctx, s.cachePath) err := os.MkdirAll(filepath.Dir(basePath), 0o777) if err != nil { return err } - cacheBinary, err := s.encodeCache() + err = os.WriteFile(basePath, cacheBinary, 0o644) if err != nil { return err } - return os.WriteFile(s.cachePath, cacheBinary, 0o644) + s.lastSavedCache = cacheBinary + return nil } func (s *Service) decodeCache(cacheBinary []byte) error { diff --git a/service/ssmapi/server.go b/service/ssmapi/server.go index f9b382af..157ea150 100644 --- a/service/ssmapi/server.go +++ b/service/ssmapi/server.go @@ -4,6 +4,8 @@ import ( "context" "errors" "net/http" + "sync" + "time" "github.com/sagernet/sing-box/adapter" boxService "github.com/sagernet/sing-box/adapter/service" @@ -28,21 +30,27 @@ func RegisterService(registry *boxService.Registry) { type Service struct { boxService.Adapter - ctx context.Context - logger log.ContextLogger - listener *listener.Listener - tlsConfig tls.ServerConfig - httpServer *http.Server - traffics map[string]*TrafficManager - users map[string]*UserManager - cachePath string + ctx context.Context + cancel context.CancelFunc + logger log.ContextLogger + listener *listener.Listener + tlsConfig tls.ServerConfig + httpServer *http.Server + traffics map[string]*TrafficManager + users map[string]*UserManager + cachePath string + saveTicker *time.Ticker + lastSavedCache []byte + cacheMutex sync.Mutex } func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.SSMAPIServiceOptions) (adapter.Service, error) { + ctx, cancel := context.WithCancel(ctx) chiRouter := chi.NewRouter() s := &Service{ Adapter: boxService.NewAdapter(C.TypeSSMAPI, tag), ctx: ctx, + cancel: cancel, logger: logger, listener: listener.New(listener.Options{ Context: ctx, @@ -95,6 +103,8 @@ func (s *Service) Start(stage adapter.StartStage) error { if err != nil { s.logger.Error(E.Cause(err, "load cache")) } + s.saveTicker = time.NewTicker(1 * time.Minute) + go s.loopSaveCache() if s.tlsConfig != nil { err = s.tlsConfig.Start() if err != nil { @@ -120,7 +130,27 @@ func (s *Service) Start(stage adapter.StartStage) error { return nil } +func (s *Service) loopSaveCache() { + for { + select { + case <-s.ctx.Done(): + return + case <-s.saveTicker.C: + err := s.saveCache() + if err != nil { + s.logger.Error(E.Cause(err, "save cache")) + } + } + } +} + func (s *Service) Close() error { + if s.cancel != nil { + s.cancel() + } + if s.saveTicker != nil { + s.saveTicker.Stop() + } err := s.saveCache() if err != nil { s.logger.Error(E.Cause(err, "save cache"))