WebSocket缓慢-Java和JavaScript [英] WebSocket Slow - Java and JavaScript

查看:1101
本文介绍了WebSocket缓慢-Java和JavaScript的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理minecraft插件的编码。
但是现在我遇到以下问题,我的websocket服务器响应非常慢。



这是我的WebSocketClass(用于插件)



//套接字服务器类

  package me.mickerd.pcoc; 

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;

import org.bukkit.Bukkit;
import org.java_websocket.WebSocket;
import org.java_websocket.WebSocketImpl;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

公共类WebsocketServer扩展了WebSocketServer {
public static WebsocketServer s;

公共WebsocketServer(int端口)抛出UnknownHostException {
super(new InetSocketAddress(port));
}

public WebsocketServer(InetSocketAddress address){
super(address);
}

@Override
public void onOpen(WebSocket conn,ClientHandshake handshake){
WebsocketSessionManager.getSessionManager()。openSession(conn.getRemoteSocketAddress()。getAddress( ).getHostAddress());
Bukkit.getLogger()。info(conn.getRemoteSocketAddress()。getAddress()。getHostName()+已连接到Websocket服务器!);
}

@Override
public void onClose(WebSocket conn,int code,字符串原因,布尔型远程){
WebsocketSessionManager.getSessionManager()。endSession(conn。 getRemoteSocketAddress()。getAddress()。getHostAddress());
Bukkit.getLogger()。info(conn +已从Websocket服务器断开连接);
}

@Override
public void onMessage(WebSocket conn,String message){
Bukkit.getLogger()。info(接收Websocket数据包- + conn +: +讯息);
if(message.split(:)[0] .equalsIgnoreCase( name)){
WebsocketSessionManager.getSessionManager()。addSessionUsername(conn.getRemoteSocketAddress()。getAddress()。getHostAddress( ),message.split(:)[1]);
}
}

public static void runServer()抛出InterruptedException,IOException {
WebSocketImpl.DEBUG = true;
int端口= 8887;
s =新的WebsocketServer(端口);
s.start();
Bukkit.getLogger()。info( Websocket服务器在端口上启动: + s.getPort());
}

@Override
public void onError(WebSocket conn,Exception ex){
ex.printStackTrace();
if(conn!= null){
//某些错误,例如端口绑定失败,可能无法分配给特定的websocket
}
}

public void sendToAll(String data){
Collection< WebSocket> con = connections();
同步(con){
for(WebSocket c:con){
c.send(data);
}
}
}

public void sendData(WebsocketSession会话,字符串数据){
Collection< WebSocket> con = connections();
同步(con){
for(WebSocket c:con){
if(c.getRemoteSocketAddress()。getAddress()。getHostAddress()。equalsIgnoreCase(session.getHost())) {
Bukkit.getLogger()。info(发送数据包: +数据);
c.send(data);
}
}
}
}
}

这是我的Java语言接收者:

  var sound = null; 
var名称= window.location
document.session.name.value =名称

var文本= document.session.name.value

