如何在 Java 中处理来自客户端的 Websocket 消息? [英] How to process Websocket messages from client in Java?

查看:51
本文介绍了如何在 Java 中处理来自客户端的 Websocket 消息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Websocket 用 Ja​​va 开发客户端-服务器应用程序.目前,所有客户端消息都使用 switch-case 处理,如下所示.

I am developing a client-server application in Java using Websocket. Currently, all the client messages are processed using switch-case as shown below.

@OnMessage
public String onMessage(String unscrambledWord, Session session) {
    switch (unscrambledWord) {
    case "start":
        logger.info("Starting the game by sending first word");
        String scrambledWord = WordRepository.getInstance().getRandomWord().getScrambledWord();
        session.getUserProperties().put("scrambledWord", scrambledWord);
        return scrambledWord;
    case "quit":
        logger.info("Quitting the game");
        try {
            session.close(new CloseReason(CloseCodes.NORMAL_CLOSURE, "Game finished"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    String scrambledWord = (String) session.getUserProperties().get("scrambledWord");
    return checkLastWordAndSendANewWord(scrambledWord, unscrambledWord, session);
}

服务器必须处理来自客户端的 50 多个不同请求,这导致了 50 多个 case 语句.在未来,我预计它会增长.有没有更好的方法来处理来自客户端的 Websocket 消息?或者,这是通常的做法吗?

The server has to process more than 50 different requests from client and that results in more than 50 case statements. And in future, I expect it to grow. Is there any better way to process Websocket messages from client? Or, is this how it is usually done?

我在某处阅读了有关使用哈希表通过映射到函数指针来避免长切换情况的信息.这在Java中可能吗?或者,有没有更好的解决方案?

I read somewhere about the use of hashtable to avoid long switch-case scenario by mapping to function pointers. Is this possible in Java? Or, is there any better solutions?

谢谢.

推荐答案

经过一些测试和研究,我找到了两个替代方案来避免长时间切换案例场景.

After a bit of testing and study, I found two alternatives to avoid long switch case scenario.

  1. 匿名类方法(策略模式)
  2. 带有注释的反射

使用匿名类

匿名类方法是常态,下面的代码展示了如何实现它.我在这个例子中使用了 Runnable.如果需要更多控制,请创建自定义界面.

Anonymous class method is the norm and following code shows how to implement it. I used Runnable in this example. If more control is required, create a custom interface.

public class ClientMessageHandler {

    private final HashMap<String, Runnable> taskList = new HashMap<>();

    ClientMessageHandler() {

        this.populateTaskList();
    }

    private void populateTaskList() {

        // Populate the map with client request as key
       // and the task performing objects as value

        taskList.put("action1", new Runnable() {
            @Override
            public void run() {
                // define the action to perform.
            }
        });

       //Populate map with all the tasks
    }

    public void onMessageReceived(JSONObject clientRequest) throws JSONException {

        Runnable taskToExecute = taskList.get(clientRequest.getString("task"));

        if (taskToExecute == null)
            return;

        taskToExecute.run();
    }
}

此方法的主要缺点是创建对象.比如说,我们有 100 项不同的任务要执行.这种匿名类方法将导致为单个客户端创建 100 个对象.我的应用程序无法承受过多的对象创建,因为我的应用程序将有超过 5,000 个活动的并发连接.看看这篇文章 http://blogs.microsoft.co.il/gilf/2009/11/22/applying-strategy-pattern-instead-of-using-switch-statements/

Major drawback of this method is object creation. Say, we have 100 different tasks to perform. This Anonymous class approach will result in creating 100 objects for a single client. Too much object creation is not affordable for my application, where there will be more than 5,000 active concurrent connections. Have a look at this article http://blogs.microsoft.co.il/gilf/2009/11/22/applying-strategy-pattern-instead-of-using-switch-statements/

带有注释的反射

我真的很喜欢这种方法.我创建了一个自定义注释来表示方法执行的任务.没有对象创建的开销,就像在策略模式方法中一样,因为任务由单个类执行.

I really like this approach. I created a custom annotation to represent the tasks performed by methods. There is no overhead of object creation, like in Strategy pattern method, as tasks are performed by a single class.

注释

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)

public @interface TaskAnnotation {
    public String value();
}

下面给出的代码将客户端请求键映射到处理任务的方法.在这里,地图仅被实例化和填充一次.

The code given below maps the client request keys to the methods which process the task. Here, map is instantiated and populated only once.

public static final HashMap<String, Method> taskList = new HashMap<>();

public static void main(String[] args) throws Exception {

    // Retrieves declared methods from ClientMessageHandler class 

    Method[] classMethods = ClientMessageHandler.class.getDeclaredMethods();

    for (Method method : classMethods) {            
        // We will iterate through the declared methods and look for
        // the methods annotated with our TaskAnnotation

        TaskAnnotation annot = method.getAnnotation(TaskAnnotation.class);

        if (annot != null) {                
            // if a method with TaskAnnotation is found, its annotation
            // value is mapped to that method.

            taskList.put(annot.value(), method);
        }
    }

    // Start server
}

现在终于,我们的 ClientMessageHandler 类如下所示

Now finally, our ClientMessageHandler class looks like the following

public class ClientMessageHandler {

    public void onMessageReceived(JSONObject clientRequest) throws JSONException {

        // Retrieve the Method corresponding to the task from map
        Method method = taskList.get(clientRequest.getString("task"));

        if (method == null)
            return;

        try {
            // Invoke the Method for this object, if Method corresponding
            // to client request is found 

            method.invoke(this);
        } catch (IllegalAccessException | IllegalArgumentException
                | InvocationTargetException e) {
            logger.error(e);
        }
    }

    @TaskAnnotation("task1")
    public void processTaskOne() {

    }

    @TaskAnnotation("task2")
    public void processTaskTwo() {

    }

    // Methods for different tasks, annotated with the corresponding
    // clientRequest code
}

这种方法的主要缺点是性能下降.与直接方法调用方法相比,此方法速度较慢.此外,许多文章都建议远离反射,除非我们正在处理动态编程.

Major drawback of this approach is the performance hit. This approach is slow compared to Direct Method calling approach. Moreover, many articles are suggesting to stay away from Reflection, unless we are dealing with dynamic programming.

阅读这些答案以了解有关反射的更多信息什么是反射以及为什么有用吗?

Read these answers to know more about reflection What is reflection and why is it useful?

反射性能相关文章

Java 反射的更快替代方案

https://dzone.com/articles/the-performance-cost-反射

最终结果

我继续在我的应用程序中使用 switch 语句以避免任何性能下降.

这篇关于如何在 Java 中处理来自客户端的 Websocket 消息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