4

I am developing a java application using Apache Mina 2.0.16 as a NIO library. I admire the work and efforts that people used to create this kind of library so NIO beginners can simply create a non-blocking threaded TCP server and client. After creating my custom class TCPServer class and writing my main application logic I tried using the Netbeans java profiler to see the performance of this application.

This is the screen shot of the java profiler: Profiler screenshot

I noticed that I have 2 NioSocketAcceptors and that is correct because I use 2 separate TCP servers with different codec filter chains (one for java to java application communication using object serialization and one for java to C application using UTF-8 or windows-1251). This screen shot was taken with 0 active clients on the both servers. As I can see from the java profiler both are running 99% from the application time even when they are idle. The whole application with everything (GUI, database, timer-tasks, threads ...) on Windows and Linux uses approximately 20% of processor time on my gaming laptop with intel core i7 processor.

This is my TCPServer java class:

package es.modules.communication.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.service.IoServiceStatistics;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

/**
 *
 * @author adrian
 */
public abstract class TCPServer {

private final int tcp_server_port; //Порта на која слуша серверот
private final NioSocketAcceptor acceptor; //Акцептор на сесии со клиенти
private final IoServiceStatistics statistics;
private Timer timer = new Timer("Timer for: TCP Server");

public static final int PROTOCOL_TRANSFER_OBJECT = 1;
public static final int PROTOCOL_TRANSFER_UTF_8 = 2;
public static final int PROTOCOL_TRANSFER_WINDOWS_1251 = 3;

/**
 * Конструктор на кластата
 *
 * @param tcp_server_port Порта на која слуша серверот
 * @param idle_timeout_sec Време во секунди после колку време клиентот ќе ја
 * прогласи сесијата за IDLE односно доколку не прими било какви податоци од
 * серверот
 * @param write_timeout_sec После колку секунди клиентот да се откаже од
 * испраќање на податоци до серверот
 * @param trasfer_protocol Протокол за пренос на дата
 * @throws java.io.IOException проблем при подигање на TCP серверот
 */
public TCPServer(int tcp_server_port, int idle_timeout_sec, int write_timeout_sec, int trasfer_protocol) throws IOException {
    this.tcp_server_port = tcp_server_port;
    this.acceptor = new NioSocketAcceptor();
    this.acceptor.setReuseAddress(true);
    this.acceptor.setCloseOnDeactivation(true);
    this.acceptor.getSessionConfig().setWriteTimeout(write_timeout_sec);
    this.acceptor.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, idle_timeout_sec);
    switch (trasfer_protocol) {
        case PROTOCOL_TRANSFER_OBJECT: {
            this.acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
            break;
        }
        case PROTOCOL_TRANSFER_UTF_8: {
            this.acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
            break;
        }
        case PROTOCOL_TRANSFER_WINDOWS_1251: {
            TextLineCodecFactory text_line_codec_factory = new TextLineCodecFactory(Charset.forName("windows-1251"));
            text_line_codec_factory.setDecoderMaxLineLength(64 * 1024);
            text_line_codec_factory.setEncoderMaxLineLength(64 * 1024);
            this.acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(text_line_codec_factory));
            break;
        }
    }
    this.acceptor.setHandler(new TCPServerHandler());
    this.acceptor.bind(new InetSocketAddress(this.tcp_server_port));
    this.statistics = this.acceptor.getStatistics();
    this.statistics.setThroughputCalculationInterval(1);
    this.timer.schedule(new IOStatistics(), 0, 1000);
}

/**
 * Изгаси го серверот
 */
public void shutdown_server() {
    if (this.acceptor != null && this.acceptor.isActive()) {
        this.acceptor.unbind();
        acceptor.getManagedSessions().values().stream().forEach((ss) -> {
            ss.close(true);
        });
        this.acceptor.unbind(); //Just to be sure
        this.acceptor.dispose(true);
    }
}

/**
 * Дали серверот е активен
 *
 * @return true/false активен/неактивен
 */
public boolean is_active() {
    return this.acceptor.isActive();
}

/**
 * Број на отворени сесии
 *
 * @return бројот на отворени сесии
 */
public int number_of_opened_session() {
    return this.acceptor.getManagedSessionCount();
}

public ArrayList<String> get_all_connected_sessions() {
    ArrayList<String> result_list = new ArrayList<>();
    Map<Long, IoSession> sessions_map = acceptor.getManagedSessions();
    sessions_map.values().stream().forEach((session) -> {
        result_list.add(get_tcp_address(session.getRemoteAddress()));
    });
    return result_list;
}

/**
 * Синхороно испраќање користејки ја сесијата
 *
 * @param session TCP/IP сесија
 * @param message порка
 * @throws Exception грешка при испраќање
 */
public void send_message_sync(IoSession session, Object message) throws Exception {
    WriteFuture write_future = session.write(message);
    if (write_future.getException() != null) {
        throw new Exception(write_future.getException().getMessage(), write_future.getException());
    }
}

