Getting ClassNotFound Exception in Flink SourceFunction - java

I'm using protocol buffer to send stream of data to Apache Flink.
I have two classes. one is Producer and one is Consumer.
Producer is a java thread class which reads the data from socket and Protobuf deserializes it and then I store it in my BlockingQueue
Consumer is a class which implements SourceFunction in Flink.
I tested this program with using:
DataStream<Event.MyEvent> stream = env.fromCollection(queue);
instead of custom source and it works fine.
But when I try to use a SourceFunction class it throws this exception:
Caused by: java.lang.RuntimeException: Unable to find proto buffer class
at com.google.protobuf.GeneratedMessageLite$SerializedForm.readResolve(GeneratedMessageLite.java:775)
...
Caused by: java.lang.ClassNotFoundException: event.Event$MyEvent
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
...
And in another attempt I mixed both classed into one (the class which implements SourceFunction). I get data from socket and deserialize it with protobuf and store it in BlockingQueue and then I read from BlockingQueue right after that. My code works fine with this approach too.
But I want to use two separate classes (multi-threading) but it throws that exception.
I'm trying to solve it in last 2 days and also did lots of searching but no luck.
Any help would be apperciated.
Producer:
public class Producer implements Runnable {
Boolean running = true;
Socket socket = null, bufferSocket = null;
PrintStream ps = null;
BlockingQueue<Event.MyEvent> queue;
final int port;
public Producer(BlockingQueue<Event.MyEvent> queue, int port){
this.port = port;
this.queue = queue;
}
#Override
public void run() {
try {
socket = new Socket("127.0.0.1", port);
bufferSocket = new Socket(InetAddress.getLocalHost(), 6060);
ps = new PrintStream(bufferSocket.getOutputStream());
while (running) {
queue.put(Event.MyEvent.parseDelimitedFrom(socket.getInputStream()));
ps.println("Items in Queue: " + queue.size());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Consumer:
public class Consumer implements SourceFunction<Event.MyEvent> {
Boolean running = true;
BlockingQueue<Event.MyEvent> queue;
Event.MyEvent event;
public Consumer(BlockingQueue<Event.MyEvent> queue){
this.queue = queue;
}
#Override
public void run(SourceContext<Event.MyEvent> sourceContext) {
try {
while (running) {
event = queue.take();
sourceContext.collect(event);
}
}catch (Exception e){
e.printStackTrace();
}
}
#Override
public void cancel() {
running = false;
}
}
Event.MyEvent is my protobuf class. I'm using version 2.6.1 and I compiled classes with v2.6.1 . I double checked the versions to be sure it's not the problem.
The Producer class is working fine.
I tested this with both Flink v1.1.3 and v1.1.4.
I'm running it in local mode.
EDIT: Answer was included in question, posted it separately and removed it here.
UPDATE 12/28/2016
...
But I'm still curious. What is causing this error? Is it a bug in Flink or am I doing something wrong?
...

The asker already found a way to make this working. I have extracted the relevant part from the question. Note that the reason why it happened remains unexplained.
I did not use quote syntax as it is a lot of text, but the below was shared by the asker:
So finally I got it to work. I created my BlockingQueue object inside SourceFunction (Consumer), and called Producer class from inside the SourceFunction class (Consumer) instead of making BlockingQueue and calling Producer class in main method of the program. and it now works!
Here's my full working code in Flink:
public class Main {
public static void main(String[] args) throws Exception {
final int port, buffer;
//final String ip;
try {
final ParameterTool params = ParameterTool.fromArgs(args);
port = params.getInt("p");
buffer = params.getInt("b");
} catch (Exception e) {
System.err.println("No port number and/or buffer size specified.");
return;
}
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<Event.MyEvent> stream = env.addSource(new Consumer(port, buffer));
//DataStream<Event.MyEvent> stream = env.fromCollection(queue);
Pattern<Event.MyEvent, ?> crashedPattern = Pattern.<Event.MyEvent>begin("start")
.where(new FilterFunction<Event.MyEvent>() {
#Override
public boolean filter(Event.MyEvent myEvent) throws Exception {
return (myEvent.getItems().getValue() >= 120);
}
})
.<Event.MyEvent>followedBy("next").where(new FilterFunction<Event.MyEvent>() {
#Override
public boolean filter(Event.MyEvent myEvent) throws Exception {
return (myEvent.getItems().getValue() <= 10);
}
})
.within(Time.seconds(3));
PatternStream<Event.MyEvent> crashed = CEP.pattern(stream.keyBy(new KeySelector<Event.MyEvent, String>() {
#Override
public String getKey(Event.MyEvent myEvent) throws Exception {
return myEvent.getEventType();
}
}), crashedPattern);
DataStream<String> alarm = crashed.select(new PatternSelectFunction<Event.MyEvent, String>() {
#Override
public String select(Map<String, Event.MyEvent> pattern) throws Exception {
Event.MyEvent start = pattern.get("start");
Event.MyEvent next = pattern.get("next");
return start.getEventType() + " | Speed from " + start.getItems().getValue() + " to " + next.getItems().getValue() + " in 3 seconds\n";
}
});
DataStream<String> rate = alarm.windowAll(TumblingProcessingTimeWindows.of(Time.seconds(1)))
.apply(new AllWindowFunction<String, String, TimeWindow>() {
#Override
public void apply(TimeWindow timeWindow, Iterable<String> iterable, Collector<String> collector) throws Exception {
int sum = 0;
for (String s: iterable) {
sum ++;
}
collector.collect ("CEP Output Rate: " + sum + "\n");
}
});
rate.writeToSocket(InetAddress.getLocalHost().getHostName(), 7070, new SimpleStringSchema());
env.execute("Flink Taxi Crash Streaming");
}
private static class Producer implements Runnable {
Boolean running = true;
Socket socket = null, bufferSocket = null;
PrintStream ps = null;
BlockingQueue<Event.MyEvent> queue;
final int port;
Producer(BlockingQueue<Event.MyEvent> queue, int port){
this.port = port;
this.queue = queue;
}
#Override
public void run() {
try {
socket = new Socket("127.0.0.1", port);
bufferSocket = new Socket(InetAddress.getLocalHost(), 6060);
ps = new PrintStream(bufferSocket.getOutputStream());
while (running) {
queue.put(Event.MyEvent.parseDelimitedFrom(socket.getInputStream()));
ps.println("Items in Queue: " + queue.size());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
private static class Consumer implements SourceFunction<Event.MyEvent> {
Boolean running = true;
final int port;
BlockingQueue<Event.MyEvent> queue;
Consumer(int port, int buffer){
queue = new ArrayBlockingQueue<>(buffer);
this.port = port;
}
#Override
public void run(SourceContext<Event.MyEvent> sourceContext) {
try {
new Thread(new Producer(queue, port)).start();
while (running) {
sourceContext.collect(queue.take());
}
}catch (Exception e){
e.printStackTrace();
}
}
#Override
public void cancel() {
running = false;
}
}

Related

How can I get the data from a thread that acts as a UDP server?

I am making an Android application that receives messages from an Arduino, I implemented a UDP server opening a Thread, but I can not get the value of the answer string "lastMessage", because with this value I will make a series of actions.
This is my class served_UDP:
public class Servidor_UDP {
private boolean server_activado = true;
private String lastMessage = "";
DatagramSocket socket;
private byte[] resp;
private DatagramPacket pqtResp;
Servidor_UDP()
{
resp = new byte[1024];
try {
socket = new DatagramSocket(6000);
pqtResp = new DatagramPacket(resp, resp.length);
} catch (SocketException e) {
e.printStackTrace();
}
}
public void start(){
Thread t = new Thread(new server());
t.start();
}
public class server implements Runnable {
server() { run(); }
public void run() {
String message = "";
try {
do
{
socket.receive(pqtResp);
//message = new String(resp).trim();
message = new String(pqtResp.getData(),0,pqtResp.getLength());
lastMessage = message;
} while(server_activado);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public String getString()
{
return lastMessage;
}
public void setServer(boolean b)
{
server_activado = b;
}
}
This class I implemented from the onCreate of my MainActivity:
Servidor_UDP UDP_S;
UDP_S = new Servidor_UDP();
UDP_S.start();
I try to get the results of a method of the main class and show them in a TextView to make sure that the messages are arriving but it does not show me anything, just empty.
public void actualizarUI()
{
respuesta = UDP_S.getString();
txt.setText(respuesta);
}
I await your help, thanks in advance.
Your server() constructor should not call run(). Thread.start() will do that. At present Thread.start() is never executing, let alone completing, so any code that calls your start() method will never return.
EDIT: The other answer is right!
Ok so, I think you have the problem when creating the new server()...
You could try this:
private server server;
public void start(){
server = new server();
Thread t = new Thread(server);
t.start();
}
public class server implements Runnable {
server() { /*NO run();*/ }
public void run() {
String message = "";
try {
do
{
socket.receive(pqtResp);
//message = new String(resp).trim();
message = new String(pqtResp.getData(),0,pqtResp.getLength());
lastMessage = message;
} while(server_activado);
} catch (Exception e) {
e.printStackTrace();
}
}
public String getLastMessage() {
return lastMessage;
}
}
public String getString()
{
return server.getLastMessage();
}
You are doing new server(); so I you are creating a new object, you should save it in a class variable like I did, and then create a get method in the server class...
PD: Intenta no hacer codigo en castellano :)

Process items in queue as the items are added inside a while loop

I have a method where I listen for UDP packets in a while loop. I want to parse the packets using another method in a different class as they arrive and do many different parsing and analyzing of each packet in another part of the application. I am thinking it would be better to have the PacketParser methods process the Queue outside of the loop. Would it be possible to just add the packets to a Queue as they come in and then have another part of the application listen for items as they come into the Queue and perform other actions as the original while loop keeps listening for packets and adds them to the queue? I would like to have another function monitor the queue and process the packets, is there something in Java to monitor a Queue or Stack? Is there a better way to do this?
public void read(String multicastIpAddress, int multicastPortNumber) {
PacketParser parser = new PacketParser(logger);
InetAddress multicastAddress = null;
MulticastSocket multicastSocket = null;
final int PortNumber = multicastPortNumber;
try {
multicastAddress = InetAddress.getByName(multicastIpAddress);
multicastSocket = new MulticastSocket(PortNumber);
String hostname = InetAddress.getLocalHost().getHostName();
byte[] buffer = new byte[8192];
multicastSocket.joinGroup(multicastAddress);
System.out.println("Listening from " + hostname + " at " + multicastAddress.getHostName());
int numberOfPackets = 0;
while (true) {
numberOfPackets++;
DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);
multicastSocket.receive(datagramPacket);
// add to queue for another function to process the packets
}
} catch (SocketException socketException) {
System.out.println("Socket exception " + socketException);
} catch (IOException exception) {
System.out.println("Exception " + exception);
} finally {
if (multicastSocket != null) {
try {
multicastSocket.leaveGroup(multicastAddress);
multicastSocket.close();
} catch (IOException exception) {
System.out.println(exception.toString());
}
}
}
}
Ok, so I did some reading about the producer-consumer pattern and figured it out so here is what I did.
Basically the producer-consumer pattern involves three things: a producer, a consumer and a shared queue. In this context the PacketReader is the producer that takes in network packets and places them into the shared queue. The PacketParser is the consumer who processes the packets in the shared queue. So I created an instance of a LinkedBlockingQueue and passed that shared queue into an instance of the consumer (PacketReader) and an instance of the producer (PacketParser). Then the consumer and producer instances are each passed into an instance of the Thread class. Finally, call the start() method on each thread instance.
public class Main {
public static void main(String[] args) {
BlockingQueue<Packet> queue = new LinkedBlockingQueue<>();
ILogger logger = Injector.getLogger();
Thread reader = new Thread(new PacketReader(logger, queue, "239.1.1.1", 49410));
Thread parser = new Thread(new PacketParser(logger, queue));
reader.start();
parser.start();
}
}
The reason to use the LinkedBlockingQueue is because the put() method will block the queue if full and take() will block if queue if empty. The producer and consumer classes need to implement the Runnable interface and contain a method named run() that takes no parameters.
Consumer class
public class PacketParser implements Runnable {
private ILogger logger;
private BlockingQueue<Packet> queue;
private boolean running = true;
public PacketParser(ILogger logger, BlockingQueue<Packet> queue) {
this.logger = logger;
this.queue = queue;
}
public void stop() {
running = false;
}
public void run() {
while (running) {
Packet packet;
try {
packet = queue.take();
parse(packet);
} catch (InterruptedException exception) {
logger.Log(exception.getStackTrace().toString());
}
}
}
Producer class
public class PacketReader implements Runnable {
private ILogger logger;
private final Queue<Packet> queue;
private String multicastIpAddress;
private int multicastPortNumber;
private boolean running = true;
public PacketReader(ILogger logger, Queue<Packet> queue, String multicastIpAddress, int multicastPortNumber) {
this.logger = logger;
this.queue = queue;
this.multicastIpAddress = multicastIpAddress;
this.multicastPortNumber = multicastPortNumber;
}
public void stop() {
running = false;
}
public void run() {
InetAddress multicastAddress = null;
MulticastSocket multicastSocket = null;
try {
multicastAddress = InetAddress.getByName(multicastIpAddress);
multicastSocket = new MulticastSocket(multicastPortNumber);
String hostname = InetAddress.getLocalHost().getHostName();
byte[] buffer = new byte[8192];
multicastSocket.joinGroup(multicastAddress);
System.out.println("Listening from " + hostname + " at " + multicastAddress.getHostName());
int numberOfPackets = 0;
while (running) {
numberOfPackets++;
DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);
multicastSocket.receive(datagramPacket);
Packet packet = new Packet(numberOfPackets, datagramPacket);
queue.add(packet);
}
} catch (SocketException socketException) {
System.out.println("Socket exception " + socketException);
} catch (IOException exception) {
System.out.println("Exception " + exception);
} finally {
if (multicastSocket != null) {
try {
multicastSocket.leaveGroup(multicastAddress);
multicastSocket.close();
} catch (IOException exception) {
System.out.println(exception.toString());
}
}
}
}
}

Why used heap is always increasing in my MINA application?

I have an application which consists of two parts as server and client.
It works like this :
Client connects to the server and sends a string; server receives the string and returns an ArrayList (by converting string) which contains 10000 elements.
I wrote a class (ClientConnector.java) which simulates many clients use one connection to take those 10000 elements from server.
When I run this two programs, server side is ok. However on the client side, used heap is always increasing ! I tried to release the used objects by "null" but the used memory is still getting larger and larger.
http://s10.postimage.org/egf4ugrd5/mem.png
My Server Side Codes :
Client.java
public class Client {
private static final int PORT = 7571;
ClientHandler handler = new ClientHandler("hey");
IoConnector connector;
boolean available = true;
public synchronized void setAvailable(boolean available) {
this.available = available;
}
public synchronized boolean isAvailable() {
return available;
}
public void starter() throws InterruptedException {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
connector = new NioSocketConnector();
connector.getSessionConfig().setReadBufferSize(2048);
TextLineCodecFactory t = new TextLineCodecFactory(Charset.forName("UTF-8"));
t.setEncoderMaxLineLength(20 * 150000);
t.setDecoderMaxLineLength(20 * 150000);
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(t));
connector.setHandler(handler);
ConnectFuture future = connector.connect(new InetSocketAddress("localhost", PORT));
future.awaitUninterruptibly();
if (!future.isConnected()) {
return;
}
IoSession session = future.getSession();
session.getConfig().setUseReadOperation(true);
session.getCloseFuture().awaitUninterruptibly();
connector.dispose();
}
});
t.start();
Thread.sleep(300);
}
public void conClose() {
connector.dispose();
}
public ClientHandler getHandler() {
return handler;
}
public void reqInf() {
handler.reqInfo();
}
public static void main(String[] args) {
try {
Client c = new Client();
c.starter();
} catch (InterruptedException ex) {
System.out.println("error");
}
}
}
ClientHandler.java
public class ClientHandler extends IoHandlerAdapter {
long time;
private final String values;
IoSession session;
public ClientHandler(String values) {
this.values = values;
}
#Override
public void sessionOpened(IoSession session) throws InterruptedException {
this.session = session;
}
public ArrayList<String> convert(String str) {
Gson gson = new Gson();
return gson.fromJson(str, ArrayList.class);
}
#Override
public void messageReceived(IoSession session, Object message) throws InterruptedException {
try {
ArrayList<String> test = convert(message.toString());
System.out.println("TIME : " + (System.currentTimeMillis() - time) + " strList:" + test.size());
message = null;
test = null;
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
session.close();
System.out.println(cause.toString());
}
#Override
public void sessionClosed(IoSession session) {
System.out.println("Connection Lost");
}
public void reqInfo() {
time = System.currentTimeMillis();
session.write("test");
}
}
My Server Side :
Server.java
public class Server {
private static final int PORT = 7571; //TEST PORT
IoAcceptor acceptor = new NioSocketAcceptor();
public Server() throws IOException {
TextLineCodecFactory t = new TextLineCodecFactory(Charset.forName("UTF-8"));
t.setEncoderMaxLineLength(20*150000);
t.setDecoderMaxLineLength(20*150000);
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(t));
// acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
Executor executor = new ThreadPoolExecutor(5, 70, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(executor));
acceptor.setHandler(new ServerHandler());
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 1000);
//timer();
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("***Mina Server is ready !");
System.out.println("");
System.out.println("");
}
public static void main(String[] args) throws IOException {
Server m = new Server();
}
}
ServerHandler.java
public class ServerHandler extends IoHandlerAdapter {
private final Logger logger = (Logger) LoggerFactory.getLogger(getClass());
IoSession sessions;
//Communication communication;
public ServerHandler() throws IOException {
loader();
// communication = new Communication(this);
}
#Override
public void sessionOpened(IoSession session) {
// set idle time to 10 seconds
session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 1000);
System.out.println("Client Connected !!!");
//session.setAttribute("Values: ");
this.sessions = session;
}
public String toGSon(ArrayList<String> list) {
Gson gson = new Gson();
String str = gson.toJson(list);
return str;
}
ArrayList<String> str = new ArrayList<String>();
public void loader() {
for (int i = 0; i < 10000; i++) {
str.add("test" + i);
}
}
#Override
public void messageReceived(IoSession session, Object message) throws InterruptedException {
long time = System.currentTimeMillis();
session.write(toGSon(str));
System.out.println("TIME : " + (System.currentTimeMillis() - time));
}
#Override
public void sessionIdle(IoSession session, IdleStatus status) {
System.out.println("Socket #" + session.getId() + " is disconnecting... (IDLE)");
session.close();
}
#Override
public void exceptionCaught(IoSession session, Throwable cause) {
System.out.println("------------>" + cause.toString());
session.close();
}
}
And my Main Class
public class ClientConnector {
public ClientConnector() throws InterruptedException {
Client cl = new Client();
cl.starter();
while (true) {
cl.reqInf();
Thread.sleep(100);
}
}
public static void main(String[] args) throws InterruptedException {
ClientConnector cl = new ClientConnector();
}
}
You must remove below code from client side.
session.getConfig().setUseReadOperation(true);
Above code will cause memory leak.
One of our developers found an issue in Mina with the way clean up was being done and a patch was applied to version 2.0.8. Since as of today this is "snapshot" you have to grab it from git and build it yourself. Here is the command to get it from git:
git checkout 2.0
Repository uri:
git clone http://git-wip-us.apache.org/repos/asf/mina.git
This code is not enough to reach to a pin pointed answer.
Heap size continuous increase and no effect of GC --> Signs of memory leak.
Probably you should Profile your application and use some OQL Tools to find out which class is using char[], which here is the culprit looking at heap dump in your case

Netty client sometimes doesn't receive all expected messages

I have a fairly simple test Netty server/client project . I am testing some aspects of the stability of the communication by flooding the server with messages and counting the messages and bytes that I get back to make sure that everything matches.
When I run the flood from the client, the client keeps track of the number of messages it sends and how many it gets back and then when the number equal to each other it prints out some stats.
On certain occassions when running locally (I'm guessing because of congestion?) the client never ends up printing out the final message. I haven't run into this issue when the 2 components are on remote machines. Any suggestions would be appreciated:
The Encoder is just a simple OneToOneEncoder that encodes an Envelope type to a ChannelBuffer and the Decoder is a simple ReplayDecoder that does the opposite.
I tried adding a ChannelInterestChanged method to my client handler to see if the channel's interest was getting changed to not read, but that did not seem to be the case.
The relevant code is below:
Thanks!
SERVER
public class Server {
// configuration --------------------------------------------------------------------------------------------------
private final int port;
private ServerChannelFactory serverFactory;
// constructors ---------------------------------------------------------------------------------------------------
public Server(int port) {
this.port = port;
}
// public methods -------------------------------------------------------------------------------------------------
public boolean start() {
ExecutorService bossThreadPool = Executors.newCachedThreadPool();
ExecutorService childThreadPool = Executors.newCachedThreadPool();
this.serverFactory = new NioServerSocketChannelFactory(bossThreadPool, childThreadPool);
this.channelGroup = new DeviceIdAwareChannelGroup(this + "-channelGroup");
ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("encoder", Encoder.getInstance());
pipeline.addLast("decoder", new Decoder());
pipeline.addLast("handler", new ServerHandler());
return pipeline;
}
};
ServerBootstrap bootstrap = new ServerBootstrap(this.serverFactory);
bootstrap.setOption("reuseAddress", true);
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.setPipelineFactory(pipelineFactory);
Channel channel = bootstrap.bind(new InetSocketAddress(this.port));
if (!channel.isBound()) {
this.stop();
return false;
}
this.channelGroup.add(channel);
return true;
}
public void stop() {
if (this.channelGroup != null) {
ChannelGroupFuture channelGroupCloseFuture = this.channelGroup.close();
System.out.println("waiting for ChannelGroup shutdown...");
channelGroupCloseFuture.awaitUninterruptibly();
}
if (this.serverFactory != null) {
this.serverFactory.releaseExternalResources();
}
}
// main -----------------------------------------------------------------------------------------------------------
public static void main(String[] args) {
int port;
if (args.length != 3) {
System.out.println("No arguments found using default values");
port = 9999;
} else {
port = Integer.parseInt(args[1]);
}
final Server server = new Server( port);
if (!server.start()) {
System.exit(-1);
}
System.out.println("Server started on port 9999 ... ");
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
server.stop();
}
});
}
}
SERVER HANDLER
public class ServerHandler extends SimpleChannelUpstreamHandler {
// internal vars --------------------------------------------------------------------------------------------------
private AtomicInteger numMessagesReceived=new AtomicInteger(0);
// constructors ---------------------------------------------------------------------------------------------------
public ServerHandler() {
}
// SimpleChannelUpstreamHandler -----------------------------------------------------------------------------------
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
Channel c = e.getChannel();
System.out.println("ChannelConnected: channel id: " + c.getId() + ", remote host: " + c.getRemoteAddress() + ", isChannelConnected(): " + c.isConnected());
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("*** EXCEPTION CAUGHT!!! ***");
e.getChannel().close();
}
#Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelDisconnected(ctx, e);
System.out.println("*** CHANNEL DISCONNECTED ***");
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if(numMessagesReceived.incrementAndGet()%1000==0 ){
System.out.println("["+numMessagesReceived+"-TH MSG]: Received message: " + e.getMessage());
}
if (e.getMessage() instanceof Envelope) {
// echo it...
if (e.getChannel().isWritable()) {
e.getChannel().write(e.getMessage());
}
} else {
super.messageReceived(ctx, e);
}
}
}
CLIENT
public class Client implements ClientHandlerListener {
// configuration --------------------------------------------------------------------------------------------------
private final String host;
private final int port;
private final int messages;
// internal vars --------------------------------------------------------------------------------------------------
private ChannelFactory clientFactory;
private ChannelGroup channelGroup;
private ClientHandler handler;
private final AtomicInteger received;
private long startTime;
private ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// constructors ---------------------------------------------------------------------------------------------------
public Client(String host, int port, int messages) {
this.host = host;
this.port = port;
this.messages = messages;
this.received = new AtomicInteger(0);
}
// ClientHandlerListener ------------------------------------------------------------------------------------------
#Override
public void messageReceived(Envelope message) {
if (this.received.incrementAndGet() == this.messages) {
long stopTime = System.currentTimeMillis();
float timeInSeconds = (stopTime - this.startTime) / 1000f;
System.err.println("Sent and received " + this.messages + " in " + timeInSeconds + "s");
System.err.println("That's " + (this.messages / timeInSeconds) + " echoes per second!");
}
}
// public methods -------------------------------------------------------------------------------------------------
public boolean start() {
// For production scenarios, use limited sized thread pools
this.clientFactory = new NioClientSocketChannelFactory(cachedThreadPool, cachedThreadPool);
this.channelGroup = new DefaultChannelGroup(this + "-channelGroup");
this.handler = new ClientHandler(this, this.channelGroup);
ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("byteCounter", new ByteCounter("clientByteCounter"));
pipeline.addLast("encoder", Encoder.getInstance());
pipeline.addLast("decoder", new Decoder());
pipeline.addLast("handler", handler);
return pipeline;
}
};
ClientBootstrap bootstrap = new ClientBootstrap(this.clientFactory);
bootstrap.setOption("reuseAddress", true);
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
bootstrap.setPipelineFactory(pipelineFactory);
boolean connected = bootstrap.connect(new InetSocketAddress(host, port)).awaitUninterruptibly().isSuccess();
System.out.println("isConnected: " + connected);
if (!connected) {
this.stop();
}
return connected;
}
public void stop() {
if (this.channelGroup != null) {
this.channelGroup.close();
}
if (this.clientFactory != null) {
this.clientFactory.releaseExternalResources();
}
}
public ChannelFuture sendMessage(Envelope env) {
Channel ch = this.channelGroup.iterator().next();
ChannelFuture cf = ch.write(env);
return cf;
}
private void flood() {
if ((this.channelGroup == null) || (this.clientFactory == null)) {
return;
}
System.out.println("sending " + this.messages + " messages");
this.startTime = System.currentTimeMillis();
for (int i = 0; i < this.messages; i++) {
this.handler.sendMessage(new Envelope(Version.VERSION1, Type.REQUEST, 1, new byte[1]));
}
}
// main -----------------------------------------------------------------------------------------------------------
public static void main(String[] args) throws InterruptedException {
final Client client = new Client("localhost", 9999, 10000);
if (!client.start()) {
System.exit(-1);
return;
}
while (client.channelGroup.size() == 0) {
Thread.sleep(200);
}
System.out.println("Client started...");
client.flood();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
System.out.println("shutting down client");
client.stop();
}
});
}
}
CLIENT HANDLER
public class ClientHandler extends SimpleChannelUpstreamHandler {
// internal vars --------------------------------------------------------------------------------------------------
private final ClientHandlerListener listener;
private final ChannelGroup channelGroup;
private Channel channel;
// constructors ---------------------------------------------------------------------------------------------------
public ClientHandler(ClientHandlerListener listener, ChannelGroup channelGroup) {
this.listener = listener;
this.channelGroup = channelGroup;
}
// SimpleChannelUpstreamHandler -----------------------------------------------------------------------------------
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (e.getMessage() instanceof Envelope) {
Envelope env = (Envelope) e.getMessage();
this.listener.messageReceived(env);
} else {
System.out.println("NOT ENVELOPE!!");
super.messageReceived(ctx, e);
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
System.out.println("**** CAUGHT EXCEPTION CLOSING CHANNEL ***");
e.getCause().printStackTrace();
e.getChannel().close();
}
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
this.channel = e.getChannel();
System.out.println("Server connected, channel id: " + this.channel.getId());
this.channelGroup.add(e.getChannel());
}
// public methods -------------------------------------------------------------------------------------------------
public void sendMessage(Envelope envelope) {
if (this.channel != null) {
this.channel.write(envelope);
}
}
}
CLIENT HANDLER LISTENER INTERFACE
public interface ClientHandlerListener {
void messageReceived(Envelope message);
}
Without knowing how big the envelope is on the network I'm going to guess that your problem is that your client writes 10,000 messages without checking if the channel is writable.
Netty 3.x processes network events and writes in a particular fashion. It's possible that your client is writing so much data so fast that Netty isn't getting a chance to process receive events. On the server side this would result in the channel becoming non writable and your handler dropping the reply.
There are a few reasons why you see the problem on localhost but it's probably because the write bandwidth is much higher than your network bandwidth. The client doesn't check if the channel is writable, so over a network your messages are buffered by Netty until the network can catch up (if you wrote significantly more than 10,000 messages you might see an OutOfMemoryError). This acts as a natural break because Netty will suspend writing until the network is ready, allowing it to process incoming data and preventing the server from seeing a channel that's not writable.
The DiscardClientHandler in the discard handler shows how to test if the channel is writable, and how to resume when it becomes writable again. Another option is to have sendMessage return the ChannelFuture associated with the write and, if the channel is not writable after the write, block until the future completes.
Also your server handler should write the message and then check if the channel is writable. If it isn't you should set channel readable to false. Netty will notify ChannelInterestChanged when the channel becomes writable again. Then you can set channel readable to true to resume reading messages.

