基于 Swift 服务器计时器的线程在 AWS 上重新部署 Docker 容器后仍然存在

问题描述

在我的基于 Swift 语言的服务器的临时设计中,它由两个主要部分组成:

  1. 一个请求处理部分,它只处理服务器 API 的 HTTP 请求。对于服务器来说,这一切照旧。

  2. 一个基于计时器的线程,它会定期唤醒并处理某些类型的请求已创建的任务。这些任务位于服务器的两个部分都可以访问的数据库表中。

服务器的这两部分位于同一个 Docker 容器中,并在 AWS Elastic Beanstalk 上运行。我关心的是第二个基于计时器的部分。

它使用以下计时器:

// Adapted from https://gist.github.com/danielgalasko/1da90276f23ea24cb3467c33d2c05768

import Foundation
import LoggerAPI

/// RepeatingTimer mimics the API of dispatchSourceTimer but in a way that prevents
/// crashes that occur from calling resume multiple times on a timer that is
/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52
class RepeatingTimer {

    let timeInterval: TimeInterval
    
    init(timeInterval: TimeInterval) {
        self.timeInterval = timeInterval
    }
    
    private lazy var timer: dispatchSourceTimer = {
        let t = dispatchSource.makeTimerSource()
        t.schedule(deadline: .Now() + .seconds(Int(self.timeInterval)),repeating: .seconds(Int(self.timeInterval)))
        t.setEventHandler(handler: { [weak self] in
            self?.eventHandler?()
        })
        return t
    }()

    var eventHandler: (() -> Void)?

    private enum State {
        case suspended
        case resumed
    }

    private var state: State = .suspended

    deinit {
        Log.debug("RepeatingTimer: deinit")
        eventHandler = nil

        timer.setEventHandler {}
        timer.cancel()
        /*
         If the timer is suspended,calling cancel without resuming
         triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
         */
        resume()
    }

    func resume() {
        if state == .resumed {
            return
        }
        state = .resumed
        timer.resume()
    }

    func suspend() {
        if state == .suspended {
            return
        }
        state = .suspended
        timer.suspend()
    }
}

在服务器启动时调用,如下所示:

        repeatingTimer = RepeatingTimer(timeInterval: interval)
        repeatingTimer?.eventHandler = { [weak self] in
            guard let self = self else { return }
            
            Log.debug("PeriodicUploader: About to run Uploader")

            self.uploader = Uploader(services: self.services,delegate: nil)

            do {
                try self.uploader.run()
            } catch let error {
                Log.error("\(error)")
            }
            
            self.schedule()
        }
        
        repeatingTimer?.resume()

奇怪的是,当我部署一个新的服务器版本(一个新的 Docker 容器)时,我发现至少在某些情况下,服务器的基于计时器的部分不会关闭。我采取了一些服务器端步骤 (https://github.com/SyncServerII/ServerMain/issues/14),在新部署上彻底/完全重启服务器实例来解决这个问题,但我仍然很好奇。看起来这些预定的计时器实例不应该在 Docker 容器更换后继续存在。

我的猜测是,我正在使用的计时器必须以某种方式有效地保持对正在运行的代码的引用,并且即使在新的 Docker 容器部署中,该引用也会保留。

[对于那些可能会问的人:为什么要以这种方式使用计时器?我不打算长期这样做。我计划将服务器的第二部分转移到 AWS Lambda 之类的地方,但还没有这样做]。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...