/**
 * Асинхороно испраќање користејки ја сесијата
 *
 * @param session TCP/IP сесија
 * @param message порка
 */
public void send_message_async(IoSession session, Object message) {
    session.write(message);
}

/**
 * Синхороно испраќање користејки идентификатор на сесија
 *
 * @param sessionID идентификатор на сесија
 * @param message порка
 * @throws Exception грешка при испраќање
 */
public void send_message_sync(long sessionID, Object message) throws Exception {
    WriteFuture write_future = this.acceptor.getManagedSessions().get(sessionID).write(message);
    write_future.awaitUninterruptibly();
    if (write_future.getException() != null) {
        throw new Exception(write_future.getException().getMessage(), write_future.getException());
    }

}

/**
 * Асинхороно испраќање користејки идентификатор на сесија
 *
 * @param sessionID идентификатор на сесија
 * @param message порка
 */
public void send_message_async(long sessionID, Object message) {
    this.acceptor.getManagedSessions().get(sessionID).write(message);
}

/**
 * Синхороно испраќање користејки адреса/порта на сесијата
 *
 * @param remote_tcp_address адреса на клиентот
 * @param remote_tcp_port порта на клиентот
 * @param message порка
 * @throws Exception грешка при испраќање
 */
public void send_message_sync(String remote_tcp_address, int remote_tcp_port, Object message) throws Exception {
    for (Map.Entry entry : this.acceptor.getManagedSessions().entrySet()) {
        IoSession session = (IoSession) entry.getValue();
        if (get_tcp_address(session.getRemoteAddress()).equals(remote_tcp_address) && get_tcp_port(session.getRemoteAddress()) == remote_tcp_port) {
            WriteFuture write_future = session.write(message);
            write_future.awaitUninterruptibly();
            if (write_future.getException() == null) {
                return;
            } else {
                throw new Exception(write_future.getException().getMessage(), write_future.getException());
            }
        }
    }
    throw new Exception("Threre is no session with " + remote_tcp_address + ":" + remote_tcp_port);
}

/**
 * Асинхороно испраќање користејки адреса/порта на сесијата
 *
 * @param remote_tcp_address адреса на клиентот
 * @param remote_tcp_port порта на клиентот
 * @param message порка
 * @throws Exception грешка при испраќање
 */
public void send_message_async(String remote_tcp_address, int remote_tcp_port, Object message) throws Exception {
    for (Map.Entry entry : this.acceptor.getManagedSessions().entrySet()) {
        IoSession session = (IoSession) entry.getValue();
        if (get_tcp_address(session.getRemoteAddress()).equals(remote_tcp_address) && get_tcp_port(session.getRemoteAddress()) == remote_tcp_port) {
            session.write(message);
            return;
        }
    }
    throw new Exception("Threre is no session with " + remote_tcp_address + ":" + remote_tcp_port);
}

public void broadcast_message_sync(Object message) throws BroadCastException {
    boolean at_least_on_exception = false;
    ArrayList<Exception> error_list = new ArrayList<>();
    Set<WriteFuture> write_futures = this.acceptor.broadcast(message);
    for (WriteFuture write_future : write_futures) {
        if (write_future.getException() != null) {
            at_least_on_exception = true;
            error_list.add(new Exception(write_future.getException().getMessage(), write_future.getException()));
        }
    }
    if (at_least_on_exception) {
        throw new BroadCastException(error_list);
    }
}

public void broadcast_message_async(Object message) {
    this.acceptor.broadcast(message);
}

/**
 * (настан) Сесија креирана
 *
 * @param session tcp/ip сесија
 * @param ID идентификатор на сесијата
 * @param remote_tcp_address оддалечена TCP/IP адреса
 * @param remote_tcp_port одлачена TCP/IP порта
 */
public abstract void session_created(IoSession session, long ID, String remote_tcp_address, int remote_tcp_port);

/**
 * (настан) Сесија отворена
 *
 * @param session tcp/ip сесија
 * @param ID идентификатор на сесијата
 * @param remote_tcp_address оддалечена TCP/IP адреса
 * @param remote_tcp_port одлачена TCP/IP порта
 */
public abstract void session_opened(IoSession session, long ID, String remote_tcp_address, int remote_tcp_port);

/**
 * (настан) Сесија затворена
 *
 * @param session tcp/ip сесија
 * @param ID идентификатор на сесијата
 * @param remote_tcp_address оддалечена TCP/IP адреса
 * @param remote_tcp_port одлачена TCP/IP порта
 */
public abstract void session_closed(IoSession session, long ID, String remote_tcp_address, int remote_tcp_port);

/**
 * (настан) Примена порака
 *
 * @param session tcp/ip сесија
 * @param ID идентификатор на сесијата
 * @param remote_tcp_address оддалечена TCP/IP адреса
 * @param remote_tcp_port одлачена TCP/IP порта
 * @param message порака добиена од серверот
 */
public abstract void message_received(IoSession session, long ID, String remote_tcp_address, int remote_tcp_port, Object message);