Netty echo server can't handle 100+ channels

I want to create chat-server which would handle 100-500 users in different rooms.
I decided to use Netty framework, because of event-based arcitecture (which is very familiar to me). I started with small server which respond "NYA" for everything it recieve.
main:
public class ChatServer{
public static void main(String[] args) throws Exception {
ExecutorService bossExec = new OrderedMemoryAwareThreadPoolExecutor(1, 400000000, 2000000000, 60, TimeUnit.SECONDS);
ExecutorService ioExec = new OrderedMemoryAwareThreadPoolExecutor(4 /* число рабочих потоков */, 400000000, 2000000000, 60, TimeUnit.SECONDS);
ChannelFactory factory = new NioServerSocketChannelFactory(bossExec,ioExec);
ServerBootstrap bootstrap = new ServerBootstrap(factory);
bootstrap.setPipelineFactory(new ChannelPipelineFactory(){
public ChannelPipeline getPipeline(){
return Channels.pipeline(new ChatChannelHandler());
}
});
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.setOption("reuseAddress", true);
bootstrap.bind(new InetSocketAddress(5555));
System.out.println("ChatServer started at last...");
}
}
handler:
public class ChatChannelHandler extends SimpleChannelUpstreamHandler {
private final ChannelBuffer messageBuffer = ChannelBuffers.dynamicBuffer();
private boolean processingMessage = false;
private short messageLength;
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
System.out.println("Connection from: " + e.getChannel().getRemoteAddress().toString());
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
e.getChannel().write(ChannelBuffers.copiedBuffer("NYA!", "UTF-8");
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
System.out.println("Exception: " + e.getCause());
Channel ch = e.getChannel();
ch.close();
}
}
I use simulation program which creates N connections and then randomly write stuff to server with 1-5 second pause. Then i connect with my Flash-based client, to see how it is working. So problem is that under 10-90 connections my flash client immediately get responds from the server, but when number of simulated connections exceed 100 my flash client remain silent. I simply don't understand why.
I figured out, that all messages from my 100+ client gets into buffer, but messageReceive event simply not fired for them. Looks like event queue reachs some limit of registered events, or something like that.
It's really sadden me, because i read about even simpler servers handles 1k+ request per second.
I work under Windows 7, if it is necessary. Also my server never use more then 2-3% of CPU.
My simulation generator:
public class ClientLoadEmulation implements Runnable {
private String host;
private int port;
private static final int minStringLength = 5;
private static final int maxStringLength = 40;
private static final int minPause = (int) (1 * 1000);
private static final int maxPause = (int) (5 * 1000);
Random rand = new Random();
public ClientLoadEmulation(String host, int port, int numThreads) {
this.host = "192.168.0.100";
this.port = 5555;
for (int i = 0; i < numThreads; ++i) {
new Thread(this).start();
try {
Thread.sleep(100);
} catch (Exception ex) {
}
}
}
#Override
public void run() {
byte buffer[] = new byte[255];
try {
Socket s = new Socket(host, port);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
while (true) {
String govno = "";
...STRING GENERATION STUFF CUTTED...
ByteBuffer LENGTH_BUFFER = ByteBuffer.allocate(2);
LENGTH_BUFFER.putShort((short) govno.length());
LENGTH_BUFFER.flip();
out.write(LENGTH_BUFFER.array());
out.write(govno.getBytes("UTF-8"));
//System.out.println(Thread.currentThread() + " wrote " + govno);
int pause = minPause
+ (int) (rand.nextDouble() * (maxPause - minPause));
try {
Thread.sleep(pause);
} catch (InterruptedException ie) {
}
}
} catch (IOException ie) {
System.out.println("ERROR: " + ie.getMessage());
}
}
static public void main(String args[]) throws Exception {
new ClientLoadEmulation("127.0.0.1", 5555, 100);
}
}
(Sorry for my English skills)
I'm not sure about your sample, but when I tried to run ChatServer you provided, it was not waiting for the client conneciton, but rather quit.
Moreover I don't see there port 5555 setup at all (the one expected from the client).
Please post the working sample to check.

Categories

Resources