var ws = new WebSocket( ws:// + window.location.hostname +:8887 /);

ws.onopen = function(){
ws.send( name: + delineate(text)); ;
document.getElementById( title)。innerHTML =欢迎使用音乐服务器。请保持此窗口打开!

};

ws.onmessage =函数(evt){
函数loadScript(url,回调)
{
//按照$之前的建议将脚本标签添加到头部b $ b var head = document.getElementsByTagName('head')[0];
var script = document.createElement(’script’);
script.type =‘text / javascript’;
script.src =网址;

//然后将事件绑定到回调函数。
//有几个事件可实现跨浏览器兼容性。
script.onreadystatechange =回调;
script.onload =回调;

//触发加载的
head.appendChild(script);
}
if(evt.data == stop){
sound.fadeOut(0,3700);
}否则{
声音=新How叫({
网址:[’music /’+ evt.data +’.ogg’]
})。play();
console.log(播放音乐);
};
}

ws.onclose = function(){
alert( Closed!);
};

ws.onerror =函数(err){
alert( Error: + err);
};

函数delineate(str){
theleft = str.indexOf( =)+ 1;
theright = str.lastIndexOf(&);
return(str.substring(theleft,theright));
}

响应速度非常慢,但是服务器上的其他功能却非常快! / p>

有人可以帮忙吗?

解决方案

您使用的websocket库发送数据阻止,这意味着对 c.send(的调用将一直阻止,直到发送帧为止。



在那里有不同的解决方法,例如:



为每个消息使用单独的异步线程:



  public void sendToAll(String data){
// Ferrybig-添加了bukkit异步任务
Bukkit.getSchedular()。runTaskAsynchronously(plugin,new Runnable(){
@Override public void run(){
Collection< WebSocket> con = connections();
同步(con){
for(WebSocket c:con){
c。 send(data);
}
}
// Ferrybig-添加了bukkit异步任务
}});
}

虽然这可以快速解决您的问题,但这不是一个干净的解决方案,因为大量的发送消息意味着为了消息发送而创建了许多线程,即不要经常发送,或查看下一个解决方案:



使用专用的消息发送线程:



使用专用的消息发送线程是更好的解决方案,但是它附带了大代码。



对于此解决方案,我们需要执行以下操作:




  • 使用变量存储需要发送给每个客户端的邮件

      private final BlockingQueue< Pair< WebSocket,String>> messageQueue 
    =新的LinkedBlockingDeque<>();

    我们使用阻止队列,该队列包含包含Web套接字和要发送的消息的Pair对象。虽然我们也可以使用Map中的Map.Entry类,但我选择使用Pair,因为稍后我们可以稍稍更改代码以使其根据优先级自动求助消息。我用于此答案的Pair类可以在 Java中的C ++ Pair等价物中找到。。 p>


  • 使用专用线程发送消息



    我们有以下列表现在,我们收到传入消息,就可以处理传入的消息。这可以通过在 messageQueue.take() 。以下是对此的快速实现:

     公共类MessageProcessor扩展了BukkitRunnable {

    private BlockingQueue<对< WebSocket,String>> messageQueue;

    公共MessageProcessor(BlockingQueue< Pair< WebSocket,String>> messageQueue){
    this.messageQueue = messageQueue;
    }

    @Override
    public void run(){
    try {
    Pair< WebSocket,String>下一个;
    while(true){
    next = messageQueue.take();
    if(next.getFirst()== null){
    //特殊情况,将在后面解释
    return; //退出运行方法
    }
    // System.out.println(将消息发送到 + next.getFirst()); //调试
    next.getFirst()。send(next.getSecond());
    }
    } catch(InterruptedException e){
    Thread.currentThread()。interrupt();
    //有人想退出我们的线程,所以我们最终退出了
    } {
    messageQueue.clear();
    }
    }
    }

    以上课程有2个特殊项目条件, next.getFirst()== null catch(InterruptedException e),将在禁用时使用




    • 在启动bukkit时启动专用任务



    当bukkit和Websocket服务器启动时,我们需要启动任务,以便它可以开始处理消息和发送数据。这很容易在我们的 onEnable()中使用以下代码进行:



    新MessageProcessor(messageQueue).runTaskAsynchronouslyly (this);




    • 停止专用任务



    我们需要确保在禁用我们的插件时停止专用任务,以防止bukkit散发错误消息 。当此插件未正确关闭其异步任务时,正在重新加载。

    为此,我们确实很容易做到这一点。



    为此,我们将以下代码放置在 onDisable中()



    messageQueue.add(new Pair<>(null,null));




    • 重写我们的方法以使用messageQueue



    此过程的最后一步是重写 sendToAll 方法以使用队列。这真的很容易做到,只需要我们替换一行即可。

      public void sendToAll(String data){
    Collection< WebSocket> con = connections();
    同步(con){
    for(WebSocket c:con){
    messageQueue.add(new Pair<>(c,data)); // Ferrybig:使用messageQueue
    }
    }
    }

    对于 sendData 方法也可以进行相同的小修改,但是我并没有将其作为读者的练习。




边注



A BlockingQueue 设计为注意并发操作,不需要外部同步。



您可以选择使用 BlockingQueue.offer() 而不是 BlockingQueue.add(),因为事实是后者在列表已满时会引发异常,但第一个返回false。



LinkedBlockingDeque 的默认大小为 Integer.MAX_VALUE ,并且可以对其进行更改构造函数


I am dealing with encoding of a minecraft plugin. But now I have the following problem, my websocket server respond very very slow.

Here is my WebSocketClass (For the plugin)

// socket server class

package me.mickerd.pcoc;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Collection;

import org.bukkit.Bukkit;
import org.java_websocket.WebSocket;
import org.java_websocket.WebSocketImpl;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

public class WebsocketServer extends WebSocketServer {
public static WebsocketServer s;

public WebsocketServer(int port) throws UnknownHostException {
    super(new InetSocketAddress(port));
}

public WebsocketServer(InetSocketAddress address) {
    super(address);
}

@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
    WebsocketSessionManager.getSessionManager().openSession(conn.getRemoteSocketAddress().getAddress().getHostAddress());
    Bukkit.getLogger().info(conn.getRemoteSocketAddress().getAddress().getHostName() + " has connected to the Websocket server!");
}

@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
    WebsocketSessionManager.getSessionManager().endSession(conn.getRemoteSocketAddress().getAddress().getHostAddress());
    Bukkit.getLogger().info(conn + " has disconnected form the Websocket server");
}

@Override
public void onMessage(WebSocket conn, String message) {
    Bukkit.getLogger().info("Recieve Websocket packet - " + conn + ":" + message);
    if (message.split(":")[0].equalsIgnoreCase("name")) {
        WebsocketSessionManager.getSessionManager().addSessionUsername(conn.getRemoteSocketAddress().getAddress().getHostAddress(), message.split(":")[1]);
    }
}

public static void runServer() throws InterruptedException, IOException {
    WebSocketImpl.DEBUG = true;
    int port = 8887;
    s = new WebsocketServer(port);
    s.start();
    Bukkit.getLogger().info("Websocket server started on port: " + s.getPort());
}

@Override
public void onError(WebSocket conn, Exception ex) {
    ex.printStackTrace();
    if (conn != null) {
        // some errors like port binding failed may not be assignable to a specific websocket
    }
}

public void sendToAll(String data) {
    Collection<WebSocket> con = connections();
    synchronized (con) {
        for (WebSocket c : con) {
            c.send(data);
        }
    }
}

public void sendData(WebsocketSession session, String data) {
    Collection<WebSocket> con = connections();
    synchronized (con) {
        for (WebSocket c : con) {
            if (c.getRemoteSocketAddress().getAddress().getHostAddress().equalsIgnoreCase(session.getHost())) {
                Bukkit.getLogger().info("Send data packet: " + data);
                c.send(data);
            }
        }
    }
}
}