/**
 * (настан) Испратена порака
 *
 * @param session tcp/ip сесија
 * @param ID идентификатор на сесијата
 * @param remote_tcp_address оддалечена TCP/IP адреса
 * @param remote_tcp_port одлачена TCP/IP порта
 * @param message порака испратена до серверот
 */
public abstract void message_sent(IoSession session, long ID, String remote_tcp_address, int remote_tcp_port, Object message);

/**
 * (настан) Грешка во сесијата
 *
 * @param session tcp/ip сесија
 * @param ID идентификатор на сесијата
 * @param remote_tcp_address оддалечена TCP/IP адреса
 * @param remote_tcp_port одлачена TCP/IP порта
 * @param cause грека која е настаната
 */
public abstract void session_exception(IoSession session, long ID, String remote_tcp_address, int remote_tcp_port, Throwable cause);

/**
 * (настан) Неактивна сесија
 *
 * @param session tcp/ip сесија
 * @param ID идентификатор на сесијата
 * @param remote_tcp_address оддалечена TCP/IP адреса
 * @param remote_tcp_port одлачена TCP/IP порта
 */
public abstract void session_idle(IoSession session, long ID, String remote_tcp_address, int remote_tcp_port);

public abstract void read_write_statisctics(String read_bytes_throughput, String written_bytes_throughput);

private String get_tcp_address(SocketAddress address) {
    return address.toString().split(":")[0].substring(1);
}

private int get_tcp_port(SocketAddress address) {
    return Integer.parseInt(address.toString().split(":")[1]);
}

private class TCPServerHandler extends IoHandlerAdapter {

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        session_exception(session, session.getId(), get_tcp_address(session.getRemoteAddress()), get_tcp_port(session.getRemoteAddress()), cause);
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        message_received(session, session.getId(), get_tcp_address(session.getRemoteAddress()), get_tcp_port(session.getRemoteAddress()), message);
    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        message_sent(session, session.getId(), get_tcp_address(session.getRemoteAddress()), get_tcp_port(session.getRemoteAddress()), message);
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        session_idle(session, session.getId(), get_tcp_address(session.getRemoteAddress()), get_tcp_port(session.getRemoteAddress()));
    }

    @Override
    public void sessionCreated(IoSession session) throws Exception {
        session_created(session, session.getId(), get_tcp_address(session.getRemoteAddress()), get_tcp_port(session.getRemoteAddress()));
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        session_closed(session, session.getId(), get_tcp_address(session.getRemoteAddress()), get_tcp_port(session.getRemoteAddress()));
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        session_opened(session, session.getId(), get_tcp_address(session.getRemoteAddress()), get_tcp_port(session.getRemoteAddress()));
    }
}

private class IOStatistics extends TimerTask {

    @Override
    public void run() {
        statistics.updateThroughput(System.currentTimeMillis());
        String read_bytes_throughput = String.format("%.2f", statistics.getReadBytesThroughput());
        String written_bytes_throughput = String.format("%.2f", statistics.getWrittenBytesThroughput());
        read_write_statisctics(read_bytes_throughput, written_bytes_throughput);
    }
}

public class BroadCastException extends Exception {

    final private ArrayList<Exception> error_list;

    public BroadCastException(ArrayList<Exception> error_list) {
        super();
        this.error_list = error_list;
    }

    public ArrayList<Exception> getError_list() {
        return error_list;
    }

}

}

I started searching on the web and found that version 2.0.12 had a bug with 100% CPU using, but at that time I was using 2.0.13. Still I downloaded the latest version 2.0.16 but still the same. This is a quote from the official web site.

MINA 2.0.12 released posted on February, 07, 2016

This new release of MINA is a bug fix release. There are a few new bugs that were wound in the way we handle closure, leading to some infinite loop consuming 100% CPU, and a bad counter update forbidding the main loop to be exited.

In this link: https://issues.apache.org/jira/browse/DIRMINA-1006 I found a solution that needs changing the AbstractPollingIoProcessor class and adding IDLE_TIMEOUT, but i cannot find the source-code so I can edit it. This is how this AbstractPollingIoProcessor should look like: https://git1-us-west.apache.org/repos/asf?p=mina.git;a=blob;f=mina-core/src/main/java/org/apache/mina/core/polling/AbstractPollingIoProcessor.java;h=abd704580e65a2f6e381655efa971fd7ee18752e;hb=2d6f8256.

On this forum thread: http://apache-mina.10907.n7.nabble.com/MINA-hogs-the-CPU-100-with-resetWakeupSocket-td41471.html it says that:

Roughly 1.5 months ago. It worked fine until a few days ago. Another interesting thing I just noted: When I nullroute the external IP (by removing it from the network adapter), the CPU usage goes to 0%. When I add it back, it slowly raises back up to what it was before (~78 or 100%).

I am not expert in network configuration and I read from wiki what nullroute means but cannot see how that will help.

I will continue my search for solving this problem and if I find a solution I will post it.

If anyone working with apache mina has a solutions for this problem please help me.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
AdrianES
  • 670
  • 3
  • 13
  • 29

0 Answers0