Статьи

Akka Notes: Конфигурация и планирование ActorSystem

Как мы видели из наших предыдущих постов, мы могли бы создать актера, используя  actorOf метод  ActorSystem. На самом деле с ActorSystem можно сделать гораздо больше. Мы коснемся только бита Конфигурация и Планирование в этой статье

Давайте посмотрим на подмножества методов, доступных в  ActorSystem .

Actor API

1. КОНФИГУРАЦИЯ УПРАВЛЕНИЯ

Помните  application.conf файл, который мы использовали для настройки уровня нашего журнала в  предыдущей записи ? Этот файл конфигурации похож на эти .properties файлы в приложениях Java и многое другое. Скоро мы увидим, как мы могли бы использовать этот файл конфигурации для настройки наших диспетчеров, почтовых ящиков и т. Д.  (Я даже близко не оцениваю мощь конфигурации типа  safeafe . Пожалуйста, ознакомьтесь с некоторыми  примерами,  чтобы по-настоящему оценить его удивительность)

Таким образом, когда мы создаем ActorSystem с использованием объектом ActorSystem в  apply метод без указания какой — либо конфигурации, она высматривает application.confapplication.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. РАСПИСАНИЕ

Планировщик метода ActorSystem

Как вы можете видеть из  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 параметра:

  1. Как долго должна быть начальная задержка до начала первого выполнения
  2. Частота последующих казней
  3. Целевой ActorRef, на который мы собираемся отправить сообщение
  4. Сообщение
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 здесь .