Как мы видели из наших предыдущих постов, мы могли бы создать актера, используя actorOf метод ActorSystem. На самом деле с ActorSystem можно сделать гораздо больше. Мы коснемся только бита Конфигурация и Планирование в этой статье
Давайте посмотрим на подмножества методов, доступных в ActorSystem .
1. КОНФИГУРАЦИЯ УПРАВЛЕНИЯ
Помните application.conf файл, который мы использовали для настройки уровня нашего журнала в предыдущей записи ? Этот файл конфигурации похож на эти .properties файлы в приложениях Java и многое другое. Скоро мы увидим, как мы могли бы использовать этот файл конфигурации для настройки наших диспетчеров, почтовых ящиков и т. Д. (Я даже близко не оцениваю мощь конфигурации типа safeafe . Пожалуйста, ознакомьтесь с некоторыми примерами, чтобы по-настоящему оценить его удивительность)
Таким образом, когда мы создаем ActorSystem с использованием объектом ActorSystem в apply метод без указания какой — либо конфигурации, она высматривает application.conf, application.json и application.properties в корне и загружают классы их автоматически.
Так,
val system=ActorSystem("UniversityMessagingSystem")
такой же как
val system=ActorSystem("UniversityMessagingSystem", ConfigFactory.load())
Чтобы предоставить доказательства этому аргументу, проверьте метод apply в ActorSystem.scala
def apply(name: String, config: Option[Config] = None, classLoader: Option[ClassLoader] = None, defaultExecutionContext: Option[ExecutionContext] = None): ActorSystem = {
val cl = classLoader.getOrElse(findClassLoader())
val appConfig = config.getOrElse(ConfigFactory.load(cl))
new ActorSystemImpl(name, appConfig, cl, defaultExecutionContext).start()
}
A. ПЕРЕХОДНАЯ КОНФИГУРАЦИЯ ПО УМОЛЧАНИЮ
Если вы не заинтересованы в использовании application.conf (как в тестовых случаях) или хотели бы иметь свой собственный файл конфигурации (как при тестировании другой конфигурации или развертывании в других средах), вы можете переопределить это, передав вместо этого свою собственную конфигурацию желая одного из classpath.
ConfigFactory.parseString является одним из вариантов
val actorSystem=ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]"""))
или же
просто в вашем тесте как
class TeacherTestLogListener extends TestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll {
Там также ConfigFactory.load
val system = ActorSystem("UniversityMessageSystem", ConfigFactory.load("uat-application.conf"))
Если вам нужен доступ к вашим собственным параметрам конфигурации во время выполнения, вы можете сделать это через его API следующим образом:
val system=ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]"""))
println (system.settings.config.getValue("akka.loggers")) // Results in > SimpleConfigList(["akka.testkit.TestEventListener"])
B. РАСШИРЕНИЕ КОНФИГУРАЦИИ ПО УМОЛЧАНИЮ
Помимо переопределения, вы также можете расширить конфигурацию по умолчанию с помощью пользовательской конфигурации, используя withFallback метод Config .
Допустим, ваша application.conf внешность выглядит так:
akka{
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = DEBUG
arun="hello"
}
и вы решили переопределить akka.loggers свойство как:
val config=ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")
val system=ActorSystem("UniversityMessageSystem", config.withFallback(ConfigFactory.load()))
В итоге вы получите объединенную конфигурацию:
println (system.settings.config.getValue("akka.arun")) //> ConfigString("hello")
println (system.settings.config.getValue("akka.loggers")) //> SimpleConfigList(["akka.testkit.TestEventListener"])
Итак, почему я рассказал всю эту историю о конфигурации? Потому что наш ActorSystem загружает и предоставляет доступ ко всей информации о конфигурации.
ВАЖНАЯ ЗАМЕТКА :
Следите за порядком отступления здесь — это значение по умолчанию и конфигурация расширения. Помните, что вы должны вернуться к конфигурации по умолчанию. Так,
config.withFallback(ConfigFactory.load())
должно сработать
но
ConfigFactory.load().withFallback(config)
не получит результаты, которые вам могут понадобиться.
2. РАСПИСАНИЕ
Как вы можете видеть из API ActorSystem , в ActorSystem есть небольшой мощный метод, который вызывает scheduler a Scheduler. Планировщик имеет целый ряд schedule методов , с которыми мы могли бы сделать некоторые прикольные вещи внутри окружения актера.
А. ГРАФИК ЧТО-ТО, ЧТОБЫ ВЫПОЛНИТЬ ОДИН РАЗ
На нашем примере Student-Teacher , предположим, что StudentActor захочет отправить сообщение учителю только через 5 секунд после его получения InitSignal из нашего Testcase, а не сразу, наш код выглядит следующим образом:
class StudentDelayedActor (teacherActorRef:ActorRef) extends Actor with ActorLogging {
def receive = {
case InitSignal=> {
import context.dispatcher
context.system.scheduler.scheduleOnce(5 seconds, teacherActorRef, QuoteRequest)
//teacherActorRef!QuoteRequest
}
...
...
}
}
Прецедент
Давайте подготовим тестовый сценарий, чтобы проверить это:
"A delayed student" must {
"fire the QuoteRequest after 5 seconds when an InitSignal is sent to it" in {
import me.rerun.akkanotes.messaging.protocols.StudentProtocol._
val teacherRef = system.actorOf(Props[TeacherActor], "teacherActorDelayed")
val studentRef = system.actorOf(Props(new StudentDelayedActor(teacherRef)), "studentDelayedActor")
EventFilter.info (start="Printing from Student Actor", occurrences=1).intercept{
studentRef!InitSignal
}
}
}
Увеличение времени ожидания перехвата Eventfilter
Уч. Время ожидания по умолчанию для EventFilter для ожидания появления сообщения EventStream составляет 3 секунды. Давайте увеличим это до 7 секунд, чтобы проверить наш тестовый пример. Свойство filter-leeway конфигурации помогает нам достичь этого.
class RequestResponseTest extends TestKit(ActorSystem("TestUniversityMessageSystem", ConfigFactory.parseString("""
akka{
loggers = ["akka.testkit.TestEventListener"]
test{
filter-leeway = 7s
}
}
""")))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll
with ImplicitSender {
...
...
Б. ГРАФИК ЧТО-ТО, ЧТОБЫ ВЫПОЛНИТЬ ПОВТОРНО
Для повторного выполнения чего-либо вы используете schedule метод планировщика.
Одна из часто используемых перегрузок метода расписания — это то, что отправляет сообщение актеру на регулярной основе. Он принимает 4 параметра:
- Как долго должна быть начальная задержка до начала первого выполнения
- Частота последующих казней
- Целевой ActorRef, на который мы собираемся отправить сообщение
- Сообщение
case InitSignal=> {
import context.dispatcher
context.system.scheduler.schedule(0 seconds, 5 seconds, teacherActorRef, QuoteRequest)
//teacherActorRef!QuoteRequest
}
TRIVIA
Импорт import context.dispatcher здесь очень важен.
Методы расписания требуют очень важного неявного параметра ExecutionContext, причина которого будет очевидна, как только мы увидим реализацию schedule метода:
final def schedule(
initialDelay: FiniteDuration,
interval: FiniteDuration,
receiver: ActorRef,
message: Any)(implicit executor: ExecutionContext,
sender: ActorRef = Actor.noSender): Cancellable =
schedule(initialDelay, interval, new Runnable {
def run = {
receiver ! message
if (receiver.isTerminated)
throw new SchedulerException("timer active for terminated actor")
}
})
Метод schedule просто оборачивает tell объект, Runnable который в конечном итоге исполняется с помощью ExecutionContext, который мы передаем.
Чтобы сделать ExecutionContext доступным в области видимости как неявный, мы используем неявный диспетчер, доступный в контексте.
Из ActorCell.scala (Контекст)
/** * Returns the dispatcher (MessageDispatcher) that is used for this Actor. * Importing this member will place an implicit ExecutionContext in scope. */ implicit def dispatcher: ExecutionContextExecutor
КОД
Как всегда, весь проект можно скачать с github здесь .

