简体   繁体   中英

Akka remote deploy misunderstanding?

I'm learning about Akka (2.0-M4) and trying to understand the concept of remote actor deployment/creation .

I'd assumed that remote deployment worked by sending the actor code over the network and then running it, say, on the microkernel. Is this right?

I ask since I can't get it to do that. If I download the samples here then the actor CreationApp works, but only if the appropriate jar is placed in the lib directory of the microkernel. Otherwise I get a ClassNotFoundException.

Have I misunderstood remote actor creation?

I think you have misunderstood. Remote deployment forwards the create to the remote machine but the Actor that is created is within the ActorSystem on your local machine (for all intents and purposes). If the code isn't on the remote machine, then you're out of luck.

In contrast to RMI has no build-in mechanism to send class-code to a remote machine. But Akka makes it very easy to do that yourself.

An ActorSystem may be created with a classloader that can load the necessary classes:

val system = ActorSystem(
                 "TestSystem", 
                 ConfigFactory.load(), 
                 new ByteClassloader(
                       Thread.currentThread().getContextClassLoader()))

Your classloader may cooperate with an actor that accepts classes sent to it:

import akka.actor.Actor

// messages with classcode
case class RegisterRemoteMsg(name: String, clazzB: Array[Byte])

class RegistryActor extends Actor {
  def receive = {
    case RegisterRemoteMsg(name, bytes) =>
      ByteClassLoader.register(name, bytes)
  }
}

This actor stores classes in a map, the classloader retrieves classes from the map:

import java.net.URLClassLoader
import java.net.URL

class ByteArrayClassloader(parent: ClassLoader) extends URLClassLoader(Array[URL](), parent) {
  import ByteClassLoader._
  override
  protected def findClass(name: String) : Class[_] = {
    var result = findLoadedClass(name);
    if (result == null) {
      try {
          result = findSystemClass(name);
      } catch {
          case e: /* ignore */ 
      }
    }
    if (result == null) {
      try {
          val classBytes = registeredClasses(name)
          result = defineClass(name, classBytes, 0, classBytes.length);
      } catch {
          case e: Exception => {
            throw new ClassNotFoundException(name);
          }
      }
    }
    result;
  }    
}
object ByteClassLoader {
  var registeredClasses : Map[String, Array[Byte]] = Map()
  def register(name : String, classBytes : Array[Byte]) {
    registeredClasses += (name -> classBytes)
  }
}

Note that the sending side has to send byte code - not class-objects. Class-objects (classOf[SomeClass]) are not serializable because they are "linked into" the sending JVM.

Class code is on the disk. Note that a scala class usually has some nested classes which are in separate files. The sender may find all necessary classes using:

object LoadClassBytes {
  def apply(clazz: Class[_]) : Map[String, Array[Byte]] = { 
    val basePath : Path = Paths.get(clazz.getProtectionDomain.getCodeSource.getLocation.toURI)
    val relName = clazz.getName().replace('.', '/')
    val fullPath = basePath.resolve(relName)
    val fileName = fullPath.getFileName()
    val fileNameHead = (fileName.toString()).split("\\.")(0)
    val parentDir = fullPath.getParent()

    var res : Map[String, Array[Byte]] = Map()
    // find class file and class files of inner classes
    val ds = Files.newDirectoryStream(
        parentDir, 
        new DirectoryStream.Filter[Path]{
          def accept(file: Path) : Boolean = file.getFileName().toString().matches(fileNameHead+"(\\$.+)?\\.class")
        })
    try {
        val iter = ds.iterator()
        while (iter.hasNext()) {
          val p = iter.next()
          res += (((basePath.relativize(p)).toString().split("\\.")(0).replace('/', '.')) -> Files.readAllBytes(p))
        }
    } finally {
       ds.close
    }
    res
  }
}

and send it to the RegistryActor at the remote side in a RegisterRemoteMsg

 val registryActorR = ... retrieve remote registry actor ...
 val classesToBeSent = LoadClassBytes(classOf[SomeClass])
 for ((name, bytes) <- classesToBeSent) {
  registryActorR ! RegisterRemoteMsg(name, bytes)
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM