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

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

问题描述

我正在使用Websocket用Java开发客户端服务器应用程序.当前,所有客户端消息都是使用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
}

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

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天全站免登陆