我正在学习Akka(2.0-M4)并尝试理解remote actor deployment/creation的概念。
我假设远程部署通过在网络上发送actor代码然后在微内核上运行它来工作。这是对的吗?
我问,因为我无法做到这一点。如果我下载样本here,则actor CreationApp可以正常工作,但前提是相应的jar放在微内核的lib目录中。否则我得到一个ClassNotFoundException。
我是否误解了远程演员的创作?
答案 0 :(得分:10)
我觉得你误解了。远程部署将 create 转发到远程计算机,但创建的Actor位于本地计算机上的ActorSystem内(出于所有意图和目的)。如果代码不在远程计算机上,那么你运气不好。
答案 1 :(得分:4)
与RMI相比,没有内置机制来发送类代码 一台远程机器。但是Akka让你自己很容易做到这一点。
可以使用可以加载必要类的类加载器创建ActorSystem:
val system = ActorSystem(
"TestSystem",
ConfigFactory.load(),
new ByteClassloader(
Thread.currentThread().getContextClassLoader()))
您的类加载器可以与接受发送给它的类的actor合作:
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)
}
}
此actor将类存储在地图中,类加载器从地图中检索类:
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)
}
}
请注意,发送方必须发送字节代码 - 而不是类对象。 类对象(classOf [SomeClass])不可序列化,因为 它们“链接到”发送JVM。
类代码在磁盘上。请注意,scala类通常有一些嵌套 在单独的文件中的类。发件人可以找到所有必要的类 使用:
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
}
}
并将其发送到RegisterRemoteMsg
中远程端的RegistryActor val registryActorR = ... retrieve remote registry actor ...
val classesToBeSent = LoadClassBytes(classOf[SomeClass])
for ((name, bytes) <- classesToBeSent) {
registryActorR ! RegisterRemoteMsg(name, bytes)
}