Как мы видели из наших предыдущих постов, мы могли бы создать Actor, используя метод actorOf системы ActorSystem . На самом деле с ActorSystem можно сделать гораздо больше. Мы коснемся только бита Конфигурация и Планирование в этой статье.
Давайте посмотрим на подмножества методов, доступных в ActorSystem .
1. Управление конфигурацией
Помните файл application.conf который мы использовали для настройки уровня нашего журнала в предыдущей записи ? Этот файл конфигурации похож на файлы .properties в приложениях Java и многое другое. Скоро мы увидим, как мы могли бы использовать этот файл конфигурации для настройки наших диспетчеров, почтовых ящиков и т. Д. (Я даже близко не оцениваю мощь конфигурации типа safeafe . Пожалуйста, ознакомьтесь с некоторыми примерами, чтобы действительно оценить его удивительность) .
Поэтому, когда мы создаем ActorSystem с помощью метода apply объекта ActorSystem без указания какой-либо конфигурации, он ищет application.conf , application.json и application.properties в корне пути к классам и загружает их автоматически.
Так:
|
1
|
val system=ActorSystem("UniversityMessagingSystem") |
такой же как:
|
1
|
val system=ActorSystem("UniversityMessagingSystem", ConfigFactory.load()) |
Чтобы предоставить доказательства этому аргументу, проверьте метод apply в ActorSystem.scala :
|
1
2
3
4
5
|
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() } |
а. Переопределение конфигурации по умолчанию
Если вы не заинтересованы в использовании application.conf (как в тестовых случаях) или хотели бы иметь свой собственный файл конфигурации (как при тестировании другой конфигурации или развертывании в других средах), вы можете отменить это, передав свой собственная конфигурация вместо того, чтобы хотеть тот из classpath.
ConfigFactory.parseString является одним из вариантов
|
1
|
val actorSystem=ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")) |
или просто в вашем Testcase как:
|
1
2
3
4
|
class TeacherTestLogListener extends TestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]"""))) with WordSpecLike with MustMatchers with BeforeAndAfterAll { |
Там также ConfigFactory.load
|
1
|
val system = ActorSystem("UniversityMessageSystem", ConfigFactory.load("uat-application.conf")) |
Если вам нужен доступ к вашим собственным параметрам конфигурации во время выполнения, вы можете сделать это через его API следующим образом:
|
1
2
|
val system=ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")) println (system.settings.config.getValue("akka.loggers")) // Results in > SimpleConfigList(["akka.testkit.TestEventListener"]) |
б. Расширение конфигурации по умолчанию
Помимо переопределения, вы также можете расширить конфигурацию по умолчанию с помощью своей пользовательской конфигурации, используя метод withFallback Config .
Допустим, ваш application.conf выглядит так:
|
1
2
3
4
5
|
akka{ loggers = ["akka.event.slf4j.Slf4jLogger"] loglevel = DEBUG arun="hello"} |
и вы решили переопределить свойство akka.loggers например:
|
1
2
|
val config=ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""") val system=ActorSystem("UniversityMessageSystem", config.withFallback(ConfigFactory.load())) |
В итоге вы получите объединенную конфигурацию:
|
1
2
|
println (system.settings.config.getValue("akka.arun")) //> ConfigString("hello") println (system.settings.config.getValue("akka.loggers")) //> SimpleConfigList(["akka.testkit.TestEventListener"]) |
Итак, почему я рассказал всю эту историю о конфигурации? Потому что наша ActorSystem является той, которая загружает и предоставляет доступ ко всей информации о конфигурации.
ВАЖНАЯ ЗАМЕТКА
Следите за порядком отступления здесь — это значение по умолчанию и конфигурация расширения. Помните, что вы должны вернуться к конфигурации по умолчанию. Так:
|
1
|
config.withFallback(ConfigFactory.load()) |
будет работать, но:
|
1
|
ConfigFactory.load().withFallback(config) |
не получит результаты, которые вам могут понадобиться.
2. Планировщик
Как вы можете видеть из API ActorSystem , в ActorSystem есть небольшой мощный метод, называемый scheduler который возвращает Scheduler . В планировщике есть множество методов schedule с помощью которых мы могли бы делать забавные вещи в среде Actor.
а. Запланируйте что-нибудь для выполнения
На нашем примере Student-Teacher , предположим, что StudentActor захочет отправить сообщение учителю только через 5 секунд после получения InitSignal из нашего Testcase, а не сразу, наш код выглядит следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
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 } ... ... }} |
Прецедент
Давайте подготовим тестовый сценарий, чтобы проверить это:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
"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 помогает нам достичь этого.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
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 { ... ... |
б. Запланируйте что-нибудь для выполнения повторно
Для повторного выполнения чего-либо вы используете метод расписания Планировщика.
Одна из часто используемых перегрузок метода расписания — это то, что отправляет сообщение актеру на регулярной основе. Он принимает 4 параметра:
- Как долго должна быть начальная задержка до начала первого выполнения
- Частота последующих казней
- Целевой ActorRef, на который мы собираемся отправить сообщение
- Сообщение
|
1
2
3
4
5
|
case InitSignal=> { import context.dispatcher context.system.scheduler.schedule(0 seconds, 5 seconds, teacherActorRef, QuoteRequest) //teacherActorRef!QuoteRequest } |
TRIVIA
Импорт- import context.dispatcher здесь очень важен.
Методы расписания требуют очень важного неявного параметра — ExecutionContext , причина которого будет очевидна, как только мы увидим реализацию метода schedule :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
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 просто помещает сообщение в Runnable которое в конечном итоге исполняется с помощью ExecutionContext, который мы передаем.
Чтобы сделать ExecutionContext доступным в области видимости как неявный, мы используем неявный диспетчер, доступный в контексте.
Из ActorCell.scala (Context):
|
1
2
3
4
5
|
/** * 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 здесь .
| Ссылка: | Akka Notes — ActorSystem (Конфигурирование и планирование) — 4 от нашего партнера JCG Аруна Маниваннана в блоге Rerun.me . |

