Merge remote-tracking branch 'origin/master' into wip-1750-remove-ReflectiveAccess-∂π
This commit is contained in:
commit
d2f28a06cd
83 changed files with 1579 additions and 561 deletions
|
|
@ -14,6 +14,7 @@ import akka.util.duration._
|
||||||
object SupervisorMiscSpec {
|
object SupervisorMiscSpec {
|
||||||
val config = """
|
val config = """
|
||||||
pinned-dispatcher {
|
pinned-dispatcher {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
test-dispatcher {
|
test-dispatcher {
|
||||||
|
|
|
||||||
|
|
@ -435,6 +435,7 @@ object DispatcherModelSpec {
|
||||||
val config = {
|
val config = {
|
||||||
"""
|
"""
|
||||||
boss {
|
boss {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
""" +
|
""" +
|
||||||
|
|
@ -506,6 +507,7 @@ object BalancingDispatcherModelSpec {
|
||||||
val config = {
|
val config = {
|
||||||
"""
|
"""
|
||||||
boss {
|
boss {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
""" +
|
""" +
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,12 @@ import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
||||||
import scala.reflect.{ Manifest }
|
import scala.reflect.{ Manifest }
|
||||||
import akka.dispatch._
|
import akka.dispatch._
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
|
import akka.testkit.ImplicitSender
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import akka.actor.Actor
|
||||||
|
import akka.actor.Props
|
||||||
|
import akka.util.duration._
|
||||||
|
|
||||||
object DispatchersSpec {
|
object DispatchersSpec {
|
||||||
val config = """
|
val config = """
|
||||||
|
|
@ -16,13 +20,22 @@ object DispatchersSpec {
|
||||||
mydispatcher {
|
mydispatcher {
|
||||||
throughput = 17
|
throughput = 17
|
||||||
}
|
}
|
||||||
|
thread-pool-dispatcher {
|
||||||
|
executor = thread-pool-executor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class ThreadNameEcho extends Actor {
|
||||||
|
def receive = {
|
||||||
|
case _ ⇒ sender ! Thread.currentThread.getName
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class DispatchersSpec extends AkkaSpec(DispatchersSpec.config) {
|
class DispatchersSpec extends AkkaSpec(DispatchersSpec.config) with ImplicitSender {
|
||||||
|
import DispatchersSpec._
|
||||||
val df = system.dispatchers
|
val df = system.dispatchers
|
||||||
import df._
|
import df._
|
||||||
|
|
||||||
|
|
@ -92,6 +105,30 @@ class DispatchersSpec extends AkkaSpec(DispatchersSpec.config) {
|
||||||
d1 must be === d2
|
d1 must be === d2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"include system name and dispatcher id in thread names for fork-join-executor" in {
|
||||||
|
system.actorOf(Props[ThreadNameEcho].withDispatcher("myapp.mydispatcher")) ! "what's the name?"
|
||||||
|
val Expected = "(DispatchersSpec-myapp.mydispatcher-[1-9][0-9]*)".r
|
||||||
|
expectMsgPF(5 seconds) {
|
||||||
|
case Expected(x) ⇒
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"include system name and dispatcher id in thread names for thread-pool-executor" in {
|
||||||
|
system.actorOf(Props[ThreadNameEcho].withDispatcher("myapp.thread-pool-dispatcher")) ! "what's the name?"
|
||||||
|
val Expected = "(DispatchersSpec-myapp.thread-pool-dispatcher-[1-9][0-9]*)".r
|
||||||
|
expectMsgPF(5 seconds) {
|
||||||
|
case Expected(x) ⇒
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"include system name and dispatcher id in thread names for default-dispatcher" in {
|
||||||
|
system.actorOf(Props[ThreadNameEcho]) ! "what's the name?"
|
||||||
|
val Expected = "(DispatchersSpec-akka.actor.default-dispatcher-[1-9][0-9]*)".r
|
||||||
|
expectMsgPF(5 seconds) {
|
||||||
|
case Expected(x) ⇒
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import akka.pattern.ask
|
||||||
object PinnedActorSpec {
|
object PinnedActorSpec {
|
||||||
val config = """
|
val config = """
|
||||||
pinned-dispatcher {
|
pinned-dispatcher {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,14 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference) {
|
||||||
getBoolean("akka.actor.serialize-messages") must equal(false)
|
getBoolean("akka.actor.serialize-messages") must equal(false)
|
||||||
settings.SerializeAllMessages must equal(false)
|
settings.SerializeAllMessages must equal(false)
|
||||||
|
|
||||||
getInt("akka.scheduler.ticksPerWheel") must equal(512)
|
getInt("akka.scheduler.ticks-per-wheel") must equal(512)
|
||||||
settings.SchedulerTicksPerWheel must equal(512)
|
settings.SchedulerTicksPerWheel must equal(512)
|
||||||
|
|
||||||
getMilliseconds("akka.scheduler.tickDuration") must equal(100)
|
getMilliseconds("akka.scheduler.tick-duration") must equal(100)
|
||||||
settings.SchedulerTickDuration must equal(100 millis)
|
settings.SchedulerTickDuration must equal(100 millis)
|
||||||
|
|
||||||
|
settings.Daemonicity must be(false)
|
||||||
|
settings.JvmExitOnFatalError must be(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -46,7 +49,7 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference) {
|
||||||
c.getString("executor") must equal("fork-join-executor")
|
c.getString("executor") must equal("fork-join-executor")
|
||||||
c.getInt("mailbox-capacity") must equal(-1)
|
c.getInt("mailbox-capacity") must equal(-1)
|
||||||
c.getMilliseconds("mailbox-push-timeout-time") must equal(10 * 1000)
|
c.getMilliseconds("mailbox-push-timeout-time") must equal(10 * 1000)
|
||||||
c.getString("mailboxType") must be("")
|
c.getString("mailbox-type") must be("")
|
||||||
c.getMilliseconds("shutdown-timeout") must equal(1 * 1000)
|
c.getMilliseconds("shutdown-timeout") must equal(1 * 1000)
|
||||||
c.getInt("throughput") must equal(5)
|
c.getInt("throughput") must equal(5)
|
||||||
c.getMilliseconds("throughput-deadline-time") must equal(0)
|
c.getMilliseconds("throughput-deadline-time") must equal(0)
|
||||||
|
|
|
||||||
|
|
@ -860,7 +860,7 @@ class FutureSpec extends AkkaSpec with Checkers with BeforeAndAfterAll with Defa
|
||||||
|
|
||||||
val l1, l2 = new TestLatch
|
val l1, l2 = new TestLatch
|
||||||
val complex = Future() map { _ ⇒
|
val complex = Future() map { _ ⇒
|
||||||
Future.blocking(system.dispatcher)
|
Future.blocking()
|
||||||
val nested = Future(())
|
val nested = Future(())
|
||||||
nested foreach (_ ⇒ l1.open())
|
nested foreach (_ ⇒ l1.open())
|
||||||
Await.ready(l1, TestLatch.DefaultTimeout) // make sure nested is completed
|
Await.ready(l1, TestLatch.DefaultTimeout) // make sure nested is completed
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ class PriorityMailboxSpec extends MailboxSpec {
|
||||||
object CustomMailboxSpec {
|
object CustomMailboxSpec {
|
||||||
val config = """
|
val config = """
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
mailboxType = "akka.dispatch.CustomMailboxSpec$MyMailboxType"
|
mailbox-type = "akka.dispatch.CustomMailboxSpec$MyMailboxType"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ import com.typesafe.config.Config
|
||||||
object PriorityDispatcherSpec {
|
object PriorityDispatcherSpec {
|
||||||
val config = """
|
val config = """
|
||||||
unbounded-prio-dispatcher {
|
unbounded-prio-dispatcher {
|
||||||
mailboxType = "akka.dispatch.PriorityDispatcherSpec$Unbounded"
|
mailbox-type = "akka.dispatch.PriorityDispatcherSpec$Unbounded"
|
||||||
}
|
}
|
||||||
bounded-prio-dispatcher {
|
bounded-prio-dispatcher {
|
||||||
mailboxType = "akka.dispatch.PriorityDispatcherSpec$Bounded"
|
mailbox-type = "akka.dispatch.PriorityDispatcherSpec$Bounded"
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@
|
||||||
|
|
||||||
package akka.serialization
|
package akka.serialization
|
||||||
|
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.{ AkkaSpec, EventFilter }
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
import java.io._
|
import java.io._
|
||||||
import akka.dispatch.Await
|
import akka.dispatch.Await
|
||||||
|
|
@ -17,21 +16,25 @@ import akka.pattern.ask
|
||||||
|
|
||||||
object SerializeSpec {
|
object SerializeSpec {
|
||||||
|
|
||||||
val serializationConf = ConfigFactory.parseString("""
|
val config = """
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serializers {
|
serializers {
|
||||||
java = "akka.serialization.JavaSerializer"
|
|
||||||
test = "akka.serialization.TestSerializer"
|
test = "akka.serialization.TestSerializer"
|
||||||
}
|
}
|
||||||
|
|
||||||
serialization-bindings {
|
serialization-bindings {
|
||||||
java = ["akka.serialization.SerializeSpec$Person", "akka.serialization.SerializeSpec$Address", "akka.serialization.MyJavaSerializableActor", "akka.serialization.MyStatelessActorWithMessagesInMailbox", "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
"akka.serialization.SerializeSpec$Person" = java
|
||||||
test = ["akka.serialization.TestSerializble", "akka.serialization.SerializeSpec$PlainMessage"]
|
"akka.serialization.SerializeSpec$Address" = java
|
||||||
|
"akka.serialization.TestSerializble" = test
|
||||||
|
"akka.serialization.SerializeSpec$PlainMessage" = test
|
||||||
|
"akka.serialization.SerializeSpec$A" = java
|
||||||
|
"akka.serialization.SerializeSpec$B" = test
|
||||||
|
"akka.serialization.SerializeSpec$D" = test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
"""
|
||||||
|
|
||||||
@BeanInfo
|
@BeanInfo
|
||||||
case class Address(no: String, street: String, city: String, zip: String) { def this() = this("", "", "", "") }
|
case class Address(no: String, street: String, city: String, zip: String) { def this() = this("", "", "", "") }
|
||||||
|
|
@ -54,10 +57,18 @@ object SerializeSpec {
|
||||||
|
|
||||||
class ExtendedPlainMessage extends PlainMessage
|
class ExtendedPlainMessage extends PlainMessage
|
||||||
|
|
||||||
|
class Both(s: String) extends SimpleMessage(s) with Serializable
|
||||||
|
|
||||||
|
trait A
|
||||||
|
trait B
|
||||||
|
class C extends B with A
|
||||||
|
class D extends A
|
||||||
|
class E extends D
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class SerializeSpec extends AkkaSpec(SerializeSpec.serializationConf) {
|
class SerializeSpec extends AkkaSpec(SerializeSpec.config) {
|
||||||
import SerializeSpec._
|
import SerializeSpec._
|
||||||
|
|
||||||
val ser = SerializationExtension(system)
|
val ser = SerializationExtension(system)
|
||||||
|
|
@ -69,8 +80,8 @@ class SerializeSpec extends AkkaSpec(SerializeSpec.serializationConf) {
|
||||||
"Serialization" must {
|
"Serialization" must {
|
||||||
|
|
||||||
"have correct bindings" in {
|
"have correct bindings" in {
|
||||||
ser.bindings(addr.getClass.getName) must be("java")
|
ser.bindings.collectFirst { case (c, s) if c == addr.getClass ⇒ s.getClass } must be(Some(classOf[JavaSerializer]))
|
||||||
ser.bindings(classOf[PlainMessage].getName) must be("test")
|
ser.bindings.collectFirst { case (c, s) if c == classOf[PlainMessage] ⇒ s.getClass } must be(Some(classOf[TestSerializer]))
|
||||||
}
|
}
|
||||||
|
|
||||||
"serialize Address" in {
|
"serialize Address" in {
|
||||||
|
|
@ -144,58 +155,68 @@ class SerializeSpec extends AkkaSpec(SerializeSpec.serializationConf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by direct interface" in {
|
"resolve serializer by direct interface" in {
|
||||||
val msg = new SimpleMessage("foo")
|
ser.serializerFor(classOf[SimpleMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by interface implemented by super class" in {
|
"resolve serializer by interface implemented by super class" in {
|
||||||
val msg = new ExtendedSimpleMessage("foo", 17)
|
ser.serializerFor(classOf[ExtendedSimpleMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by indirect interface" in {
|
"resolve serializer by indirect interface" in {
|
||||||
val msg = new AnotherMessage
|
ser.serializerFor(classOf[AnotherMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer by indirect interface implemented by super class" in {
|
"resolve serializer by indirect interface implemented by super class" in {
|
||||||
val msg = new ExtendedAnotherMessage
|
ser.serializerFor(classOf[ExtendedAnotherMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer for message with binding" in {
|
"resolve serializer for message with binding" in {
|
||||||
val msg = new PlainMessage
|
ser.serializerFor(classOf[PlainMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"resove serializer for message extending class with with binding" in {
|
"resolve serializer for message extending class with with binding" in {
|
||||||
val msg = new ExtendedPlainMessage
|
ser.serializerFor(classOf[ExtendedPlainMessage]).getClass must be(classOf[TestSerializer])
|
||||||
ser.serializerFor(msg.getClass).getClass must be(classOf[TestSerializer])
|
}
|
||||||
|
|
||||||
|
"give warning for message with several bindings" in {
|
||||||
|
EventFilter.warning(start = "Multiple serializers found", occurrences = 1) intercept {
|
||||||
|
ser.serializerFor(classOf[Both]).getClass must be(classOf[TestSerializer])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"resolve serializer in the order of the bindings" in {
|
||||||
|
ser.serializerFor(classOf[A]).getClass must be(classOf[JavaSerializer])
|
||||||
|
ser.serializerFor(classOf[B]).getClass must be(classOf[TestSerializer])
|
||||||
|
EventFilter.warning(start = "Multiple serializers found", occurrences = 1) intercept {
|
||||||
|
ser.serializerFor(classOf[C]).getClass must be(classOf[JavaSerializer])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"resolve serializer in the order of most specific binding first" in {
|
||||||
|
ser.serializerFor(classOf[A]).getClass must be(classOf[JavaSerializer])
|
||||||
|
ser.serializerFor(classOf[D]).getClass must be(classOf[TestSerializer])
|
||||||
|
ser.serializerFor(classOf[E]).getClass must be(classOf[TestSerializer])
|
||||||
|
}
|
||||||
|
|
||||||
|
"throw java.io.NotSerializableException when no binding" in {
|
||||||
|
intercept[java.io.NotSerializableException] {
|
||||||
|
ser.serializerFor(classOf[Actor])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object VerifySerializabilitySpec {
|
object VerifySerializabilitySpec {
|
||||||
val conf = ConfigFactory.parseString("""
|
val conf = """
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serialize-messages = on
|
serialize-messages = on
|
||||||
|
|
||||||
serialize-creators = on
|
serialize-creators = on
|
||||||
|
|
||||||
serializers {
|
|
||||||
java = "akka.serialization.JavaSerializer"
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
java = ["akka.serialization.SerializeSpec$Address", "akka.serialization.MyJavaSerializableActor", "akka.serialization.MyStatelessActorWithMessagesInMailbox", "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
"""
|
||||||
|
|
||||||
class FooActor extends Actor {
|
class FooActor extends Actor {
|
||||||
def receive = {
|
def receive = {
|
||||||
|
|
@ -203,6 +224,10 @@ object VerifySerializabilitySpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FooUntypedActor extends UntypedActor {
|
||||||
|
def onReceive(message: Any) {}
|
||||||
|
}
|
||||||
|
|
||||||
class NonSerializableActor(system: ActorSystem) extends Actor {
|
class NonSerializableActor(system: ActorSystem) extends Actor {
|
||||||
def receive = {
|
def receive = {
|
||||||
case s: String ⇒ sender ! s
|
case s: String ⇒ sender ! s
|
||||||
|
|
@ -210,6 +235,7 @@ object VerifySerializabilitySpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
class VerifySerializabilitySpec extends AkkaSpec(VerifySerializabilitySpec.conf) {
|
class VerifySerializabilitySpec extends AkkaSpec(VerifySerializabilitySpec.conf) {
|
||||||
import VerifySerializabilitySpec._
|
import VerifySerializabilitySpec._
|
||||||
implicit val timeout = Timeout(5 seconds)
|
implicit val timeout = Timeout(5 seconds)
|
||||||
|
|
@ -221,17 +247,28 @@ class VerifySerializabilitySpec extends AkkaSpec(VerifySerializabilitySpec.conf)
|
||||||
|
|
||||||
"verify creators" in {
|
"verify creators" in {
|
||||||
val a = system.actorOf(Props[FooActor])
|
val a = system.actorOf(Props[FooActor])
|
||||||
intercept[NotSerializableException] {
|
|
||||||
Await.result(a ? new AnyRef, timeout.duration)
|
|
||||||
}
|
|
||||||
system stop a
|
system stop a
|
||||||
|
|
||||||
|
val b = system.actorOf(Props(new FooActor))
|
||||||
|
system stop b
|
||||||
|
|
||||||
|
val c = system.actorOf(Props().withCreator(new UntypedActorFactory {
|
||||||
|
def create() = new FooUntypedActor
|
||||||
|
}))
|
||||||
|
system stop c
|
||||||
|
|
||||||
|
intercept[java.io.NotSerializableException] {
|
||||||
|
val d = system.actorOf(Props(new NonSerializableActor(system)))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"verify messages" in {
|
"verify messages" in {
|
||||||
val a = system.actorOf(Props[FooActor])
|
val a = system.actorOf(Props[FooActor])
|
||||||
Await.result(a ? "pigdog", timeout.duration) must be("pigdog")
|
Await.result(a ? "pigdog", timeout.duration) must be("pigdog")
|
||||||
intercept[java.io.NotSerializableException] {
|
|
||||||
val b = system.actorOf(Props(new NonSerializableActor(system)))
|
intercept[NotSerializableException] {
|
||||||
|
Await.result(a ? new AnyRef, timeout.duration)
|
||||||
}
|
}
|
||||||
system stop a
|
system stop a
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ object CallingThreadDispatcherModelSpec {
|
||||||
val config = {
|
val config = {
|
||||||
"""
|
"""
|
||||||
boss {
|
boss {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
""" +
|
""" +
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,16 @@ class NonFatalSpec extends AkkaSpec with MustMatchers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"not match StackOverflowError" in {
|
"match StackOverflowError" in {
|
||||||
//not @tailrec
|
//not @tailrec
|
||||||
def blowUp(n: Long): Long = {
|
def blowUp(n: Long): Long = {
|
||||||
blowUp(n + 1) + 1
|
blowUp(n + 1) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
intercept[StackOverflowError] {
|
try {
|
||||||
try {
|
blowUp(0)
|
||||||
blowUp(0)
|
} catch {
|
||||||
} catch {
|
case NonFatal(e) ⇒ // as expected
|
||||||
case NonFatal(e) ⇒ assert(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ akka {
|
||||||
|
|
||||||
# Log the complete configuration at INFO level when the actor system is started.
|
# Log the complete configuration at INFO level when the actor system is started.
|
||||||
# This is useful when you are uncertain of what configuration is used.
|
# This is useful when you are uncertain of what configuration is used.
|
||||||
logConfigOnStart = off
|
log-config-on-start = off
|
||||||
|
|
||||||
# List FQCN of extensions which shall be loaded at actor system startup.
|
# List FQCN of extensions which shall be loaded at actor system startup.
|
||||||
# Should be on the format: 'extensions = ["foo", "bar"]' etc.
|
# Should be on the format: 'extensions = ["foo", "bar"]' etc.
|
||||||
|
|
@ -36,6 +36,9 @@ akka {
|
||||||
# Toggles whether the threads created by this ActorSystem should be daemons or not
|
# Toggles whether the threads created by this ActorSystem should be daemons or not
|
||||||
daemonic = off
|
daemonic = off
|
||||||
|
|
||||||
|
# JVM shutdown, System.exit(-1), in case of a fatal error, such as OutOfMemoryError
|
||||||
|
jvm-exit-on-fatal-error = on
|
||||||
|
|
||||||
actor {
|
actor {
|
||||||
|
|
||||||
provider = "akka.actor.LocalActorRefProvider"
|
provider = "akka.actor.LocalActorRefProvider"
|
||||||
|
|
@ -97,7 +100,7 @@ akka {
|
||||||
paths = []
|
paths = []
|
||||||
}
|
}
|
||||||
|
|
||||||
# Routers with dynamically resizable number of routees; this feature is enabled
|
# Routers with dynamically resizable number of routees; this feature is enabled
|
||||||
# by including (parts of) this section in the deployment
|
# by including (parts of) this section in the deployment
|
||||||
resizer {
|
resizer {
|
||||||
|
|
||||||
|
|
@ -156,7 +159,8 @@ akka {
|
||||||
# the same type), PinnedDispatcher, or a FQCN to a class inheriting
|
# the same type), PinnedDispatcher, or a FQCN to a class inheriting
|
||||||
# MessageDispatcherConfigurator with a constructor with
|
# MessageDispatcherConfigurator with a constructor with
|
||||||
# com.typesafe.config.Config parameter and akka.dispatch.DispatcherPrerequisites
|
# com.typesafe.config.Config parameter and akka.dispatch.DispatcherPrerequisites
|
||||||
# parameters
|
# parameters.
|
||||||
|
# PinnedDispatcher must be used toghether with executor=thread-pool-executor.
|
||||||
type = "Dispatcher"
|
type = "Dispatcher"
|
||||||
|
|
||||||
# Which kind of ExecutorService to use for this dispatcher
|
# Which kind of ExecutorService to use for this dispatcher
|
||||||
|
|
@ -181,37 +185,37 @@ akka {
|
||||||
|
|
||||||
# This will be used if you have set "executor = "thread-pool-executor""
|
# This will be used if you have set "executor = "thread-pool-executor""
|
||||||
thread-pool-executor {
|
thread-pool-executor {
|
||||||
# Keep alive time for threads
|
# Keep alive time for threads
|
||||||
keep-alive-time = 60s
|
keep-alive-time = 60s
|
||||||
|
|
||||||
# Min number of threads to cap factor-based core number to
|
# Min number of threads to cap factor-based core number to
|
||||||
core-pool-size-min = 8
|
core-pool-size-min = 8
|
||||||
|
|
||||||
# No of core threads ... ceil(available processors * factor)
|
# No of core threads ... ceil(available processors * factor)
|
||||||
core-pool-size-factor = 3.0
|
core-pool-size-factor = 3.0
|
||||||
|
|
||||||
# Max number of threads to cap factor-based number to
|
# Max number of threads to cap factor-based number to
|
||||||
core-pool-size-max = 64
|
core-pool-size-max = 64
|
||||||
|
|
||||||
# Hint: max-pool-size is only used for bounded task queues
|
# Hint: max-pool-size is only used for bounded task queues
|
||||||
# minimum number of threads to cap factor-based max number to
|
# minimum number of threads to cap factor-based max number to
|
||||||
max-pool-size-min = 8
|
max-pool-size-min = 8
|
||||||
|
|
||||||
# Max no of threads ... ceil(available processors * factor)
|
# Max no of threads ... ceil(available processors * factor)
|
||||||
max-pool-size-factor = 3.0
|
max-pool-size-factor = 3.0
|
||||||
|
|
||||||
# Max number of threads to cap factor-based max number to
|
# Max number of threads to cap factor-based max number to
|
||||||
max-pool-size-max = 64
|
max-pool-size-max = 64
|
||||||
|
|
||||||
# Specifies the bounded capacity of the task queue (< 1 == unbounded)
|
# Specifies the bounded capacity of the task queue (< 1 == unbounded)
|
||||||
task-queue-size = -1
|
task-queue-size = -1
|
||||||
|
|
||||||
# Specifies which type of task queue will be used, can be "array" or
|
# Specifies which type of task queue will be used, can be "array" or
|
||||||
# "linked" (default)
|
# "linked" (default)
|
||||||
task-queue-type = "linked"
|
task-queue-type = "linked"
|
||||||
|
|
||||||
# Allow core threads to time out
|
# Allow core threads to time out
|
||||||
allow-core-timeout = on
|
allow-core-timeout = on
|
||||||
}
|
}
|
||||||
|
|
||||||
# How long time the dispatcher will wait for new actors until it shuts down
|
# How long time the dispatcher will wait for new actors until it shuts down
|
||||||
|
|
@ -241,7 +245,7 @@ akka {
|
||||||
# FQCN of the MailboxType, if not specified the default bounded or unbounded
|
# FQCN of the MailboxType, if not specified the default bounded or unbounded
|
||||||
# mailbox is used. The Class of the FQCN must have a constructor with a
|
# mailbox is used. The Class of the FQCN must have a constructor with a
|
||||||
# com.typesafe.config.Config parameter.
|
# com.typesafe.config.Config parameter.
|
||||||
mailboxType = ""
|
mailbox-type = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
|
|
@ -262,23 +266,20 @@ akka {
|
||||||
event-stream = off
|
event-stream = off
|
||||||
}
|
}
|
||||||
|
|
||||||
# Entries for pluggable serializers and their bindings. If a binding for a specific
|
# Entries for pluggable serializers and their bindings.
|
||||||
# class is not found, then the default serializer (Java serialization) is used.
|
|
||||||
serializers {
|
serializers {
|
||||||
# java = "akka.serialization.JavaSerializer"
|
java = "akka.serialization.JavaSerializer"
|
||||||
# proto = "akka.serialization.ProtobufSerializer"
|
|
||||||
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# serialization-bindings {
|
# Class to Serializer binding. You only need to specify the name of an interface
|
||||||
# java = ["akka.serialization.SerializeSpec$Address",
|
# or abstract base class of the messages. In case of ambiguity it is using the
|
||||||
# "akka.serialization.MyJavaSerializableActor",
|
# most specific configured class, or giving a warning and choosing the “first” one.
|
||||||
# "akka.serialization.MyStatelessActorWithMessagesInMailbox",
|
#
|
||||||
# "akka.serialization.MyActorWithProtobufMessagesInMailbox"]
|
# To disable one of the default serializers, assign its class to "none", like
|
||||||
# proto = ["com.google.protobuf.Message",
|
# "java.io.Serializable" = none
|
||||||
# "akka.actor.ProtobufProtocol$MyMessage"]
|
serialization-bindings {
|
||||||
# }
|
"java.io.Serializable" = java
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Used to set the behavior of the scheduler.
|
# Used to set the behavior of the scheduler.
|
||||||
|
|
@ -293,7 +294,7 @@ akka {
|
||||||
# or larger tick duration.
|
# or larger tick duration.
|
||||||
# If you are scheduling a lot of tasks you should consider increasing the ticks per wheel.
|
# If you are scheduling a lot of tasks you should consider increasing the ticks per wheel.
|
||||||
# For more information see: http://www.jboss.org/netty/
|
# For more information see: http://www.jboss.org/netty/
|
||||||
tickDuration = 100ms
|
tick-duration = 100ms
|
||||||
ticksPerWheel = 512
|
ticks-per-wheel = 512
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ object AkkaException {
|
||||||
sb.append("\tat %s\n" format trace(i))
|
sb.append("\tat %s\n" format trace(i))
|
||||||
sb.toString
|
sb.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,6 +34,7 @@ object AkkaException {
|
||||||
* <li>toLongString which also includes the stack trace</li>
|
* <li>toLongString which also includes the stack trace</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
class AkkaException(message: String = "", cause: Throwable = null) extends RuntimeException(message, cause) with Serializable {
|
class AkkaException(message: String = "", cause: Throwable = null) extends RuntimeException(message, cause) with Serializable {
|
||||||
val uuid = "%s_%s".format(AkkaException.hostname, newUuid)
|
val uuid = "%s_%s".format(AkkaException.hostname, newUuid)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -476,10 +476,7 @@ private[akka] class ActorCell(
|
||||||
cancelReceiveTimeout() // FIXME: leave this here???
|
cancelReceiveTimeout() // FIXME: leave this here???
|
||||||
messageHandle.message match {
|
messageHandle.message match {
|
||||||
case msg: AutoReceivedMessage ⇒ autoReceiveMessage(messageHandle)
|
case msg: AutoReceivedMessage ⇒ autoReceiveMessage(messageHandle)
|
||||||
// FIXME: actor can be null when creation fails with fatal error, why?
|
case msg ⇒ actor(msg)
|
||||||
case msg if actor == null ⇒
|
|
||||||
system.eventStream.publish(Warning(self.path.toString, this.getClass, "Ignoring message due to null actor [%s]" format msg))
|
|
||||||
case msg ⇒ actor(msg)
|
|
||||||
}
|
}
|
||||||
currentMessage = null // reset current message after successful invocation
|
currentMessage = null // reset current message after successful invocation
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ object ActorPath {
|
||||||
* is sorted by path elements FROM RIGHT TO LEFT, where RootActorPath >
|
* is sorted by path elements FROM RIGHT TO LEFT, where RootActorPath >
|
||||||
* ChildActorPath in case the number of elements is different.
|
* ChildActorPath in case the number of elements is different.
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
sealed trait ActorPath extends Comparable[ActorPath] with Serializable {
|
sealed trait ActorPath extends Comparable[ActorPath] with Serializable {
|
||||||
/**
|
/**
|
||||||
* The Address under which this path can be reached; walks up the tree to
|
* The Address under which this path can be reached; walks up the tree to
|
||||||
|
|
@ -108,6 +109,7 @@ sealed trait ActorPath extends Comparable[ActorPath] with Serializable {
|
||||||
* Root of the hierarchy of ActorPaths. There is exactly root per ActorSystem
|
* Root of the hierarchy of ActorPaths. There is exactly root per ActorSystem
|
||||||
* and node (for remote-enabled or clustered systems).
|
* and node (for remote-enabled or clustered systems).
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
final case class RootActorPath(address: Address, name: String = "/") extends ActorPath {
|
final case class RootActorPath(address: Address, name: String = "/") extends ActorPath {
|
||||||
|
|
||||||
def parent: ActorPath = this
|
def parent: ActorPath = this
|
||||||
|
|
@ -130,6 +132,7 @@ final case class RootActorPath(address: Address, name: String = "/") extends Act
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
final class ChildActorPath(val parent: ActorPath, val name: String) extends ActorPath {
|
final class ChildActorPath(val parent: ActorPath, val name: String) extends ActorPath {
|
||||||
if (name.indexOf('/') != -1) throw new IllegalArgumentException("/ is a path separator and is not legal in ActorPath names: [%s]" format name)
|
if (name.indexOf('/') != -1) throw new IllegalArgumentException("/ is a path separator and is not legal in ActorPath names: [%s]" format name)
|
||||||
|
|
||||||
|
|
@ -157,7 +160,7 @@ final class ChildActorPath(val parent: ActorPath, val name: String) extends Acto
|
||||||
|
|
||||||
// TODO research whether this should be cached somehow (might be fast enough, but creates GC pressure)
|
// TODO research whether this should be cached somehow (might be fast enough, but creates GC pressure)
|
||||||
/*
|
/*
|
||||||
* idea: add one field which holds the total length (because that is known)
|
* idea: add one field which holds the total length (because that is known)
|
||||||
* so that only one String needs to be allocated before traversal; this is
|
* so that only one String needs to be allocated before traversal; this is
|
||||||
* cheaper than any cache
|
* cheaper than any cache
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,7 @@ private[akka] class LocalActorRef private[akka] (
|
||||||
/**
|
/**
|
||||||
* Memento pattern for serializing ActorRefs transparently
|
* Memento pattern for serializing ActorRefs transparently
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class SerializedActorRef private (path: String) {
|
case class SerializedActorRef private (path: String) {
|
||||||
import akka.serialization.JavaSerializer.currentSystem
|
import akka.serialization.JavaSerializer.currentSystem
|
||||||
|
|
||||||
|
|
@ -397,6 +398,7 @@ private[akka] object MinimalActorRef {
|
||||||
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef)
|
case class DeadLetter(message: Any, sender: ActorRef, recipient: ActorRef)
|
||||||
|
|
||||||
private[akka] object DeadLetterActorRef {
|
private[akka] object DeadLetterActorRef {
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
class SerializedDeadLetterActorRef extends Serializable { //TODO implement as Protobuf for performance?
|
class SerializedDeadLetterActorRef extends Serializable { //TODO implement as Protobuf for performance?
|
||||||
@throws(classOf[java.io.ObjectStreamException])
|
@throws(classOf[java.io.ObjectStreamException])
|
||||||
private def readResolve(): AnyRef = JavaSerializer.currentSystem.value.deadLetters
|
private def readResolve(): AnyRef = JavaSerializer.currentSystem.value.deadLetters
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ object ActorSystem {
|
||||||
final val LogLevel = getString("akka.loglevel")
|
final val LogLevel = getString("akka.loglevel")
|
||||||
final val StdoutLogLevel = getString("akka.stdout-loglevel")
|
final val StdoutLogLevel = getString("akka.stdout-loglevel")
|
||||||
final val EventHandlers: Seq[String] = getStringList("akka.event-handlers").asScala
|
final val EventHandlers: Seq[String] = getStringList("akka.event-handlers").asScala
|
||||||
final val LogConfigOnStart = config.getBoolean("akka.logConfigOnStart")
|
final val LogConfigOnStart = config.getBoolean("akka.log-config-on-start")
|
||||||
final val AddLoggingReceive = getBoolean("akka.actor.debug.receive")
|
final val AddLoggingReceive = getBoolean("akka.actor.debug.receive")
|
||||||
final val DebugAutoReceive = getBoolean("akka.actor.debug.autoreceive")
|
final val DebugAutoReceive = getBoolean("akka.actor.debug.autoreceive")
|
||||||
final val DebugLifecycle = getBoolean("akka.actor.debug.lifecycle")
|
final val DebugLifecycle = getBoolean("akka.actor.debug.lifecycle")
|
||||||
|
|
@ -89,9 +89,10 @@ object ActorSystem {
|
||||||
case x ⇒ Some(x)
|
case x ⇒ Some(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
final val SchedulerTickDuration = Duration(getMilliseconds("akka.scheduler.tickDuration"), MILLISECONDS)
|
final val SchedulerTickDuration = Duration(getMilliseconds("akka.scheduler.tick-duration"), MILLISECONDS)
|
||||||
final val SchedulerTicksPerWheel = getInt("akka.scheduler.ticksPerWheel")
|
final val SchedulerTicksPerWheel = getInt("akka.scheduler.ticks-per-wheel")
|
||||||
final val Daemonicity = getBoolean("akka.daemonic")
|
final val Daemonicity = getBoolean("akka.daemonic")
|
||||||
|
final val JvmExitOnFatalError = getBoolean("akka.jvm-exit-on-fatal-error")
|
||||||
|
|
||||||
if (ConfigVersion != Version)
|
if (ConfigVersion != Version)
|
||||||
throw new ConfigurationException("Akka JAR version [" + Version + "] does not match the provided config version [" + ConfigVersion + "]")
|
throw new ConfigurationException("Akka JAR version [" + Version + "] does not match the provided config version [" + ConfigVersion + "]")
|
||||||
|
|
@ -348,6 +349,7 @@ class ActorSystemImpl protected[akka] (val name: String, applicationConfig: Conf
|
||||||
log.error(cause, "Uncaught error from thread [{}]", thread.getName)
|
log.error(cause, "Uncaught error from thread [{}]", thread.getName)
|
||||||
cause match {
|
cause match {
|
||||||
case NonFatal(_) | _: InterruptedException ⇒
|
case NonFatal(_) | _: InterruptedException ⇒
|
||||||
|
case _ if settings.JvmExitOnFatalError ⇒ System.exit(-1)
|
||||||
case _ ⇒ shutdown()
|
case _ ⇒ shutdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.{ TimeUnit, ConcurrentHashMap }
|
||||||
* context.actorOf(someProps, "someName", Deploy(scope = RemoteScope("someOtherNodeName")))
|
* context.actorOf(someProps, "someName", Deploy(scope = RemoteScope("someOtherNodeName")))
|
||||||
* }}}
|
* }}}
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
final case class Deploy(
|
final case class Deploy(
|
||||||
path: String = "",
|
path: String = "",
|
||||||
config: Config = ConfigFactory.empty,
|
config: Config = ConfigFactory.empty,
|
||||||
|
|
@ -61,6 +62,7 @@ trait Scope {
|
||||||
def withFallback(other: Scope): Scope
|
def withFallback(other: Scope): Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case object LocalScope extends Scope {
|
case object LocalScope extends Scope {
|
||||||
/**
|
/**
|
||||||
* Java API
|
* Java API
|
||||||
|
|
@ -73,6 +75,7 @@ case object LocalScope extends Scope {
|
||||||
/**
|
/**
|
||||||
* This is the default value and as such allows overrides.
|
* This is the default value and as such allows overrides.
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case object NoScopeGiven extends Scope {
|
case object NoScopeGiven extends Scope {
|
||||||
def withFallback(other: Scope): Scope = other
|
def withFallback(other: Scope): Scope = other
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,10 @@ object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {
|
||||||
*/
|
*/
|
||||||
def makeDecider(flat: Iterable[CauseDirective]): Decider = {
|
def makeDecider(flat: Iterable[CauseDirective]): Decider = {
|
||||||
val directives = sort(flat)
|
val directives = sort(flat)
|
||||||
return { case x ⇒ directives find (_._1 isInstance x) map (_._2) getOrElse Escalate }
|
|
||||||
|
{
|
||||||
|
case x ⇒ directives find (_._1 isInstance x) map (_._2) getOrElse Escalate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def makeDecider(func: JDecider): Decider = {
|
def makeDecider(func: JDecider): Decider = {
|
||||||
|
|
@ -233,7 +236,7 @@ abstract class SupervisorStrategy {
|
||||||
*
|
*
|
||||||
* @param maxNrOfRetries the number of times an actor is allowed to be restarted, negative value means no limit
|
* @param maxNrOfRetries the number of times an actor is allowed to be restarted, negative value means no limit
|
||||||
* @param withinTimeRange duration of the time window for maxNrOfRetries, Duration.Inf means no window
|
* @param withinTimeRange duration of the time window for maxNrOfRetries, Duration.Inf means no window
|
||||||
* @param decider = mapping from Throwable to [[akka.actor.SupervisorStrategy.Directive]], you can also use a
|
* @param decider mapping from Throwable to [[akka.actor.SupervisorStrategy.Directive]], you can also use a
|
||||||
* `Seq` of Throwables which maps the given Throwables to restarts, otherwise escalates.
|
* `Seq` of Throwables which maps the given Throwables to restarts, otherwise escalates.
|
||||||
*/
|
*/
|
||||||
case class AllForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration = Duration.Inf)(val decider: SupervisorStrategy.Decider)
|
case class AllForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration = Duration.Inf)(val decider: SupervisorStrategy.Decider)
|
||||||
|
|
@ -279,7 +282,7 @@ case class AllForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration
|
||||||
*
|
*
|
||||||
* @param maxNrOfRetries the number of times an actor is allowed to be restarted, negative value means no limit
|
* @param maxNrOfRetries the number of times an actor is allowed to be restarted, negative value means no limit
|
||||||
* @param withinTimeRange duration of the time window for maxNrOfRetries, Duration.Inf means no window
|
* @param withinTimeRange duration of the time window for maxNrOfRetries, Duration.Inf means no window
|
||||||
* @param decider = mapping from Throwable to [[akka.actor.SupervisorStrategy.Directive]], you can also use a
|
* @param decider mapping from Throwable to [[akka.actor.SupervisorStrategy.Directive]], you can also use a
|
||||||
* `Seq` of Throwables which maps the given Throwables to restarts, otherwise escalates.
|
* `Seq` of Throwables which maps the given Throwables to restarts, otherwise escalates.
|
||||||
*/
|
*/
|
||||||
case class OneForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration = Duration.Inf)(val decider: SupervisorStrategy.Decider)
|
case class OneForOneStrategy(maxNrOfRetries: Int = -1, withinTimeRange: Duration = Duration.Inf)(val decider: SupervisorStrategy.Decider)
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ object Props {
|
||||||
* Props props = new Props(MyActor.class).withRouter(new RoundRobinRouter(..));
|
* Props props = new Props(MyActor.class).withRouter(new RoundRobinRouter(..));
|
||||||
* }}}
|
* }}}
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed when SI-4804 is fixed
|
||||||
case class Props(
|
case class Props(
|
||||||
creator: () ⇒ Actor = Props.defaultCreator,
|
creator: () ⇒ Actor = Props.defaultCreator,
|
||||||
dispatcher: String = Dispatchers.DefaultDispatcherId,
|
dispatcher: String = Dispatchers.DefaultDispatcherId,
|
||||||
|
|
|
||||||
|
|
@ -431,6 +431,7 @@ object TypedProps {
|
||||||
* TypedProps is a TypedActor configuration object, that is thread safe and fully sharable.
|
* TypedProps is a TypedActor configuration object, that is thread safe and fully sharable.
|
||||||
* It's used in TypedActorFactory.typedActorOf to configure a TypedActor instance.
|
* It's used in TypedActorFactory.typedActorOf to configure a TypedActor instance.
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class TypedProps[T <: AnyRef] protected[TypedProps] (
|
case class TypedProps[T <: AnyRef] protected[TypedProps] (
|
||||||
interfaces: Seq[Class[_]],
|
interfaces: Seq[Class[_]],
|
||||||
creator: () ⇒ T,
|
creator: () ⇒ T,
|
||||||
|
|
|
||||||
|
|
@ -158,4 +158,4 @@ abstract class UntypedActor extends Actor {
|
||||||
/**
|
/**
|
||||||
* Factory closure for an UntypedActor, to be used with 'Actors.actorOf(factory)'.
|
* Factory closure for an UntypedActor, to be used with 'Actors.actorOf(factory)'.
|
||||||
*/
|
*/
|
||||||
trait UntypedActorFactory extends Creator[Actor]
|
trait UntypedActorFactory extends Creator[Actor] with Serializable
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ import scala.annotation.tailrec
|
||||||
import akka.event.EventStream
|
import akka.event.EventStream
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import akka.serialization.SerializationExtension
|
import akka.serialization.SerializationExtension
|
||||||
import akka.jsr166y.ForkJoinPool
|
|
||||||
import akka.util.NonFatal
|
import akka.util.NonFatal
|
||||||
import akka.event.Logging.LogEventException
|
import akka.event.Logging.LogEventException
|
||||||
|
import akka.jsr166y.{ ForkJoinTask, ForkJoinPool }
|
||||||
|
|
||||||
final case class Envelope(val message: Any, val sender: ActorRef)(system: ActorSystem) {
|
final case class Envelope(val message: Any, val sender: ActorRef)(system: ActorSystem) {
|
||||||
if (message.isInstanceOf[AnyRef]) {
|
if (message.isInstanceOf[AnyRef]) {
|
||||||
|
|
@ -167,7 +167,7 @@ object MessageDispatcher {
|
||||||
implicit def defaultDispatcher(implicit system: ActorSystem): MessageDispatcher = system.dispatcher
|
implicit def defaultDispatcher(implicit system: ActorSystem): MessageDispatcher = system.dispatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) extends AbstractMessageDispatcher with Serializable with Executor with ExecutionContext {
|
abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) extends AbstractMessageDispatcher with Executor with ExecutionContext {
|
||||||
|
|
||||||
import MessageDispatcher._
|
import MessageDispatcher._
|
||||||
import AbstractMessageDispatcher.{ inhabitantsUpdater, shutdownScheduleUpdater }
|
import AbstractMessageDispatcher.{ inhabitantsUpdater, shutdownScheduleUpdater }
|
||||||
|
|
@ -353,12 +353,12 @@ abstract class MessageDispatcherConfigurator(val config: Config, val prerequisit
|
||||||
/**
|
/**
|
||||||
* Returns a factory for the [[akka.dispatch.Mailbox]] given the configuration.
|
* Returns a factory for the [[akka.dispatch.Mailbox]] given the configuration.
|
||||||
* Default implementation instantiate the [[akka.dispatch.MailboxType]] specified
|
* Default implementation instantiate the [[akka.dispatch.MailboxType]] specified
|
||||||
* as FQCN in mailboxType config property. If mailboxType is unspecified (empty)
|
* as FQCN in mailbox-type config property. If mailbox-type is unspecified (empty)
|
||||||
* then [[akka.dispatch.UnboundedMailbox]] is used when capacity is < 1,
|
* then [[akka.dispatch.UnboundedMailbox]] is used when capacity is < 1,
|
||||||
* otherwise [[akka.dispatch.BoundedMailbox]].
|
* otherwise [[akka.dispatch.BoundedMailbox]].
|
||||||
*/
|
*/
|
||||||
def mailboxType(): MailboxType = {
|
def mailboxType(): MailboxType = {
|
||||||
config.getString("mailboxType") match {
|
config.getString("mailbox-type") match {
|
||||||
case "" ⇒
|
case "" ⇒
|
||||||
val capacity = config.getInt("mailbox-capacity")
|
val capacity = config.getInt("mailbox-capacity")
|
||||||
if (capacity < 1) UnboundedMailbox()
|
if (capacity < 1) UnboundedMailbox()
|
||||||
|
|
@ -421,26 +421,74 @@ class ThreadPoolExecutorConfigurator(config: Config, prerequisites: DispatcherPr
|
||||||
})(queueFactory ⇒ _.setQueueFactory(queueFactory)))
|
})(queueFactory ⇒ _.setQueueFactory(queueFactory)))
|
||||||
}
|
}
|
||||||
|
|
||||||
def createExecutorServiceFactory(name: String, threadFactory: ThreadFactory): ExecutorServiceFactory =
|
def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory = {
|
||||||
threadPoolConfig.createExecutorServiceFactory(name, threadFactory)
|
val tf = threadFactory match {
|
||||||
|
case m: MonitorableThreadFactory ⇒
|
||||||
|
// add the dispatcher id to the thread names
|
||||||
|
m.copy(m.name + "-" + id)
|
||||||
|
case other ⇒ other
|
||||||
|
}
|
||||||
|
threadPoolConfig.createExecutorServiceFactory(id, tf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ForkJoinExecutorConfigurator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL AKKA USAGE ONLY
|
||||||
|
*/
|
||||||
|
final class AkkaForkJoinPool(parallelism: Int,
|
||||||
|
threadFactory: ForkJoinPool.ForkJoinWorkerThreadFactory,
|
||||||
|
unhandledExceptionHandler: Thread.UncaughtExceptionHandler)
|
||||||
|
extends ForkJoinPool(parallelism, threadFactory, unhandledExceptionHandler, true) {
|
||||||
|
override def execute(r: Runnable): Unit = r match {
|
||||||
|
case m: Mailbox ⇒ super.execute(new MailboxExecutionTask(m))
|
||||||
|
case other ⇒ super.execute(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL AKKA USAGE ONLY
|
||||||
|
*/
|
||||||
|
final class MailboxExecutionTask(mailbox: Mailbox) extends ForkJoinTask[Unit] {
|
||||||
|
final override def setRawResult(u: Unit): Unit = ()
|
||||||
|
final override def getRawResult(): Unit = ()
|
||||||
|
final override def exec(): Boolean = try { mailbox.run; true } catch {
|
||||||
|
case anything ⇒
|
||||||
|
val t = Thread.currentThread
|
||||||
|
t.getUncaughtExceptionHandler match {
|
||||||
|
case null ⇒
|
||||||
|
case some ⇒ some.uncaughtException(t, anything)
|
||||||
|
}
|
||||||
|
throw anything
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForkJoinExecutorConfigurator(config: Config, prerequisites: DispatcherPrerequisites) extends ExecutorServiceConfigurator(config, prerequisites) {
|
class ForkJoinExecutorConfigurator(config: Config, prerequisites: DispatcherPrerequisites) extends ExecutorServiceConfigurator(config, prerequisites) {
|
||||||
|
import ForkJoinExecutorConfigurator._
|
||||||
|
|
||||||
def validate(t: ThreadFactory): ForkJoinPool.ForkJoinWorkerThreadFactory = prerequisites.threadFactory match {
|
def validate(t: ThreadFactory): ForkJoinPool.ForkJoinWorkerThreadFactory = t match {
|
||||||
case correct: ForkJoinPool.ForkJoinWorkerThreadFactory ⇒ correct
|
case correct: ForkJoinPool.ForkJoinWorkerThreadFactory ⇒ correct
|
||||||
case x ⇒ throw new IllegalStateException("The prerequisites for the ForkJoinExecutorConfigurator is a ForkJoinPool.ForkJoinWorkerThreadFactory!")
|
case x ⇒ throw new IllegalStateException("The prerequisites for the ForkJoinExecutorConfigurator is a ForkJoinPool.ForkJoinWorkerThreadFactory!")
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForkJoinExecutorServiceFactory(val threadFactory: ForkJoinPool.ForkJoinWorkerThreadFactory,
|
class ForkJoinExecutorServiceFactory(val threadFactory: ForkJoinPool.ForkJoinWorkerThreadFactory,
|
||||||
val parallelism: Int) extends ExecutorServiceFactory {
|
val parallelism: Int) extends ExecutorServiceFactory {
|
||||||
def createExecutorService: ExecutorService = new ForkJoinPool(parallelism, threadFactory, MonitorableThreadFactory.doNothing, true)
|
def createExecutorService: ExecutorService = new AkkaForkJoinPool(parallelism, threadFactory, MonitorableThreadFactory.doNothing)
|
||||||
}
|
}
|
||||||
final def createExecutorServiceFactory(name: String, threadFactory: ThreadFactory): ExecutorServiceFactory =
|
final def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory = {
|
||||||
|
val tf = threadFactory match {
|
||||||
|
case m: MonitorableThreadFactory ⇒
|
||||||
|
// add the dispatcher id to the thread names
|
||||||
|
m.copy(m.name + "-" + id)
|
||||||
|
case other ⇒ other
|
||||||
|
}
|
||||||
new ForkJoinExecutorServiceFactory(
|
new ForkJoinExecutorServiceFactory(
|
||||||
validate(threadFactory),
|
validate(tf),
|
||||||
ThreadPoolConfig.scaledPoolSize(
|
ThreadPoolConfig.scaledPoolSize(
|
||||||
config.getInt("parallelism-min"),
|
config.getInt("parallelism-min"),
|
||||||
config.getDouble("parallelism-factor"),
|
config.getDouble("parallelism-factor"),
|
||||||
config.getInt("parallelism-max")))
|
config.getInt("parallelism-max")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,7 @@ class Dispatcher(
|
||||||
extends MessageDispatcher(_prerequisites) {
|
extends MessageDispatcher(_prerequisites) {
|
||||||
|
|
||||||
protected[akka] val executorServiceFactory: ExecutorServiceFactory =
|
protected[akka] val executorServiceFactory: ExecutorServiceFactory =
|
||||||
executorServiceFactoryProvider.createExecutorServiceFactory(
|
executorServiceFactoryProvider.createExecutorServiceFactory(id, prerequisites.threadFactory)
|
||||||
id,
|
|
||||||
prerequisites.threadFactory match {
|
|
||||||
case m: MonitorableThreadFactory ⇒ m.copy(m.name + "-" + id)
|
|
||||||
case other ⇒ other
|
|
||||||
})
|
|
||||||
|
|
||||||
protected[akka] val executorService = new AtomicReference[ExecutorService](new ExecutorServiceDelegate {
|
protected[akka] val executorService = new AtomicReference[ExecutorService](new ExecutorServiceDelegate {
|
||||||
lazy val executor = executorServiceFactory.createExecutorService
|
lazy val executor = executorServiceFactory.createExecutorService
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,18 @@ import akka.event.Logging.Error
|
||||||
import scala.Option
|
import scala.Option
|
||||||
import akka.japi.{ Function ⇒ JFunc, Option ⇒ JOption }
|
import akka.japi.{ Function ⇒ JFunc, Option ⇒ JOption }
|
||||||
import scala.util.continuations._
|
import scala.util.continuations._
|
||||||
import java.util.concurrent.TimeUnit.NANOSECONDS
|
|
||||||
import java.lang.{ Iterable ⇒ JIterable }
|
import java.lang.{ Iterable ⇒ JIterable }
|
||||||
import java.util.{ LinkedList ⇒ JLinkedList }
|
import java.util.{ LinkedList ⇒ JLinkedList }
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable.Stack
|
import scala.collection.mutable.Stack
|
||||||
import akka.util.{ Duration, BoxedType }
|
import akka.util.{ Duration, BoxedType }
|
||||||
import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger }
|
|
||||||
import akka.dispatch.Await.CanAwait
|
import akka.dispatch.Await.CanAwait
|
||||||
import java.util.concurrent._
|
|
||||||
import akka.util.NonFatal
|
import akka.util.NonFatal
|
||||||
import akka.event.Logging.LogEventException
|
import akka.event.Logging.LogEventException
|
||||||
import akka.event.Logging.Debug
|
import akka.event.Logging.Debug
|
||||||
|
import java.util.concurrent.TimeUnit.NANOSECONDS
|
||||||
|
import java.util.concurrent.{ ExecutionException, Callable, TimeoutException }
|
||||||
|
import java.util.concurrent.atomic.{ AtomicInteger, AtomicReferenceFieldUpdater }
|
||||||
|
|
||||||
object Await {
|
object Await {
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ object Await {
|
||||||
* WARNING: Blocking operation, use with caution.
|
* WARNING: Blocking operation, use with caution.
|
||||||
*
|
*
|
||||||
* @throws [[java.util.concurrent.TimeoutException]] if times out
|
* @throws [[java.util.concurrent.TimeoutException]] if times out
|
||||||
* @returns The returned value as returned by Awaitable.ready
|
* @return The returned value as returned by Awaitable.ready
|
||||||
*/
|
*/
|
||||||
def ready[T <: Awaitable[_]](awaitable: T, atMost: Duration): T = awaitable.ready(atMost)
|
def ready[T <: Awaitable[_]](awaitable: T, atMost: Duration): T = awaitable.ready(atMost)
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ object Await {
|
||||||
* WARNING: Blocking operation, use with caution.
|
* WARNING: Blocking operation, use with caution.
|
||||||
*
|
*
|
||||||
* @throws [[java.util.concurrent.TimeoutException]] if times out
|
* @throws [[java.util.concurrent.TimeoutException]] if times out
|
||||||
* @returns The returned value as returned by Awaitable.result
|
* @return The returned value as returned by Awaitable.result
|
||||||
*/
|
*/
|
||||||
def result[T](awaitable: Awaitable[T], atMost: Duration): T = awaitable.result(atMost)
|
def result[T](awaitable: Awaitable[T], atMost: Duration): T = awaitable.result(atMost)
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +151,26 @@ object Futures {
|
||||||
for (r ← fr; b ← fb) yield { r add b; r }
|
for (r ← fr; b ← fb) yield { r add b; r }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals that the current thread of execution will potentially engage
|
||||||
|
* in blocking calls after the call to this method, giving the system a
|
||||||
|
* chance to spawn new threads, reuse old threads or otherwise, to prevent
|
||||||
|
* starvation and/or unfairness.
|
||||||
|
*
|
||||||
|
* Assures that any Future tasks initiated in the current thread will be
|
||||||
|
* executed asynchronously, including any tasks currently queued to be
|
||||||
|
* executed in the current thread. This is needed if the current task may
|
||||||
|
* block, causing delays in executing the remaining tasks which in some
|
||||||
|
* cases may cause a deadlock.
|
||||||
|
*
|
||||||
|
* Usage: Call this method in a callback (map, flatMap etc also count) to a Future,
|
||||||
|
* if you will be doing blocking in the callback.
|
||||||
|
*
|
||||||
|
* Note: Calling 'Await.result(future)' or 'Await.ready(future)' will automatically trigger this method.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
def blocking(): Unit = Future.blocking()
|
||||||
}
|
}
|
||||||
|
|
||||||
object Future {
|
object Future {
|
||||||
|
|
@ -192,7 +212,7 @@ object Future {
|
||||||
def firstCompletedOf[T](futures: Traversable[Future[T]])(implicit executor: ExecutionContext): Future[T] = {
|
def firstCompletedOf[T](futures: Traversable[Future[T]])(implicit executor: ExecutionContext): Future[T] = {
|
||||||
val futureResult = Promise[T]()
|
val futureResult = Promise[T]()
|
||||||
|
|
||||||
val completeFirst: Either[Throwable, T] ⇒ Unit = futureResult complete _
|
val completeFirst: Either[Throwable, T] ⇒ Unit = futureResult tryComplete _
|
||||||
futures.foreach(_ onComplete completeFirst)
|
futures.foreach(_ onComplete completeFirst)
|
||||||
|
|
||||||
futureResult
|
futureResult
|
||||||
|
|
@ -208,12 +228,12 @@ object Future {
|
||||||
val ref = new AtomicInteger(futures.size)
|
val ref = new AtomicInteger(futures.size)
|
||||||
val search: Either[Throwable, T] ⇒ Unit = v ⇒ try {
|
val search: Either[Throwable, T] ⇒ Unit = v ⇒ try {
|
||||||
v match {
|
v match {
|
||||||
case Right(r) ⇒ if (predicate(r)) result success Some(r)
|
case Right(r) ⇒ if (predicate(r)) result tryComplete Right(Some(r))
|
||||||
case _ ⇒
|
case _ ⇒
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (ref.decrementAndGet == 0)
|
if (ref.decrementAndGet == 0)
|
||||||
result success None
|
result tryComplete Right(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
futures.foreach(_ onComplete search)
|
futures.foreach(_ onComplete search)
|
||||||
|
|
@ -279,13 +299,13 @@ object Future {
|
||||||
* The Delimited Continuations compiler plugin must be enabled in order to use this method.
|
* The Delimited Continuations compiler plugin must be enabled in order to use this method.
|
||||||
*/
|
*/
|
||||||
def flow[A](body: ⇒ A @cps[Future[Any]])(implicit executor: ExecutionContext): Future[A] = {
|
def flow[A](body: ⇒ A @cps[Future[Any]])(implicit executor: ExecutionContext): Future[A] = {
|
||||||
val future = Promise[A]
|
val p = Promise[A]
|
||||||
dispatchTask({ () ⇒
|
dispatchTask({ () ⇒
|
||||||
(reify(body) foreachFull (future success, future failure): Future[Any]) onFailure {
|
(reify(body) foreachFull (p success, p failure): Future[Any]) onFailure {
|
||||||
case e: Exception ⇒ future failure e
|
case NonFatal(e) ⇒ p tryComplete Left(e)
|
||||||
}
|
}
|
||||||
}, true)
|
}, true)
|
||||||
future
|
p.future
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -317,17 +337,22 @@ object Future {
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
def blocking(implicit executor: ExecutionContext): Unit =
|
def blocking(): Unit =
|
||||||
_taskStack.get match {
|
_taskStack.get match {
|
||||||
case stack if (stack ne null) && stack.nonEmpty ⇒
|
case stack if (stack ne null) && stack.nonEmpty ⇒
|
||||||
|
val executionContext = _executionContext.get match {
|
||||||
|
case null ⇒ throw new IllegalStateException("'blocking' needs to be invoked inside a Future callback.")
|
||||||
|
case some ⇒ some
|
||||||
|
}
|
||||||
val tasks = stack.elems
|
val tasks = stack.elems
|
||||||
stack.clear()
|
stack.clear()
|
||||||
_taskStack.remove()
|
_taskStack.remove()
|
||||||
dispatchTask(() ⇒ _taskStack.get.elems = tasks, true)
|
dispatchTask(() ⇒ _taskStack.get.elems = tasks, true)(executionContext)
|
||||||
case _ ⇒ _taskStack.remove()
|
case _ ⇒ _taskStack.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _taskStack = new ThreadLocal[Stack[() ⇒ Unit]]()
|
private val _taskStack = new ThreadLocal[Stack[() ⇒ Unit]]()
|
||||||
|
private val _executionContext = new ThreadLocal[ExecutionContext]()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal API, do not call
|
* Internal API, do not call
|
||||||
|
|
@ -339,7 +364,7 @@ object Future {
|
||||||
new Runnable {
|
new Runnable {
|
||||||
def run =
|
def run =
|
||||||
try {
|
try {
|
||||||
|
_executionContext set executor
|
||||||
val taskStack = Stack.empty[() ⇒ Unit]
|
val taskStack = Stack.empty[() ⇒ Unit]
|
||||||
taskStack push task
|
taskStack push task
|
||||||
_taskStack set taskStack
|
_taskStack set taskStack
|
||||||
|
|
@ -352,7 +377,10 @@ object Future {
|
||||||
case NonFatal(e) ⇒ executor.reportFailure(e)
|
case NonFatal(e) ⇒ executor.reportFailure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally { _taskStack.remove() }
|
} finally {
|
||||||
|
_executionContext.remove()
|
||||||
|
_taskStack.remove()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,7 +407,7 @@ sealed trait Future[+T] extends Await.Awaitable[T] {
|
||||||
case Left(t) ⇒ p failure t
|
case Left(t) ⇒ p failure t
|
||||||
case Right(r) ⇒ that onSuccess { case r2 ⇒ p success ((r, r2)) }
|
case Right(r) ⇒ that onSuccess { case r2 ⇒ p success ((r, r2)) }
|
||||||
}
|
}
|
||||||
that onFailure { case f ⇒ p failure f }
|
that onFailure { case f ⇒ p tryComplete Left(f) }
|
||||||
p.future
|
p.future
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,7 +439,7 @@ sealed trait Future[+T] extends Await.Awaitable[T] {
|
||||||
* callbacks may be registered; there is no guarantee that they will be
|
* callbacks may be registered; there is no guarantee that they will be
|
||||||
* executed in a particular order.
|
* executed in a particular order.
|
||||||
*/
|
*/
|
||||||
def onComplete(func: Either[Throwable, T] ⇒ Unit): this.type
|
def onComplete[U](func: Either[Throwable, T] ⇒ U): this.type
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the future is completed with a valid result, apply the provided
|
* When the future is completed with a valid result, apply the provided
|
||||||
|
|
@ -483,7 +511,7 @@ sealed trait Future[+T] extends Await.Awaitable[T] {
|
||||||
final def recover[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = {
|
final def recover[A >: T](pf: PartialFunction[Throwable, A]): Future[A] = {
|
||||||
val p = Promise[A]()
|
val p = Promise[A]()
|
||||||
onComplete {
|
onComplete {
|
||||||
case Left(e) if pf isDefinedAt e ⇒ p.complete(try { Right(pf(e)) } catch { case x: Exception ⇒ Left(x) })
|
case Left(e) if pf isDefinedAt e ⇒ p.complete(try { Right(pf(e)) } catch { case NonFatal(x) ⇒ Left(x) })
|
||||||
case otherwise ⇒ p complete otherwise
|
case otherwise ⇒ p complete otherwise
|
||||||
}
|
}
|
||||||
p.future
|
p.future
|
||||||
|
|
@ -699,9 +727,12 @@ trait Promise[T] extends Future[T] {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completes this Promise with the specified result, if not already completed.
|
* Completes this Promise with the specified result, if not already completed.
|
||||||
|
* @throws IllegalStateException if already completed, this is to aid in debugging of complete-races,
|
||||||
|
* use tryComplete to do a conditional complete.
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
final def complete(value: Either[Throwable, T]): this.type = { tryComplete(value); this }
|
final def complete(value: Either[Throwable, T]): this.type =
|
||||||
|
if (tryComplete(value)) this else throw new IllegalStateException("Promise already completed: " + this + " tried to complete with " + value)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completes this Promise with the specified result, if not already completed.
|
* Completes this Promise with the specified result, if not already completed.
|
||||||
|
|
@ -721,7 +752,7 @@ trait Promise[T] extends Future[T] {
|
||||||
* @return this.
|
* @return this.
|
||||||
*/
|
*/
|
||||||
final def completeWith(other: Future[T]): this.type = {
|
final def completeWith(other: Future[T]): this.type = {
|
||||||
other onComplete { complete(_) }
|
other onComplete { tryComplete(_) }
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -840,7 +871,7 @@ class DefaultPromise[T](implicit val executor: ExecutionContext) extends Abstrac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def onComplete(func: Either[Throwable, T] ⇒ Unit): this.type = {
|
def onComplete[U](func: Either[Throwable, T] ⇒ U): this.type = {
|
||||||
@tailrec //Returns whether the future has already been completed or not
|
@tailrec //Returns whether the future has already been completed or not
|
||||||
def tryAddCallback(): Either[Throwable, T] = {
|
def tryAddCallback(): Either[Throwable, T] = {
|
||||||
val cur = getState
|
val cur = getState
|
||||||
|
|
@ -858,9 +889,8 @@ class DefaultPromise[T](implicit val executor: ExecutionContext) extends Abstrac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final def notifyCompleted(func: Either[Throwable, T] ⇒ Unit, result: Either[Throwable, T]) {
|
private final def notifyCompleted[U](func: Either[Throwable, T] ⇒ U, result: Either[Throwable, T]): Unit =
|
||||||
try { func(result) } catch { case NonFatal(e) ⇒ executor.reportFailure(e) }
|
try func(result) catch { case NonFatal(e) ⇒ executor reportFailure e }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -871,7 +901,7 @@ final class KeptPromise[T](suppliedValue: Either[Throwable, T])(implicit val exe
|
||||||
val value = Some(resolve(suppliedValue))
|
val value = Some(resolve(suppliedValue))
|
||||||
|
|
||||||
def tryComplete(value: Either[Throwable, T]): Boolean = false
|
def tryComplete(value: Either[Throwable, T]): Boolean = false
|
||||||
def onComplete(func: Either[Throwable, T] ⇒ Unit): this.type = {
|
def onComplete[U](func: Either[Throwable, T] ⇒ U): this.type = {
|
||||||
val completedAs = value.get
|
val completedAs = value.get
|
||||||
Future dispatchTask (() ⇒ func(completedAs))
|
Future dispatchTask (() ⇒ func(completedAs))
|
||||||
this
|
this
|
||||||
|
|
@ -982,7 +1012,7 @@ abstract class Recover[+T] extends japi.RecoverBridge[T] {
|
||||||
* This method will be invoked once when/if the Future this recover callback is registered on
|
* This method will be invoked once when/if the Future this recover callback is registered on
|
||||||
* becomes completed with a failure.
|
* becomes completed with a failure.
|
||||||
*
|
*
|
||||||
* @returns a successful value for the passed in failure
|
* @return a successful value for the passed in failure
|
||||||
* @throws the passed in failure to propagate it.
|
* @throws the passed in failure to propagate it.
|
||||||
*
|
*
|
||||||
* Java API
|
* Java API
|
||||||
|
|
@ -1005,7 +1035,7 @@ abstract class Filter[-T] extends japi.BooleanFunctionBridge[T] {
|
||||||
* This method will be invoked once when/if a Future that this callback is registered on
|
* This method will be invoked once when/if a Future that this callback is registered on
|
||||||
* becomes completed with a success.
|
* becomes completed with a success.
|
||||||
*
|
*
|
||||||
* @returns true if the successful value should be propagated to the new Future or not
|
* @return true if the successful value should be propagated to the new Future or not
|
||||||
*/
|
*/
|
||||||
def filter(result: T): Boolean
|
def filter(result: T): Boolean
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -328,7 +328,7 @@ trait MailboxType {
|
||||||
* It's a case class for Java (new UnboundedMailbox)
|
* It's a case class for Java (new UnboundedMailbox)
|
||||||
*/
|
*/
|
||||||
case class UnboundedMailbox() extends MailboxType {
|
case class UnboundedMailbox() extends MailboxType {
|
||||||
override def create(receiver: ActorContext) =
|
final override def create(receiver: ActorContext): Mailbox =
|
||||||
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with UnboundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with UnboundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
||||||
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +339,7 @@ case class BoundedMailbox( final val capacity: Int, final val pushTimeOut: Durat
|
||||||
if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative")
|
if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative")
|
||||||
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
||||||
|
|
||||||
override def create(receiver: ActorContext) =
|
final override def create(receiver: ActorContext): Mailbox =
|
||||||
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with BoundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with BoundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
||||||
final val queue = new LinkedBlockingQueue[Envelope](capacity)
|
final val queue = new LinkedBlockingQueue[Envelope](capacity)
|
||||||
final val pushTimeOut = BoundedMailbox.this.pushTimeOut
|
final val pushTimeOut = BoundedMailbox.this.pushTimeOut
|
||||||
|
|
@ -347,7 +347,7 @@ case class BoundedMailbox( final val capacity: Int, final val pushTimeOut: Durat
|
||||||
}
|
}
|
||||||
|
|
||||||
case class UnboundedPriorityMailbox( final val cmp: Comparator[Envelope]) extends MailboxType {
|
case class UnboundedPriorityMailbox( final val cmp: Comparator[Envelope]) extends MailboxType {
|
||||||
override def create(receiver: ActorContext) =
|
final override def create(receiver: ActorContext): Mailbox =
|
||||||
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with UnboundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with UnboundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
||||||
final val queue = new PriorityBlockingQueue[Envelope](11, cmp)
|
final val queue = new PriorityBlockingQueue[Envelope](11, cmp)
|
||||||
}
|
}
|
||||||
|
|
@ -358,7 +358,7 @@ case class BoundedPriorityMailbox( final val cmp: Comparator[Envelope], final va
|
||||||
if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative")
|
if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative")
|
||||||
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
||||||
|
|
||||||
override def create(receiver: ActorContext) =
|
final override def create(receiver: ActorContext): Mailbox =
|
||||||
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with BoundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
new Mailbox(receiver.asInstanceOf[ActorCell]) with QueueBasedMessageQueue with BoundedMessageQueueSemantics with DefaultSystemMessageQueue {
|
||||||
final val queue = new BoundedBlockingQueue[Envelope](capacity, new PriorityQueue[Envelope](11, cmp))
|
final val queue = new BoundedBlockingQueue[Envelope](capacity, new PriorityQueue[Envelope](11, cmp))
|
||||||
final val pushTimeOut = BoundedPriorityMailbox.this.pushTimeOut
|
final val pushTimeOut = BoundedPriorityMailbox.this.pushTimeOut
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ trait ExecutorServiceFactory {
|
||||||
* Generic way to specify an ExecutorService to a Dispatcher, create it with the given name if desired
|
* Generic way to specify an ExecutorService to a Dispatcher, create it with the given name if desired
|
||||||
*/
|
*/
|
||||||
trait ExecutorServiceFactoryProvider {
|
trait ExecutorServiceFactoryProvider {
|
||||||
def createExecutorServiceFactory(name: String, threadFactory: ThreadFactory): ExecutorServiceFactory
|
def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -93,7 +93,7 @@ case class ThreadPoolConfig(allowCorePoolTimeout: Boolean = ThreadPoolConfig.def
|
||||||
service
|
service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final def createExecutorServiceFactory(name: String, threadFactory: ThreadFactory): ExecutorServiceFactory =
|
final def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory =
|
||||||
new ThreadPoolExecutorServiceFactory(threadFactory)
|
new ThreadPoolExecutorServiceFactory(threadFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,9 +170,14 @@ case class MonitorableThreadFactory(name: String,
|
||||||
extends ThreadFactory with ForkJoinPool.ForkJoinWorkerThreadFactory {
|
extends ThreadFactory with ForkJoinPool.ForkJoinWorkerThreadFactory {
|
||||||
protected val counter = new AtomicLong
|
protected val counter = new AtomicLong
|
||||||
|
|
||||||
def newThread(pool: ForkJoinPool): ForkJoinWorkerThread = wire(ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool))
|
def newThread(pool: ForkJoinPool): ForkJoinWorkerThread = {
|
||||||
|
val t = wire(ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool))
|
||||||
|
// Name of the threads for the ForkJoinPool are not customizable. Change it here.
|
||||||
|
t.setName(name + "-" + counter.incrementAndGet())
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
def newThread(runnable: Runnable): Thread = wire(new Thread(runnable, name + counter.incrementAndGet()))
|
def newThread(runnable: Runnable): Thread = wire(new Thread(runnable, name + "-" + counter.incrementAndGet()))
|
||||||
|
|
||||||
protected def wire[T <: Thread](t: T): T = {
|
protected def wire[T <: Thread](t: T): T = {
|
||||||
t.setUncaughtExceptionHandler(exceptionHandler)
|
t.setUncaughtExceptionHandler(exceptionHandler)
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,7 @@ case class Destination(sender: ActorRef, recipient: ActorRef)
|
||||||
* from lower-precendence sources. The decision whether or not to create a
|
* from lower-precendence sources. The decision whether or not to create a
|
||||||
* router is taken in the LocalActorRefProvider based on Props.
|
* router is taken in the LocalActorRefProvider based on Props.
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case object NoRouter extends RouterConfig {
|
case object NoRouter extends RouterConfig {
|
||||||
def createRoute(props: Props, routeeProvider: RouteeProvider): Route = null
|
def createRoute(props: Props, routeeProvider: RouteeProvider): Route = null
|
||||||
override def withFallback(other: RouterConfig): RouterConfig = other
|
override def withFallback(other: RouterConfig): RouterConfig = other
|
||||||
|
|
@ -315,6 +316,7 @@ case object FromConfig extends RouterConfig {
|
||||||
/**
|
/**
|
||||||
* Java API: Router configuration which has no default, i.e. external configuration is required.
|
* Java API: Router configuration which has no default, i.e. external configuration is required.
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class FromConfig() extends RouterConfig {
|
case class FromConfig() extends RouterConfig {
|
||||||
def createRoute(props: Props, routeeProvider: RouteeProvider): Route =
|
def createRoute(props: Props, routeeProvider: RouteeProvider): Route =
|
||||||
throw new ConfigurationException("router " + routeeProvider.context.self + " needs external configuration from file (e.g. application.conf)")
|
throw new ConfigurationException("router " + routeeProvider.context.self + " needs external configuration from file (e.g. application.conf)")
|
||||||
|
|
@ -345,6 +347,7 @@ object RoundRobinRouter {
|
||||||
* @param routees string representation of the actor paths of the routees that will be looked up
|
* @param routees string representation of the actor paths of the routees that will be looked up
|
||||||
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class RoundRobinRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
case class RoundRobinRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
||||||
extends RouterConfig with RoundRobinLike {
|
extends RouterConfig with RoundRobinLike {
|
||||||
|
|
||||||
|
|
@ -424,6 +427,7 @@ object RandomRouter {
|
||||||
* @param routees string representation of the actor paths of the routees that will be looked up
|
* @param routees string representation of the actor paths of the routees that will be looked up
|
||||||
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class RandomRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
case class RandomRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
||||||
extends RouterConfig with RandomLike {
|
extends RouterConfig with RandomLike {
|
||||||
|
|
||||||
|
|
@ -509,6 +513,7 @@ object SmallestMailboxRouter {
|
||||||
* @param routees string representation of the actor paths of the routees that will be looked up
|
* @param routees string representation of the actor paths of the routees that will be looked up
|
||||||
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class SmallestMailboxRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
case class SmallestMailboxRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
||||||
extends RouterConfig with SmallestMailboxLike {
|
extends RouterConfig with SmallestMailboxLike {
|
||||||
|
|
||||||
|
|
@ -653,6 +658,7 @@ object BroadcastRouter {
|
||||||
* @param routees string representation of the actor paths of the routees that will be looked up
|
* @param routees string representation of the actor paths of the routees that will be looked up
|
||||||
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class BroadcastRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
case class BroadcastRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, override val resizer: Option[Resizer] = None)
|
||||||
extends RouterConfig with BroadcastLike {
|
extends RouterConfig with BroadcastLike {
|
||||||
|
|
||||||
|
|
@ -724,6 +730,7 @@ object ScatterGatherFirstCompletedRouter {
|
||||||
* @param routees string representation of the actor paths of the routees that will be looked up
|
* @param routees string representation of the actor paths of the routees that will be looked up
|
||||||
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
* using `actorFor` in [[akka.actor.ActorRefProvider]]
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class ScatterGatherFirstCompletedRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, within: Duration,
|
case class ScatterGatherFirstCompletedRouter(nrOfInstances: Int = 0, routees: Iterable[String] = Nil, within: Duration,
|
||||||
override val resizer: Option[Resizer] = None)
|
override val resizer: Option[Resizer] = None)
|
||||||
extends RouterConfig with ScatterGatherFirstCompletedLike {
|
extends RouterConfig with ScatterGatherFirstCompletedLike {
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,20 @@ package akka.serialization
|
||||||
import akka.AkkaException
|
import akka.AkkaException
|
||||||
import scala.util.DynamicVariable
|
import scala.util.DynamicVariable
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import akka.config.ConfigurationException
|
|
||||||
import akka.actor.{ Extension, ExtendedActorSystem, Address }
|
import akka.actor.{ Extension, ExtendedActorSystem, Address }
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import akka.event.Logging
|
|
||||||
import akka.util.NonFatal
|
import akka.util.NonFatal
|
||||||
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
import java.io.NotSerializableException
|
||||||
|
|
||||||
case class NoSerializerFoundException(m: String) extends AkkaException(m)
|
case class NoSerializerFoundException(m: String) extends AkkaException(m)
|
||||||
|
|
||||||
object Serialization {
|
object Serialization {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuple that represents mapping from Class to Serializer
|
||||||
|
*/
|
||||||
|
type ClassSerializer = (Class[_], Serializer)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This holds a reference to the current transport address to be inserted
|
* This holds a reference to the current transport address to be inserted
|
||||||
* into local actor refs during serialization.
|
* into local actor refs during serialization.
|
||||||
|
|
@ -27,28 +32,19 @@ object Serialization {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import config._
|
import config._
|
||||||
|
|
||||||
val Serializers: Map[String, String] =
|
val Serializers: Map[String, String] = configToMap(getConfig("akka.actor.serializers"))
|
||||||
getConfig("akka.actor.serializers").root.unwrapped.asScala.toMap.map { case (k, v) ⇒ (k, v.toString) }
|
|
||||||
|
|
||||||
val SerializationBindings: Map[String, Seq[String]] = {
|
val SerializationBindings: Map[String, String] = configToMap(getConfig("akka.actor.serialization-bindings"))
|
||||||
val configPath = "akka.actor.serialization-bindings"
|
|
||||||
hasPath(configPath) match {
|
private def configToMap(cfg: Config): Map[String, String] =
|
||||||
case false ⇒ Map()
|
cfg.root.unwrapped.asScala.toMap.map { case (k, v) ⇒ (k, v.toString) }
|
||||||
case true ⇒
|
|
||||||
val serializationBindings: Map[String, Seq[String]] = getConfig(configPath).root.unwrapped.asScala.toMap.map {
|
|
||||||
case (k: String, v: java.util.Collection[_]) ⇒ (k -> v.asScala.toSeq.asInstanceOf[Seq[String]])
|
|
||||||
case invalid ⇒ throw new ConfigurationException("Invalid serialization-bindings [%s]".format(invalid))
|
|
||||||
}
|
|
||||||
serializationBindings
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization module. Contains methods for serialization and deserialization as well as
|
* Serialization module. Contains methods for serialization and deserialization as well as
|
||||||
* locating a Serializer for a particular class as defined in the mapping in the 'akka.conf' file.
|
* locating a Serializer for a particular class as defined in the mapping in the configuration.
|
||||||
*/
|
*/
|
||||||
class Serialization(val system: ExtendedActorSystem) extends Extension {
|
class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
import Serialization._
|
import Serialization._
|
||||||
|
|
@ -85,8 +81,10 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
catch { case NonFatal(e) ⇒ Left(e) }
|
catch { case NonFatal(e) ⇒ Left(e) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Serializer configured for the given object, returns the NullSerializer if it's null,
|
* Returns the Serializer configured for the given object, returns the NullSerializer if it's null.
|
||||||
* falls back to the Serializer named "default"
|
*
|
||||||
|
* @throws akka.config.ConfigurationException if no `serialization-bindings` is configured for the
|
||||||
|
* class of the object
|
||||||
*/
|
*/
|
||||||
def findSerializerFor(o: AnyRef): Serializer = o match {
|
def findSerializerFor(o: AnyRef): Serializer = o match {
|
||||||
case null ⇒ NullSerializer
|
case null ⇒ NullSerializer
|
||||||
|
|
@ -94,36 +92,38 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the configured Serializer for the given Class, falls back to the Serializer named "default".
|
* Returns the configured Serializer for the given Class. The configured Serializer
|
||||||
* It traverses interfaces and super classes to find any configured Serializer that match
|
* is used if the configured class `isAssignableFrom` from the `clazz`, i.e.
|
||||||
* the class name.
|
* the configured class is a super class or implemented interface. In case of
|
||||||
|
* ambiguity it is primarily using the most specific configured class,
|
||||||
|
* and secondly the entry configured first.
|
||||||
|
*
|
||||||
|
* @throws java.io.NotSerializableException if no `serialization-bindings` is configured for the class
|
||||||
*/
|
*/
|
||||||
def serializerFor(clazz: Class[_]): Serializer =
|
def serializerFor(clazz: Class[_]): Serializer =
|
||||||
if (bindings.isEmpty) {
|
serializerMap.get(clazz) match {
|
||||||
// quick path to default when no bindings are registered
|
case null ⇒
|
||||||
serializers("default")
|
// bindings are ordered from most specific to least specific
|
||||||
} else {
|
def unique(possibilities: Seq[(Class[_], Serializer)]): Boolean =
|
||||||
|
possibilities.size == 1 ||
|
||||||
|
(possibilities map (_._1) forall (_ isAssignableFrom possibilities(0)._1)) ||
|
||||||
|
(possibilities map (_._2) forall (_ == possibilities(0)._2))
|
||||||
|
|
||||||
def resolve(c: Class[_]): Option[Serializer] =
|
val ser = bindings filter { _._1 isAssignableFrom clazz } match {
|
||||||
serializerMap.get(c.getName) match {
|
case Seq() ⇒
|
||||||
case null ⇒
|
throw new NotSerializableException("No configured serialization-bindings for class [%s]" format clazz.getName)
|
||||||
val classes = c.getInterfaces ++ Option(c.getSuperclass)
|
case possibilities ⇒
|
||||||
classes.view map resolve collectFirst { case Some(x) ⇒ x }
|
if (!unique(possibilities))
|
||||||
case x ⇒ Some(x)
|
log.warning("Multiple serializers found for " + clazz + ", choosing first: " + possibilities)
|
||||||
|
possibilities(0)._2
|
||||||
}
|
}
|
||||||
|
serializerMap.putIfAbsent(clazz, ser) match {
|
||||||
serializerMap.get(clazz.getName) match {
|
case null ⇒
|
||||||
case null ⇒
|
log.debug("Using serializer[{}] for message [{}]", ser.getClass.getName, clazz.getName)
|
||||||
val ser = resolve(clazz).getOrElse(serializers("default"))
|
ser
|
||||||
// memorize the lookups for performance
|
case some ⇒ some
|
||||||
serializerMap.putIfAbsent(clazz.getName, ser) match {
|
}
|
||||||
case null ⇒
|
case ser ⇒ ser
|
||||||
log.debug("Using serializer[{}] for message [{}]", ser.getClass.getName, clazz.getName)
|
|
||||||
ser
|
|
||||||
case some ⇒ some
|
|
||||||
}
|
|
||||||
case ser ⇒ ser
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -138,42 +138,52 @@ class Serialization(val system: ExtendedActorSystem) extends Extension {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Map of serializer from alias to implementation (class implementing akka.serialization.Serializer)
|
* A Map of serializer from alias to implementation (class implementing akka.serialization.Serializer)
|
||||||
* By default always contains the following mapping: "default" -> akka.serialization.JavaSerializer
|
* By default always contains the following mapping: "java" -> akka.serialization.JavaSerializer
|
||||||
* But "default" can be overridden in config
|
|
||||||
*/
|
*/
|
||||||
lazy val serializers: Map[String, Serializer] = {
|
private val serializers: Map[String, Serializer] = {
|
||||||
val serializersConf = settings.Serializers
|
for ((k: String, v: String) ← settings.Serializers)
|
||||||
for ((k: String, v: String) ← serializersConf)
|
|
||||||
yield k -> serializerOf(v).fold(throw _, identity)
|
yield k -> serializerOf(v).fold(throw _, identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bindings is a Map whose keys = FQN of class that is serializable and values = the alias of the serializer to be used
|
* bindings is a Seq of tuple representing the mapping from Class to Serializer.
|
||||||
|
* It is primarily ordered by the most specific classes first, and secondly in the configured order.
|
||||||
*/
|
*/
|
||||||
lazy val bindings: Map[String, String] = {
|
private[akka] val bindings: Seq[ClassSerializer] = {
|
||||||
settings.SerializationBindings.foldLeft(Map[String, String]()) {
|
val configuredBindings = for ((k: String, v: String) ← settings.SerializationBindings if v != "none") yield {
|
||||||
//All keys which are lists, take the Strings from them and Map them
|
val c = ReflectiveAccess.getClassFor(k, system.internalClassLoader).fold(throw _, identity[Class[_]])
|
||||||
case (result, (k: String, vs: Seq[_])) ⇒ result ++ (vs collect { case v: String ⇒ (v, k) })
|
(c, serializers(v))
|
||||||
//For any other values, just skip them
|
|
||||||
case (result, _) ⇒ result
|
|
||||||
}
|
}
|
||||||
|
sort(configuredBindings)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* serializerMap is a Map whose keys = FQN of class that is serializable and values is the serializer to be used for that class
|
* Sort so that subtypes always precede their supertypes, but without
|
||||||
|
* obeying any order between unrelated subtypes (insert sort).
|
||||||
*/
|
*/
|
||||||
private lazy val serializerMap: ConcurrentHashMap[String, Serializer] = {
|
private def sort(in: Iterable[ClassSerializer]): Seq[ClassSerializer] =
|
||||||
val serializerMap = new ConcurrentHashMap[String, Serializer]
|
(new ArrayBuffer[ClassSerializer](in.size) /: in) { (buf, ca) ⇒
|
||||||
for ((k, v) ← bindings) {
|
buf.indexWhere(_._1 isAssignableFrom ca._1) match {
|
||||||
serializerMap.put(k, serializers(v))
|
case -1 ⇒ buf append ca
|
||||||
|
case x ⇒ buf insert (x, ca)
|
||||||
|
}
|
||||||
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* serializerMap is a Map whose keys is the class that is serializable and values is the serializer
|
||||||
|
* to be used for that class.
|
||||||
|
*/
|
||||||
|
private val serializerMap: ConcurrentHashMap[Class[_], Serializer] = {
|
||||||
|
val serializerMap = new ConcurrentHashMap[Class[_], Serializer]
|
||||||
|
for ((c, s) ← bindings) serializerMap.put(c, s)
|
||||||
serializerMap
|
serializerMap
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps from a Serializer Identity (Int) to a Serializer instance (optimization)
|
* Maps from a Serializer Identity (Int) to a Serializer instance (optimization)
|
||||||
*/
|
*/
|
||||||
lazy val serializerByIdentity: Map[Int, Serializer] =
|
val serializerByIdentity: Map[Int, Serializer] =
|
||||||
Map(NullSerializer.identifier -> NullSerializer) ++ serializers map { case (_, v) ⇒ (v.identifier, v) }
|
Map(NullSerializer.identifier -> NullSerializer) ++ serializers map { case (_, v) ⇒ (v.identifier, v) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import scala.util.DynamicVariable
|
||||||
* avoid strange match errors and inequalities which arise from different class loaders loading
|
* avoid strange match errors and inequalities which arise from different class loaders loading
|
||||||
* the same class.
|
* the same class.
|
||||||
*/
|
*/
|
||||||
trait Serializer extends scala.Serializable {
|
trait Serializer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Completely unique value to identify this implementation of Serializer, used to optimize network traffic
|
* Completely unique value to identify this implementation of Serializer, used to optimize network traffic
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import java.util.concurrent.TimeUnit
|
||||||
import TimeUnit._
|
import TimeUnit._
|
||||||
import java.lang.{ Double ⇒ JDouble }
|
import java.lang.{ Double ⇒ JDouble }
|
||||||
|
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class Deadline private (time: Duration) {
|
case class Deadline private (time: Duration) {
|
||||||
def +(other: Duration): Deadline = copy(time = time + other)
|
def +(other: Duration): Deadline = copy(time = time + other)
|
||||||
def -(other: Duration): Deadline = copy(time = time - other)
|
def -(other: Duration): Deadline = copy(time = time - other)
|
||||||
|
|
@ -232,6 +233,7 @@ object Duration {
|
||||||
* val d3 = d2 + 1.millisecond
|
* val d3 = d2 + 1.millisecond
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
abstract class Duration extends Serializable with Ordered[Duration] {
|
abstract class Duration extends Serializable with Ordered[Duration] {
|
||||||
def length: Long
|
def length: Long
|
||||||
def unit: TimeUnit
|
def unit: TimeUnit
|
||||||
|
|
@ -276,6 +278,7 @@ object FiniteDuration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
|
class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
|
||||||
import Duration._
|
import Duration._
|
||||||
|
|
||||||
|
|
@ -525,6 +528,7 @@ class DurationDouble(d: Double) {
|
||||||
def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS))
|
def day[C, CC <: Classifier[C]](c: C)(implicit ev: CC): CC#R = ev.convert(Duration(d, DAYS))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO add @SerialVersionUID(1L) when SI-4804 is fixed
|
||||||
case class Timeout(duration: Duration) {
|
case class Timeout(duration: Duration) {
|
||||||
def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS))
|
def this(timeout: Long) = this(Duration(timeout, TimeUnit.MILLISECONDS))
|
||||||
def this(length: Long, unit: TimeUnit) = this(Duration(length, unit))
|
def this(length: Long, unit: TimeUnit) = this(Duration(length, unit))
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ package akka.util
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extractor of non-fatal Throwables. Will not match fatal errors
|
* Extractor of non-fatal Throwables. Will not match fatal errors
|
||||||
* like VirtualMachineError (OutOfMemoryError, StackOverflowError)
|
* like VirtualMachineError (OutOfMemoryError)
|
||||||
* ThreadDeath, and InterruptedException.
|
* ThreadDeath, LinkageError and InterruptedException.
|
||||||
|
* StackOverflowError is matched, i.e. considered non-fatal.
|
||||||
*
|
*
|
||||||
* Usage to catch all harmless throwables:
|
* Usage to catch all harmless throwables:
|
||||||
* {{{
|
* {{{
|
||||||
|
|
@ -20,8 +21,9 @@ package akka.util
|
||||||
object NonFatal {
|
object NonFatal {
|
||||||
|
|
||||||
def unapply(t: Throwable): Option[Throwable] = t match {
|
def unapply(t: Throwable): Option[Throwable] = t match {
|
||||||
// VirtualMachineError includes OutOfMemoryError, StackOverflowError and other fatal errors
|
case e: StackOverflowError ⇒ Some(e) // StackOverflowError ok even though it is a VirtualMachineError
|
||||||
case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException ⇒ None
|
// VirtualMachineError includes OutOfMemoryError and other fatal errors
|
||||||
|
case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError ⇒ None
|
||||||
case e ⇒ Some(e)
|
case e ⇒ Some(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,14 @@ akka {
|
||||||
|
|
||||||
# The dispatcher used for agent-send-off actor
|
# The dispatcher used for agent-send-off actor
|
||||||
send-off-dispatcher {
|
send-off-dispatcher {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
# The dispatcher used for agent-alter-off actor
|
# The dispatcher used for agent-alter-off actor
|
||||||
alter-off-dispatcher {
|
alter-off-dispatcher {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ akka {
|
||||||
}
|
}
|
||||||
|
|
||||||
gossip {
|
gossip {
|
||||||
initialDelay = 5s
|
initial-delay = 5s
|
||||||
frequency = 1s
|
frequency = 1s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class ClusterSettings(val config: Config, val systemName: String) {
|
||||||
val FailureDetectorMaxSampleSize = getInt("akka.cluster.failure-detector.max-sample-size")
|
val FailureDetectorMaxSampleSize = getInt("akka.cluster.failure-detector.max-sample-size")
|
||||||
val SeedNodeConnectionTimeout = Duration(config.getMilliseconds("akka.cluster.seed-node-connection-timeout"), MILLISECONDS)
|
val SeedNodeConnectionTimeout = Duration(config.getMilliseconds("akka.cluster.seed-node-connection-timeout"), MILLISECONDS)
|
||||||
val MaxTimeToRetryJoiningCluster = Duration(config.getMilliseconds("akka.cluster.max-time-to-retry-joining-cluster"), MILLISECONDS)
|
val MaxTimeToRetryJoiningCluster = Duration(config.getMilliseconds("akka.cluster.max-time-to-retry-joining-cluster"), MILLISECONDS)
|
||||||
val InitialDelayForGossip = Duration(getMilliseconds("akka.cluster.gossip.initialDelay"), MILLISECONDS)
|
val InitialDelayForGossip = Duration(getMilliseconds("akka.cluster.gossip.initial-delay"), MILLISECONDS)
|
||||||
val GossipFrequency = Duration(getMilliseconds("akka.cluster.gossip.frequency"), MILLISECONDS)
|
val GossipFrequency = Duration(getMilliseconds("akka.cluster.gossip.frequency"), MILLISECONDS)
|
||||||
val SeedNodes = Set.empty[Address] ++ getStringList("akka.cluster.seed-nodes").asScala.collect {
|
val SeedNodes = Set.empty[Address] ++ getStringList("akka.cluster.seed-nodes").asScala.collect {
|
||||||
case AddressExtractor(addr) ⇒ addr
|
case AddressExtractor(addr) ⇒ addr
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,7 @@ case class Gossiper(remote: RemoteActorRefProvider, system: ActorSystemImpl) {
|
||||||
/**
|
/**
|
||||||
* Gossips to a random member in the set of members passed in as argument.
|
* Gossips to a random member in the set of members passed in as argument.
|
||||||
*
|
*
|
||||||
* @returns 'true' if it gossiped to a "seed" member.
|
* @return 'true' if it gossiped to a "seed" member.
|
||||||
*/
|
*/
|
||||||
private def gossipToRandomNodeOf(members: Set[Member]): Boolean = {
|
private def gossipToRandomNodeOf(members: Set[Member]): Boolean = {
|
||||||
val peers = members filter (_.address != address) // filter out myself
|
val peers = members filter (_.address != address) // filter out myself
|
||||||
|
|
|
||||||
|
|
@ -172,12 +172,12 @@ More advanced include and substitution mechanisms are explained in the `HOCON <h
|
||||||
specification.
|
specification.
|
||||||
|
|
||||||
|
|
||||||
.. _-Dakka.logConfigOnStart:
|
.. _-Dakka.log-config-on-start:
|
||||||
|
|
||||||
Logging of Configuration
|
Logging of Configuration
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
If the system or config property ``akka.logConfigOnStart`` is set to ``on``, then the
|
If the system or config property ``akka.log-config-on-start`` is set to ``on``, then the
|
||||||
complete configuration at INFO level when the actor system is started. This is useful
|
complete configuration at INFO level when the actor system is started. This is useful
|
||||||
when you are uncertain of what configuration is used.
|
when you are uncertain of what configuration is used.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -397,17 +397,25 @@ When this in done we can run our application directly inside Maven::
|
||||||
|
|
||||||
Yippee! It is working.
|
Yippee! It is working.
|
||||||
|
|
||||||
Overriding Configuration Externally
|
Overriding Configuration Externally (Optional)
|
||||||
-----------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
The sample project includes an ``application.conf`` file in the resources directory:
|
The sample project includes an ``application.conf`` file in the resources directory:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/resources/application.conf
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/resources/application.conf
|
||||||
|
|
||||||
If you uncomment the two lines, you should see a change in performance,
|
If you uncomment the two lines, you should see a change in performance,
|
||||||
hopefully for the better. It should be noted that overriding only works if a
|
hopefully for the better (you might want to increase the number of messages in
|
||||||
router type is given, so just uncommenting ``nr-of-instances`` does not work;
|
the code to prolong the time the application runs). It should be noted that
|
||||||
see :ref:`routing-java` for more details.
|
overriding only works if a router type is given, so just uncommenting
|
||||||
|
``nr-of-instances`` does not work; see :ref:`routing-java` for more details.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Make sure that your ``application.conf`` is on the class path when you run
|
||||||
|
the application. If running from inside Maven that should already be the
|
||||||
|
case, otherwise you need to add the directory containing this file to the
|
||||||
|
JVM’s ``-classpath`` option.
|
||||||
|
|
||||||
Conclusion
|
Conclusion
|
||||||
----------
|
----------
|
||||||
|
|
|
||||||
|
|
@ -284,7 +284,7 @@ In Scala we have 'case classes' which make excellent messages. So let's start by
|
||||||
We also create a common base trait for our messages (that we define as being ``sealed`` in order to prevent creating messages
|
We also create a common base trait for our messages (that we define as being ``sealed`` in order to prevent creating messages
|
||||||
outside our control):
|
outside our control):
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#messages
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#messages
|
||||||
|
|
||||||
|
|
||||||
Creating the worker
|
Creating the worker
|
||||||
|
|
@ -295,7 +295,7 @@ trait and defining the ``receive`` method. The ``receive`` method defines our
|
||||||
message handler. We expect it to be able to handle the ``Work`` message so we
|
message handler. We expect it to be able to handle the ``Work`` message so we
|
||||||
need to add a handler for this message:
|
need to add a handler for this message:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#worker
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#worker
|
||||||
:exclude: calculatePiFor
|
:exclude: calculatePiFor
|
||||||
|
|
||||||
The ``Actor`` trait is defined in ``akka.actor`` and you can either import it explicitly,
|
The ``Actor`` trait is defined in ``akka.actor`` and you can either import it explicitly,
|
||||||
|
|
@ -318,7 +318,7 @@ The only thing missing in our ``Worker`` actor is the implementation on the
|
||||||
algorithm in Scala, in this introductory tutorial we have chosen an imperative
|
algorithm in Scala, in this introductory tutorial we have chosen an imperative
|
||||||
style using a for comprehension and an accumulator:
|
style using a for comprehension and an accumulator:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#calculatePiFor
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#calculatePiFor
|
||||||
|
|
||||||
|
|
||||||
Creating the master
|
Creating the master
|
||||||
|
|
@ -328,11 +328,11 @@ Now create a new class for the master actor. The master actor is a little bit
|
||||||
more involved. In its constructor we create a round-robin router to make it easier
|
more involved. In its constructor we create a round-robin router to make it easier
|
||||||
to spread out the work evenly between the workers. First we need to add some imports:
|
to spread out the work evenly between the workers. First we need to add some imports:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#imports
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#imports
|
||||||
|
|
||||||
and then we can create the router:
|
and then we can create the router:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#create-router
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#create-router
|
||||||
|
|
||||||
Now we have a router that is representing all our workers in a single
|
Now we have a router that is representing all our workers in a single
|
||||||
abstraction. So now let's create the master actor. We pass it three integer variables:
|
abstraction. So now let's create the master actor. We pass it three integer variables:
|
||||||
|
|
@ -343,7 +343,7 @@ abstraction. So now let's create the master actor. We pass it three integer vari
|
||||||
|
|
||||||
Here is the master actor:
|
Here is the master actor:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#master
|
||||||
:exclude: handle-messages
|
:exclude: handle-messages
|
||||||
|
|
||||||
A couple of things are worth explaining further.
|
A couple of things are worth explaining further.
|
||||||
|
|
@ -370,7 +370,7 @@ will propagate down to all its supervised 'children'.
|
||||||
|
|
||||||
Let's capture this in code:
|
Let's capture this in code:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master-receive
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#master-receive
|
||||||
|
|
||||||
Creating the result listener
|
Creating the result listener
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
@ -378,7 +378,7 @@ Creating the result listener
|
||||||
The listener is straightforward. When it receives the ``PiApproximation`` from the ``Master`` it
|
The listener is straightforward. When it receives the ``PiApproximation`` from the ``Master`` it
|
||||||
prints the result and shuts down the ``ActorSystem``.
|
prints the result and shuts down the ``ActorSystem``.
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#result-listener
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#result-listener
|
||||||
|
|
||||||
Bootstrap the calculation
|
Bootstrap the calculation
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
@ -390,7 +390,7 @@ which means that we will be able to run this as an application directly from the
|
||||||
The ``Pi`` object is a perfect container module for our actors and messages, so let's put them all there.
|
The ``Pi`` object is a perfect container module for our actors and messages, so let's put them all there.
|
||||||
We also create a method ``calculate`` in which we start up the ``Master`` actor and wait for it to finish:
|
We also create a method ``calculate`` in which we start up the ``Master`` actor and wait for it to finish:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#app
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#app
|
||||||
:exclude: actors-and-messages
|
:exclude: actors-and-messages
|
||||||
|
|
||||||
As you can see the *calculate* method above it creates an ``ActorSystem`` and this is the Akka container which
|
As you can see the *calculate* method above it creates an ``ActorSystem`` and this is the Akka container which
|
||||||
|
|
@ -421,17 +421,25 @@ arguments to the JVM on the ``Arguments`` page, for instance to define where :re
|
||||||
Once you finished your run configuration, click ``Run``. You should see the same output in the ``Console`` window.
|
Once you finished your run configuration, click ``Run``. You should see the same output in the ``Console`` window.
|
||||||
You can use the same configuration for debugging the application, by choosing ``Run/Debug History`` or just ``Debug As``.
|
You can use the same configuration for debugging the application, by choosing ``Run/Debug History`` or just ``Debug As``.
|
||||||
|
|
||||||
Overriding Configuration Externally
|
Overriding Configuration Externally (Optional)
|
||||||
-----------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
The sample project includes an ``application.conf`` file in the resources directory:
|
The sample project includes an ``application.conf`` file in the resources directory:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/resources/application.conf
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/resources/application.conf
|
||||||
|
|
||||||
If you uncomment the two lines, you should see a change in performance,
|
If you uncomment the two lines, you should see a change in performance,
|
||||||
hopefully for the better. It should be noted that overriding only works if a
|
hopefully for the better (you might want to increase the number of messages in
|
||||||
router type is given, so just uncommenting ``nr-of-instances`` does not work;
|
the code to prolong the time the application runs). It should be noted that
|
||||||
see :ref:`routing-java` for more details.
|
overriding only works if a router type is given, so just uncommenting
|
||||||
|
``nr-of-instances`` does not work; see :ref:`routing-scala` for more details.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Make sure that your ``application.conf`` is on the class path when you run
|
||||||
|
the application. If running from inside SBT that should already be the case,
|
||||||
|
otherwise you need to add the directory containing this file to the JVM’s
|
||||||
|
``-classpath`` option.
|
||||||
|
|
||||||
Conclusion
|
Conclusion
|
||||||
----------
|
----------
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ check out the full tutorial from the Akka GitHub repository. It is in the
|
||||||
`here`__, with the actual source code `here`__.
|
`here`__, with the actual source code `here`__.
|
||||||
|
|
||||||
__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first
|
__ https://github.com/jboner/akka/tree/master/akka-tutorials/akka-tutorial-first
|
||||||
__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala
|
__ https://github.com/jboner/akka/blob/master/akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala
|
||||||
|
|
||||||
To check out the code using Git invoke the following::
|
To check out the code using Git invoke the following::
|
||||||
|
|
||||||
|
|
@ -244,7 +244,7 @@ Now it's about time to start hacking.
|
||||||
We start by creating a ``Pi.scala`` file and adding these import statements at
|
We start by creating a ``Pi.scala`` file and adding these import statements at
|
||||||
the top of the file:
|
the top of the file:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#imports
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#imports
|
||||||
|
|
||||||
If you are using SBT in this tutorial then create the file in the
|
If you are using SBT in this tutorial then create the file in the
|
||||||
``src/main/scala`` directory.
|
``src/main/scala`` directory.
|
||||||
|
|
@ -282,7 +282,7 @@ start by creating three messages as case classes. We also create a common base
|
||||||
trait for our messages (that we define as being ``sealed`` in order to prevent
|
trait for our messages (that we define as being ``sealed`` in order to prevent
|
||||||
creating messages outside our control):
|
creating messages outside our control):
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#messages
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#messages
|
||||||
|
|
||||||
|
|
||||||
Creating the worker
|
Creating the worker
|
||||||
|
|
@ -293,7 +293,7 @@ trait and defining the ``receive`` method. The ``receive`` method defines our
|
||||||
message handler. We expect it to be able to handle the ``Work`` message so we
|
message handler. We expect it to be able to handle the ``Work`` message so we
|
||||||
need to add a handler for this message:
|
need to add a handler for this message:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#worker
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#worker
|
||||||
:exclude: calculatePiFor
|
:exclude: calculatePiFor
|
||||||
|
|
||||||
As you can see we have now created an ``Actor`` with a ``receive`` method as a
|
As you can see we have now created an ``Actor`` with a ``receive`` method as a
|
||||||
|
|
@ -308,7 +308,7 @@ The only thing missing in our ``Worker`` actor is the implementation on the
|
||||||
algorithm in Scala, in this introductory tutorial we have chosen an imperative
|
algorithm in Scala, in this introductory tutorial we have chosen an imperative
|
||||||
style using a for comprehension and an accumulator:
|
style using a for comprehension and an accumulator:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#calculatePiFor
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#calculatePiFor
|
||||||
|
|
||||||
|
|
||||||
Creating the master
|
Creating the master
|
||||||
|
|
@ -317,7 +317,7 @@ Creating the master
|
||||||
The master actor is a little bit more involved. In its constructor we create a round-robin router
|
The master actor is a little bit more involved. In its constructor we create a round-robin router
|
||||||
to make it easier to spread out the work evenly between the workers. Let's do that first:
|
to make it easier to spread out the work evenly between the workers. Let's do that first:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#create-router
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#create-router
|
||||||
|
|
||||||
Now we have a router that is representing all our workers in a single
|
Now we have a router that is representing all our workers in a single
|
||||||
abstraction. So now let's create the master actor. We pass it three integer variables:
|
abstraction. So now let's create the master actor. We pass it three integer variables:
|
||||||
|
|
@ -328,7 +328,7 @@ abstraction. So now let's create the master actor. We pass it three integer vari
|
||||||
|
|
||||||
Here is the master actor:
|
Here is the master actor:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#master
|
||||||
:exclude: handle-messages
|
:exclude: handle-messages
|
||||||
|
|
||||||
A couple of things are worth explaining further.
|
A couple of things are worth explaining further.
|
||||||
|
|
@ -355,7 +355,7 @@ will propagate down to all its supervised 'children'.
|
||||||
|
|
||||||
Let's capture this in code:
|
Let's capture this in code:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master-receive
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#master-receive
|
||||||
|
|
||||||
|
|
||||||
Creating the result listener
|
Creating the result listener
|
||||||
|
|
@ -364,7 +364,7 @@ Creating the result listener
|
||||||
The listener is straightforward. When it receives the ``PiApproximation`` from the ``Master`` it
|
The listener is straightforward. When it receives the ``PiApproximation`` from the ``Master`` it
|
||||||
prints the result and shuts down the ``ActorSystem``.
|
prints the result and shuts down the ``ActorSystem``.
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#result-listener
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#result-listener
|
||||||
|
|
||||||
Bootstrap the calculation
|
Bootstrap the calculation
|
||||||
=========================
|
=========================
|
||||||
|
|
@ -378,7 +378,7 @@ The ``Pi`` object is a perfect container module for our actors and messages, so
|
||||||
let's put them all there. We also create a method ``calculate`` in which we
|
let's put them all there. We also create a method ``calculate`` in which we
|
||||||
start up the ``Master`` actor and wait for it to finish:
|
start up the ``Master`` actor and wait for it to finish:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#app
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala#app
|
||||||
:exclude: actors-and-messages
|
:exclude: actors-and-messages
|
||||||
|
|
||||||
As you can see the *calculate* method above it creates an ``ActorSystem`` and this is the Akka container which
|
As you can see the *calculate* method above it creates an ``ActorSystem`` and this is the Akka container which
|
||||||
|
|
@ -392,14 +392,14 @@ That's it. Now we are done.
|
||||||
But before we package it up and run it, let's take a look at the full code now,
|
But before we package it up and run it, let's take a look at the full code now,
|
||||||
with package declaration, imports and all:
|
with package declaration, imports and all:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala
|
||||||
|
|
||||||
|
|
||||||
Run it as a command line application
|
Run it as a command line application
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
If you have not typed in (or copied) the code for the tutorial as in
|
If you have not typed in (or copied) the code for the tutorial as in
|
||||||
``$AKKA_HOME/akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala`` then now is the time.
|
``$AKKA_HOME/akka-tutorials/akka-tutorial-first/src/main/scala/akka/tutorial/first/scala/Pi.scala`` then now is the time.
|
||||||
When that's done open up a shell and step in to the Akka distribution (``cd $AKKA_HOME``).
|
When that's done open up a shell and step in to the Akka distribution (``cd $AKKA_HOME``).
|
||||||
|
|
||||||
First we need to compile the source file. That is done with Scala's compiler
|
First we need to compile the source file. That is done with Scala's compiler
|
||||||
|
|
@ -442,17 +442,25 @@ When this in done we can run our application directly inside SBT::
|
||||||
|
|
||||||
Yippee! It is working.
|
Yippee! It is working.
|
||||||
|
|
||||||
Overriding Configuration Externally
|
Overriding Configuration Externally (Optional)
|
||||||
===================================
|
==============================================
|
||||||
|
|
||||||
The sample project includes an ``application.conf`` file in the resources directory:
|
The sample project includes an ``application.conf`` file in the resources directory:
|
||||||
|
|
||||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/resources/application.conf
|
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/resources/application.conf
|
||||||
|
|
||||||
If you uncomment the two lines, you should see a change in performance,
|
If you uncomment the two lines, you should see a change in performance,
|
||||||
hopefully for the better. It should be noted that overriding only works if a
|
hopefully for the better (you might want to increase the number of messages in
|
||||||
router type is given, so just uncommenting ``nr-of-instances`` does not work;
|
the code to prolong the time the application runs). It should be noted that
|
||||||
see :ref:`routing-scala` for more details.
|
overriding only works if a router type is given, so just uncommenting
|
||||||
|
``nr-of-instances`` does not work; see :ref:`routing-scala` for more details.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Make sure that your ``application.conf`` is on the class path when you run
|
||||||
|
the application. If running from inside SBT that should already be the case,
|
||||||
|
otherwise you need to add the directory containing this file to the JVM’s
|
||||||
|
``-classpath`` option.
|
||||||
|
|
||||||
Conclusion
|
Conclusion
|
||||||
==========
|
==========
|
||||||
|
|
|
||||||
|
|
@ -381,6 +381,7 @@ public class FutureDocTestBase {
|
||||||
@Test public void useOnSuccessOnFailureAndOnComplete() {
|
@Test public void useOnSuccessOnFailureAndOnComplete() {
|
||||||
{
|
{
|
||||||
Future<String> future = Futures.successful("foo", system.dispatcher());
|
Future<String> future = Futures.successful("foo", system.dispatcher());
|
||||||
|
|
||||||
//#onSuccess
|
//#onSuccess
|
||||||
future.onSuccess(new OnSuccess<String>() {
|
future.onSuccess(new OnSuccess<String>() {
|
||||||
public void onSuccess(String result) {
|
public void onSuccess(String result) {
|
||||||
|
|
|
||||||
|
|
@ -54,61 +54,7 @@ public class SerializationDocTestBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#my-own-serializer
|
//#my-own-serializer
|
||||||
@Test public void haveExamples() {
|
|
||||||
/*
|
|
||||||
//#serialize-messages-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serialize-messages = on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialize-messages-config
|
|
||||||
|
|
||||||
//#serialize-creators-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serialize-creators = on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialize-creators-config
|
|
||||||
|
|
||||||
|
|
||||||
//#serialize-serializers-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
|
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialize-serializers-config
|
|
||||||
|
|
||||||
//#serialization-bindings-config
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
java = "akka.serialization.JavaSerializer"
|
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
java = ["java.lang.String",
|
|
||||||
"app.my.Customer"]
|
|
||||||
proto = ["com.google.protobuf.Message"]
|
|
||||||
myown = ["my.own.BusinessObject",
|
|
||||||
"something.equally.Awesome",
|
|
||||||
"akka.docs.serialization.MyOwnSerializable"
|
|
||||||
"java.lang.Boolean"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#serialization-bindings-config
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test public void demonstrateTheProgrammaticAPI() {
|
@Test public void demonstrateTheProgrammaticAPI() {
|
||||||
//#programmatic
|
//#programmatic
|
||||||
|
|
|
||||||
8
akka-docs/java/code/akka/docs/zeromq/ZeromqDocTest.scala
Normal file
8
akka-docs/java/code/akka/docs/zeromq/ZeromqDocTest.scala
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
package akka.docs.zeromq
|
||||||
|
|
||||||
|
import org.scalatest.junit.JUnitSuite
|
||||||
|
|
||||||
|
class ZeromqDocTest extends ZeromqDocTestBase with JUnitSuite
|
||||||
286
akka-docs/java/code/akka/docs/zeromq/ZeromqDocTestBase.java
Normal file
286
akka-docs/java/code/akka/docs/zeromq/ZeromqDocTestBase.java
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
package akka.docs.zeromq;
|
||||||
|
|
||||||
|
//#pub-socket
|
||||||
|
import akka.zeromq.Bind;
|
||||||
|
import akka.zeromq.ZeroMQExtension;
|
||||||
|
|
||||||
|
//#pub-socket
|
||||||
|
//#sub-socket
|
||||||
|
import akka.zeromq.Connect;
|
||||||
|
import akka.zeromq.Listener;
|
||||||
|
import akka.zeromq.Subscribe;
|
||||||
|
|
||||||
|
//#sub-socket
|
||||||
|
//#unsub-topic-socket
|
||||||
|
import akka.zeromq.Unsubscribe;
|
||||||
|
|
||||||
|
//#unsub-topic-socket
|
||||||
|
//#pub-topic
|
||||||
|
import akka.zeromq.Frame;
|
||||||
|
import akka.zeromq.ZMQMessage;
|
||||||
|
|
||||||
|
//#pub-topic
|
||||||
|
|
||||||
|
import akka.zeromq.HighWatermark;
|
||||||
|
import akka.zeromq.SocketOption;
|
||||||
|
import akka.zeromq.ZeroMQVersion;
|
||||||
|
|
||||||
|
//#health
|
||||||
|
import akka.actor.ActorRef;
|
||||||
|
import akka.actor.UntypedActor;
|
||||||
|
import akka.actor.Props;
|
||||||
|
import akka.event.Logging;
|
||||||
|
import akka.event.LoggingAdapter;
|
||||||
|
import akka.util.Duration;
|
||||||
|
import akka.serialization.SerializationExtension;
|
||||||
|
import akka.serialization.Serialization;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
//#health
|
||||||
|
|
||||||
|
import com.typesafe.config.ConfigFactory;
|
||||||
|
|
||||||
|
import java.lang.management.MemoryMXBean;
|
||||||
|
import java.lang.management.MemoryUsage;
|
||||||
|
import java.lang.management.OperatingSystemMXBean;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.testkit.AkkaSpec;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.Assume;
|
||||||
|
|
||||||
|
import akka.zeromq.SocketType;
|
||||||
|
|
||||||
|
public class ZeromqDocTestBase {
|
||||||
|
|
||||||
|
ActorSystem system;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
system = ActorSystem.create("ZeromqDocTest",
|
||||||
|
ConfigFactory.parseString("akka.loglevel=INFO").withFallback(AkkaSpec.testConf()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
system.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void demonstrateCreateSocket() {
|
||||||
|
Assume.assumeTrue(checkZeroMQInstallation());
|
||||||
|
|
||||||
|
//#pub-socket
|
||||||
|
ActorRef pubSocket = ZeroMQExtension.get(system).newPubSocket(new Bind("tcp://127.0.0.1:1233"));
|
||||||
|
//#pub-socket
|
||||||
|
|
||||||
|
//#sub-socket
|
||||||
|
ActorRef listener = system.actorOf(new Props(ListenerActor.class));
|
||||||
|
ActorRef subSocket = ZeroMQExtension.get(system).newSubSocket(new Connect("tcp://127.0.0.1:1233"),
|
||||||
|
new Listener(listener), Subscribe.all());
|
||||||
|
//#sub-socket
|
||||||
|
|
||||||
|
//#sub-topic-socket
|
||||||
|
ActorRef subTopicSocket = ZeroMQExtension.get(system).newSubSocket(new Connect("tcp://127.0.0.1:1233"),
|
||||||
|
new Listener(listener), new Subscribe("foo.bar"));
|
||||||
|
//#sub-topic-socket
|
||||||
|
|
||||||
|
//#unsub-topic-socket
|
||||||
|
subTopicSocket.tell(new Unsubscribe("foo.bar"));
|
||||||
|
//#unsub-topic-socket
|
||||||
|
|
||||||
|
byte[] payload = new byte[0];
|
||||||
|
//#pub-topic
|
||||||
|
pubSocket.tell(new ZMQMessage(new Frame("foo.bar"), new Frame(payload)));
|
||||||
|
//#pub-topic
|
||||||
|
|
||||||
|
//#high-watermark
|
||||||
|
ActorRef highWatermarkSocket = ZeroMQExtension.get(system).newRouterSocket(
|
||||||
|
new SocketOption[] { new Listener(listener), new Bind("tcp://127.0.0.1:1233"), new HighWatermark(50000) });
|
||||||
|
//#high-watermark
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void demonstratePubSub() throws Exception {
|
||||||
|
Assume.assumeTrue(checkZeroMQInstallation());
|
||||||
|
|
||||||
|
//#health2
|
||||||
|
|
||||||
|
system.actorOf(new Props(HealthProbe.class), "health");
|
||||||
|
//#health2
|
||||||
|
|
||||||
|
//#logger2
|
||||||
|
|
||||||
|
system.actorOf(new Props(Logger.class), "logger");
|
||||||
|
//#logger2
|
||||||
|
|
||||||
|
//#alerter2
|
||||||
|
|
||||||
|
system.actorOf(new Props(HeapAlerter.class), "alerter");
|
||||||
|
//#alerter2
|
||||||
|
|
||||||
|
// Let it run for a while to see some output.
|
||||||
|
// Don't do like this in real tests, this is only doc demonstration.
|
||||||
|
Thread.sleep(3000L);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkZeroMQInstallation() {
|
||||||
|
try {
|
||||||
|
ZeroMQVersion v = ZeroMQExtension.get(system).version();
|
||||||
|
return (v.major() == 2 && v.minor() == 1);
|
||||||
|
} catch (LinkageError e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#listener-actor
|
||||||
|
public static class ListenerActor extends UntypedActor {
|
||||||
|
public void onReceive(Object message) throws Exception {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#listener-actor
|
||||||
|
|
||||||
|
//#health
|
||||||
|
|
||||||
|
public static final Object TICK = "TICK";
|
||||||
|
|
||||||
|
public static class Heap implements Serializable {
|
||||||
|
public final long timestamp;
|
||||||
|
public final long used;
|
||||||
|
public final long max;
|
||||||
|
|
||||||
|
public Heap(long timestamp, long used, long max) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.used = used;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Load implements Serializable {
|
||||||
|
public final long timestamp;
|
||||||
|
public final double loadAverage;
|
||||||
|
|
||||||
|
public Load(long timestamp, double loadAverage) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.loadAverage = loadAverage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HealthProbe extends UntypedActor {
|
||||||
|
|
||||||
|
ActorRef pubSocket = ZeroMQExtension.get(getContext().system()).newPubSocket(new Bind("tcp://127.0.0.1:1237"));
|
||||||
|
MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
|
||||||
|
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
|
||||||
|
Serialization ser = SerializationExtension.get(getContext().system());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preStart() {
|
||||||
|
getContext().system().scheduler()
|
||||||
|
.schedule(Duration.parse("1 second"), Duration.parse("1 second"), getSelf(), TICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postRestart(Throwable reason) {
|
||||||
|
// don't call preStart, only schedule once
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Object message) {
|
||||||
|
if (message.equals(TICK)) {
|
||||||
|
MemoryUsage currentHeap = memory.getHeapMemoryUsage();
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// use akka SerializationExtension to convert to bytes
|
||||||
|
byte[] heapPayload = ser.serializerFor(Heap.class).toBinary(
|
||||||
|
new Heap(timestamp, currentHeap.getUsed(), currentHeap.getMax()));
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
pubSocket.tell(new ZMQMessage(new Frame("health.heap"), new Frame(heapPayload)));
|
||||||
|
|
||||||
|
// use akka SerializationExtension to convert to bytes
|
||||||
|
byte[] loadPayload = ser.serializerFor(Load.class).toBinary(new Load(timestamp, os.getSystemLoadAverage()));
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
pubSocket.tell(new ZMQMessage(new Frame("health.load"), new Frame(loadPayload)));
|
||||||
|
} else {
|
||||||
|
unhandled(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//#health
|
||||||
|
|
||||||
|
//#logger
|
||||||
|
public static class Logger extends UntypedActor {
|
||||||
|
|
||||||
|
ActorRef subSocket = ZeroMQExtension.get(getContext().system()).newSubSocket(new Connect("tcp://127.0.0.1:1237"),
|
||||||
|
new Listener(getSelf()), new Subscribe("health"));
|
||||||
|
Serialization ser = SerializationExtension.get(getContext().system());
|
||||||
|
SimpleDateFormat timestampFormat = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||||
|
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Object message) {
|
||||||
|
if (message instanceof ZMQMessage) {
|
||||||
|
ZMQMessage m = (ZMQMessage) message;
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
if (m.firstFrameAsString().equals("health.heap")) {
|
||||||
|
Heap heap = (Heap) ser.serializerFor(Heap.class).fromBinary(m.payload(1));
|
||||||
|
log.info("Used heap {} bytes, at {}", heap.used, timestampFormat.format(new Date(heap.timestamp)));
|
||||||
|
} else if (m.firstFrameAsString().equals("health.load")) {
|
||||||
|
Load load = (Load) ser.serializerFor(Load.class).fromBinary(m.payload(1));
|
||||||
|
log.info("Load average {}, at {}", load.loadAverage, timestampFormat.format(new Date(load.timestamp)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unhandled(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//#logger
|
||||||
|
|
||||||
|
//#alerter
|
||||||
|
public static class HeapAlerter extends UntypedActor {
|
||||||
|
|
||||||
|
ActorRef subSocket = ZeroMQExtension.get(getContext().system()).newSubSocket(new Connect("tcp://127.0.0.1:1237"),
|
||||||
|
new Listener(getSelf()), new Subscribe("health.heap"));
|
||||||
|
Serialization ser = SerializationExtension.get(getContext().system());
|
||||||
|
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Object message) {
|
||||||
|
if (message instanceof ZMQMessage) {
|
||||||
|
ZMQMessage m = (ZMQMessage) message;
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
if (m.firstFrameAsString().equals("health.heap")) {
|
||||||
|
Heap heap = (Heap) ser.serializerFor(Heap.class).fromBinary(m.payload(1));
|
||||||
|
if (((double) heap.used / heap.max) > 0.9) {
|
||||||
|
count += 1;
|
||||||
|
} else {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
if (count > 10) {
|
||||||
|
log.warning("Need more memory, using {} %", (100.0 * heap.used / heap.max));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unhandled(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//#alerter
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -55,6 +55,24 @@ Default values are taken from ``default-dispatcher``, i.e. all options doesn't n
|
||||||
:ref:`configuration` for the default values of the ``default-dispatcher``. You can also override
|
:ref:`configuration` for the default values of the ``default-dispatcher``. You can also override
|
||||||
the values for the ``default-dispatcher`` in your configuration.
|
the values for the ``default-dispatcher`` in your configuration.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
It should be noted that the ``dispatcher-id`` used in :class:`Props` is in
|
||||||
|
fact an absolute path into the configuration object, i.e. you can declare a
|
||||||
|
dispatcher configuration nested within other configuration objects and refer
|
||||||
|
to it like so: ``"my.config.object.myAwesomeDispatcher"``
|
||||||
|
|
||||||
|
There are two different executor services:
|
||||||
|
|
||||||
|
* executor = "fork-join-executor", ``ExecutorService`` based on ForkJoinPool (jsr166y). This is used by default for
|
||||||
|
``default-dispatcher``.
|
||||||
|
* executor = "thread-pool-executor", ``ExecutorService`` based on ``java.util.concurrent.ThreadPoolExecutor``.
|
||||||
|
|
||||||
|
Note that the pool size is configured differently for the two executor services. The configuration above
|
||||||
|
is an example for ``fork-join-executor``. Below is an example for ``thread-pool-executor``:
|
||||||
|
|
||||||
|
.. includecode:: ../scala/code/akka/docs/dispatcher/DispatcherDocSpec.scala#my-thread-pool-dispatcher-config
|
||||||
|
|
||||||
Let's now walk through the different dispatchers in more detail.
|
Let's now walk through the different dispatchers in more detail.
|
||||||
|
|
||||||
Thread-based
|
Thread-based
|
||||||
|
|
@ -67,9 +85,11 @@ has worse performance and scalability than the event-based dispatcher but works
|
||||||
a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with
|
a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with
|
||||||
this dispatcher is that Actors do not block threads for each other.
|
this dispatcher is that Actors do not block threads for each other.
|
||||||
|
|
||||||
The ``PinnedDispatcher`` can't be configured, but is created and associated with an actor like this:
|
The ``PinnedDispatcher`` is configured like this:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/dispatcher/DispatcherDocTestBase.java#defining-pinned-dispatcher
|
.. includecode:: ../scala/code/akka/docs/dispatcher/DispatcherDocSpec.scala#my-pinned-dispatcher-config
|
||||||
|
|
||||||
|
Note that it must be used with ``executor = "thread-pool-executor"``.
|
||||||
|
|
||||||
Event-based
|
Event-based
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
|
@ -23,3 +23,4 @@ Java API
|
||||||
transactors
|
transactors
|
||||||
fsm
|
fsm
|
||||||
extending-akka
|
extending-akka
|
||||||
|
zeromq
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ This config option is very good if you want to know what config settings are loa
|
||||||
akka {
|
akka {
|
||||||
# Log the complete configuration at INFO level when the actor system is started.
|
# Log the complete configuration at INFO level when the actor system is started.
|
||||||
# This is useful when you are uncertain of what configuration is used.
|
# This is useful when you are uncertain of what configuration is used.
|
||||||
logConfigOnStart = on
|
log-config-on-start = on
|
||||||
}
|
}
|
||||||
|
|
||||||
If you want very detailed logging of all automatically received messages that are processed
|
If you want very detailed logging of all automatically received messages that are processed
|
||||||
|
|
@ -218,13 +218,13 @@ Mapped Diagnostic Context (MDC) with attribute name ``sourceThread``.
|
||||||
With Logback the thread name is available with ``%X{sourceThread}`` specifier within the pattern layout configuration::
|
With Logback the thread name is available with ``%X{sourceThread}`` specifier within the pattern layout configuration::
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<layout>
|
<encoder>
|
||||||
<pattern>%date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n</pattern>
|
<pattern>%date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n</pattern>
|
||||||
</layout>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
It will probably be a good idea to use the ``sourceThread`` MDC value also in
|
It will probably be a good idea to use the ``sourceThread`` MDC value also in
|
||||||
non-Akka parts of the application in order to have this property consistently
|
non-Akka parts of the application in order to have this property consistently
|
||||||
available in the logs.
|
available in the logs.
|
||||||
|
|
@ -235,9 +235,9 @@ is available for associating log messages e.g. with members of a router. This
|
||||||
information is available in the MDC with attribute name ``akkaSource``::
|
information is available in the MDC with attribute name ``akkaSource``::
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<layout>
|
<encoder>
|
||||||
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
|
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
|
||||||
</layout>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
For more details on what this attribute contains—also for non-actors—please see
|
For more details on what this attribute contains—also for non-actors—please see
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ to your ``application.conf`` file::
|
||||||
}
|
}
|
||||||
remote {
|
remote {
|
||||||
transport = "akka.remote.netty.NettyRemoteTransport"
|
transport = "akka.remote.netty.NettyRemoteTransport"
|
||||||
server {
|
netty {
|
||||||
hostname = "127.0.0.1"
|
hostname = "127.0.0.1"
|
||||||
port = 2552
|
port = 2552
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,47 +25,39 @@ For Akka to know which ``Serializer`` to use for what, you need edit your :ref:`
|
||||||
in the "akka.actor.serializers"-section you bind names to implementations of the ``akka.serialization.Serializer``
|
in the "akka.actor.serializers"-section you bind names to implementations of the ``akka.serialization.Serializer``
|
||||||
you wish to use, like this:
|
you wish to use, like this:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialize-serializers-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialize-serializers-config
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The name ``default`` is special in the sense that the ``Serializer``
|
|
||||||
mapped to it will be used as default.
|
|
||||||
|
|
||||||
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
||||||
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialization-bindings-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
|
||||||
|
|
||||||
.. note::
|
You only need to specify the name of an interface or abstract base class of the
|
||||||
|
messages. In case of ambiguity, i.e. the message implements several of the
|
||||||
|
configured classes, the most specific configured class will be used, i.e. the
|
||||||
|
one of which all other candidates are superclasses. If this condition cannot be
|
||||||
|
met, because e.g. ``java.io.Serializable`` and ``MyOwnSerializable`` both apply
|
||||||
|
and neither is a subtype of the other, a warning will be issued.
|
||||||
|
|
||||||
You only need to specify the name of an interface or abstract base class if the messages implements
|
Akka provides serializers for :class:`java.io.Serializable` and `protobuf
|
||||||
that. E.g. ``com.google.protobuf.Message`` for protobuf serialization.
|
<http://code.google.com/p/protobuf/>`_
|
||||||
|
:class:`com.google.protobuf.GeneratedMessage` by default (the latter only if
|
||||||
|
depending on the akka-remote module), so normally you don't need to add
|
||||||
|
configuration for that; since :class:`com.google.protobuf.GeneratedMessage`
|
||||||
|
implements :class:`java.io.Serializable`, protobuf messages will always by
|
||||||
|
serialized using the protobuf protocol unless specifically overridden. In order
|
||||||
|
to disable a default serializer, map its marker type to “none”::
|
||||||
|
|
||||||
Protobuf
|
akka.actor.serialization-bindings {
|
||||||
--------
|
"java.io.Serializable" = none
|
||||||
|
}
|
||||||
Akka provides a ``Serializer`` for `protobuf <http://code.google.com/p/protobuf/>`_ messages.
|
|
||||||
To use that you need to add the following to the configuration::
|
|
||||||
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
proto = ["com.google.protobuf.Message"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Verification
|
Verification
|
||||||
------------
|
------------
|
||||||
|
|
||||||
If you want to verify that your messages are serializable you can enable the following config option:
|
If you want to verify that your messages are serializable you can enable the following config option:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialize-messages-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialize-messages-config
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
|
@ -74,7 +66,7 @@ If you want to verify that your messages are serializable you can enable the fol
|
||||||
|
|
||||||
If you want to verify that your ``Props`` are serializable you can enable the following config option:
|
If you want to verify that your ``Props`` are serializable you can enable the following config option:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java#serialize-creators-config
|
.. includecode:: ../scala/code/akka/docs/serialization/SerializationDocSpec.scala#serialize-creators-config
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
|
|
@ -122,3 +114,4 @@ reading in the representation of an :class:`ActorRef` for turning the string
|
||||||
representation into a real reference. :class:`DynamicVariable` is a
|
representation into a real reference. :class:`DynamicVariable` is a
|
||||||
thread-local variable, so be sure to have it set while deserializing anything
|
thread-local variable, so be sure to have it set while deserializing anything
|
||||||
which might contain actor references.
|
which might contain actor references.
|
||||||
|
|
||||||
|
|
|
||||||
98
akka-docs/java/zeromq.rst
Normal file
98
akka-docs/java/zeromq.rst
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
|
||||||
|
.. _zeromq-java:
|
||||||
|
|
||||||
|
###############
|
||||||
|
ZeroMQ (Java)
|
||||||
|
###############
|
||||||
|
|
||||||
|
.. sidebar:: Contents
|
||||||
|
|
||||||
|
.. contents:: :local:
|
||||||
|
|
||||||
|
Akka provides a ZeroMQ module which abstracts a ZeroMQ connection and therefore allows interaction between Akka actors to take place over ZeroMQ connections. The messages can be of a proprietary format or they can be defined using Protobuf. The socket actor is fault-tolerant by default and when you use the newSocket method to create new sockets it will properly reinitialize the socket.
|
||||||
|
|
||||||
|
ZeroMQ is very opinionated when it comes to multi-threading so configuration option `akka.zeromq.socket-dispatcher` always needs to be configured to a PinnedDispatcher, because the actual ZeroMQ socket can only be accessed by the thread that created it.
|
||||||
|
|
||||||
|
The ZeroMQ module for Akka is written against an API introduced in JZMQ, which uses JNI to interact with the native ZeroMQ library. Instead of using JZMQ, the module uses ZeroMQ binding for Scala that uses the native ZeroMQ library through JNA. In other words, the only native library that this module requires is the native ZeroMQ library.
|
||||||
|
The benefit of the scala library is that you don't need to compile and manage native dependencies at the cost of some runtime performance. The scala-bindings are compatible with the JNI bindings so they are a drop-in replacement, in case you really need to get that extra bit of performance out.
|
||||||
|
|
||||||
|
Connection
|
||||||
|
==========
|
||||||
|
|
||||||
|
ZeroMQ supports multiple connectivity patterns, each aimed to meet a different set of requirements. Currently, this module supports publisher-subscriber connections and connections based on dealers and routers. For connecting or accepting connections, a socket must be created.
|
||||||
|
Sockets are always created using the ``akka.zeromq.ZeroMQExtension``, for example:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#pub-socket
|
||||||
|
|
||||||
|
Above examples will create a ZeroMQ Publisher socket that is Bound to the port 1233 on localhost.
|
||||||
|
|
||||||
|
Similarly you can create a subscription socket, with a listener, that subscribes to all messages from the publisher using:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#sub-socket
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#listener-actor
|
||||||
|
|
||||||
|
The following sub-sections describe the supported connection patterns and how they can be used in an Akka environment. However, for a comprehensive discussion of connection patterns, please refer to `ZeroMQ -- The Guide <http://zguide.zeromq.org/page:all>`_.
|
||||||
|
|
||||||
|
Publisher-subscriber connection
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
In a publisher-subscriber (pub-sub) connection, the publisher accepts one or more subscribers. Each subscriber shall
|
||||||
|
subscribe to one or more topics, whereas the publisher publishes messages to a set of topics. Also, a subscriber can
|
||||||
|
subscribe to all available topics. In an Akka environment, pub-sub connections shall be used when an actor sends messages
|
||||||
|
to one or more actors that do not interact with the actor that sent the message.
|
||||||
|
|
||||||
|
When you're using zeromq pub/sub you should be aware that it needs multicast - check your cloud - to work properly and that the filtering of events for topics happens client side, so all events are always broadcasted to every subscriber.
|
||||||
|
|
||||||
|
An actor is subscribed to a topic as follows:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#sub-topic-socket
|
||||||
|
|
||||||
|
It is a prefix match so it is subscribed to all topics starting with ``foo.bar``. Note that if the given string is empty or
|
||||||
|
``Subscribe.all()`` is used, the actor is subscribed to all topics.
|
||||||
|
|
||||||
|
To unsubscribe from a topic you do the following:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#unsub-topic-socket
|
||||||
|
|
||||||
|
To publish messages to a topic you must use two Frames with the topic in the first frame.
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#pub-topic
|
||||||
|
|
||||||
|
Pub-Sub in Action
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following example illustrates one publisher with two subscribers.
|
||||||
|
|
||||||
|
The publisher monitors current heap usage and system load and periodically publishes ``Heap`` events on the ``"health.heap"`` topic
|
||||||
|
and ``Load`` events on the ``"health.load"`` topic.
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#health
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#health2
|
||||||
|
|
||||||
|
Let's add one subscriber that logs the information. It subscribes to all topics starting with ``"health"``, i.e. both ``Heap`` and
|
||||||
|
``Load`` events.
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#logger
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#logger2
|
||||||
|
|
||||||
|
Another subscriber keep track of used heap and warns if too much heap is used. It only subscribes to ``Heap`` events.
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#alerter
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#alerter2
|
||||||
|
|
||||||
|
Router-Dealer connection
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
While Pub/Sub is nice the real advantage of zeromq is that it is a "lego-box" for reliable messaging. And because there are so many integrations the multi-language support is fantastic.
|
||||||
|
When you're using ZeroMQ to integrate many systems you'll probably need to build your own ZeroMQ devices. This is where the router and dealer socket types come in handy.
|
||||||
|
With those socket types you can build your own reliable pub sub broker that uses TCP/IP and does publisher side filtering of events.
|
||||||
|
|
||||||
|
To create a Router socket that has a high watermark configured, you would do:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocTestBase.java#high-watermark
|
||||||
|
|
||||||
|
The akka-zeromq module accepts most if not all the available configuration options for a zeromq socket.
|
||||||
|
|
@ -23,7 +23,7 @@ object DurableMailboxDocSpec {
|
||||||
val config = """
|
val config = """
|
||||||
//#dispatcher-config
|
//#dispatcher-config
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.FileBasedMailboxType
|
mailbox-type = akka.actor.mailbox.FileBasedMailboxType
|
||||||
}
|
}
|
||||||
//#dispatcher-config
|
//#dispatcher-config
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ You configure durable mailboxes through the dispatcher, as described in
|
||||||
Config::
|
Config::
|
||||||
|
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.FileBasedMailboxType
|
mailbox-type = akka.actor.mailbox.FileBasedMailboxType
|
||||||
}
|
}
|
||||||
|
|
||||||
You can also configure and tune the file-based durable mailbox. This is done in
|
You can also configure and tune the file-based durable mailbox. This is done in
|
||||||
|
|
@ -124,7 +124,7 @@ You configure durable mailboxes through the dispatcher, as described in
|
||||||
Config::
|
Config::
|
||||||
|
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.RedisBasedMailboxType
|
mailbox-type = akka.actor.mailbox.RedisBasedMailboxType
|
||||||
}
|
}
|
||||||
|
|
||||||
You also need to configure the IP and port for the Redis server. This is done in
|
You also need to configure the IP and port for the Redis server. This is done in
|
||||||
|
|
@ -150,7 +150,7 @@ You configure durable mailboxes through the dispatcher, as described in
|
||||||
Config::
|
Config::
|
||||||
|
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.ZooKeeperBasedMailboxType
|
mailbox-type = akka.actor.mailbox.ZooKeeperBasedMailboxType
|
||||||
}
|
}
|
||||||
|
|
||||||
You also need to configure ZooKeeper server addresses, timeouts, etc. This is
|
You also need to configure ZooKeeper server addresses, timeouts, etc. This is
|
||||||
|
|
@ -173,7 +173,7 @@ You configure durable mailboxes through the dispatcher, as described in
|
||||||
Config::
|
Config::
|
||||||
|
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.BeanstalkBasedMailboxType
|
mailbox-type = akka.actor.mailbox.BeanstalkBasedMailboxType
|
||||||
}
|
}
|
||||||
|
|
||||||
You also need to configure the IP, and port, and so on, for the Beanstalk
|
You also need to configure the IP, and port, and so on, for the Beanstalk
|
||||||
|
|
@ -202,7 +202,7 @@ You configure durable mailboxes through the dispatcher, as described in
|
||||||
Config::
|
Config::
|
||||||
|
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.MongoBasedMailboxType
|
mailbox-type = akka.actor.mailbox.MongoBasedMailboxType
|
||||||
}
|
}
|
||||||
|
|
||||||
You will need to configure the URI for the MongoDB server, using the URI Format specified in the
|
You will need to configure the URI for the MongoDB server, using the URI Format specified in the
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ v1.3::
|
||||||
v2.0::
|
v2.0::
|
||||||
|
|
||||||
-Dconfig.file=<file path to configuration file>
|
-Dconfig.file=<file path to configuration file>
|
||||||
-Dakka.logConfigOnStart=on
|
-Dakka.log-config-on-start=on
|
||||||
|
|
||||||
|
|
||||||
Several configuration properties have been changed, such as:
|
Several configuration properties have been changed, such as:
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ case class Message(s: String)
|
||||||
class FirstActor extends Actor {
|
class FirstActor extends Actor {
|
||||||
val myActor = context.actorOf(Props[MyActor], name = "myactor")
|
val myActor = context.actorOf(Props[MyActor], name = "myactor")
|
||||||
//#context-actorOf
|
//#context-actorOf
|
||||||
|
def receive = {
|
||||||
|
case x ⇒ sender ! x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnonymousActor extends Actor {
|
||||||
//#anonymous-actor
|
//#anonymous-actor
|
||||||
def receive = {
|
def receive = {
|
||||||
case m: DoIt ⇒
|
case m: DoIt ⇒
|
||||||
|
|
@ -48,9 +54,7 @@ class FirstActor extends Actor {
|
||||||
context.stop(self)
|
context.stop(self)
|
||||||
}
|
}
|
||||||
def doSomeDangerousWork(msg: ImmutableMessage): String = { "done" }
|
def doSomeDangerousWork(msg: ImmutableMessage): String = { "done" }
|
||||||
})) ! m
|
})) forward m
|
||||||
|
|
||||||
case replyMsg: String ⇒ sender ! replyMsg
|
|
||||||
}
|
}
|
||||||
//#anonymous-actor
|
//#anonymous-actor
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +245,7 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||||
import akka.util.duration._
|
import akka.util.duration._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
implicit val timeout = Timeout(500 millis)
|
implicit val timeout = Timeout(5 seconds)
|
||||||
val future = myActor ? "hello"
|
val future = myActor ? "hello"
|
||||||
//#using-implicit-timeout
|
//#using-implicit-timeout
|
||||||
Await.result(future, timeout.duration) must be("hello")
|
Await.result(future, timeout.duration) must be("hello")
|
||||||
|
|
@ -253,9 +257,9 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||||
//#using-explicit-timeout
|
//#using-explicit-timeout
|
||||||
import akka.util.duration._
|
import akka.util.duration._
|
||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
val future = myActor.ask("hello")(500 millis)
|
val future = myActor.ask("hello")(5 seconds)
|
||||||
//#using-explicit-timeout
|
//#using-explicit-timeout
|
||||||
Await.result(future, 500 millis) must be("hello")
|
Await.result(future, 5 seconds) must be("hello")
|
||||||
}
|
}
|
||||||
|
|
||||||
"using receiveTimeout" in {
|
"using receiveTimeout" in {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,27 @@ object DispatcherDocSpec {
|
||||||
val config = """
|
val config = """
|
||||||
//#my-dispatcher-config
|
//#my-dispatcher-config
|
||||||
my-dispatcher {
|
my-dispatcher {
|
||||||
|
# Dispatcher is the name of the event-based dispatcher
|
||||||
|
type = Dispatcher
|
||||||
|
# What kind of ExecutionService to use
|
||||||
|
executor = "fork-join-executor"
|
||||||
|
# Configuration for the fork join pool
|
||||||
|
fork-join-executor {
|
||||||
|
# Min number of threads to cap factor-based parallelism number to
|
||||||
|
parallelism-min = 2
|
||||||
|
# Parallelism (threads) ... ceil(available processors * factor)
|
||||||
|
parallelism-factor = 2.0
|
||||||
|
# Max number of threads to cap factor-based parallelism number to
|
||||||
|
parallelism-max = 10
|
||||||
|
}
|
||||||
|
# Throughput defines the number of messages that are processed in a batch before the
|
||||||
|
# thread is returned to the pool. Set to 1 for as fair as possible.
|
||||||
|
throughput = 100
|
||||||
|
}
|
||||||
|
//#my-dispatcher-config
|
||||||
|
|
||||||
|
//#my-thread-pool-dispatcher-config
|
||||||
|
my-thread-pool-dispatcher {
|
||||||
# Dispatcher is the name of the event-based dispatcher
|
# Dispatcher is the name of the event-based dispatcher
|
||||||
type = Dispatcher
|
type = Dispatcher
|
||||||
# What kind of ExecutionService to use
|
# What kind of ExecutionService to use
|
||||||
|
|
@ -37,7 +58,14 @@ object DispatcherDocSpec {
|
||||||
# thread is returned to the pool. Set to 1 for as fair as possible.
|
# thread is returned to the pool. Set to 1 for as fair as possible.
|
||||||
throughput = 100
|
throughput = 100
|
||||||
}
|
}
|
||||||
//#my-dispatcher-config
|
//#my-thread-pool-dispatcher-config
|
||||||
|
|
||||||
|
//#my-pinned-dispatcher-config
|
||||||
|
my-pinned-dispatcher {
|
||||||
|
executor = "thread-pool-executor"
|
||||||
|
type = PinnedDispatcher
|
||||||
|
}
|
||||||
|
//#my-pinned-dispatcher-config
|
||||||
|
|
||||||
//#my-bounded-config
|
//#my-bounded-config
|
||||||
my-dispatcher-bounded-queue {
|
my-dispatcher-bounded-queue {
|
||||||
|
|
@ -66,13 +94,13 @@ object DispatcherDocSpec {
|
||||||
|
|
||||||
//#prio-dispatcher-config
|
//#prio-dispatcher-config
|
||||||
prio-dispatcher {
|
prio-dispatcher {
|
||||||
mailboxType = "akka.docs.dispatcher.DispatcherDocSpec$PrioMailbox"
|
mailbox-type = "akka.docs.dispatcher.DispatcherDocSpec$PrioMailbox"
|
||||||
}
|
}
|
||||||
//#prio-dispatcher-config
|
//#prio-dispatcher-config
|
||||||
|
|
||||||
//#prio-dispatcher-config-java
|
//#prio-dispatcher-config-java
|
||||||
prio-dispatcher-java {
|
prio-dispatcher-java {
|
||||||
mailboxType = "akka.docs.dispatcher.DispatcherDocTestBase$PrioMailbox"
|
mailbox-type = "akka.docs.dispatcher.DispatcherDocTestBase$PrioMailbox"
|
||||||
}
|
}
|
||||||
//#prio-dispatcher-config-java
|
//#prio-dispatcher-config-java
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ class MyOwnSerializer extends Serializer {
|
||||||
}
|
}
|
||||||
//#my-own-serializer
|
//#my-own-serializer
|
||||||
|
|
||||||
|
trait MyOwnSerializable
|
||||||
|
case class Customer(name: String) extends MyOwnSerializable
|
||||||
|
|
||||||
class SerializationDocSpec extends AkkaSpec {
|
class SerializationDocSpec extends AkkaSpec {
|
||||||
"demonstrate configuration of serialize messages" in {
|
"demonstrate configuration of serialize messages" in {
|
||||||
//#serialize-messages-config
|
//#serialize-messages-config
|
||||||
|
|
@ -81,8 +84,8 @@ class SerializationDocSpec extends AkkaSpec {
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serializers {
|
serializers {
|
||||||
default = "akka.serialization.JavaSerializer"
|
java = "akka.serialization.JavaSerializer"
|
||||||
|
proto = "akka.serialization.ProtobufSerializer"
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
myown = "akka.docs.serialization.MyOwnSerializer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,8 +93,6 @@ class SerializationDocSpec extends AkkaSpec {
|
||||||
""")
|
""")
|
||||||
//#serialize-serializers-config
|
//#serialize-serializers-config
|
||||||
val a = ActorSystem("system", config)
|
val a = ActorSystem("system", config)
|
||||||
SerializationExtension(a).serializers("default").getClass.getName must equal("akka.serialization.JavaSerializer")
|
|
||||||
SerializationExtension(a).serializers("myown").getClass.getName must equal("akka.docs.serialization.MyOwnSerializer")
|
|
||||||
a.shutdown()
|
a.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,31 +102,26 @@ class SerializationDocSpec extends AkkaSpec {
|
||||||
akka {
|
akka {
|
||||||
actor {
|
actor {
|
||||||
serializers {
|
serializers {
|
||||||
default = "akka.serialization.JavaSerializer"
|
|
||||||
java = "akka.serialization.JavaSerializer"
|
java = "akka.serialization.JavaSerializer"
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
proto = "akka.serialization.ProtobufSerializer"
|
||||||
myown = "akka.docs.serialization.MyOwnSerializer"
|
myown = "akka.docs.serialization.MyOwnSerializer"
|
||||||
}
|
}
|
||||||
|
|
||||||
serialization-bindings {
|
serialization-bindings {
|
||||||
java = ["java.lang.String",
|
"java.lang.String" = java
|
||||||
"app.my.Customer"]
|
"akka.docs.serialization.Customer" = java
|
||||||
proto = ["com.google.protobuf.Message"]
|
"com.google.protobuf.Message" = proto
|
||||||
myown = ["my.own.BusinessObject",
|
"akka.docs.serialization.MyOwnSerializable" = myown
|
||||||
"something.equally.Awesome",
|
"java.lang.Boolean" = myown
|
||||||
"akka.docs.serialization.MyOwnSerializable"
|
}
|
||||||
"java.lang.Boolean"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
//#serialization-bindings-config
|
//#serialization-bindings-config
|
||||||
val a = ActorSystem("system", config)
|
val a = ActorSystem("system", config)
|
||||||
SerializationExtension(a).serializers("default").getClass.getName must equal("akka.serialization.JavaSerializer")
|
SerializationExtension(a).serializerFor(classOf[String]).getClass must equal(classOf[JavaSerializer])
|
||||||
SerializationExtension(a).serializers("java").getClass.getName must equal("akka.serialization.JavaSerializer")
|
SerializationExtension(a).serializerFor(classOf[Customer]).getClass must equal(classOf[JavaSerializer])
|
||||||
SerializationExtension(a).serializers("myown").getClass.getName must equal("akka.docs.serialization.MyOwnSerializer")
|
SerializationExtension(a).serializerFor(classOf[java.lang.Boolean]).getClass must equal(classOf[MyOwnSerializer])
|
||||||
SerializationExtension(a).serializerFor(classOf[String]).getClass.getName must equal("akka.serialization.JavaSerializer")
|
|
||||||
SerializationExtension(a).serializerFor(classOf[java.lang.Boolean]).getClass.getName must equal("akka.docs.serialization.MyOwnSerializer")
|
|
||||||
a.shutdown()
|
a.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
195
akka-docs/scala/code/akka/docs/zeromq/ZeromqDocSpec.scala
Normal file
195
akka-docs/scala/code/akka/docs/zeromq/ZeromqDocSpec.scala
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
package akka.docs.zeromq
|
||||||
|
|
||||||
|
import akka.actor.Actor
|
||||||
|
import akka.actor.Props
|
||||||
|
import akka.util.duration._
|
||||||
|
import akka.testkit._
|
||||||
|
import akka.zeromq.ZeroMQVersion
|
||||||
|
import akka.zeromq.ZeroMQExtension
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import akka.zeromq.SocketType
|
||||||
|
import akka.zeromq.Bind
|
||||||
|
|
||||||
|
object ZeromqDocSpec {
|
||||||
|
|
||||||
|
//#health
|
||||||
|
import akka.zeromq._
|
||||||
|
import akka.actor.Actor
|
||||||
|
import akka.actor.Props
|
||||||
|
import akka.actor.ActorLogging
|
||||||
|
import akka.serialization.SerializationExtension
|
||||||
|
import java.lang.management.ManagementFactory
|
||||||
|
|
||||||
|
case object Tick
|
||||||
|
case class Heap(timestamp: Long, used: Long, max: Long)
|
||||||
|
case class Load(timestamp: Long, loadAverage: Double)
|
||||||
|
|
||||||
|
class HealthProbe extends Actor {
|
||||||
|
|
||||||
|
val pubSocket = context.system.newSocket(SocketType.Pub, Bind("tcp://127.0.0.1:1235"))
|
||||||
|
val memory = ManagementFactory.getMemoryMXBean
|
||||||
|
val os = ManagementFactory.getOperatingSystemMXBean
|
||||||
|
val ser = SerializationExtension(context.system)
|
||||||
|
|
||||||
|
override def preStart() {
|
||||||
|
context.system.scheduler.schedule(1 second, 1 second, self, Tick)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def postRestart(reason: Throwable) {
|
||||||
|
// don't call preStart, only schedule once
|
||||||
|
}
|
||||||
|
|
||||||
|
def receive: Receive = {
|
||||||
|
case Tick ⇒
|
||||||
|
val currentHeap = memory.getHeapMemoryUsage
|
||||||
|
val timestamp = System.currentTimeMillis
|
||||||
|
|
||||||
|
// use akka SerializationExtension to convert to bytes
|
||||||
|
val heapPayload = ser.serialize(Heap(timestamp, currentHeap.getUsed, currentHeap.getMax)).fold(throw _, identity)
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
pubSocket ! ZMQMessage(Seq(Frame("health.heap"), Frame(heapPayload)))
|
||||||
|
|
||||||
|
// use akka SerializationExtension to convert to bytes
|
||||||
|
val loadPayload = ser.serialize(Load(timestamp, os.getSystemLoadAverage)).fold(throw _, identity)
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
pubSocket ! ZMQMessage(Seq(Frame("health.load"), Frame(loadPayload)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#health
|
||||||
|
|
||||||
|
//#logger
|
||||||
|
class Logger extends Actor with ActorLogging {
|
||||||
|
|
||||||
|
context.system.newSocket(SocketType.Sub, Listener(self), Connect("tcp://127.0.0.1:1235"), Subscribe("health"))
|
||||||
|
val ser = SerializationExtension(context.system)
|
||||||
|
val timestampFormat = new SimpleDateFormat("HH:mm:ss.SSS")
|
||||||
|
|
||||||
|
def receive = {
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
case m: ZMQMessage if m.firstFrameAsString == "health.heap" ⇒
|
||||||
|
ser.deserialize(m.payload(1), classOf[Heap], None) match {
|
||||||
|
case Right(Heap(timestamp, used, max)) ⇒
|
||||||
|
log.info("Used heap {} bytes, at {}", used, timestampFormat.format(new Date(timestamp)))
|
||||||
|
case Left(e) ⇒ throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
case m: ZMQMessage if m.firstFrameAsString == "health.load" ⇒
|
||||||
|
ser.deserialize(m.payload(1), classOf[Load], None) match {
|
||||||
|
case Right(Load(timestamp, loadAverage)) ⇒
|
||||||
|
log.info("Load average {}, at {}", loadAverage, timestampFormat.format(new Date(timestamp)))
|
||||||
|
case Left(e) ⇒ throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#logger
|
||||||
|
|
||||||
|
//#alerter
|
||||||
|
class HeapAlerter extends Actor with ActorLogging {
|
||||||
|
|
||||||
|
context.system.newSocket(SocketType.Sub, Listener(self), Connect("tcp://127.0.0.1:1235"), Subscribe("health.heap"))
|
||||||
|
val ser = SerializationExtension(context.system)
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
def receive = {
|
||||||
|
// the first frame is the topic, second is the message
|
||||||
|
case m: ZMQMessage if m.firstFrameAsString == "health.heap" ⇒
|
||||||
|
ser.deserialize(m.payload(1), classOf[Heap], None) match {
|
||||||
|
case Right(Heap(timestamp, used, max)) ⇒
|
||||||
|
if ((used.toDouble / max) > 0.9) count += 1
|
||||||
|
else count = 0
|
||||||
|
if (count > 10) log.warning("Need more memory, using {} %", (100.0 * used / max))
|
||||||
|
case Left(e) ⇒ throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#alerter
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ZeromqDocSpec extends AkkaSpec("akka.loglevel=INFO") {
|
||||||
|
import ZeromqDocSpec._
|
||||||
|
|
||||||
|
"demonstrate how to create socket" in {
|
||||||
|
checkZeroMQInstallation()
|
||||||
|
|
||||||
|
//#pub-socket
|
||||||
|
import akka.zeromq.ZeroMQExtension
|
||||||
|
val pubSocket = ZeroMQExtension(system).newSocket(SocketType.Pub, Bind("tcp://127.0.0.1:1234"))
|
||||||
|
//#pub-socket
|
||||||
|
|
||||||
|
//#pub-socket2
|
||||||
|
import akka.zeromq._
|
||||||
|
val pubSocket2 = system.newSocket(SocketType.Pub, Bind("tcp://127.0.0.1:1234"))
|
||||||
|
//#pub-socket2
|
||||||
|
|
||||||
|
//#sub-socket
|
||||||
|
import akka.zeromq._
|
||||||
|
val listener = system.actorOf(Props(new Actor {
|
||||||
|
def receive: Receive = {
|
||||||
|
case Connecting ⇒ //...
|
||||||
|
case m: ZMQMessage ⇒ //...
|
||||||
|
case _ ⇒ //...
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
val subSocket = system.newSocket(SocketType.Sub, Listener(listener), Connect("tcp://127.0.0.1:1234"), SubscribeAll)
|
||||||
|
//#sub-socket
|
||||||
|
|
||||||
|
//#sub-topic-socket
|
||||||
|
val subTopicSocket = system.newSocket(SocketType.Sub, Listener(listener), Connect("tcp://127.0.0.1:1234"), Subscribe("foo.bar"))
|
||||||
|
//#sub-topic-socket
|
||||||
|
|
||||||
|
//#unsub-topic-socket
|
||||||
|
subTopicSocket ! Unsubscribe("foo.bar")
|
||||||
|
//#unsub-topic-socket
|
||||||
|
|
||||||
|
val payload = Array.empty[Byte]
|
||||||
|
//#pub-topic
|
||||||
|
pubSocket ! ZMQMessage(Seq(Frame("foo.bar"), Frame(payload)))
|
||||||
|
//#pub-topic
|
||||||
|
|
||||||
|
//#high-watermark
|
||||||
|
val highWatermarkSocket = system.newSocket(
|
||||||
|
SocketType.Router,
|
||||||
|
Listener(listener),
|
||||||
|
Bind("tcp://127.0.0.1:1234"),
|
||||||
|
HighWatermark(50000))
|
||||||
|
//#high-watermark
|
||||||
|
}
|
||||||
|
|
||||||
|
"demonstrate pub-sub" in {
|
||||||
|
checkZeroMQInstallation()
|
||||||
|
|
||||||
|
//#health
|
||||||
|
|
||||||
|
system.actorOf(Props[HealthProbe], name = "health")
|
||||||
|
//#health
|
||||||
|
|
||||||
|
//#logger
|
||||||
|
|
||||||
|
system.actorOf(Props[Logger], name = "logger")
|
||||||
|
//#logger
|
||||||
|
|
||||||
|
//#alerter
|
||||||
|
|
||||||
|
system.actorOf(Props[HeapAlerter], name = "alerter")
|
||||||
|
//#alerter
|
||||||
|
|
||||||
|
// Let it run for a while to see some output.
|
||||||
|
// Don't do like this in real tests, this is only doc demonstration.
|
||||||
|
3.seconds.sleep()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def checkZeroMQInstallation() = try {
|
||||||
|
ZeroMQExtension(system).version match {
|
||||||
|
case ZeroMQVersion(2, 1, _) ⇒ Unit
|
||||||
|
case version ⇒ pending
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: LinkageError ⇒ pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -54,6 +54,24 @@ Default values are taken from ``default-dispatcher``, i.e. all options doesn't n
|
||||||
:ref:`configuration` for the default values of the ``default-dispatcher``. You can also override
|
:ref:`configuration` for the default values of the ``default-dispatcher``. You can also override
|
||||||
the values for the ``default-dispatcher`` in your configuration.
|
the values for the ``default-dispatcher`` in your configuration.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
It should be noted that the ``dispatcher-id`` used in :class:`Props` is in
|
||||||
|
fact an absolute path into the configuration object, i.e. you can declare a
|
||||||
|
dispatcher configuration nested within other configuration objects and refer
|
||||||
|
to it like so: ``"my.config.object.myAwesomeDispatcher"``
|
||||||
|
|
||||||
|
There are two different executor services:
|
||||||
|
|
||||||
|
* executor = "fork-join-executor", ``ExecutorService`` based on ForkJoinPool (jsr166y). This is used by default for
|
||||||
|
``default-dispatcher``.
|
||||||
|
* executor = "thread-pool-executor", ``ExecutorService`` based on ``java.util.concurrent.ThreadPoolExecutor``.
|
||||||
|
|
||||||
|
Note that the pool size is configured differently for the two executor services. The configuration above
|
||||||
|
is an example for ``fork-join-executor``. Below is an example for ``thread-pool-executor``:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/dispatcher/DispatcherDocSpec.scala#my-thread-pool-dispatcher-config
|
||||||
|
|
||||||
Let's now walk through the different dispatchers in more detail.
|
Let's now walk through the different dispatchers in more detail.
|
||||||
|
|
||||||
Thread-based
|
Thread-based
|
||||||
|
|
@ -66,9 +84,11 @@ has worse performance and scalability than the event-based dispatcher but works
|
||||||
a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with
|
a low frequency of messages and are allowed to go off and do their own thing for a longer period of time. Another advantage with
|
||||||
this dispatcher is that Actors do not block threads for each other.
|
this dispatcher is that Actors do not block threads for each other.
|
||||||
|
|
||||||
The ``PinnedDispatcher`` can't be configured, but is created and associated with an actor like this:
|
The ``PinnedDispatcher`` is configured like this:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/dispatcher/DispatcherDocSpec.scala#defining-pinned-dispatcher
|
.. includecode:: code/akka/docs/dispatcher/DispatcherDocSpec.scala#my-pinned-dispatcher-config
|
||||||
|
|
||||||
|
Note that it must be used with ``executor = "thread-pool-executor"``.
|
||||||
|
|
||||||
Event-based
|
Event-based
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ This config option is very good if you want to know what config settings are loa
|
||||||
akka {
|
akka {
|
||||||
# Log the complete configuration at INFO level when the actor system is started.
|
# Log the complete configuration at INFO level when the actor system is started.
|
||||||
# This is useful when you are uncertain of what configuration is used.
|
# This is useful when you are uncertain of what configuration is used.
|
||||||
logConfigOnStart = on
|
log-config-on-start = on
|
||||||
}
|
}
|
||||||
|
|
||||||
If you want very detailed logging of all user-level messages that are processed
|
If you want very detailed logging of all user-level messages that are processed
|
||||||
|
|
@ -251,13 +251,13 @@ Mapped Diagnostic Context (MDC) with attribute name ``sourceThread``.
|
||||||
With Logback the thread name is available with ``%X{sourceThread}`` specifier within the pattern layout configuration::
|
With Logback the thread name is available with ``%X{sourceThread}`` specifier within the pattern layout configuration::
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<layout>
|
<encoder>
|
||||||
<pattern>%date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n</pattern>
|
<pattern>%date{ISO8601} %-5level %logger{36} %X{sourceThread} - %msg%n</pattern>
|
||||||
</layout>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
It will probably be a good idea to use the ``sourceThread`` MDC value also in
|
It will probably be a good idea to use the ``sourceThread`` MDC value also in
|
||||||
non-Akka parts of the application in order to have this property consistently
|
non-Akka parts of the application in order to have this property consistently
|
||||||
available in the logs.
|
available in the logs.
|
||||||
|
|
@ -268,9 +268,9 @@ is available for associating log messages e.g. with members of a router. This
|
||||||
information is available in the MDC with attribute name ``akkaSource``::
|
information is available in the MDC with attribute name ``akkaSource``::
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<layout>
|
<encoder>
|
||||||
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
|
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
|
||||||
</layout>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
For more details on what this attribute contains—also for non-actors—please see
|
For more details on what this attribute contains—also for non-actors—please see
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ to your ``application.conf`` file::
|
||||||
}
|
}
|
||||||
remote {
|
remote {
|
||||||
transport = "akka.remote.netty.NettyRemoteTransport"
|
transport = "akka.remote.netty.NettyRemoteTransport"
|
||||||
server {
|
netty {
|
||||||
hostname = "127.0.0.1"
|
hostname = "127.0.0.1"
|
||||||
port = 2552
|
port = 2552
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,38 +27,30 @@ you wish to use, like this:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialize-serializers-config
|
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialize-serializers-config
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The name ``default`` is special in the sense that the ``Serializer``
|
|
||||||
mapped to it will be used as default.
|
|
||||||
|
|
||||||
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
After you've bound names to different implementations of ``Serializer`` you need to wire which classes
|
||||||
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
should be serialized using which ``Serializer``, this is done in the "akka.actor.serialization-bindings"-section:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
|
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala#serialization-bindings-config
|
||||||
|
|
||||||
.. note::
|
You only need to specify the name of an interface or abstract base class of the
|
||||||
|
messages. In case of ambiguity, i.e. the message implements several of the
|
||||||
|
configured classes, the most specific configured class will be used, i.e. the
|
||||||
|
one of which all other candidates are superclasses. If this condition cannot be
|
||||||
|
met, because e.g. ``java.io.Serializable`` and ``MyOwnSerializable`` both apply
|
||||||
|
and neither is a subtype of the other, a warning will be issued
|
||||||
|
|
||||||
You only need to specify the name of an interface or abstract base class if the messages implements
|
Akka provides serializers for :class:`java.io.Serializable` and `protobuf
|
||||||
that. E.g. ``com.google.protobuf.Message`` for protobuf serialization.
|
<http://code.google.com/p/protobuf/>`_
|
||||||
|
:class:`com.google.protobuf.GeneratedMessage` by default (the latter only if
|
||||||
|
depending on the akka-remote module), so normally you don't need to add
|
||||||
|
configuration for that; since :class:`com.google.protobuf.GeneratedMessage`
|
||||||
|
implements :class:`java.io.Serializable`, protobuf messages will always by
|
||||||
|
serialized using the protobuf protocol unless specifically overridden. In order
|
||||||
|
to disable a default serializer, map its marker type to “none”::
|
||||||
|
|
||||||
Protobuf
|
akka.actor.serialization-bindings {
|
||||||
--------
|
"java.io.Serializable" = none
|
||||||
|
}
|
||||||
Akka provides a ``Serializer`` for `protobuf <http://code.google.com/p/protobuf/>`_ messages.
|
|
||||||
To use that you need to add the following to the configuration::
|
|
||||||
|
|
||||||
akka {
|
|
||||||
actor {
|
|
||||||
serializers {
|
|
||||||
proto = "akka.serialization.ProtobufSerializer"
|
|
||||||
}
|
|
||||||
|
|
||||||
serialization-bindings {
|
|
||||||
proto = ["com.google.protobuf.Message"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Verification
|
Verification
|
||||||
------------
|
------------
|
||||||
|
|
@ -120,3 +112,4 @@ reading in the representation of an :class:`ActorRef` for turning the string
|
||||||
representation into a real reference. :class:`DynamicVariable` is a
|
representation into a real reference. :class:`DynamicVariable` is a
|
||||||
thread-local variable, so be sure to have it set while deserializing anything
|
thread-local variable, so be sure to have it set while deserializing anything
|
||||||
which might contain actor references.
|
which might contain actor references.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
|
||||||
.. _zeromq-module:
|
.. _zeromq-scala:
|
||||||
|
|
||||||
|
################
|
||||||
|
ZeroMQ (Scala)
|
||||||
|
################
|
||||||
|
|
||||||
ZeroMQ
|
|
||||||
======
|
|
||||||
|
|
||||||
.. sidebar:: Contents
|
.. sidebar:: Contents
|
||||||
|
|
||||||
|
|
@ -12,83 +14,76 @@ Akka provides a ZeroMQ module which abstracts a ZeroMQ connection and therefore
|
||||||
|
|
||||||
ZeroMQ is very opinionated when it comes to multi-threading so configuration option `akka.zeromq.socket-dispatcher` always needs to be configured to a PinnedDispatcher, because the actual ZeroMQ socket can only be accessed by the thread that created it.
|
ZeroMQ is very opinionated when it comes to multi-threading so configuration option `akka.zeromq.socket-dispatcher` always needs to be configured to a PinnedDispatcher, because the actual ZeroMQ socket can only be accessed by the thread that created it.
|
||||||
|
|
||||||
The ZeroMQ module for Akka is written against an API introduced in JZMQ, which uses JNI to interact with the native ZeroMQ library. Instead of using JZMQ, the module uses ZeroMQ binding for Scala that uses the native ZeroMQ library through JNA. In other words, the only native library that this module requires is the native ZeroMQ library.
|
The ZeroMQ module for Akka is written against an API introduced in JZMQ, which uses JNI to interact with the native ZeroMQ library. Instead of using JZMQ, the module uses ZeroMQ binding for Scala that uses the native ZeroMQ library through JNA. In other words, the only native library that this module requires is the native ZeroMQ library.
|
||||||
The benefit of the scala library is that you don't need to compile and manage native dependencies at the cost of some runtime performance. The scala-bindings are compatible with the JNI bindings so they are a drop-in replacement, in case you really need to get that extra bit of performance out.
|
The benefit of the scala library is that you don't need to compile and manage native dependencies at the cost of some runtime performance. The scala-bindings are compatible with the JNI bindings so they are a drop-in replacement, in case you really need to get that extra bit of performance out.
|
||||||
|
|
||||||
Connection
|
Connection
|
||||||
----------
|
==========
|
||||||
|
|
||||||
ZeroMQ supports multiple connectivity patterns, each aimed to meet a different set of requirements. Currently, this module supports publisher-subscriber connections and connections based on dealers and routers. For connecting or accepting connections, a socket must be created. Sockets are always created using ``akka.zeromq.ZeroMQ.newSocket``, for example:
|
ZeroMQ supports multiple connectivity patterns, each aimed to meet a different set of requirements. Currently, this module supports publisher-subscriber connections and connections based on dealers and routers. For connecting or accepting connections, a socket must be created.
|
||||||
|
Sockets are always created using the ``akka.zeromq.ZeroMQExtension``, for example:
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#pub-socket
|
||||||
|
|
||||||
import akka.zeromq._
|
or by importing the ``akka.zeromq._`` package to make newSocket method available on system, via an implicit conversion.
|
||||||
val socket = system.zeromq.newSocket(SocketType.Pub, Bind("tcp://127.0.0.1:1234"))
|
|
||||||
|
|
||||||
will create a ZeroMQ Publisher socket that is Bound to the port 1234 on localhost.
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#pub-socket2
|
||||||
Importing the akka.zeromq._ package ensures that the implicit zeromq method is available.
|
|
||||||
Similarly you can create a subscription socket, that subscribes to all messages from the publisher using:
|
|
||||||
|
|
||||||
.. code-block:: scala
|
|
||||||
|
|
||||||
val socket = system.zeromq.newSocket(SocketType.Sub, Connect("tcp://127.0.0.1:1234"), SubscribeAll)
|
Above examples will create a ZeroMQ Publisher socket that is Bound to the port 1234 on localhost.
|
||||||
|
|
||||||
Also, a socket may be created with a listener that handles received messages as well as notifications:
|
Similarly you can create a subscription socket, with a listener, that subscribes to all messages from the publisher using:
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#sub-socket
|
||||||
|
|
||||||
val listener = system.actorOf(Props(new Actor {
|
|
||||||
def receive: Receive = {
|
|
||||||
case Connecting => ...
|
|
||||||
case _ => ...
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
val socket = system.zeromq.newSocket(SocketType.Router, Listener(listener), Connect("tcp://localhost:1234"))
|
|
||||||
|
|
||||||
The following sub-sections describe the supported connection patterns and how they can be used in an Akka environment. However, for a comprehensive discussion of connection patterns, please refer to `ZeroMQ -- The Guide <http://zguide.zeromq.org/page:all>`_.
|
The following sub-sections describe the supported connection patterns and how they can be used in an Akka environment. However, for a comprehensive discussion of connection patterns, please refer to `ZeroMQ -- The Guide <http://zguide.zeromq.org/page:all>`_.
|
||||||
|
|
||||||
Publisher-subscriber connection
|
Publisher-subscriber connection
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-------------------------------
|
||||||
|
|
||||||
In a publisher-subscriber (pub-sub) connection, the publisher accepts one or more subscribers. Each subscriber shall subscribe to one or more topics, whereas the publisher publishes messages to a set of topics. Also, a subscriber can subscribe to all available topics.
|
In a publisher-subscriber (pub-sub) connection, the publisher accepts one or more subscribers. Each subscriber shall
|
||||||
|
subscribe to one or more topics, whereas the publisher publishes messages to a set of topics. Also, a subscriber can
|
||||||
|
subscribe to all available topics. In an Akka environment, pub-sub connections shall be used when an actor sends messages
|
||||||
|
to one or more actors that do not interact with the actor that sent the message.
|
||||||
|
|
||||||
When you're using zeromq pub/sub you should be aware that it needs multicast - check your cloud - to work properly and that the filtering of events for topics happens client side, so all events are always broadcasted to every subscriber.
|
When you're using zeromq pub/sub you should be aware that it needs multicast - check your cloud - to work properly and that the filtering of events for topics happens client side, so all events are always broadcasted to every subscriber.
|
||||||
|
|
||||||
An actor is subscribed to a topic as follows:
|
An actor is subscribed to a topic as follows:
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#sub-topic-socket
|
||||||
|
|
||||||
val socket = system.zeromq.newSocket(SocketType.Sub, Listener(listener), Connect("tcp://localhost:1234"), Subscribe("the-topic"))
|
It is a prefix match so it is subscribed to all topics starting with ``foo.bar``. Note that if the given string is empty or
|
||||||
|
``SubscribeAll`` is used, the actor is subscribed to all topics.
|
||||||
|
|
||||||
Note that if the given string is empty (see below), the actor is subscribed to all topics. To unsubscribe from a topic you do the following:
|
To unsubscribe from a topic you do the following:
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#unsub-topic-socket
|
||||||
|
|
||||||
socket ! Unsubscribe("SomeTopic1")
|
To publish messages to a topic you must use two Frames with the topic in the first frame.
|
||||||
|
|
||||||
In an Akka environment, pub-sub connections shall be used when an actor sends messages to one or more actors that do not interact with the actor that sent the message. The following piece of code creates a publisher actor, binds the socket, and sends a message to be published:
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#pub-topic
|
||||||
|
|
||||||
.. code-block:: scala
|
Pub-Sub in Action
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
import akka.zeromq._
|
The following example illustrates one publisher with two subscribers.
|
||||||
val socket = system.zeromq.newSocket(SocketType.Pub, Bind("tcp://127.0.0.1:1234"))
|
|
||||||
socket ! Send("hello".getBytes)
|
|
||||||
|
|
||||||
In the following code, the subscriber is configured to receive messages for all topics:
|
The publisher monitors current heap usage and system load and periodically publishes ``Heap`` events on the ``"health.heap"`` topic
|
||||||
|
and ``Load`` events on the ``"health.load"`` topic.
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#health
|
||||||
|
|
||||||
import akka.zeromq._
|
Let's add one subscriber that logs the information. It subscribes to all topics starting with ``"health"``, i.e. both ``Heap`` and
|
||||||
val listener = system.actorOf(Props(new Actor {
|
``Load`` events.
|
||||||
def receive: Receive = {
|
|
||||||
case Connecting => ...
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#logger
|
||||||
case _ => ...
|
|
||||||
}
|
Another subscriber keep track of used heap and warns if too much heap is used. It only subscribes to ``Heap`` events.
|
||||||
}))
|
|
||||||
val socket = system.zeromq.newSocket(SocketType.Sub, Listener(listener), Connect("tcp://127.0.0.1:1234"), SubscribeAll)
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#alerter
|
||||||
|
|
||||||
Router-Dealer connection
|
Router-Dealer connection
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
------------------------
|
||||||
|
|
||||||
While Pub/Sub is nice the real advantage of zeromq is that it is a "lego-box" for reliable messaging. And because there are so many integrations the multi-language support is fantastic.
|
While Pub/Sub is nice the real advantage of zeromq is that it is a "lego-box" for reliable messaging. And because there are so many integrations the multi-language support is fantastic.
|
||||||
When you're using ZeroMQ to integrate many systems you'll probably need to build your own ZeroMQ devices. This is where the router and dealer socket types come in handy.
|
When you're using ZeroMQ to integrate many systems you'll probably need to build your own ZeroMQ devices. This is where the router and dealer socket types come in handy.
|
||||||
|
|
@ -96,19 +91,6 @@ With those socket types you can build your own reliable pub sub broker that uses
|
||||||
|
|
||||||
To create a Router socket that has a high watermark configured, you would do:
|
To create a Router socket that has a high watermark configured, you would do:
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: code/akka/docs/zeromq/ZeromqDocSpec.scala#high-watermark
|
||||||
|
|
||||||
import akka.zeromq._
|
|
||||||
val listener = system.actorOf(Props(new Actor {
|
|
||||||
def receive: Receive = {
|
|
||||||
case Connecting => ...
|
|
||||||
case _ => ...
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
val socket = system.zeromq.newSocket(
|
|
||||||
SocketType.Router,
|
|
||||||
Listener(listener),
|
|
||||||
Bind("tcp://127.0.0.1:1234"),
|
|
||||||
HWM(50000))
|
|
||||||
|
|
||||||
The akka-zeromq module accepts most if not all the available configuration options for a zeromq socket.
|
The akka-zeromq module accepts most if not all the available configuration options for a zeromq socket.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package akka.actor.mailbox
|
||||||
object BeanstalkBasedMailboxSpec {
|
object BeanstalkBasedMailboxSpec {
|
||||||
val config = """
|
val config = """
|
||||||
Beanstalkd-dispatcher {
|
Beanstalkd-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.BeanstalkBasedMailboxType
|
mailbox-type = akka.actor.mailbox.BeanstalkBasedMailboxType
|
||||||
throughput = 1
|
throughput = 1
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import org.apache.commons.io.FileUtils
|
||||||
object FileBasedMailboxSpec {
|
object FileBasedMailboxSpec {
|
||||||
val config = """
|
val config = """
|
||||||
File-dispatcher {
|
File-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.FileBasedMailboxType
|
mailbox-type = akka.actor.mailbox.FileBasedMailboxType
|
||||||
throughput = 1
|
throughput = 1
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import akka.dispatch.MessageDispatcher
|
||||||
object MongoBasedMailboxSpec {
|
object MongoBasedMailboxSpec {
|
||||||
val config = """
|
val config = """
|
||||||
mongodb-dispatcher {
|
mongodb-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.MongoBasedMailboxType
|
mailbox-type = akka.actor.mailbox.MongoBasedMailboxType
|
||||||
throughput = 1
|
throughput = 1
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package akka.actor.mailbox
|
||||||
object RedisBasedMailboxSpec {
|
object RedisBasedMailboxSpec {
|
||||||
val config = """
|
val config = """
|
||||||
Redis-dispatcher {
|
Redis-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.RedisBasedMailboxType
|
mailbox-type = akka.actor.mailbox.RedisBasedMailboxType
|
||||||
throughput = 1
|
throughput = 1
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import akka.actor.ActorRef
|
||||||
object ZooKeeperBasedMailboxSpec {
|
object ZooKeeperBasedMailboxSpec {
|
||||||
val config = """
|
val config = """
|
||||||
ZooKeeper-dispatcher {
|
ZooKeeper-dispatcher {
|
||||||
mailboxType = akka.actor.mailbox.ZooKeeperBasedMailboxType
|
mailbox-type = akka.actor.mailbox.ZooKeeperBasedMailboxType
|
||||||
throughput = 1
|
throughput = 1
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
# In this file you can override any option defined in the 'reference.conf' files.
|
# In this file you can override any option defined in the 'reference.conf' files.
|
||||||
# Copy in all or parts of the 'reference.conf' files and modify as you please.
|
# Copy in all or parts of the 'reference.conf' files and modify as you please.
|
||||||
|
# For more info about config, please visit the Akka Documentation: http://akka.io/docs/akka/2.0-SNAPSHOT/
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,24 @@
|
||||||
# This the reference config file has all the default settings.
|
# This the reference config file has all the default settings.
|
||||||
# Make your edits/overrides in your application.conf.
|
# Make your edits/overrides in your application.conf.
|
||||||
|
|
||||||
|
# comments above akka.actor settings left out where they are already in akka-
|
||||||
|
# actor.jar, because otherwise they would be repeated in config rendering.
|
||||||
|
|
||||||
akka {
|
akka {
|
||||||
|
|
||||||
actor {
|
actor {
|
||||||
|
|
||||||
|
serializers {
|
||||||
|
proto = "akka.serialization.ProtobufSerializer"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
serialization-bindings {
|
||||||
|
# Since com.google.protobuf.Message does not extend Serializable but GeneratedMessage
|
||||||
|
# does, need to use the more specific one here in order to avoid ambiguity
|
||||||
|
"com.google.protobuf.GeneratedMessage" = proto
|
||||||
|
}
|
||||||
|
|
||||||
deployment {
|
deployment {
|
||||||
|
|
||||||
default {
|
default {
|
||||||
|
|
@ -133,6 +147,7 @@ akka {
|
||||||
|
|
||||||
# The dispatcher used for the system actor "network-event-sender"
|
# The dispatcher used for the system actor "network-event-sender"
|
||||||
network-event-sender-dispatcher {
|
network-event-sender-dispatcher {
|
||||||
|
executor = thread-pool-executor
|
||||||
type = PinnedDispatcher
|
type = PinnedDispatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class NettySettings(config: Config, val systemName: String) {
|
||||||
case value ⇒ value
|
case value ⇒ value
|
||||||
}
|
}
|
||||||
|
|
||||||
@deprecated("WARNING: This should only be used by professionals.")
|
@deprecated("WARNING: This should only be used by professionals.", "2.0")
|
||||||
val PortSelector = getInt("port")
|
val PortSelector = getInt("port")
|
||||||
|
|
||||||
val ConnectionTimeout = Duration(getMilliseconds("connection-timeout"), MILLISECONDS)
|
val ConnectionTimeout = Duration(getMilliseconds("connection-timeout"), MILLISECONDS)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.serialization
|
||||||
|
|
||||||
|
import akka.testkit.AkkaSpec
|
||||||
|
import akka.remote.RemoteProtocol.MessageProtocol
|
||||||
|
import akka.actor.ProtobufProtocol.MyMessage
|
||||||
|
|
||||||
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
class ProtobufSerializerSpec extends AkkaSpec {
|
||||||
|
|
||||||
|
val ser = SerializationExtension(system)
|
||||||
|
|
||||||
|
"Serialization" must {
|
||||||
|
|
||||||
|
"resolve protobuf serializer" in {
|
||||||
|
ser.serializerFor(classOf[MessageProtocol]).getClass must be(classOf[ProtobufSerializer])
|
||||||
|
ser.serializerFor(classOf[MyMessage]).getClass must be(classOf[ProtobufSerializer])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<layout>
|
<encoder>
|
||||||
<pattern>%date{ISO8601} %-5level %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
|
<pattern>%date{ISO8601} %-5level %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
|
||||||
</layout>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="info">
|
<root level="info">
|
||||||
|
|
|
||||||
26
akka-slf4j/src/test/resources/logback-test.xml
Normal file
26
akka-slf4j/src/test/resources/logback-test.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date{ISO8601} %-5level %logger %X{akkaSource} %X{sourceThread} - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="TEST"
|
||||||
|
class="akka.event.slf4j.Slf4jEventHandlerSpec$TestAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date{ISO8601} level=[%level] logger=[%logger] akkaSource=[%X{akkaSource}] sourceThread=[%X{sourceThread}] - msg=[%msg]%n----%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="akka.event.slf4j.Slf4jEventHandlerSpec" level="info" additivity="false">
|
||||||
|
<appender-ref ref="TEST" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
package akka.event.slf4j
|
||||||
|
|
||||||
|
import akka.testkit.AkkaSpec
|
||||||
|
import akka.actor.Actor
|
||||||
|
import akka.actor.ActorLogging
|
||||||
|
import akka.util.duration._
|
||||||
|
import akka.event.Logging
|
||||||
|
import akka.actor.Props
|
||||||
|
import ch.qos.logback.core.OutputStreamAppender
|
||||||
|
import java.io.StringWriter
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import org.scalatest.BeforeAndAfterEach
|
||||||
|
|
||||||
|
object Slf4jEventHandlerSpec {
|
||||||
|
|
||||||
|
// This test depends on logback configuration in src/test/resources/logback-test.xml
|
||||||
|
|
||||||
|
val config = """
|
||||||
|
akka {
|
||||||
|
loglevel = INFO
|
||||||
|
event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
class LogProducer extends Actor with ActorLogging {
|
||||||
|
def receive = {
|
||||||
|
case e: Exception ⇒
|
||||||
|
log.error(e, e.getMessage)
|
||||||
|
case (s: String, x: Int, y: Int) ⇒
|
||||||
|
log.info(s, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyLogSource
|
||||||
|
|
||||||
|
val output = new ByteArrayOutputStream
|
||||||
|
def outputString: String = output.toString("UTF-8")
|
||||||
|
|
||||||
|
class TestAppender extends OutputStreamAppender {
|
||||||
|
|
||||||
|
override def start(): Unit = {
|
||||||
|
setOutputStream(output)
|
||||||
|
super.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
||||||
|
class Slf4jEventHandlerSpec extends AkkaSpec(Slf4jEventHandlerSpec.config) with BeforeAndAfterEach {
|
||||||
|
import Slf4jEventHandlerSpec._
|
||||||
|
|
||||||
|
val producer = system.actorOf(Props[LogProducer], name = "logProducer")
|
||||||
|
|
||||||
|
override def beforeEach(): Unit = {
|
||||||
|
output.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
val sourceThreadRegex = "sourceThread=\\[Slf4jEventHandlerSpec-akka.actor.default-dispatcher-[1-9][0-9]*\\]"
|
||||||
|
|
||||||
|
"Slf4jEventHandler" must {
|
||||||
|
|
||||||
|
"log error with stackTrace" in {
|
||||||
|
producer ! new RuntimeException("Simulated error")
|
||||||
|
|
||||||
|
awaitCond(outputString.contains("----"), 5 seconds)
|
||||||
|
val s = outputString
|
||||||
|
s must include("akkaSource=[akka://Slf4jEventHandlerSpec/user/logProducer]")
|
||||||
|
s must include("level=[ERROR]")
|
||||||
|
s must include("logger=[akka.event.slf4j.Slf4jEventHandlerSpec$LogProducer]")
|
||||||
|
s must include regex (sourceThreadRegex)
|
||||||
|
s must include("msg=[Simulated error]")
|
||||||
|
s must include("java.lang.RuntimeException: Simulated error")
|
||||||
|
s must include("at akka.event.slf4j.Slf4jEventHandlerSpec")
|
||||||
|
}
|
||||||
|
|
||||||
|
"log info with parameters" in {
|
||||||
|
producer ! ("test x={} y={}", 3, 17)
|
||||||
|
|
||||||
|
awaitCond(outputString.contains("----"), 5 seconds)
|
||||||
|
val s = outputString
|
||||||
|
s must include("akkaSource=[akka://Slf4jEventHandlerSpec/user/logProducer]")
|
||||||
|
s must include("level=[INFO]")
|
||||||
|
s must include("logger=[akka.event.slf4j.Slf4jEventHandlerSpec$LogProducer]")
|
||||||
|
s must include regex (sourceThreadRegex)
|
||||||
|
s must include("msg=[test x=3 y=17]")
|
||||||
|
}
|
||||||
|
|
||||||
|
"include system info in akkaSource when creating Logging with system" in {
|
||||||
|
val log = Logging(system, "akka.event.slf4j.Slf4jEventHandlerSpec.MyLogSource")
|
||||||
|
log.info("test")
|
||||||
|
awaitCond(outputString.contains("----"), 5 seconds)
|
||||||
|
val s = outputString
|
||||||
|
s must include("akkaSource=[akka.event.slf4j.Slf4jEventHandlerSpec.MyLogSource(akka://Slf4jEventHandlerSpec)]")
|
||||||
|
s must include("logger=[akka.event.slf4j.Slf4jEventHandlerSpec.MyLogSource(akka://Slf4jEventHandlerSpec)]")
|
||||||
|
}
|
||||||
|
|
||||||
|
"not include system info in akkaSource when creating Logging with system.eventStream" in {
|
||||||
|
val log = Logging(system.eventStream, "akka.event.slf4j.Slf4jEventHandlerSpec.MyLogSource")
|
||||||
|
log.info("test")
|
||||||
|
awaitCond(outputString.contains("----"), 5 seconds)
|
||||||
|
val s = outputString
|
||||||
|
s must include("akkaSource=[akka.event.slf4j.Slf4jEventHandlerSpec.MyLogSource]")
|
||||||
|
s must include("logger=[akka.event.slf4j.Slf4jEventHandlerSpec.MyLogSource]")
|
||||||
|
}
|
||||||
|
|
||||||
|
"use short class name and include system info in akkaSource when creating Logging with system and class" in {
|
||||||
|
val log = Logging(system, classOf[MyLogSource])
|
||||||
|
log.info("test")
|
||||||
|
awaitCond(outputString.contains("----"), 5 seconds)
|
||||||
|
val s = outputString
|
||||||
|
s must include("akkaSource=[Slf4jEventHandlerSpec$MyLogSource(akka://Slf4jEventHandlerSpec)]")
|
||||||
|
s must include("logger=[akka.event.slf4j.Slf4jEventHandlerSpec$MyLogSource]")
|
||||||
|
}
|
||||||
|
|
||||||
|
"use short class name in akkaSource when creating Logging with system.eventStream and class" in {
|
||||||
|
val log = Logging(system.eventStream, classOf[MyLogSource])
|
||||||
|
log.info("test")
|
||||||
|
awaitCond(outputString.contains("----"), 5 seconds)
|
||||||
|
val s = outputString
|
||||||
|
s must include("akkaSource=[Slf4jEventHandlerSpec$MyLogSource]")
|
||||||
|
s must include("logger=[akka.event.slf4j.Slf4jEventHandlerSpec$MyLogSource]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ akka {
|
||||||
socket-dispatcher {
|
socket-dispatcher {
|
||||||
# A zeromq socket needs to be pinned to the thread that created it.
|
# A zeromq socket needs to be pinned to the thread that created it.
|
||||||
# Changing this value results in weird errors and race conditions within zeromq
|
# Changing this value results in weird errors and race conditions within zeromq
|
||||||
|
executor = thread-pool-executor
|
||||||
type = "PinnedDispatcher"
|
type = "PinnedDispatcher"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,9 +174,12 @@ private[zeromq] case object Close extends Request
|
||||||
*
|
*
|
||||||
* @param payload the topic to subscribe to
|
* @param payload the topic to subscribe to
|
||||||
*/
|
*/
|
||||||
case class Subscribe(payload: Seq[Byte]) extends PubSubOption
|
case class Subscribe(payload: Seq[Byte]) extends PubSubOption {
|
||||||
|
def this(topic: String) = this(topic.getBytes("UTF-8"))
|
||||||
|
}
|
||||||
object Subscribe {
|
object Subscribe {
|
||||||
def apply(topic: String): Subscribe = new Subscribe(topic.getBytes)
|
def apply(topic: String): Subscribe = new Subscribe(topic)
|
||||||
|
val all = Subscribe(Seq.empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -188,9 +191,11 @@ object Subscribe {
|
||||||
*
|
*
|
||||||
* @param payload
|
* @param payload
|
||||||
*/
|
*/
|
||||||
case class Unsubscribe(payload: Seq[Byte]) extends PubSubOption
|
case class Unsubscribe(payload: Seq[Byte]) extends PubSubOption {
|
||||||
|
def this(topic: String) = this(topic.getBytes("UTF-8"))
|
||||||
|
}
|
||||||
object Unsubscribe {
|
object Unsubscribe {
|
||||||
def apply(topic: String): Unsubscribe = Unsubscribe(topic.getBytes)
|
def apply(topic: String): Unsubscribe = new Unsubscribe(topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -204,7 +209,21 @@ case class Send(frames: Seq[Frame]) extends Request
|
||||||
* @param frames
|
* @param frames
|
||||||
*/
|
*/
|
||||||
case class ZMQMessage(frames: Seq[Frame]) {
|
case class ZMQMessage(frames: Seq[Frame]) {
|
||||||
def firstFrameAsString = new String(frames.head.payload.toArray)
|
|
||||||
|
def this(frame: Frame) = this(Seq(frame))
|
||||||
|
def this(frame1: Frame, frame2: Frame) = this(Seq(frame1, frame2))
|
||||||
|
def this(frameArray: Array[Frame]) = this(frameArray.toSeq)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the bytes in the first frame to a String, using specified charset.
|
||||||
|
*/
|
||||||
|
def firstFrameAsString(charsetName: String): String = new String(frames.head.payload.toArray, charsetName)
|
||||||
|
/**
|
||||||
|
* Convert the bytes in the first frame to a String, using "UTF-8" charset.
|
||||||
|
*/
|
||||||
|
def firstFrameAsString: String = firstFrameAsString("UTF-8")
|
||||||
|
|
||||||
|
def payload(frameIndex: Int): Array[Byte] = frames(frameIndex).payload.toArray
|
||||||
}
|
}
|
||||||
object ZMQMessage {
|
object ZMQMessage {
|
||||||
def apply(bytes: Array[Byte]): ZMQMessage = ZMQMessage(Seq(Frame(bytes)))
|
def apply(bytes: Array[Byte]): ZMQMessage = ZMQMessage(Seq(Frame(bytes)))
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,18 @@
|
||||||
*/
|
*/
|
||||||
package akka.zeromq
|
package akka.zeromq
|
||||||
|
|
||||||
|
object Frame {
|
||||||
|
def apply(text: String): Frame = new Frame(text)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single message frame of a zeromq message
|
* A single message frame of a zeromq message
|
||||||
* @param payload
|
* @param payload
|
||||||
*/
|
*/
|
||||||
case class Frame(payload: Seq[Byte])
|
case class Frame(payload: Seq[Byte]) {
|
||||||
|
def this(bytes: Array[Byte]) = this(bytes.toSeq)
|
||||||
|
def this(text: String) = this(text.getBytes("UTF-8"))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserializes ZeroMQ messages into an immutable sequence of frames
|
* Deserializes ZeroMQ messages into an immutable sequence of frames
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ case class ZeroMQVersion(major: Int, minor: Int, patch: Int) {
|
||||||
* The [[akka.actor.ExtensionId]] and [[akka.actor.ExtensionIdProvider]] for the ZeroMQ module
|
* The [[akka.actor.ExtensionId]] and [[akka.actor.ExtensionIdProvider]] for the ZeroMQ module
|
||||||
*/
|
*/
|
||||||
object ZeroMQExtension extends ExtensionId[ZeroMQExtension] with ExtensionIdProvider {
|
object ZeroMQExtension extends ExtensionId[ZeroMQExtension] with ExtensionIdProvider {
|
||||||
|
override def get(system: ActorSystem): ZeroMQExtension = super.get(system)
|
||||||
def lookup() = this
|
def lookup() = this
|
||||||
def createExtension(system: ExtendedActorSystem) = new ZeroMQExtension(system)
|
def createExtension(system: ExtendedActorSystem) = new ZeroMQExtension(system)
|
||||||
|
|
||||||
|
|
@ -141,92 +142,94 @@ class ZeroMQExtension(system: ActorSystem) extends Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Java API factory method to create the actor representing the ZeroMQ Publisher socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Publisher socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
* They are matched on type and the first one found wins.
|
* They are matched on type and the first one found wins.
|
||||||
*
|
*
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socket
|
||||||
* @return the [[akka.actor.ActorRef]]
|
* @return the [[akka.actor.ActorRef]]
|
||||||
*/
|
*/
|
||||||
def newPubSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Pub +: socketParameters): _*)
|
def newPubSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Pub +: socketParameters): _*)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Convenience for creating a publisher socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Subscriber socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
|
||||||
* They are matched on type and the first one found wins.
|
|
||||||
*
|
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
|
||||||
* @return the [[akka.actor.ActorRef]]
|
|
||||||
*/
|
*/
|
||||||
def newSubSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Sub +: socketParameters): _*)
|
def newPubSocket(bind: Bind): ActorRef = newSocket(SocketType.Pub, bind)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Java API factory method to create the actor representing the ZeroMQ Subscriber socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Dealer socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
* They are matched on type and the first one found wins.
|
* They are matched on type and the first one found wins.
|
||||||
*
|
*
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socket
|
||||||
* @return the [[akka.actor.ActorRef]]
|
* @return the [[akka.actor.ActorRef]]
|
||||||
*/
|
*/
|
||||||
def newDealerSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Dealer +: socketParameters): _*)
|
def newSubSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Sub +: socketParameters): _*)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Convenience for creating a subscriber socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Router socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
|
||||||
* They are matched on type and the first one found wins.
|
|
||||||
*
|
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
|
||||||
* @return the [[akka.actor.ActorRef]]
|
|
||||||
*/
|
*/
|
||||||
def newRouterSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Router +: socketParameters): _*)
|
def newSubSocket(connect: Connect, listener: Listener, subscribe: Subscribe): ActorRef = newSocket(SocketType.Sub, connect, listener, subscribe)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Java API factory method to create the actor representing the ZeroMQ Dealer socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Push socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
* They are matched on type and the first one found wins.
|
* They are matched on type and the first one found wins.
|
||||||
*
|
*
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socket
|
||||||
* @return the [[akka.actor.ActorRef]]
|
* @return the [[akka.actor.ActorRef]]
|
||||||
*/
|
*/
|
||||||
def newPushSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Push +: socketParameters): _*)
|
def newDealerSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Dealer +: socketParameters): _*)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Java API factory method to create the actor representing the ZeroMQ Router socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Pull socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
* They are matched on type and the first one found wins.
|
* They are matched on type and the first one found wins.
|
||||||
*
|
*
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socket
|
||||||
* @return the [[akka.actor.ActorRef]]
|
* @return the [[akka.actor.ActorRef]]
|
||||||
*/
|
*/
|
||||||
def newPullSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Pull +: socketParameters): _*)
|
def newRouterSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Router +: socketParameters): _*)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Java API factory method to create the actor representing the ZeroMQ Push socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Req socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
* They are matched on type and the first one found wins.
|
* They are matched on type and the first one found wins.
|
||||||
*
|
*
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socket
|
||||||
* @return the [[akka.actor.ActorRef]]
|
* @return the [[akka.actor.ActorRef]]
|
||||||
*/
|
*/
|
||||||
def newReqSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Req +: socketParameters): _*)
|
def newPushSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Push +: socketParameters): _*)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API helper
|
* Java API factory method to create the actor representing the ZeroMQ Pull socket.
|
||||||
* Factory method to create the actor representing the ZeroMQ Rep socket.
|
|
||||||
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
* They are matched on type and the first one found wins.
|
* They are matched on type and the first one found wins.
|
||||||
*
|
*
|
||||||
* @param socketParameters a varargs list of [[akka.zeromq.SocketOption]] to configure the socke
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socket
|
||||||
* @return the [[akka.actor.ActorRef]]
|
* @return the [[akka.actor.ActorRef]]
|
||||||
*/
|
*/
|
||||||
def newRepSocket(socketParameters: SocketOption*): ActorRef = newSocket((SocketType.Rep +: socketParameters): _*)
|
def newPullSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Pull +: socketParameters): _*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java API factory method to create the actor representing the ZeroMQ Req socket.
|
||||||
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
|
* They are matched on type and the first one found wins.
|
||||||
|
*
|
||||||
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socket
|
||||||
|
* @return the [[akka.actor.ActorRef]]
|
||||||
|
*/
|
||||||
|
def newReqSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Req +: socketParameters): _*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java API factory method to create the actor representing the ZeroMQ Rep socket.
|
||||||
|
* You can pass in as many configuration options as you want and the order of the configuration options doesn't matter
|
||||||
|
* They are matched on type and the first one found wins.
|
||||||
|
*
|
||||||
|
* @param socketParameters array of [[akka.zeromq.SocketOption]] to configure the socke
|
||||||
|
* @return the [[akka.actor.ActorRef]]
|
||||||
|
*/
|
||||||
|
def newRepSocket(socketParameters: Array[SocketOption]): ActorRef = newSocket((SocketType.Rep +: socketParameters): _*)
|
||||||
|
|
||||||
private val zeromqGuardian: ActorRef = {
|
private val zeromqGuardian: ActorRef = {
|
||||||
verifyZeroMQVersion
|
verifyZeroMQVersion
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ class ConcurrentSocketActorSpec
|
||||||
"ConcurrentSocketActor" should {
|
"ConcurrentSocketActor" should {
|
||||||
"support pub-sub connections" in {
|
"support pub-sub connections" in {
|
||||||
checkZeroMQInstallation
|
checkZeroMQInstallation
|
||||||
val (publisherProbe, subscriberProbe) = (TestProbe(), TestProbe())
|
val publisherProbe = TestProbe()
|
||||||
|
val subscriberProbe = TestProbe()
|
||||||
val context = Context()
|
val context = Context()
|
||||||
val publisher = newPublisher(context, publisherProbe.ref)
|
val publisher = newPublisher(context, publisherProbe.ref)
|
||||||
val subscriber = newSubscriber(context, subscriberProbe.ref)
|
val subscriber = newSubscriber(context, subscriberProbe.ref)
|
||||||
|
|
@ -68,7 +69,7 @@ class ConcurrentSocketActorSpec
|
||||||
zmq.newSocket(SocketType.Pub, context, Listener(listener), Bind(endpoint))
|
zmq.newSocket(SocketType.Pub, context, Listener(listener), Bind(endpoint))
|
||||||
}
|
}
|
||||||
def newSubscriber(context: Context, listener: ActorRef) = {
|
def newSubscriber(context: Context, listener: ActorRef) = {
|
||||||
zmq.newSocket(SocketType.Sub, context, Listener(listener), Connect(endpoint), Subscribe(Seq.empty))
|
zmq.newSocket(SocketType.Sub, context, Listener(listener), Connect(endpoint), SubscribeAll)
|
||||||
}
|
}
|
||||||
def newMessageGenerator(actorRef: ActorRef) = {
|
def newMessageGenerator(actorRef: ActorRef) = {
|
||||||
system.actorOf(Props(new MessageGeneratorActor(actorRef)))
|
system.actorOf(Props(new MessageGeneratorActor(actorRef)))
|
||||||
|
|
@ -110,7 +111,7 @@ class ConcurrentSocketActorSpec
|
||||||
protected def receive = {
|
protected def receive = {
|
||||||
case _ ⇒
|
case _ ⇒
|
||||||
val payload = "%s".format(messageNumber)
|
val payload = "%s".format(messageNumber)
|
||||||
messageNumber = messageNumber + 1
|
messageNumber += 1
|
||||||
actorRef ! ZMQMessage(payload.getBytes)
|
actorRef ! ZMQMessage(payload.getBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,8 @@ object AkkaBuild extends Build {
|
||||||
lazy val docs = Project(
|
lazy val docs = Project(
|
||||||
id = "akka-docs",
|
id = "akka-docs",
|
||||||
base = file("akka-docs"),
|
base = file("akka-docs"),
|
||||||
dependencies = Seq(actor, testkit % "test->test", remote, cluster, slf4j, agent, transactor, fileMailbox, mongoMailbox, redisMailbox, beanstalkMailbox, zookeeperMailbox),
|
dependencies = Seq(actor, testkit % "test->test", remote, cluster, slf4j, agent, transactor,
|
||||||
|
fileMailbox, mongoMailbox, redisMailbox, beanstalkMailbox, zookeeperMailbox, zeroMQ),
|
||||||
settings = defaultSettings ++ Seq(
|
settings = defaultSettings ++ Seq(
|
||||||
unmanagedSourceDirectories in Test <<= baseDirectory { _ ** "code" get },
|
unmanagedSourceDirectories in Test <<= baseDirectory { _ ** "code" get },
|
||||||
libraryDependencies ++= Dependencies.docs,
|
libraryDependencies ++= Dependencies.docs,
|
||||||
|
|
@ -431,7 +432,7 @@ object Dependencies {
|
||||||
|
|
||||||
val cluster = Seq(Test.junit, Test.scalatest)
|
val cluster = Seq(Test.junit, Test.scalatest)
|
||||||
|
|
||||||
val slf4j = Seq(slf4jApi)
|
val slf4j = Seq(slf4jApi, Test.logback)
|
||||||
|
|
||||||
val agent = Seq(scalaStm, Test.scalatest, Test.junit)
|
val agent = Seq(scalaStm, Test.scalatest, Test.junit)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ addSbtPlugin("com.typesafe.sbtmultijvm" % "sbt-multi-jvm" % "0.1.9")
|
||||||
|
|
||||||
addSbtPlugin("com.typesafe.schoir" % "schoir" % "0.1.2")
|
addSbtPlugin("com.typesafe.schoir" % "schoir" % "0.1.2")
|
||||||
|
|
||||||
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse" % "1.5.0")
|
|
||||||
|
|
||||||
addSbtPlugin("com.typesafe.sbtscalariform" % "sbtscalariform" % "0.3.1")
|
addSbtPlugin("com.typesafe.sbtscalariform" % "sbtscalariform" % "0.3.1")
|
||||||
|
|
||||||
resolvers ++= Seq(
|
resolvers ++= Seq(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue