#swift #amazon-web-services #docker #amazon-elastic-beanstalk #server-side-swift
Вопрос:
В промежуточном проекте для моего сервера на основе Swift-языка он состоит из двух основных частей:
- Часть обработки запросов, которая просто обрабатывает HTTP-запросы для API для сервера. Это просто обычный бизнес для сервера.
- Поток на основе таймера, который периодически пробуждается и обрабатывает задачи, созданные определенными типами запросов. Эти задачи находятся в таблицах базы данных, к которым имеют доступ обе части сервера.
Эти две части сервера находятся в одном контейнере Docker и работают на AWS Elastic Beanstalk. Меня беспокоит 2-я часть, основанная на таймере.
Он использует следующий таймер:
// 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.
My guess is that the timer I’m using must somehow keep effectively a reference to the running code, and that that reference and timer are retained even across the new Docker container deployment.
[For those who might ask: Why are you using a timer in this manner? I don’t intend to do this long term. I plan to shift this second part of the server to something like AWS Lambda, but haven’t done that yet].