I'm planning to use Netty to design a TCP Server. When the client connects, I have to immediately start pumping
XML data to the client continuously...for hours/days. Its that simple.
So, I override "channelConnected" method and send data from that method, right?...thats great.
I will be using the following ChannelFactory
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
NioServerSocketChannelFactory documentation says
A worker thread performs non-blocking read and write for one or more Channels in a non-blocking mode.
Good.
According to effective Java Item 51: Don't depend on the thread scheduler, I want the worker thread to do a "unit of work" and then finish/return.
So in my case, though I have to send data continuously, I want to send some chunk (lets say 1 MB) and then be done (unit of work completed), so that worker thread can return. Then I'll send another 1 MB.
Below example is from the official guide of Netty HERE.
I guess the question is then, in this scenario, if i had to unconditionally keep sending time to the client, how would I do it, considering
each send as a unit of work.
One way of doing it would be to just put a while loop and do a Thread.Sleep. Any other way?
package org.jboss.netty.example.time;
public class TimeServerHandler extends SimpleChannelHandler {
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
Channel ch = e.getChannel();
ChannelBuffer time = ChannelBuffers.buffer(4);
time.writeInt(System.currentTimeMillis() / 1000);
ChannelFuture f = ch.write(time);
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
Channel ch = future.getChannel();
ch.close();
}
});
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
e.getChannel().close();
}
}
Doing a while/sleep would work, but would not be in the Netty super-scalable style. It would be thread-per-connection programming.
Instead, schedule a periodic job on an Executor that writes a message to the channel.
Related
I have a JMS application which tries to read from a JBosss Queue. I implemented MessageListener on my class and used the onMessage() to receive messages
public class JBossConnector implements MessageListener, AutoCloseable {}
Here is my method:
/**
* The listener method of JMS. It listens to messages from queue: 'jbossToAppia'
* If the message is of type MessageObject, then transfer that to Appia
*
* #param message JMS Message
*/
#Override
public void onMessage(Message message) {
// receive the message from jboss queue: 'jbossToAppia'
// then post it to appia
if (message instanceof ObjectMessage) {
try {
MessageObject messageObject = (MessageObject) ((ObjectMessage) message).getObject();
System.out.printf("JbossConnector: MessageObject received from JBOSS, %s\n", messageObject.getMessageType());
component.onMessageFromJboss(properties.getProperty("target.sessionID"), messageObject);
} catch (MessageFormatException exception) {
logger.error(ExceptionHandler.getFormattedException(exception));
ExceptionHandler.printException(exception);
} catch (JMSException exception) {
ExceptionHandler.printException(exception);
restart();
}
} else {
System.out.printf("%s: MessageFormatException(Message is not of the format MessageObject)\n", this.getClass().getSimpleName());
}
}
Whenever I find a JMSException I try to restart JBoss connection (Context, Connection, Session, Receiver, Sender). What my doubt is that I've read onMessage() uses multiple threads to receive messages from queue (correct me if I'm wrong).
When the JBoss queue connection severs, there would be at least some queues that throw this exception. That means they all will try to restart() the connection which is a waste of time (restart() first closes all the connections, sets the variables to null and then attempt to initiate connections).
Now I could do something like
synchronized (this){
restart();
}
or use volatile variables. But that would not guarantee that other threads won't attempt to restart() when current threads finishes the restart() operation (again correct me if I'm wrong).
Is there any solution to make this work?
The onMessage() of a MessageListener is indeed run from its own thread so you'll need proper concurrency controls. I think the simplest solution would just be to use a java.util.concurrent.atomic.AtomicBoolean. For example, in your restart() method you could do something like this:
private void restart() {
AtomicBoolean restarting = new AtomicBoolean(false);
if (!restarting.getAndSet(true)) {
// restart connection, session, etc.
}
}
This will make the restart() method effectively idempotent. Multiple threads will be able to call restart() but only the first thread which calls it will actually cause the resources to get re-created. All other calls will return immediately.
I want to read a message at a specific position in an class other than InboundHandler. I can't find a way to read it expect in the channelRead0 method, which is called from the netty framework.
For example:
context.writeMessage("message");
String msg = context.readMessage;
If this is not possible, how can I map a result, which I get in the channelRead0 method to a specific call I made in another class?
The Netty framework is designed to be asynchronously driven. Using this analogy, it can handle large amount of connections with minimal threading usage. I you are creating an api that uses the netty framework to dispatch calls to a remote location, you should use the same analogy for your calls.
Instead of making your api return the value direct, make it return a Future<?> or a Promise<?>. There are different ways of implementing this system in your application, the simplest way is creating a custom handler that maps the incoming requests to the Promises in a FIFO queue.
An example of this could be the following:
This is heavily based on this answer that I submitted in the past.
We start with out handler that maps the requests to requests in our pipeline:
public class MyLastHandler extends SimpleInboundHandler<String> {
private final SynchronousQueue<Promise<String>> queue;
public MyLastHandler (SynchronousQueue<Promise<String>> queue) {
super();
this.queue = queue;
}
// The following is called messageReceived(ChannelHandlerContext, String) in 5.0.
#Override
public void channelRead0(ChannelHandlerContext ctx, String msg) {
this.queue.remove().setSuccss(msg);
// Or setFailure(Throwable)
}
}
We then need to have a method of sending the commands to a remote server:
Channel channel = ....;
SynchronousQueue<Promise<String>> queue = ....;
public Future<String> sendCommandAsync(String command) {
return sendCommandAsync(command, new DefaultPromise<>());
}
public Future<String> sendCommandAsync(String command, Promise<String> promise) {
synchronized(channel) {
queue.offer(promise);
channel.write(command);
}
channel.flush();
}
After we have done our methods, we need a way to call it:
sendCommandAsync("USER anonymous",
new DefaultPromise<>().addListener(
(Future<String> f) -> {
String response = f.get();
if (response.startWidth("331")) {
// do something
}
// etc
}
)
);
If the called would like to use our a api as a blocking call, he can also do that:
String response = sendCommandAsync("USER anonymous").get();
if (response.startWidth("331")) {
// do something
}
// etc
Notice that Future.get() can throw an InterruptedException if the Thread state is interrupted, unlike a socket read operation, who can only be cancelled by some interaction on the socket. This exception should not be a problem in the FutureListener.
for (int i = 1; i <= 100; i++) {
ctx.writeAndFlush(Unpooled.copiedBuffer(Integer.toString(i).getBytes(Charsets.US_ASCII)));
}
ctx.writeAndFlush(Unpooled.copiedBuffer("ABCD".getBytes(Charsets.US_ASCII))).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
ctx.channel().close();
}
});
I write this in the channelRead() mehtod of my netty server handler, it will reponse "12345...100ABCD" back to the client as soon as the server receive a request.
As far as I see, the order of the message client received from the netty server is always "12345...100ABCD".
I don't know is this just by chance? Maybe sometime it would be "32451...ABCD100" (out of the server write order)?
Is it possible that the server execute
clientChannel.writeAndFlush(msg1);
clientChannel.writeAndFlush(msg2);
clientChannel.writeAndFlush(msg3);
but the client received msg2-msg1-msg3 or msg3-msg1-msg2 but not the write order msg1-msg2-msg3
In the proxy sample of netty project, https://github.com/netty/netty/tree/master/example/src/main/java/io/netty/example/proxy
the HexDumpProxyBackendHandler writes:
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
ctx.channel().read();
} else {
future.channel().close();
}
}
});
}
It makes sure that it trigger next channelRead() (That is inboundChannel.writeAndFlush(msg) in channelRead()) only if the wirteAndFlush() operation is finished.
So what's the purpose to write ctx.channel().read() in the listener and execute it when future.isSuccess() ? Isn't it to make sure that the messages writes to the client are received in a right order?
If I change it to
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
inboundChannel.writeAndFlush(msg);
ctx.channel().read();
}
Will it cause some issues?
No it is not possible. TCP ensures that.
As EJP states either technique should guarantee the ordering. The difference between the example and how you've changed it is a question of flow control.
In the original example the inbound channel will only be read after the data has been successfully flushed to the network buffers. This guarantees that it only reads data as fast as it can send it, preventing Netty's send queue from building up and thus preventing out of memory errors.
The altered code reads as soon as the write operation is queued. If the outbound channel is unable to keep up there's a chance you could see out of memory errors if you're transferring a lot of data.
I am using Netty 3.6.6.Final and trying to implement write timeout for my handler such that on timeout I need to write specific response. In addition I need to cancel another write response which is currently executing in the pipeline (or will be executing).
Here is my current pipeline:
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(LOGGER,
new HttpServerCodec(),
new MyHttpContentDecoder(),
new IdleStateHandler(timer, 0, 1000, 0, TimeUnit.MILLISECONDS),
handler.get());
}
});
Handler extends IdleStateAwareChannelHandler and implementing channelIdle method where I check for write timeout:
if (e.getState() == IdleState.WRITER_IDLE) {
e.getChannel().write(SOME_RESPONSE).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
future.getChannel().close();
}});
}
The question is how do I cancel write which I have planned in messageReceived method in case no timeout occurs. Is there something customary in Netty to deal with such a problem?
EDIT
Cancelling via ChannelFuture does not work. As far as I understand most of the time write will not be cancelled. During my tests it was all the time, i.e. cancel() always returned false. So I guess it is really hard to achieve it this way.
In the end I have updated the code to the latest release - 4.0.9.Final (much nicer API).
And all of the sudden, I received responses as a result of the write timeout. That didn't work this way in 3.6.6.Final.
In 4.0.9.Final the code for handling write timeout is a bit different but I always get a second write on timeout (if I comment ctx.writeAndFlush below, then I am getting write from channelRead0):
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.WRITER_IDLE) {
//the following condition was always false
//(channelFuture is a state variable of my handler for previous write)
if (channelFuture != null && channelFuture.isCancellable()) {
System.out.println("Cancel "+channelFuture.cancel(true));
}
ctx.writeAndFlush(SOME_RESPONSE);
}
}
}
Don't know if it is the right way to "overwrite" first write attempt when timeout occurs, and would be glad if someone can explain why it works and what was changed in the latest release regarding this scenario.
when message is flushed, the promise is set to be uncancellable. Once a message is write out one byte, it must be write completely so that decoder can deal with a Stream-based Transport
You can try to Channel the ChannelFuture returned by the write operation.
I'm looking to write a small client-server-based text game that handles multiple client connections and persistently affects a game state. I'm wondering what the best way would be to handle multiple connects such that commands are processed in the order they arrive at the server.
Ideally I'm not looking to take advantage of multi-threading, at least on the command processing level. I would be okay with each client having a separate thread (in order to have blocking IO on each thread), as long as I could unify the processing in a single thread thereafter.
Since the only communication between the client and server will be text, I'm not sure how best to go about setting up the communication. If I chose blocking IO, how would I get queue the processing to occur in a single thread?
Alternatively, if I choose non-blocking IO and use a selector to query for when clients have written to the server, how can I get read a String of unknown/unlimited length without using a set-size ByteBuffer? Non-blocking also favours keeping the processing in a single thread as it can just read from the client connections as and when they send new data. However, when I tried to implement it with read/writeUTF I came up against the IllegalBlockingModeException heh.
Any answers to the questions or suggestions on how to do this in a way I haven't mentioned would be sincerely appreciated! I'm fairly new to clients and servers so I don't know whether java.io or java.nio would be most appropriate.
Sorry for the convoluted question. I think I ran away with myself.
Opinions differ, but I'd definitely go with a single thread per client. The communication to the single processing thread could then go via a LinkedBlockingQueue, or just a synchronized LinkedList.
Something like this on the per-client thread:
public class Client implements Runnable, ResponseOutput {
private final BufferedReader br;
private final PrintWriter pw;
public Client(Socket s) {
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
pw = new PrintWriter(s.getOutputStream());
}
// defined by the ResponseOutput interface
public void sendReply(String reply) {
pw.println(reply);
}
public void run() {
try {
while (true) {
String s = br.readLine();
if (s==null)
break;
Processor.queue(new Processor.InputItem(this, s));
}
} catch (IOException ioe) {
... error handling ...
}
}
}
Then this for the processing:
public class Processor implements Runnable {
static public class InputItem {
final ResponseOutput client;
final String command;
public InputItem(ResponseOutput client, String command) {
this.client = client;
this.command = command;
}
}
static private Processor instance;
static public void queue(InputItem item) {
instance.commandQueue.add(item);
}
private BlockingQueue<InputItem> commandQueue;
public void run() {
try {
while (true) {
InputItem item = commandQueue.take();
String reply = doStuff(item.command);
item.client.sendReply(reply);
}
} catch (InterruptedException ie) {
... error handling ....
}
}
}
Within the InputItem class, you can also include a reference to any game state that needs updating. Since there's only the processing thread changing it, you get to do that without any synchronization.
i'm no expert in sever client systems but I'll share a couple of tips
Depending on your need you could simply set up a Tomcat server and do http request, its fairly straight forwards and of course all in Java.
the downside is that the request might be a bit slow.
The Second option you can check out is RMI.
The concept is simple you connect to another computer and when that is done you call methods on the other computer from a local object in you code.
http://java.sun.com/developer/onlineTraining/rmi/RMI.html
it might look a bit complicated (and sometimes debbuging a stack through multiple computer is a bit tricky) but I recommend because it keeps your code clear.
Finally you can try sockets but there your on your own :P