I am working on a play framework 2 system using java .The play framework uses the akka system integrated with play framework to connect with a remote akka system. the remote akka system is made up of a master node and a worker node. Both systems are on the same computer with eclipse juno IDE
I have configured two ports 2552 for the master node and port 2553 for the worker node.the akka node on the play 2 framework is selected by the system itself. the akka system in the play framework is expected to ppass a messgae to the remote master node by remote lookup using the akka configuration. the master node interns also pass the message to the remote worker for processing by a remote lookup as well. The master node and the worker node have thier application.conf files in the following formart :
src/main/resources/application.conf
however on start up both the master node and the worker node decide to use port number 2552 for their communication. I present the code snippets below :
this i the code for the play framewrok application.config file.
localNode {
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "127.0.0.1"
port = 0
}
}
}
}
this is the configuation for the play localNode
package controllers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import play.libs.F.Callback;
import play.mvc.WebSocket;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.serialization.Serialization;
import akka.serialization.SerializationExtension;
import com.typesafe.config.ConfigFactory;
import Com.RubineEngine.GesturePoints.*;
public class LocalNode {
ActorSystem csystem;
ActorRef localActor ;
public LocalNode() {
//We create the actor container and a child upon initialization
csystem = ActorSystem.create("LocalNode", ConfigFactory.load().getConfig("localNode"));
localActor = csystem.actorOf(new Props(LocalActor.class),"localActor");
}
public void connectMaster (final String classname)
{
localActor.tell(classname);
}
public void connectMaster ()
{
}
public void connectMaster (final WebSocket.In<JsonNode> in, final WebSocket.Out<JsonNode> out )
{
in.onMessage(new Callback<JsonNode>() {
public void invoke(JsonNode event) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
#SuppressWarnings("unchecked")
Map<String,ArrayList<Object>> jsonMap = mapper.readValue(event, Map.class);
GesturePoints gp = new GesturePoints();
gp.setPoints(jsonMap);
localActor.tell(gp);
}
}); }
}
this is the code for the akka actor in the play framework
package controllers;
import Com.RubineEngine.GesturePoints.*;
import akka.actor.ActorRef;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class LocalActor extends UntypedActor {
/**
*
*/
ActorRef masterActor; // = getContext().actorFor("akka://MasterNode#127.0.0.1:2552/user/masterActor");
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
#Override
public void onReceive(Object arg) throws Exception {
System.out.println(" Local Actor 1");
if(arg instanceof GesturePoints)
{ System.out.println(" local Actor 2");
masterActor.tell(" Welcome home " , getSelf());
System.out.println(" Local Actor 3");}
else
{unhandled(arg);}
}
public void preStart()
{
masterActor = getContext().actorFor("akka://MasterNode#127.0.0.1:2553/user/masterActor");
}
}
this is the code for the Master node application.conf
masterNode {
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "127.0.0.1"
remote.netty.port = 2553
}
}
}
}
this is the code for the master node
package Rubine_Cluster;
import java.util.Arrays;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.kernel.Bootable;
/**
* Hello world!
*
*/
public class MasterNode implements Bootable
{
final ActorSystem system;
ActorRef masterActor;
public MasterNode() {
//Create a child actor of this actor upon initialization
system = ActorSystem.create("MasterNode", ConfigFactory.load()
.getConfig("masterNode"));
masterActor = system.actorOf(new Props(MasterActor.class),"masterActor");
}
public void startup() {
}
public void shutdown() {
system.shutdown();
}
}
this is the code for the master akka actor
package Rubine_Cluster;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Map;
import Com.RubineEngine.GesturePoints.*;
import akka.actor.*;
import akka.serialization.Serialization;
import akka.serialization.SerializationExtension;
import akka.serialization.Serializer;
public class MasterActor extends UntypedActor {
/**
*
*/
ActorRef worker1;
#Override
public void onReceive(Object message) throws Exception {
System.out.println(" Master Actor 5");
System.out.println(message);
if(message instanceof GesturePoints)
{ //GesturePoints gp = (GesturePoints) message;
System.out.println(" Master Actor 1");
try { worker1.tell(message, getSelf());
System.out.println(" Master Actor 2");
} catch (Exception e) {
getSender().tell(new akka.actor.Status.Failure(e), getSelf());
throw e;
}
}
else{ unhandled(message);}
}
public void preStart()
{
worker1 = getContext().actorFor("akka://WorkerNode#127.0.0.1:2552/user/workerActor");
}
}
this is the code for the worker node application.conf
workerNode {
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "127.0.0.1"
remote.netty.port = 2552
}
}
}
}
this is the code for the worker nide
package com.theta.gesture;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.kernel.Bootable;
public class WorkerNode implements Bootable{
ActorSystem system;
ActorRef worker;
WorkerNode(){
system = ActorSystem.create("WorkerNode", ConfigFactory.load()
.getConfig("workerNode"));
ActorRef workerActor = system.actorOf(new Props(WorkerActor.class),"workerActor");
}
public void shutdown() {
system.shutdown();
}
public void startup() {
}
}
this is the code for the actor in the worker project
package com.theta.gesture;
import java.util.ArrayList;
import java.util.Map;
import Com.RubineEngine.GesturePoints.GesturePoints;
import akka.actor.*;
public class WorkerActor extends UntypedActor {
private static double DIST_SQ_THRESHOLD = 3 * 3; /* threshold to eliminate mouse jitter */
#Override
public void onReceive(Object msg) throws Exception {
if(msg instanceof GesturePoints)
{ GesturePoints message = (GesturePoints) msg;
initial_Theta(message);}
else {unhandled(msg);}
}
public void initial_Theta(GesturePoints p)
{ System.out.println(" Worker Actor 1");
if(p.getPoints().get("X").size() < 3) //The number of x coordinates as size
{ return;}
System.out.println(" Worker Actor 2");
double magsq,dx,dy, recip;
dx = (double) ((Integer)p.getPoints().get("x").get(2) - (Integer)p.getPoints().get("x").get(0)) ;
dy = ((Double)p.getPoints().get("y").get(2)) - ((Double)p.getPoints().get("y").get(0));
magsq = dx * dx + dy * dy;
if(magsq > DIST_SQ_THRESHOLD)
{
recip = 1/Math.sqrt(magsq);
double initial_cos = dx * recip;
System.out.println(" Worker Actor 3");
double initial_sin = dy * recip;
System.out.println("Feature cos " + initial_cos);
System.out.println("Gesture sin " + initial_sin);
}
} }
this is the console information on the Worker Node
[INFO] [10/08/2012 12:12:44.486] [main] [ActorSystem(WorkerNode)] REMOTE:
RemoteServerStarted#akka://WorkerNode#127.0.0.1:2552
this is the console information on the master nODE
[INFO] [10/08/2012 12:13:34.633] [main] [ActorSystem(MasterNode)] REMOTE:
RemoteServerStarted#akka://MasterNode#127.0.0.1:2552
any idea about the possible course of this situation and a suggested solution is dearly sought after
Your "remote.netty.port = X" inside the nested sections should be "port = X"
Related
I followed an example to create a chat room and everything went fine. Now I have to implement a chat with a multi room. How can I adapt this code so I can handle multi chat rooms? I searched a lot on the internet but I didn't find anything.
package controllers;
import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.event.LoggingAdapter;
import akka.japi.Pair;
import akka.japi.pf.PFBuilder;
import akka.stream.Materializer;
import akka.stream.javadsl.*;
import play.libs.F;
import play.mvc.*;
import play.Logger;
import akka.event.Logging;
import javax.inject.Inject;
import java.net.URL;
import java.util.concurrent.CompletableFuture;
/**
* A very simple chat client using websockets.
*/
public class HomeController extends Controller {
private final Flow userFlow;
#Inject
public HomeController(ActorSystem actorSystem,
Materializer mat) {
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
actorSystem = ActorSystem.create("Chat");
LoggingAdapter logging = Logging.getLogger(actorSystem.eventStream(), logger.getName());
//noinspection unchecked
Source<String, Sink<String, NotUsed>> source = MergeHub.of(String.class)
.log("source", logging)
.recoverWithRetries(-1, new PFBuilder().match(Throwable.class, e -> Source.empty()).build());
Sink<String, Source<String, NotUsed>> sink = BroadcastHub.of(String.class);
Pair<Sink<String, NotUsed>, Source<String, NotUsed>> sinkSourcePair = source.toMat(sink, Keep.both()).run(mat);
Sink<String, NotUsed> chatSink = sinkSourcePair.first();
Source<String, NotUsed> chatSource = sinkSourcePair.second();
this.userFlow = Flow.fromSinkAndSource(chatSink, chatSource).log("userFlow", logging);
}
public Result index() {
Http.Request request = request();
String url = routes.HomeController.chat().webSocketURL(request);
return Results.ok(views.html.index.render(url));
}
public WebSocket chat() {
return WebSocket.Text.acceptOrResult(request -> {
Logger.error("Alguem chegou");
// if (sameOriginCheck(request)) {
// return CompletableFuture.completedFuture(F.Either.Right(userFlow));
// } else {
// return CompletableFuture.completedFuture(F.Either.Left(forbidden()));
// }
return CompletableFuture.completedFuture(F.Either.Right(userFlow));
});
}
/**
* Checks that the WebSocket comes from the same origin. This is necessary to protect
* against Cross-Site WebSocket Hijacking as WebSocket does not implement Same Origin Policy.
*
* See https://tools.ietf.org/html/rfc6455#section-1.3 and
* http://blog.dewhurstsecurity.com/2013/08/30/security-testing-html5-websockets.html
*/
// private boolean sameOriginCheck(Http.RequestHeader request) {
// String[] origins = request.headers().get("Origin");
// if (origins.length > 1) {
// // more than one origin found
// return false;
// }
// String origin = origins[0];
// return originMatches(origin);
// }
private boolean originMatches(String origin) {
if (origin == null) return false;
try {
URL url = new URL(origin);
return url.getHost().equals("localhost")
&& (url.getPort() == 9000 || url.getPort() == 19001);
} catch (Exception e ) {
return false;
}
}
}
I try to run a websocket server in a Java project that was running on Tomcat6. I have set up a Tomcat 7 server where the project now is running on.
First I tried to run the socket example of Tomcat7. This run perfectly. I copied this class to my old project. When I run the old project again all the functionalities are working like before but only the websocket server doe not work.
This is the ChatAnnotation class that I have copied from the examples from Tomcat to my old project.
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.apache.log4j.Logger;
#ServerEndpoint(value = "/websocket/chat")
public class ChatAnnotation {
private static Logger logger = Logger.getLogger(ChatAnnotation.class);
private static final String GUEST_PREFIX = "Guest";
private static final AtomicInteger connectionIds = new AtomicInteger(0);
private static final Set<ChatAnnotation> connections = new CopyOnWriteArraySet<ChatAnnotation>();
private final String nickname;
private Session session;
public ChatAnnotation() {
nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
logger.info("ws instance");
}
#OnOpen
public void start(Session session) {
this.session = session;
connections.add(this);
String message = String.format("* %s %s", nickname, "has joined.");
broadcast(message);
}
#OnClose
public void end() {
connections.remove(this);
String message = String.format("* %s %s", nickname, "has disconnected.");
broadcast(message);
}
#OnMessage
public void incoming(String message) {
// Never trust the client
String filteredMessage = String.format("%s: %s", nickname, message.toString());
broadcast(filteredMessage);
}
#OnError
public void onError(Throwable t) throws Throwable {
logger.error("Chat Error: " + t.toString(), t);
}
private static void broadcast(String msg) {
for (ChatAnnotation client : connections) {
try {
synchronized (client) {
client.session.getBasicRemote().sendText(msg);
}
} catch (IOException e) {
logger.debug("Chat Error: Failed to send message to client", e);
connections.remove(client);
try {
client.session.close();
} catch (IOException e1) {
// Ignore
}
String message = String.format("* %s %s", client.nickname, "has been disconnected.");
broadcast(message);
}
}
}
}
I have noting added in my web.xml. In my old project are also tcpsockets used can this be the problem?
Can anyone help me with this problem?
EDIT
Class added:
import java.util.HashSet;
import java.util.Set;
import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;
import org.apache.log4j.Logger;
public class ExamplesConfig implements ServerApplicationConfig {
private static Logger log = Logger.getLogger(ChatAnnotation.class);
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>();
log.info("getEndpointConfigs");
return result;
}
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
log.info("getAnnotatedEndpointClasses");
return scanned;
}
}
Java websocket server use return value of ServerApplicationConfig interface to deploy programmatic endpoints and for annotated endpoints.
For Tomcat example, if you change the package name of ChatAnnotation. You have to modify websocket.ExamplesConfig too.
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
// Deploy all WebSocket endpoints defined by annotations in the examples
// web application. Filter out all others to avoid issues when running
// tests on Gump
Set<Class<?>> results = new HashSet<>();
for (Class<?> clazz : scanned) {
String name = clazz.getPackage().getName();
boolean ok = name.startsWith("websocket.");
if (ok) {
results.add(clazz);
}
}
return scanned;
}
The getAnnotatedEndpointClasses(scanned) only return classes which package name start with websocket. Unmatched classes will not deployed even they have #ServerEndpoint declarations.
I would like to make a game using LibGDX and Kryonet library, using RMI. So I created clean project. What I want to do for now is, setup server to listen on port 10048 and on new connection to print client's name which will I get by calling a method on client's class...
Here is the code:
ICardsTableImpl.java
package clzola.cardstable.client;
public interface ICardsTableGameImpl {
public String getName();
}
CardsTableServer.java
package clzola.cardstable.server;
import clzola.cardstable.client.ICardsTableGameImpl;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Server;
import com.esotericsoftware.kryonet.rmi.ObjectSpace;
import com.esotericsoftware.minlog.Log;
import java.io.IOException;
import java.util.HashMap;
public class CardsTableServer extends Server {
private HashMap<Integer, Connection> connections;
public CardsTableServer() throws IOException {
connections = new HashMap<Integer, Connection>();
addListener(new NetworkListener(this));
Kryo kryo = getKryo();
ObjectSpace.registerClasses(kryo);
kryo.register(ICardsTableGameImpl.class);
bind(10048);
}
#Override
protected Connection newConnection() {
Player player = new Player();
addConnection(player);
return player;
}
public void addConnection(Connection connection) {
this.connections.put(connection.getID(), connection);
}
public Connection getConnection(int connectionId) {
return this.connections.get(connectionId);
}
public Connection removeConnection(int connectionId) {
return this.connections.remove(connectionId);
}
public static void main(String[] args) throws IOException {
Log.set(Log.LEVEL_DEBUG);
CardsTableServer server = new CardsTableServer();
server.start();
}
}
NetworkListener.java
package clzola.cardstable.server;
import clzola.cardstable.client.ICardsTableGameImpl;
import com.badlogic.gdx.Gdx;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.rmi.ObjectSpace;
public class NetworkListener extends Listener {
private CardsTableServer server;
public NetworkListener(CardsTableServer server) {
this.server = server;
}
#Override
public void connected(Connection connection) {
Player player = ((Player) connection);
ICardsTableGameImpl game = ObjectSpace.getRemoteObject(player, 0, ICardsTableGameImpl.class);
player.name = game.getName(); // This is where I get excpetion...
Gdx.app.log("Server", "Player name: " + player.name);
}
#Override
public void disconnected(Connection connection) {
server.removeConnection(connection.getID());
}
}
Player.java
package clzola.cardstable.server;
import com.esotericsoftware.kryonet.Connection;
public class Player extends Connection {
public String name;
}
CardsTableGame.java
package clzola.cardstable.client;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Client;
import com.esotericsoftware.kryonet.rmi.ObjectSpace;
public class CardsTableGame extends ApplicationAdapter implements ICardsTableGameImpl {
SpriteBatch batch;
Stage stage;
Client client;
String name = "Lazar";
ObjectSpace objectSpace;
#Override
public void create () {
batch = new SpriteBatch();
stage = new Stage(new ScreenViewport(), batch);
try {
client = new Client();
client.start();
Kryo kryo = client.getKryo();
ObjectSpace.registerClasses(kryo);
kryo.register(ICardsTableGameImpl.class);
ObjectSpace objectSpace = new ObjectSpace();
objectSpace.register(0, this);
objectSpace.addConnection(client);
client.connect(5000, "127.0.0.1", 10048);
} catch (Exception e) {
Gdx.app.log("CardsTableGame", e.getMessage(), e);
}
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
#Override
public String getName() {
return this.name;
}
}
After running it, I get exception on the server side:
Exception in thread "Server" java.lang.IllegalStateException: Cannot wait for an RMI response on the connection's update thread.
at com.esotericsoftware.kryonet.rmi.ObjectSpace$RemoteInvocationHandler.waitForResponse(ObjectSpace.java:420)
at com.esotericsoftware.kryonet.rmi.ObjectSpace$RemoteInvocationHandler.invoke(ObjectSpace.java:408)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at clzola.cardstable.server.NetworkListener.connected(NetworkListener.java:24)
at com.esotericsoftware.kryonet.Server$1.connected(Server.java:48)
at com.esotericsoftware.kryonet.Connection.notifyConnected(Connection.java:214)
at com.esotericsoftware.kryonet.Server.acceptOperation(Server.java:417)
at com.esotericsoftware.kryonet.Server.update(Server.java:249)
at com.esotericsoftware.kryonet.Server.run(Server.java:372)
at java.lang.Thread.run(Thread.java:745)
And I have no idea why... What am I doing wrong??
(This is the first time ever I am trying to use RMI)
The Listener is executed by the Kryonet-update-thread. This thread is checking the socket regularly to receive the messages. Calling game.getName() makes the caller wait until the answer was delivered over the network. If you do that on the update thread you'd probably put your server in deadlock because kryonet cannot receive the answer it is waiting on, since you block the update thread. This is why it throws the exception.
In an rmi example from the kryonet git they solve this problem by using a Listener working on its own thread.
// The ThreadedListener means the network thread won't be blocked when waiting for RMI responses.
client.addListener(new ThreadedListener(new Listener() {
public void connected (final Connection connection) {
TestObject test = ObjectSpace.getRemoteObject(connection, 42, TestObject.class);
// Normal remote method call.
assertEquals(43.21f, test.other());
// Make a remote method call that returns another remote proxy object.
OtherObject otherObject = test.getOtherObject();
// Normal remote method call on the second object.
assertEquals(12.34f, otherObject.value());
// When a remote proxy object is sent, the other side recieves its actual remote object.
connection.sendTCP(otherObject);
}
}));
I am using Play Framework 2.2.2 and I am implementing a RabbitMQ consumer application using JavaAkka (Akka Actor System). So I have a MainActor that is initialized when the Play application comes up using Global.OnStart functions. The MainActor creates a RabbitMQ channel and then starts a consuming from a queue. Each message in that queue is a name of another queue that has to assigned to another child actor or sub actor that has to start consuming from the queue mentioned in the message. So essentially, I have one MainActor that is subscribed to ONE RabbitMQ queue and several child actors that are created by the Main actor, each of the child actor are subscribed to their own RabbitMQ queues. The problem is that I can't bring up more than 7 child actors for some reason. I suspect it is the while(true) construct in the child actor that waits for messages from RabbitMQ. Here is my implementation:
Main Actor:
import play.Logger;
import com.typesafe.config.ConfigFactory;
import java.io.IOException;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.ActorRef;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import play.libs.Akka;
import util.RabbitMQConnection;
public class MainActor extends UntypedActor {
#Override
public void onReceive(Object msg) throws Exception {
try{
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
String main_queue_name = ConfigFactory.load().getString("rabbitmq.default_queue");
channel.queueDeclare(main_queue_name, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(main_queue_name, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
ActorRef childActor = getContext().actorOf(Props.create(childActor.class));
childActor.tell(message, getSelf());
}
}catch (Exception e){
System.out.println(e.toString());
}
}
}
Child Actor:
import play.Logger;
import com.typesafe.config.ConfigFactory;
import java.io.IOException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import play.libs.Akka;
import play.libs.Json;
import akka.actor.UntypedActor;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import util.RabbitMQConnection;
public class childActor extends UntypedActor {
#Override
public void onReceive(Object msg) throws Exception {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String queue_name = ow.writeValueAsString(msg);
try{
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(queue_name, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queue_name, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
JsonNode jsonMsg = Json.parse(message);
// Call some function to process the message
}
}catch (Exception e){
System.out.println(e.toString());
}
}
}
I think you are not using the Actor correctly in this case. In my opinion you should not have a while(true) inside of a receive method for a given actor. Also, QueueingConsumer was deprecated and the rabbitmq guys recommend to implement you consumer using the interface Consumer or the default no-op implementation DefaultConsumer.
The way I would do it is:
Implement a customized consumer for rabbitmq that will send a message to the actor every time it gets something.
Use that implementation for the main actor. Send the queue name as a message and start a new child actor with the queue name as a constructor field.
Use that implementation for the child actors. Send the message received to the actor and do the JSON parsing in the actor itself.
Some code here: (WARNING: NOT COMPILED OR TEST)
Custom rabbitmq consumer:
public class MyCustomRabbitMQConsumer extends DefaultConsumer {
private ActorRef destinationActor;
public MyCustomRabbitMQConsumer(ActorRef destinationActor) {
this.destinationActor = destinationActor;
}
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
destinationActor.tell(new String(body));
}
}
Main actor:
import play.Logger;
import com.typesafe.config.ConfigFactory;
import java.io.IOException;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.ActorRef;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import play.libs.Akka;
import util.RabbitMQConnection;
public class MainActor extends UntypedActor {
private MyCustomRabbitMQConsumer rabbitConsumer;
#Override
public void preStart() {
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
String main_queue_name = ConfigFactory.load().getString("rabbitmq.default_queue");
channel.queueDeclare(main_queue_name, false, false, false, null);
rabbitConsumer = new MyCustomRabbitMQConsumer(getSelf());
channel.basicConsume(main_queue_name, true, rabbitConsumer);
}
#Override
public void onReceive(Object msg) throws Exception {
if(msg instanceOf String) {
String queueName = (String) msg;
System.out.println(" [x] Received '" + queueName + "'");
getContext().actorOf(Props.create(childActor.class, queueName));
}
}
}
ChildActor:
import akka.actor.UntypedActor;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
import util.RabbitMQConnection;
public class ChildActor extends UntypedActor {
private MyCustomRabbitMQConsumer rabbitConsumer;
private String queueName;
public ChildActor(String queueName) {
this.queueName = queueName;
}
#Override
public void preStart() {
Connection connection = RabbitMQConnection.getConnection();
Channel channel = connection.createChannel();
String main_queue_name = ConfigFactory.load().getString("rabbitmq.default_queue");
channel.queueDeclare(queueName, false, false, false, null);
rabbitConsumer = new MyCustomRabbitMQConsumer(getSelf());
channel.basicConsume(queueName, true, rabbitConsumer);
}
#Override
public void onReceive(Object msg) throws Exception {
if(msg instanceOf String) {
String strMsg = (String) msg;
JsonNode jsonMsg = Json.parse(message);
// Call some function to process the message
}
}
}
This should work for n number of actors.
I have a Grizzly Http Server with Async processing added. It is queuing my requests and processing only one request at a time, despite adding async support to it.
Path HttpHandler was bound to is: "/"
Port number: 7777
Behavior observed when I hit http://localhost:7777 from two browsers simultaneously is:
Second call waits till first one is completed. I want my second http call also to work simultaneously in tandom with first http call.
EDIT Github link of my project
Here are the classes
GrizzlyMain.java
package com.grizzly;
import java.io.IOException;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.strategies.WorkerThreadIOStrategy;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
import com.grizzly.http.IHttpHandler;
import com.grizzly.http.IHttpServerFactory;
public class GrizzlyMain {
private static HttpServer httpServer;
private static void startHttpServer(int port) throws IOException {
URI uri = getBaseURI(port);
httpServer = IHttpServerFactory.createHttpServer(uri,
new IHttpHandler(null));
TCPNIOTransport transport = getListener(httpServer).getTransport();
ThreadPoolConfig config = ThreadPoolConfig.defaultConfig()
.setPoolName("worker-thread-").setCorePoolSize(6).setMaxPoolSize(6)
.setQueueLimit(-1)/* same as default */;
transport.configureBlocking(false);
transport.setSelectorRunnersCount(3);
transport.setWorkerThreadPoolConfig(config);
transport.setIOStrategy(WorkerThreadIOStrategy.getInstance());
transport.setTcpNoDelay(true);
System.out.println("Blocking Transport(T/F): " + transport.isBlocking());
System.out.println("Num SelectorRunners: "
+ transport.getSelectorRunnersCount());
System.out.println("Num WorkerThreads: "
+ transport.getWorkerThreadPoolConfig().getCorePoolSize());
httpServer.start();
System.out.println("Server Started #" + uri.toString());
}
public static void main(String[] args) throws InterruptedException,
IOException, InstantiationException, IllegalAccessException,
ClassNotFoundException {
startHttpServer(7777);
System.out.println("Press any key to stop the server...");
System.in.read();
}
private static NetworkListener getListener(HttpServer httpServer) {
return httpServer.getListeners().iterator().next();
}
private static URI getBaseURI(int port) {
return UriBuilder.fromUri("https://0.0.0.0/").port(port).build();
}
}
HttpHandler (with async support built in)
package com.grizzly.http;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import javax.ws.rs.core.Application;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.grizzly.http.server.Response;
import org.glassfish.grizzly.http.util.HttpStatus;
import org.glassfish.grizzly.threadpool.GrizzlyExecutorService;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
import org.glassfish.jersey.server.ApplicationHandler;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spi.Container;
import com.grizzly.Utils;
/**
* Jersey {#code Container} implementation based on Grizzly
* {#link org.glassfish.grizzly.http.server.HttpHandler}.
*
* #author Jakub Podlesak (jakub.podlesak at oracle.com)
* #author Libor Kramolis (libor.kramolis at oracle.com)
* #author Marek Potociar (marek.potociar at oracle.com)
*/
public final class IHttpHandler extends HttpHandler implements Container {
private static int reqNum = 0;
final ExecutorService executorService = GrizzlyExecutorService
.createInstance(ThreadPoolConfig.defaultConfig().copy()
.setCorePoolSize(4).setMaxPoolSize(4));
private volatile ApplicationHandler appHandler;
/**
* Create a new Grizzly HTTP container.
*
* #param application
* JAX-RS / Jersey application to be deployed on Grizzly HTTP
* container.
*/
public IHttpHandler(final Application application) {
}
#Override
public void start() {
super.start();
}
#Override
public void service(final Request request, final Response response) {
System.out.println("\nREQ_ID: " + reqNum++);
System.out.println("THREAD_ID: " + Utils.getThreadName());
response.suspend();
// Instruct Grizzly to not flush response, once we exit service(...) method
executorService.execute(new Runnable() {
#Override
public void run() {
try {
System.out.println("Executor Service Current THREAD_ID: "
+ Utils.getThreadName());
Thread.sleep(25 * 1000);
} catch (Exception e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
} finally {
String content = updateResponse(response);
System.out.println("Response resumed > " + content);
response.resume();
}
}
});
}
#Override
public ApplicationHandler getApplicationHandler() {
return appHandler;
}
#Override
public void destroy() {
super.destroy();
appHandler = null;
}
// Auto-generated stuff
#Override
public ResourceConfig getConfiguration() {
return null;
}
#Override
public void reload() {
}
#Override
public void reload(ResourceConfig configuration) {
}
private String updateResponse(final Response response) {
String data = null;
try {
data = new Date().toLocaleString();
response.getWriter().write(data);
} catch (IOException e) {
data = "Unknown error from our server";
response.setStatus(500, data);
}
return data;
}
}
IHttpServerFactory.java
package com.grizzly.http;
import java.net.URI;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.http.server.ServerConfiguration;
/**
* #author smc
*/
public class IHttpServerFactory {
private static final int DEFAULT_HTTP_PORT = 80;
public static HttpServer createHttpServer(URI uri, IHttpHandler handler) {
final String host = uri.getHost() == null ? NetworkListener.DEFAULT_NETWORK_HOST
: uri.getHost();
final int port = uri.getPort() == -1 ? DEFAULT_HTTP_PORT : uri.getPort();
final NetworkListener listener = new NetworkListener("IGrizzly", host, port);
listener.setSecure(false);
final HttpServer server = new HttpServer();
server.addListener(listener);
final ServerConfiguration config = server.getServerConfiguration();
if (handler != null) {
config.addHttpHandler(handler, uri.getPath());
}
config.setPassTraceRequest(true);
return server;
}
}
It seems the problem is the browser waiting for the first request to complete, and thus more a client-side than a server-side issue. It disappears if you test with two different browser processes, or even if you open two distinct paths (let's say localhost:7777/foo and localhost:7777/bar) in the same browser process (note: the query string partecipates in making up the path in the HTTP request line).
How I understood it
Connections in HTTP/1.1 are persistent by default, ie browsers recycle the same TCP connection over and over again to speed things up. However, this doesn't mean that all requests to the same domain will be serialized: in fact, a connection pool is allocated on a per-hostname basis (source). Unfortunately, requests with the same path are effectively enqueued (at least on Firefox and Chrome) - I guess it's a device that browsers employ to protect server resources (and thus user experience)
Real-word applications don't suffer from this because different resources are deployed to different URLs.
DISCLAIMER: I wrote this answer based on my observations and some educated guess. I think things may actually be like this, however a tool like Wireshark should be used to follow the TCP stream and definitely assert this is what happens.