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

时间:2015-03-07 09:50:55

标签: java performance network-programming netty

对于当前项目,我需要以有效的方式实现自定义但预定义的网络协议,因为该软件将在不是很小的多用户环境中运行。重要的是,协议处理本身非常快,而且开销很小,因此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来说,这会有效还是成本太高?

1 个答案:

答案 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,每个都有固定数量的线程(根据特定执行者应该做的工作量决定)和命令只会被发送到正确的,并从那里执行,因此,根据您的需要分享您的有限资源。