completed protobuf protocol for remoting
This commit is contained in:
parent
a4d22af64b
commit
f26110e55c
23 changed files with 795 additions and 385 deletions
|
|
@ -7,15 +7,18 @@ package se.scalablesolutions.akka.kernel.nio
|
|||
import java.net.InetSocketAddress
|
||||
import java.util.concurrent.{Executors, ConcurrentMap, ConcurrentHashMap}
|
||||
|
||||
import kernel.nio.protobuf.RemoteProtocol.{RemoteRequest, RemoteReply}
|
||||
import kernel.actor.{Exit, Actor}
|
||||
import kernel.reactor.{DefaultCompletableFutureResult, CompletableFutureResult}
|
||||
import kernel.util.{JSONSerializer, Logging}
|
||||
import kernel.util.{Serializer, ScalaJSONSerializer, JavaJSONSerializer, Logging}
|
||||
|
||||
import org.jboss.netty.bootstrap.ClientBootstrap
|
||||
import org.jboss.netty.channel._
|
||||
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory
|
||||
import org.jboss.netty.handler.codec.serialization.{ObjectEncoder, ObjectDecoder}
|
||||
import org.jboss.netty.bootstrap.ClientBootstrap
|
||||
import org.jboss.netty.handler.codec.frame.{LengthFieldBasedFrameDecoder, LengthFieldPrepender}
|
||||
import org.jboss.netty.handler.codec.protobuf.{ProtobufDecoder, ProtobufEncoder}
|
||||
|
||||
import protobuf.RemoteProtocol.RemoteReply
|
||||
import protobuf.RemoteProtocol
|
||||
import scala.collection.mutable.HashMap
|
||||
|
||||
object RemoteClient extends Logging {
|
||||
|
|
@ -45,9 +48,8 @@ class RemoteClient(hostname: String, port: Int) extends Logging {
|
|||
Executors.newCachedThreadPool)
|
||||
|
||||
private val bootstrap = new ClientBootstrap(channelFactory)
|
||||
private val handler = new RemoteClientHandler(futures, supervisors)
|
||||
|
||||
bootstrap.getPipeline.addLast("handler", handler)
|
||||
bootstrap.setPipelineFactory(new RemoteClientPipelineFactory(futures, supervisors))
|
||||
bootstrap.setOption("tcpNoDelay", true)
|
||||
bootstrap.setOption("keepAlive", true)
|
||||
|
||||
|
|
@ -76,15 +78,14 @@ class RemoteClient(hostname: String, port: Int) extends Logging {
|
|||
}
|
||||
|
||||
def send(request: RemoteRequest): Option[CompletableFutureResult] = if (isRunning) {
|
||||
val escapedRequest = request//escapeRequest(request)
|
||||
if (escapedRequest.isOneWay) {
|
||||
connection.getChannel.write(escapedRequest)
|
||||
if (request.getIsOneWay) {
|
||||
connection.getChannel.write(request)
|
||||
None
|
||||
} else {
|
||||
futures.synchronized {
|
||||
val futureResult = new DefaultCompletableFutureResult(request.timeout)
|
||||
futures.put(request.id, futureResult)
|
||||
connection.getChannel.write(escapedRequest)
|
||||
val futureResult = new DefaultCompletableFutureResult(request.getTimeout)
|
||||
futures.put(request.getId, futureResult)
|
||||
connection.getChannel.write(request)
|
||||
Some(futureResult)
|
||||
}
|
||||
}
|
||||
|
|
@ -99,20 +100,18 @@ class RemoteClient(hostname: String, port: Int) extends Logging {
|
|||
else supervisors.remove(actor.supervisor.get.uuid)
|
||||
|
||||
def deregisterSupervisorWithUuid(uuid: String) = supervisors.remove(uuid)
|
||||
}
|
||||
|
||||
private def escapeRequest(request: RemoteRequest) = {
|
||||
if (request.message.isInstanceOf[Array[Object]]) {
|
||||
val args = request.message.asInstanceOf[Array[Object]].toList.asInstanceOf[scala.List[Object]]
|
||||
var isEscaped = false
|
||||
val escapedArgs = for (arg <- args) yield {
|
||||
val clazz = arg.getClass
|
||||
if (clazz.getName.contains("$$ProxiedByAW")) {
|
||||
isEscaped = true
|
||||
new ProxyWrapper(clazz.getSuperclass.getName)
|
||||
} else arg
|
||||
}
|
||||
request.cloneWithNewMessage(escapedArgs, isEscaped)
|
||||
} else request
|
||||
class RemoteClientPipelineFactory(futures: ConcurrentMap[Long, CompletableFutureResult],
|
||||
supervisors: ConcurrentMap[String, Actor]) extends ChannelPipelineFactory {
|
||||
def getPipeline: ChannelPipeline = {
|
||||
val p = Channels.pipeline()
|
||||
p.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
|
||||
p.addLast("protobufDecoder", new ProtobufDecoder(RemoteProtocol.RemoteReply.getDefaultInstance));
|
||||
p.addLast("frameEncoder", new LengthFieldPrepender(4));
|
||||
p.addLast("protobufEncoder", new ProtobufEncoder());
|
||||
p.addLast("handler", new RemoteClientHandler(futures, supervisors))
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -128,41 +127,36 @@ class RemoteClientHandler(val futures: ConcurrentMap[Long, CompletableFutureResu
|
|||
super.handleUpstream(ctx, event)
|
||||
}
|
||||
|
||||
override def channelOpen(ctx: ChannelHandlerContext, event: ChannelStateEvent) = {
|
||||
//event.getChannel.getPipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
|
||||
//event.getChannel.getPipeline.addLast("protobufDecoder", new ProtobufDecoder(LocalTimeProtocol.LocalTimes.getDefaultInstance()));
|
||||
//event.getChannel.getPipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
|
||||
//event.getChannel.getPipeline.addLast("protobufEncoder", new ProtobufEncoder());
|
||||
|
||||
event.getChannel.getPipeline.addFirst("encoder", new ObjectEncoder)
|
||||
event.getChannel.getPipeline.addFirst("decoder", new ObjectDecoder)
|
||||
}
|
||||
|
||||
override def channelConnected(ctx: ChannelHandlerContext, event: ChannelStateEvent) {
|
||||
// Send the first message if this handler is a client-side handler.
|
||||
// if (!firstMessage.isEmpty) e.getChannel.write(firstMessage)
|
||||
}
|
||||
|
||||
override def messageReceived(ctx: ChannelHandlerContext, event: MessageEvent) {
|
||||
try {
|
||||
val result = event.getMessage
|
||||
if (result.isInstanceOf[RemoteReply]) {
|
||||
val reply = result.asInstanceOf[RemoteReply]
|
||||
log.debug("Received RemoteReply[\n%s]", reply.toString)
|
||||
val future = futures.get(reply.getId)
|
||||
val messageBytes = reply.getMessage
|
||||
val messageType = reply.getMessageType
|
||||
val messageClass = Class.forName(messageType)
|
||||
val message = JSONSerializer.in(messageBytes, messageClass)
|
||||
if (reply.successful) future.completeWithResult(message)
|
||||
else {
|
||||
val supervisorUuid = reply.getSupervisorUuid
|
||||
if (supervisorUuid != null) {
|
||||
if (reply.getIsSuccessful) {
|
||||
val messageBytes = reply.getMessage.toByteArray
|
||||
val messageType = reply.getMessageType
|
||||
val messageClass = Class.forName(messageType)
|
||||
val message =
|
||||
if (reply.isActor) ScalaJSONSerializer.in(messageBytes, Some(messageClass))
|
||||
else JavaJSONSerializer.in(messageBytes, Some(messageClass))
|
||||
future.completeWithResult(message)
|
||||
} else {
|
||||
if (reply.hasSupervisorUuid) {
|
||||
val supervisorUuid = reply.getSupervisorUuid
|
||||
if (!supervisors.containsKey(supervisorUuid)) throw new IllegalStateException("Expected a registered supervisor for UUID [" + supervisorUuid + "] but none was found")
|
||||
val supervisedActor = supervisors.get(supervisorUuid)
|
||||
if (!supervisedActor.supervisor.isDefined) throw new IllegalStateException("Can't handle restart for remote actor " + supervisedActor + " since its supervisor has been removed")
|
||||
else supervisedActor.supervisor.get ! Exit(supervisedActor, new RuntimeException(reply.getException))
|
||||
}
|
||||
future.completeWithException(null, new RuntimeException(reply.getException))
|
||||
val exception = reply.getException
|
||||
val exceptionType = Class.forName(exception.substring(0, exception.indexOf('$')))
|
||||
val exceptionMessage = exception.substring(exception.indexOf('$') + 1, exception.length)
|
||||
val exceptionInstance = exceptionType
|
||||
.getConstructor(Array[Class[_]](classOf[String]): _*)
|
||||
.newInstance(exceptionMessage).asInstanceOf[Throwable]
|
||||
future.completeWithException(null, exceptionInstance)
|
||||
}
|
||||
futures.remove(reply.getId)
|
||||
} else throw new IllegalArgumentException("Unknown message received in remote client handler: " + result)
|
||||
|
|
@ -175,6 +169,7 @@ class RemoteClientHandler(val futures: ConcurrentMap[Long, CompletableFutureResu
|
|||
|
||||
override def exceptionCaught(ctx: ChannelHandlerContext, event: ExceptionEvent) {
|
||||
log.error("Unexpected exception from downstream in remote client: %s", event.getCause)
|
||||
event.getCause.printStackTrace
|
||||
event.getChannel.close
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue