Actors in Scala(Scala中的Actor)(预打印版) 第四章 Actor Chat (A)
Actors in Scala(Scala中的Actor)(预打印版) 第四章 Actor Chat (A)
张贵宾
2011.10.21
注:翻译这些英文书籍资料纯属个人爱好,如有不恰当之处敬请指正。
前面的章节展示了actor在消息传输方面的编程模型。不用惊奇,许多Scala的actor库定义了大量的处理发送消息和接收消息的编程结构。这些结构以内部领域语言(internal domain-specific language--DSL)的形式展现给了大家。本章将使用一个典型的消息应用程序:聊天程序,来向大家展示Scala的actor DSL的关键要素。
聊天程序允许用户通过对不同的主题交换消息来相互交互,每个主题代表一个聊天室,用户如果对一个聊天室主题下面的一系列讨论感兴趣的话,他便会订阅这个聊天室主题。一旦订阅了,用户可以给聊天室发送消息,反过来,他也能收到聊天室中其他订阅者发送的消息。图4.1提供了本章开发的聊天程序的概览。
4.1 Defining message classes(定义消息类)
4.2 Processing messages(处理消息)
在actor中所有的消息处理发生在 actor 方法内,下面的代码展示了如何定义actor:
import scala.actors.Actor class ChatRoom extends Actor { def actor() { // the actor's behavior } }当调用了actor的start方法之后,定义在actor方法中的消息处理逻辑才会被启动。如下代码所示: val chatRoom = new ChatRoom chatRoom.start
actor消息处理中的一个关键任务是从actor的mailbox中获取下一个可用的消息。Actor中的receive方法就能完成这个任务,它首先中邮箱中删除一条消息,然后把这条消息传递给你作为参数传给receive方法的一系列模式匹配语句,(让这些模式匹配语句去处理该消息。)下面代码为ChatRoom将接受的三种消息中的每一种定义了一个模式:
每一次receive方法的调用,actor都会从mailbox中取到下一个可用的消息,然后把消息传输给模式匹配的列表。每条消息会依次从上到下对每个模式进行评估,如果某个模式匹配成功,这条消息就从mailbox中被删除,这个模式之后的那些模式就不再被评估。如果没有匹配到任何模式,则消息就会留在mailbox中。
在这个例子中,ChatRoom期望收到Subscribe、Unsubscribe或者UserPost消息,当收到这三种消息时,ChatRoom将会评估模式箭头=>右边的表达式。
Scala的actor库也提供了一种快捷方式来定义和启动actor,只需要一步即可,而不需要显示的扩展Actor trait。下面的代码展示了使用简化写法的actor。
val chatRoom = actor{ while(true) { receive { case Subscribe(user) => //处理订阅 case Unsubscribe(user) =>//处理取消订阅 case UserPost(user, post) => //处理发帖 } } }处理订阅消息
收到了Subscribe消息之后,ChatRoom必须把用户添加到它的订阅者列表中。首先,看起来可以在一个list中保存聊天室的订阅者,但是要注意订阅者必须能够接受来自聊天室的消息。在我们当前的设计中,当UserPost消息到达时,ChatRoom会遍历所有的订阅者,并且把消息内容发给除了发送消息者之外的所有的订阅者。
因此用户可以接受来自聊天室的消息,在订阅会话中,我们可以用actor来代表一个用户,当ChatRoom收到了Subscribe消息,它可以创建一个新的actor来代表用户,把用户和新创建的actor关联。这个actor,反过来将要处理ChatRoom发给它的Post消息,代码如下所示:
val chatRoom = new ChatRoom chatRoom ! Subscribe(User("Bob")) var session = Map.empty[User, Actor] while(true) { receive { case Subscribe(user) => val sessionUser = actor { while(true) { self.receive { case Post(msg) => //Send the message to sender } } } session = session + (user -> sessionUser) //TODO handle the Unsubscribe message } }