在前两篇笔记中(
第一篇](http://it.deepinmind.com/akka/2014/10/11/introducing-actors-akka-notes-part-1.html),[第二篇),我们简单地介绍了一下Actor以及它的消息传递是如何工作的。在本篇中,我们将看下如何解决TeacherActor的日志打印及测试的问题。
简单回顾
前面我们的Actor是这样的:
class TeacherActor extends Actor {
val quotes = List(
"Moderation is for cowards",
"Anything worth doing is worth overdoing",
"The trouble is you think you have time",
"You never gonna know if you never even try")
def receive = {
case QuoteRequest => {
import util.Random
//Get a random Quote from the list and construct a response
val quoteResponse=QuoteResponse(quotes(Random.nextInt(quotes.size)))
println (quoteResponse)
}
}
}
在Akka中使用slf4j来打印日志
你应该也看到了,在上面的代码中我们将QuoteResponse打印到了控制台上,你一定会觉得这种方式不太好。我们将使用slf4j接口来解决日志打印的问题。
1. 修改类以支持日志打印
Akka通过一个叫做ActorLogging的特质(trait)来实现的这一功能。我们将这个trait混入(mixin)到类里边:
class TeacherLogActor extends Actor with ActorLogging {
val quotes = List(
"Moderation is for cowards",
"Anything worth doing is worth overdoing",
"The trouble is you think you have time",
"You never gonna know if you never even try")
def receive = {
case QuoteRequest => {
import util.Random
//get a random element (for now)
val quoteResponse=QuoteResponse(quotes(Random.nextInt(quotes.size)))
log.info(quoteResponse.toString())
}
}
//We'll cover the purpose of this method in the Testing section
def quoteList=quotes
}
说几句题外话:
当我们要打印一条消息的时候,ActorLogging中的日志方法会将日志信息发布到一个
EventStream流中。没错,我的确说的是发布。那么EventStream到底是何方神圣?
EventStream和日志
EventStream就像是一个我们用来发布及接收消息的消息代理。它与常见的
消息中间件的根本区别在于EventStream的订阅者只能是一个Actor。
打印消息日志的时候,所有的日志信息都会发布到EventStream里面。
DefaultLogger默认是订阅了这些消息的,它只是简单地将消息打印到了标准输出上。
class DefaultLogger extends Actor with StdOutLogger {
override def receive: Receive = {
...
case event: LogEvent ⇒ print(event)
}
}
因此,这就是为什么我们在启动了StudentSimulatorApp之后,消息日志会打印到控制台上的原因。
也就是说,EventStream不光能用来记录日志。它是Actor在同一个虚拟机内的一个通用的发布-订阅机制。
再回头来说下如何配置slf4j:
2. 配置Akka以支持slf4j
akka{
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "DEBUG"
logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
}
我们把这类信息存储到classpath路径中的一个叫做application.conf的文件里。在我们sbt的目录结构中,它是放在了/main/resources目录下。
从配置信息中我们可以看出:
1. loggers属性指定的是订阅日志事件的Actor。Slf4jLogger要做的就是去消费日志消息,并委托给slf4j日志接口去处理。
2. logLevel属性配置的是日志打印的最小级别
3. loggeing-filter会将配置的logLevel和传进来的日志消息的级别进行比较,把低于logLevel的日志都给过滤掉,然后再发布到EventStream中。
但为什么前面这个例子我们没有用到application.conf呢?
这是因为Akka提供了一些默认值,因此在我们真正使用它之前不用去整一个配置文件。后面我们还会频繁使用到这个文件来定制各式各样的东西。在application.conf中除了日志参数,还有许多很棒的参数以供使用。这里是一个
详细的说明。
3. 配置logback.xml
现在我们来配置一个通过
logback来打印日志的slf4j的logger。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs\akka.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>50MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
我把它跟application.conf一道放在了main/resources目录里。请确保main/resources已经在eclipse或者别的IDE的classpath路径中。同时还要在build.sbt中把logback和slf4j-api给包含进来。
当我们再次启动StudentSimulatorApp并发送消息到新的TeacherLogActor的时候,我们所配置的akkaxxxxx.log文件里的内容会是这样的。
测试Akka
请注意这绝对不是要覆盖到Akka测试中的所有细节。接下面的部分我们将根据各个用例所对应的主题尽量地测试更多的特性。这些用例旨在覆盖我们前面所写的各个Actor。
既然StudentSimulatorApp已经按需求实现好了,现在是时候给它测试一下了。
Akka提供了一套出色的测试工具来减轻测试时的痛苦,我们可以通过它实现许多不可思议的事情,比如可以查看Actor的内部实现。
讲得差不多了,我们来看下这些测试用例吧。
我们先给StudentSimulatorApp写一个测试用例。
先看下声明部分。
class TeacherPreTest extends TestKit(ActorSystem("UniversityMessageSystem"))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll {
从TestCase的定义中可以看出:
1. TestKit trait接收一个ActorSystem参数,这个是用来创建Actor的。在TestKit的内部实现中,它会对ActorSystem进行封装,并替换掉默认的分发器。
2. 我们使用WordSpec来编写测试用例,这是进行Scala测试的一种很有意思的方式。
3. MustMatcher提供了一些很便利的方法,能让测试用例看起来更像是自然语言。
4. 我们还将BeforeAndAfterAll混入了进来,以便在测试结束时能将ActorSystem关闭掉。trait提供的这个afterAll方法很像是JUnit中的tearDown。
1,2-将消息发送给Actor
1. 第一个测试用例只是把一条消息发送给了PrintActor。它并没有做断言:-(
2. 第二个用例将消息发送给日志Actor并使用ActorLogging里的log对象将消息发布给EventStream。它还是没有进行断言:-(
//1. Sends message to the Print Actor. Not even a testcase actually
"A teacher" must {
"print a quote when a QuoteRequest message is sent" in {
val teacherRef = TestActorRef[TeacherActor]
teacherRef ! QuoteRequest
}
}
//2. Sends message to the Log Actor. Again, not a testcase per se
"A teacher with ActorLogging" must {
"log a quote when a QuoteRequest message is sent" in {
val teacherRef = TestActorRef[TeacherLogActor]
teacherRef ! QuoteRequest
}
3 -对Actor的内部状态进行断言判断
第三个用例会使用TestActorRef里的underlyingActor方法并调用TeacherActor内部的quoteList方法。这个方法会返回一个名言的列表。我们会对这个列表的大小进行断言。
如果quoteList失败了,看一下前面提到的TeacherLogActor的代码,找一下这行
//From TeacherLogActor
//We'll cover the purpose of this method in the Testing section
def quoteList=quotes
//3. Asserts the internal State of the Log Actor.
"have a quote list of size 4" in {
val teacherRef = TestActorRef[TeacherLogActor]
teacherRef.underlyingActor.quoteList must have size (4)
teacherRef.underlyingActor.quoteList must have size (4)
}
4 - 日志消息的断言
我们在前面的EventStream和日志一节已经提到过了,所有的日志消息都会发送给EventStream,SLF4JLogger会订阅这些消息并使用自己的appender将日志写入到日志文件或者控制台中。不过在测试用例里直接从EventStream中订阅并对日志消息本身进行断言不是会更好一点么?看起来貌似是可行的。
要实现这点需要做两件事情:
1. 你需要给TestKit中添加一个额外的配置:
class TeacherTest extends TestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll {
2. 既然已经订阅到EventStream中了,现在我们可以在测试用例中对它进行断言了:
//4. Verifying log messages from eventStream
"be verifiable via EventFilter in response to a QuoteRequest that is sent" in {
val teacherRef = TestActorRef[TeacherLogActor]
EventFilter.info(pattern = "QuoteResponse*", occurrences = 1) intercept {
teacherRef ! QuoteRequest
}
}
EventFilter.info块只会拦截以QuoteResponse开头的一条日志消息(pattern='QuoteResponse*)。(或者写成start='QuoteResponse'也可以。如果没有日志消息发送给TeacherLogActor,这条测试用例就会失败)。
5 - 对带构造参数的Actor进行测试
请注意在测试用例中我们是通过TestActorRef[TeacherLogActor]而非syste.actorOf来创建Actor的。这么做是因为我们可以通过TeacherLogAcotr的underlyingActor方法来访问Actor的内部属性。而正常情况在运行时通过ActorRef是无法实现这点的。(不过这可不是在生产代码中使用TestActorRef的借口。你会被揍死的)
如果Actor是接受参数的话,那么我们可以这样来创建TestActorRef:
val teacherRef = TestActorRef(new TeacherLogParameterActor(quotes))
整个测试用例看起来会是这样的:
//5. have a quote list of the same size as the input parameter
" have a quote list of the same size as the input parameter" in {
val quotes = List(
"Moderation is for cowards",
"Anything worth doing is worth overdoing",
"The trouble is you think you have time",
"You never gonna know if you never even try")
val teacherRef = TestActorRef(new TeacherLogParameterActor(quotes))
//val teacherRef = TestActorRef(Props(new TeacherLogParameterActor(quotes)))
teacherRef.underlyingActor.quoteList must have size (4)
EventFilter.info(pattern = "QuoteResponse*", occurrences = 1) intercept {
teacherRef ! QuoteRequest
}
}
关闭ActorSystem
最后,到了afterAll方法
override def afterAll() {
super.afterAll()
system.shutdown()
}
代码
同样的,项目的完整代码可以从
Github中进行下载。
原创文章转载请注明出处:
Java译站
英文原文链接
分享到:
相关推荐
综上所述,"测试用例-测试用例之akka.zip"包含的测试用例文件很可能是针对上述各个知识点的具体实现,通过这些用例,我们可以全面评估和优化Akka应用程序的性能、稳定性和正确性。在实际项目中,执行这些测试用例将...
例如,如何配置、日志记录和部署一个Akka应用程序。Akka的应用程序结构设计为高度模块化和可配置的,这允许开发者在不同环境中快速调整应用程序的行为。 Akka的容错性也是书中的重点之一。Akka通过监督策略和容错...
akka-persistence-sql-async, 一个用于akka持久性的日志和快照存储 akka-persistence-sql-async 的日志和快照存储插件( akka持久化插件。 Akka-persistence-sql-async执行由 scalikejdbc异步查询,它提供非阻塞api来...
- 描述了Akka的工具类,例如事件总线、日志记录器、调度器、持续时间管理、扩展模块、微内核以及断路器等。 - 提供了如何实现常见模式的指导,例如调度周期性消息。 为了满足要求,以下是对上述知识点更详细的解释...
Akka is a distributed computing toolkit that enables developers to build correct concurrent and distributed applications using Java and Scala with ease, applications that scale across servers and ...
Akka是一个建立在Actor模型之上的工具包,它提供了一套用于构建并发、分布式和容错应用程序的API和服务。Akka可以运行在单个JVM上,也可以配置为在分布式系统中运行,其中的Actor可以跨多台机器进行通信。 #### ...
- **Actor:** Akka 的核心组件之一,它负责接收并处理消息。 - **Actor 系统:** 管理 Actor 的容器,负责 Actor 的生命周期管理。 - **消息传递:** Actor 之间通信的主要方式,通过发送消息来进行交互。 - **监督...
Actor 系统是 Akka 的核心组件之一,负责管理 Actor 的生命周期。本节详细解释了如何创建 Actor 系统、如何在其内部创建 Actor 实例以及如何配置 Actor 系统的属性。 ##### 2.3 什么是 Actor? Actor 是 Akka 中最...
Akka.NET是一个强大的工具包,它为.NET开发者提供了一种实现高性能、高可扩展性和容错性的分布式计算的途径。这个框架受到了Scala平台上的Akka的启发,将Actor模型引入了.NET世界。在这个"**c#分布式框架akka范例**...
- **Logging**:日志记录对于调试和运维至关重要,Akka提供了内置的日志记录功能。 - **Scheduler**:定时任务和周期性任务是许多应用的常见需求,Akka Scheduler可以轻松实现这些功能。 - **Circuit Breaker**:...
Akka 是一个强大的工具包和框架,主要用于构建高度并发、分布式和反应式的应用程序,它基于actor模型。在Java中,Akka可以用来实现TCP远程调用,这使得不同系统之间能够通过网络进行通信。下面我们将深入探讨如何...
Akka框架借鉴了Erlang的并发模型,但它是建立在JVM之上,并且提供了丰富的抽象和工具,能够简化开发工作。 标题“Akka Scala 学习高清原版pdf”表明该文档是一个专注于Scala语言在Akka框架中应用的指南,而“描述”...
Akka 支持多种日志框架,用于记录应用程序的日志。 ##### 7.3 Scheduler Scheduler 用于定时任务和周期性的消息发送。 ##### 7.4 Duration Duration 用于表示持续时间,方便进行时间相关的计算。 ##### 7.5 ...
10. **集成和测试**:了解如何将Akka与其他技术(如 Spray for RESTful服务、Cassandra for分布式数据存储)集成,以及如何编写有效的Akka测试,都是学习过程中的重要部分。 通过阅读《Learning Akka》,开发者不仅...
内容简介 本书将尝试帮助入门级、中级以及高级读者理解基本的分布式计算概念,并且展示 ...第 8 章 测试与设计:行为说明、领域驱动设计以及 Akka Testkit。 第 9 章 尾声:其他 Akka 特性。下一步需要学习的知识。
- **概述**:Akka Testkit 是用于测试 Akka 应用的一个工具集。 - **功能**:支持 Actor 测试、断言和模拟消息传递等。 - **应用场景**:用于确保 Akka 应用的正确性和稳定性。 #### 十四、领域驱动设计 (DDD) - **...
Akka 是一个强大的开源工具,主要用于构建高度并发、分布式和反应式的应用程序,主要在Java和Scala语言环境下使用。它的核心设计理念是Actor模型,这使得它能够处理大量的并发操作,同时保持系统的可扩展性和容错性...
8. **测试工具**:Akka提供了丰富的测试工具和API,帮助开发者编写和测试Actor系统,确保其正确性和性能。 阅读《Akka in Action》中文版,你可以学习到如何使用Akka构建高效的并发应用,理解Actor系统的架构设计...