and this is my receiver in Javascript:

var sound = null;
var name = window.location
document.session.name.value = name

var text = document.session.name.value

var ws = new WebSocket("ws://" + window.location.hostname + ":8887/");

ws.onopen = function () {
        ws.send("name:" + delineate(text));
document.getElementById("title").innerHTML = "Welcome on the music server. Please hold this window open!";

};

ws.onmessage = function (evt) {
function loadScript(url, callback)
{
    // Adding the script tag to the head as suggested before
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    // Then bind the event to the callback function.
    // There are several events for cross browser compatibility.
    script.onreadystatechange = callback;
    script.onload = callback;

    // Fire the loading
    head.appendChild(script);
}
if(evt.data == "stop"){
  sound.fadeOut(0, 3700);
} else {
sound = new Howl({
  urls: ['music/' + evt.data + '.ogg']
}).play();
console.log("playing music");
};
}

ws.onclose = function () {
    alert("Closed!");
};

ws.onerror = function (err) {
    alert("Error: " + err);
};

function delineate(str) {
    theleft = str.indexOf("=") + 1;
    theright = str.lastIndexOf("&");
    return (str.substring(theleft, theright));
}

The reacts very slow, but other things on the server are incredible fast!

Can someone help?

解决方案

The websocket library you are using sends data blocking, this means that a call to c.send( will block until the frame is send.

There are different ways to solve this, for example:

Using a separate async thread for every message:

public void sendToAll(String data) {
    // Ferrybig - added bukkit async task
    Bukkit.getSchedular().runTaskAsynchronously(plugin, new Runnable(){
    @Override public void run(){
        Collection<WebSocket> con = connections();
        synchronized (con) {
            for (WebSocket c : con) {
                c.send(data);
            }
        }
    // Ferrybig - added bukkit async task
    }});
}

While this solves you problem quickly, it isn't a clean solution since a large amount of send messages means that there are a large number of threads created for the purpose of message sending, ie don't send to often, or see next solution:

Using a dedicated thread for the message sending:

Using a dedicated thread for the sending of messages is the better solution, but it comes with its large code.

For this solution, we need to do the following:

  • Use a variable to store the messages that need to be send to every client

    private final BlockingQueue<Pair<WebSocket,String>> messageQueue
                  = new LinkedBlockingDeque<>();
    

    We use a Blocking queue that holds Pair objects containing the web socket and the message to be send. While we also could used the Map.Entry classes from Map, I choose to use a Pair since we can later change the code a little to make it automatically resort message based on priority. The Pair class I used for this answer can be found at What is the equivalent of the C++ Pair in Java?.

  • Using a dedicated thread to send the messages

    We have list of incoming messages now, we now to process the messages as they come in. This can be done by making a task that blocks on messageQueue.take(). The following is a quick implementation of this:

    public class MessageProcessor extends BukkitRunnable {
    
        private BlockingQueue<Pair<WebSocket,String>> messageQueue;
    
        public MessageProcessor (BlockingQueue<Pair<WebSocket,String>> messageQueue) {
            this.messageQueue = messageQueue;
        }
    
        @Override 
        public void run() {
            try {
                Pair<WebSocket,String> next;
                while(true) {
                    next = messageQueue.take();
                    if(next.getFirst() == null) {
                        // Special condition, will be explained later
                        return; // Quit run method
                    }
                    // System.out.println("Send message to " + next.getFirst()); // DEBUG
                    next.getFirst().send(next.getSecond());
                }
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
                // Someone wanted to quit our thread, so we are quiting
            } finally {
                messageQueue.clear();
            }
        }
    }  
    

    The above class has 2 special conditions, next.getFirst() == null and catch(InterruptedException e), these will be used when we disable the plugin to quit the task.

    • Start our dedicated task when bukkit is started

    We need to start our task when bukkit and our Websocket server is started, so it can start processing messages and sending data. This is easy to do in our onEnable() with the following code:

    new MessageProcessor (messageQueue).runTaskAsynchronously(this);

    • Stopping the dedicated task

    We need to make sure the dedicated task is stopped when our plugin is disabled to prevent bukkit from spamming the error "This plugin is not properly shutting down its async tasks when it is being reloaded.". This is really easy to do since we have made a special condition for this above.

    To do this, we place the following code in our onDisable():

    messageQueue.add(new Pair<>(null,null));

    • Rewriting our methods to use the messageQueue

    Our last step in the process is to rewrite the sendToAll method to use our queue. This is really easy to do any only requires us to replace 1 line.

    public void sendToAll(String data) {
        Collection<WebSocket> con = connections();
        synchronized (con) {
            for (WebSocket c : con) {
                messageQueue.add(new Pair<>(c,data)); // Ferrybig: Use messageQueue
            }
        }
    }
    

    The same small modification can also be done for sendData method, but isn't done by me as an exercise for the reader.

Sidenotes

A BlockingQueue is designed with concurrent actions in mind, and doesn't require external synchronization.

You may choose to use BlockingQueue.offer() instead of BlockingQueue.add() because the fact the latter throws an exception when the list is full, but the first returns false.

The default size for a LinkedBlockingDeque is Integer.MAX_VALUE and can be changed with its constructor.

这篇关于WebSocket缓慢-Java和JavaScript的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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