对于当前项目,我需要以有效的方式实现自定义但预定义的网络协议,因为该软件将在不是很小的多用户环境中运行。重要的是,协议处理本身非常快,而且开销很小,因此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
类在这里是抽象的,当然还有实现协议命令的子类。
在这种方法中,命令注册表从一开始就知道哪个执行器用于协议的哪个部分,所以我认为它不是很动态,但我想这对我的需求来说已经足够了。
方法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();
}
在这种方法中,只有CommandExecutor
本身知道它是否想要处理命令。在调用命令时,我们会遍历所有注册的处理程序,并调用可能无效的方法,正如我想的那样。
知道,我的问题是你的诚实意见中哪种设计更好。在回答时请考虑设计和性能,因为两者对我都非常重要。
也许你甚至可以推荐更好的设计(效率更高)?
//编辑:
在再次考虑设计后,我开始寻找另一种方法,基于我想用于网络的外部库“Netty”。
我认为我为要处理的协议的每个部分编写了ChannelInboundHandlerAdapter
个类,并将它们添加到Netty管道中。对于Netty来说,这会有效还是成本太高?
答案 0 :(得分:1)
只是在这里拍摄,但我会建议一些与Reactor design pattern类似的东西:
根据您拥有的不同类型的请求,保留不同类型的执行者(根据我的理解,您对每个请求或每种类型的请求使用不同类型)。
让您的registerMainHandler决定使用哪个执行者,并发送命令以使用执行者服务。
这样,通过分析每个执行者得到的请求数量,你可以“限制”它们中的每一个,假设频繁的一次被限制为100个请求,而频率较低,则为10个。提高服务的性能,在需要的地方提供更多的电力。
编辑: reactor设计模式基本上包含一个工作线程池,当它接收到一个请求时,你立即读取它,并将它发送到线程池中,以便在时间准备就绪时执行,这就是这样:
主服务器线程读取一个数据包,然后调用处理程序,处理程序然后执行处理数据包的所有事情,但是将proccess任务发送到一个线程池,以便被执行。
我认为你应该做的是类似的,让你的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();
}
}
}
此外,这段代码还不完整,想法是先手设置CommandHanlders,每个都有固定数量的线程(根据特定执行者应该做的工作量决定)和命令只会被发送到正确的,并从那里执行,因此,根据您的需要分享您的有限资源。