实现网络协议-关于设计和性能的思考

3
为一个当前的项目,我需要以高效的方式实现自定义但预定义的网络协议,因为该软件将在不太小的多用户环境中运行。重要的是,协议处理本身非常快速,只有很小的开销,因此CPU和其他硬件可以完全用于服务器本身的工作。
我知道已经有类似问题的提问,但我认为我的问题有所不同。
让我展示一下我目前有两种不同的方法: 方法 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();
        }
    }
}
CommandExecutor类在这里将是抽象的,当然还有实现协议命令的子类。
在这种方法中,命令注册表已经从一开始就知道哪个执行器用于协议的哪个部分,所以我认为它不是非常动态,但我想这对我的需求来说已经足够了。
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();
}

在这种方法中,只有 CommandExecutor 本身知道是否要处理命令。调用命令时,我们将遍历所有已注册的处理程序,并调用其中可能导致效率低下的方法。
现在,我的问题是,在您诚实的意见中,哪种设计更好。回答问题时,请考虑设计和性能,因为两者对我来说都非常重要。
也许你甚至可以推荐一个更好的设计(同时也更有效率)?
// 编辑:
再次思考设计后,我找到了另一种基于外部库 "Netty" 的方法,我想要用于网络通信。
我打算为我想要处理的协议的每个部分编写 ChannelInboundHandlerAdapter 类,并将它们添加到 Netty 管道中。这样做会很有效,还是太昂贵了?

你应该同时处理多少个请求? - Dinari
我猜大概会有几百个左右。问题在于这个协议的“对话”非常频繁,因此不仅有 连接 -> 一些数据 -> 断开,而且客户端和服务器之间还需要进行很多通信。 - Niklas S.
1个回答

1

只是提供一些想法,建议采用与反应堆设计模式类似的方式:

根据您拥有的不同类型请求,保留不同类型的执行器(也就是说,对于每个请求或每种类型的请求使用不同的类型)。

让您的registerMainHandler决定使用哪个执行器,并将命令发送到相应的执行器服务。

这样,通过分析每个执行器接收的请求数量,您可以“限制”它们中的每一个,例如让经常出现的执行器在同一时间被限制为100个请求,较不频繁的则为10个请求。从而增加服务的性能,在需要更多功率的地方提供更多的能力。

编辑: 反应堆设计模式基本上持有一个工作线程池,当它接收到请求时,立即读取它,并将其发送到线程池中在适当的时间执行。这样做的原因是:

主服务器线程读取数据包并调用处理程序,然后对数据包进行处理,但将处理任务发送到线程池中执行。

我认为你应该做的是类似的,让你的RegisterCommand获取命令,决定它应该去哪里,并在那里注册它。然后它将被可用的工作线程接收,该线程将处理请求。就像我说的那样,这与反应堆类似但不完全相同,大致意思如下:
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();
        }
    }

}

此外,这段代码并不完整,其思路是预先设置CommandHandlers,每个处理程序都有一定数量的线程(根据特定执行者应完成的工作量进行决策),命令将被发送到正确的处理程序,并从那里执行,因此根据需要共享有限资源。

谢谢您的回答,但我必须承认,即使在查看提供的链接后,我仍然不明白如何在我的情况下在Java中实现它。您能提供一些示例代码吗? - Niklas S.
编辑了一下,就像我说的那样,类似于反应堆,但并不完全相同,实际上相差很远,主要思想是将处理过程分解,并确定应该运行哪个进程。 - Dinari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接