js websocket同步等待,WebSocket缓慢-Java和JavaScript

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 con = connections();

synchronized (con) {

for (WebSocket c : con) {

c.send(data);

}

}

}

public void sendData(WebsocketSession session, String data) {

Collection 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 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> 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> messageQueue;

public MessageProcessor (BlockingQueue> messageQueue) {

this.messageQueue = messageQueue;

}

@Override

public void run() {

try {

Pair 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():

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 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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值