【问题标题】:Implementing a network protocol - Thoughts about Design and Performance实现一个网络协议——关于设计和性能的思考
【发布时间】:2015-03-07 09:50:54
【问题描述】:

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

【问题讨论】:

  • 您打算同时处理多少请求?
  • 我猜大概有几百个。问题是协议“说话”非常多,所以不仅有Connect -&gt; some data -&gt; disconnect,而且客户端和服务器之间有很多通信。

标签: java performance network-programming netty


【解决方案1】:

这里只是在空中拍摄,但我会建议一些类似于Reactor design pattern 的东西:

根据您拥有的不同类型的请求保留不同类型的执行程序(据我了解,您对每个请求或每种类型的请求使用不同的类型)。

让您的 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();
        }
    }

}

另外,这段代码并不完整,想法是预先设置 CommandHanlders,每个都有固定数量的线程(您根据特定执行程序应该做的工作量来决定),以及命令只需将其发送给正确的人,并从那里执行,从而根据您的需要共享您有限的资源。

【讨论】:

  • 感谢您的回答,但我不得不承认,即使查看了提供的链接,我也不明白如何在我的场景中用 Java 实现它。你能提供一些示例代码吗?
  • 编辑它,就像我说的,类似于反应堆,但并不精确,实际上远非如此,主要思想是拆分做进程的工作,并确定应该运行哪个进程。
猜你喜欢
  • 2012-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-25
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多