Niklas S.
Niklas S.

Reputation: 1054

Implementing a network protocol - Thoughts about Design and Performance

for a current project, I need to implement custom, but pre-defined network protocol in an efficient way, because the software will run in a multi-user environment which is not very small. It is important, that the protocol handling itself is very fast and there is just a small overhead so the CPU and other hardware can be fully used for the work of the server itself.

I know that there are already questions about similar things, but I think that my question is somehow different.

Let me show you two different approaches I am currently having:

Approach 1

public class CommandRegistry {
    private HashMap<String, HashSet<CommandExecutor>> mainHandlers = new HashMap<>();

    public void registerMainHandler(CommandExecutor executor, String command) {
        if (mainHandlers.get(command) == null) {
            HashSet<CommandExecutor> executors = new HashSet<>();
            executors.add(executor);

            mainHandlers.put(command, executors);
        } else {
            HashSet<CommandExecutor> executors = mainHandlers.get(command);
            executors.add(executor);

            mainHandlers.remove(command);
            mainHandlers.put(command, executors);
        }
    }

    public void executeCommand(String command) {
        for (CommandExecutor executor : mainHandlers.get(command)) {
            executor.call();
        }
    }
}

The CommandExecutor class would be abstract here and of course have sub-classes which implement the commands of the protocol.
In this approach, the command registry already knows from the beginning which executor is used for which part of the protocol, so I think it is not very dynamic but I guess it would be enough for my needs.

Approach 2

public class CommandRegistry {
    private List<CommandExecutor> executors = new ArrayList<>();

    public void registerCommand(CommandExecutor executor) {
        this.executors.add(executor);
    }

    public void callCommand(String command) {
        for (CommandExecutor exec : executors) {
            exec.callCommand(command);
        }
    }
}

public abstract class CommandExecutor {
    List<String> myCommands;

    public CommandExecutor(String... commands) {
        this.myCommands = commands.toArray();
    }

    public void callCommand(String command) {
        if (this.myCommands.contains(command)) {
            this.executeCommandProcedure();
        }
    }

    // This method contains the actual command procedure
    protected abstract void executeCommandProcedure();
}

In this approach, only the CommandExecutor itself knows whether it wants to handle a command or not. On call of a command, we would walk through all of the registered handlers and call a method of those which could be unefficient, as I think.

Know, my question is which design is better in your honest opinion. Please consider Design as well as Performance when answering, because both are very important to me.

Maybe you can even recommend a better Design (which is also more efficient)?

// EDIT:
After thinking about the design again, I came to finding another approach, based on the external library "Netty" which I want to use for networking.
I thought that I write ChannelInboundHandlerAdapter classes for each part of the protocol that I want to process and add them into the Netty pipeline. Would that be efficient or too costly for Netty?

Upvotes: 3

Views: 116

Answers (1)

Dinari
Dinari

Reputation: 2557

Just shooting in the air here, but i would suggest some thing a bit similiar to the Reactor design pattern :

Keep diffrent type of executurs, according to the diffrent types of requests you have (this is, to my understanding, you use a diffrent type for each request, or each type of requests).

Let your registerMainHandler decide which executur is to be used, and send the command to appopriate executur service.

This way, by analyzing the amount of requests each executer gets, you could "limit" each one of them , let say the frequent one to be limited to 100 requests at a time, and the less frequent, to 10. thus increasing the performence of service, giving more power where its needed.

EDIT: The reactor design Pattern basicly holds a pool of worker threads, and when it recieve a request you immediatly reads it, and send it into the pool of threads to be executued when time is ready, that so it goes like this:

Main server thread reads a packet, and invoke the handler, which then does every thing around proccesing the packet, but send the proccess task to a pool of threads, to be exectuted.

What i think you should do is similiar, Let your RegisterCommand get the command, decide where it should go, and register it over there.

from there it would be taken by an available worker thread, which will deal with the request.

As i said, it is similiar to the reactor but not exacly, this is roughly what i mean:

public class CommandRegistry {
    private HashMap<String, CommandHandler> mainHandlers = new HashMap<>();

    public void registerMainHandler(CommandExecutor executor, String command) {
        if (mainHandlers.get(command) == null) {
            CommandHandle executors = new CommandHandler(executor);
            executors.register(command);
            mainHandlers.put(command, executors);
        } else {
            CommandHandler executors = mainHandlers.get(command);
            executors.register(command);
        }
    }
}


public class CommandHandler {
    private Vector<String> commands;
    ExecutorService executers;
    CommandExecutor executor;
    Object Lock;

    public CommandHandler(CommandExecutor executor) {
        this.executor=executor;
        executers=Executors.newFixedThreadPool(10);
        executers.execute(new commandRunner(lock,this));
        //You could skip the worker thread, and add the commands straight to the executor service
        //when doing the register()
    }


    public void register(string command) {
        commands.add(command);
        Lock.notifyAll();
    }

    public void execute() {
        if(commands.size()==0)Lock.wait();
        executers.execute(executor(commands.get(0));
        commands.remove(0);

    }

}

public class commandRunner implements Runnable {
    Object Lock;
    CommandHandler handler;

    public commandRunner(Object Lock, CommandHandler handler) {
        this.Lock=Lock;
        this.handler=handler;
    }


    public void run() {
        while(true) {
            handler.execute();
        }
    }

}

Also, this code is not complete, the idea is to have the CommandHanlders set before hand, each with a fixed amount of threads(which you decide according to the amount of the work that specific executor should do), and the commands would simply be sent to the right ones, and executed from there, thus, sharing your limited resources according to your needs.

Upvotes: 1

Related Questions