After a while on our WildFly 18 server, in production, we encountered this error:
[org.xnio.listener] (default I/O-1) XNIO001007: A channel event listener threw an exception:
java.lang.OutOfMemoryError: Direct buffer memory
at java.base/java.nio.Bits.reserveMemory(Bits.java:175)
at java.base/java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:118)
at java.base/java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:317)
at org.jboss.xnio#3.7.3.Final//org.xnio.BufferAllocator$2.allocate(BufferAllocator.java:57)
at org.jboss.xnio#3.7.3.Final//org.xnio.BufferAllocator$2.allocate(BufferAllocator.java:55)
at org.jboss.xnio#3.7.3.Final//org.xnio.ByteBufferSlicePool.allocateSlices(ByteBufferSlicePool.java:162)
at org.jboss.xnio#3.7.3.Final//org.xnio.ByteBufferSlicePool.allocate(ByteBufferSlicePool.java:149)
at io.undertow.core#2.0.27.Final//io.undertow.server.XnioByteBufferPool.allocate(XnioByteBufferPool.java:53)
at io.undertow.core#2.0.27.Final//io.undertow.server.protocol.framed.AbstractFramedChannel.allocateReferenceCountedBuffer(AbstractFramedChannel.java:549)
at io.undertow.core#2.0.27.Final//io.undertow.server.protocol.framed.AbstractFramedChannel.receive(AbstractFramedChannel.java:370)
at io.undertow.core#2.0.27.Final//io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:38)
at io.undertow.core#2.0.27.Final//io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:33)
at org.jboss.xnio#3.7.3.Final//org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at io.undertow.core#2.0.27.Final//io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:950)
at io.undertow.core#2.0.27.Final//io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:931)
at org.jboss.xnio#3.7.3.Final//org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at org.jboss.xnio#3.7.3.Final//org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66)
at org.jboss.xnio.nio#3.7.3.Final//org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:89)
at org.jboss.xnio.nio#3.7.3.Final//org.xnio.nio.WorkerThread.run(WorkerThread.java:591)
We checked a JVM dump through jxray, and it seems websockets are the culprit:
Fact is that our websocket is kind of straightforward:
#ApplicationScoped
#ServerEndpoint(value = "/ws/messenger/{accountId}")
public class MessengerSocket implements Serializable
{
private static final long serialVersionUID = -3173234888004281582L;
#Inject
private Logger log;
#Inject
private MessengerHandler handler;
#OnOpen
public void onOpen(#PathParam("accountId") String accountId, Session session, EndpointConfig config)
{
log.debug("Opening for {}", accountId);
handler.subscribeSocket(session, UUID.fromString(accountId));
}
#OnClose
public void onClose(#PathParam("accountId") String accountId, Session session, CloseReason closeReason)
{
log.debug("Closing {}", accountId);
handler.unsubscribeSocket(session, UUID.fromString(accountId));
}
}
It's coupled with a simple handler, managing a map of users sessions:
#ApplicationScoped
public class MessengerHandler
{
#Inject
private Logger log;
// key: Account id
private Map<UUID, AccountMessengerSessions> sessions;
public void init()
{
sessions = new ConcurrentHashMap<>();
}
public void subscribeSocket(Session session, UUID accountId)
{
// build and store the account messenger session if new
AccountMessengerSessions messenger = sessions.getOrDefault(accountId, new AccountMessengerSessions(accountId));
messenger.getWsSessions().add(session);
sessions.putIfAbsent(accountId, messenger);
log.debug("{} has {} messenger socket session(s) (one added)", messenger.getAccountId(), messenger.getWsSessions().size());
}
/**
* Unsubscribes the provided WebSocket from the Messenger.
*/
public void unsubscribeSocket(Session session, UUID accountId)
{
if (!sessions.containsKey(accountId))
{
log.warn("Ignore unsubscription from {} socket, as {} is unknwon from messenger", session.getId(), accountId);
return;
}
AccountMessengerSessions messenger = sessions.get(accountId);
messenger.getWsSessions().remove(session);
log.debug("{} has {} messenger socket session(s) (one removed)", messenger.getAccountId(), messenger.getWsSessions().size());
if (!messenger.getWsSessions().isEmpty())
{
return;
}
// no more socket sessions, fully remove
sessions.remove(messenger.getAccountId());
}
}
Client side, we have a bit of javascript called when a page is loaded, again, nothing fancy:
var accountId = // some string found in DOM
var websocketUrl = "wss://" + window.location.host + "/ws/messenger/" + accountId;
var websocket = new WebSocket(websocketUrl);
websocket.onmessage = function (event) {
var data = JSON.parse(event.data);
// nothing fancy here...
};
Our users don't use a lot the feature offered by the websocket (an instant messenger), so what is really happening in production is basically websockets opening and closing at each page, with very few messages sent through.
Where could we get it wrong and create this buffer leak? Did we forget something critical?
Looking at this post this may happen if you have a lot of CPU. This was solved by decreasing the number of IO workers. Not sure if this can help in your case.
I also had a similar problem on our wildfly 18 (wildfly 19 suffers from it, too). It is probably triggered by a faulty xnio lib inside wildfly. After updating to wildfly 22 (using the latest xnio lib) the problem was gone.
Related
I am using netty-socket.io and I implemented the server like the demo.
I receive onConnect event both on server and client, but when I sent a message {message: message} I don't get anything on the server event though I see the message being sent in the network tab.
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(9092);
final SocketIOServer server = new SocketIOServer(config);
server.addConnectListener(socketIOClient -> System.out.println("Connection test"));
server.addEventListener("messageevent", MessageEventObject.class, new DataListener<MessageEventObject>() {
#Override
public void onData(SocketIOClient socketIOClient, MessageEventObject messageEventObject, AckRequest ackRequest) throws Exception {
System.out.println("message received!");
}
});
server.start();
My MessageEventObject has String message property, constructor getters and setters, looking the same as client-sided.
And this is my websocket service client-sided:
export class WebsocketService {
private socket;
private subject = new Subject < any > ();
constructor() {
console.log('test!');
}
public connect(host: string, port: number) {
this.socket = io(`http://${host}:${port}`, {
'reconnection': false
});
this.socket.on('connect', this.onConnected);
this.socket.on('connect_error', this.onConnectionFailure);
}
public getConnectionStateUpdate(): Observable < any > {
return this.subject.asObservable();
}
public sendMessage(message: string) {
console.log('test');
this.socket.emit('messageevent', {
message: message
});
}
private onConnected = () => {
this.subject.next({
connected: true
});
}
private onConnectionFailure = () => {
this.subject.next({
connected: false
});
}
}
Is there anything that I did wrong?
I would love to answer my own question after tons of debugging and breaking my head, my laziness to use Engine IO with tomcat or jetty, and just wanting to use that awesome netty package which does not require any servlets, I tried to fix it and figure out.
At first I thought it was the client's protocol version, so I used the exact same client as the demo shows on their github page here but that didn't work so the problem is server-sided.
It appears that your object (MessageEventObject) must have a default empty constructor aswell in addition to your other constructors, probably because netty tries to build an empty object and it fails which causes an exception that you don't see.
I am learning Akka remoting, referring to the book Learning Akka.
Using a limited network, I can't use sbt (can't config the proxy well).
First, I create a project for an Akka server with the application.conf
akka {
actor {
provider = remote
}
remote {
emabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2552
}
}
}
and the console shows
Remoting now listens on addresses: [akka.tcp://akkademy#127.0.0.1:2552]
The second project is the client having a JClient class:
public class JClient {
private static final int TIMEOUT = 2000;
private final ActorSystem system = ActorSystem.create("LocalSystem");
private final ActorSelection remoteDb;
public JClient(String remoteAddress) {
remoteDb = system.actorSelection("akka.tcp://LocalSystem#" + remoteAddress + "/user/akkademy-db");
}
public CompletionStage set(String key, Object value) {
return toJava(ask(remoteDb, new SetRequest(key, value), TIMEOUT));
}
public CompletionStage<Object> get(String key) {
return toJava(ask(remoteDb, new GetRequest(key), TIMEOUT));
}
}
I pass the value "127.0.0.1:2552" to remoteAddress, calling the set/get methods, and encounter the error:
java.util.concurrent.ExecutionException: akka.pattern.AskTimeoutException: Ask timed out on [ActorSelection[Anchor(akka://akkademy/deadLetters), Path(/user/.*)]] after [2000 ms]. Sender[null] sent message of type "javah.GetRequest".
Your client code to obtain an ActorSelection to the remote actor is incorrect. Instead of "LocalSystem", which is the name of the client's actor system, use "akkademy", the name of the server's actor system. Change the JClient constructor to the following:
public JClient(String remoteAddress) {
remoteDb = system.actorSelection("akka.tcp://akkademy#" + remoteAddress + "/user/akkademy-db");
}
In actorSelection the selector should be string of format akka.tcp://${remoteActorSystemName}#${remoteAddress}/user/$actorPath. In the snippet you've posted, looks like you were using LocalSystem as ${remoteActorSystemName} instead of the remote actor system name.
Let me know if switching it to remote works, if not, can you post the full code you are using or a link to it ?
Let me explain the purpose of my application so you can guide me about the best possible approach.
The idea is building a web application to remotely manage some particular equipments that my company manufactures. These equipments will periodically connect to the remote server to send/receive certain data (through simple socket communication but they don't use Java); this data will be stored in the corresponding data base and will be available through the web application for the different users.
In the same way, when you access through the web interface, each client will be able to see their equipments and perform different changes in the configuration. At this point there are 2 possible options and this is the reason of this post:
The easiest but not the best option: the user performs some changes and I save those changes in the data base. When the equipment later establish communication to the server, then it'll read those changes and update its configuration.
The ideal solution: as soon as the user save the changes through the web interface and push the "send" button, those changes are sent to the corresponding equipment.
As mentioned above, these equipments will periodically open a socket communication (let's say every 5 minutes) to the server to send their configuration. At this moment, in order to implement the "ideal solution", the only option I can think of is not to close that socket so I can use it to immediately send information back to the equipment when a certain user makes any changes.
If this application grows along the time, I'm afraid that too many open sockets/threads can crash my application.
Let me illustrate with some code I was playing around. I know this is far from the final solution, it's just to help you understand what I'm looking for.
First of all, I register the socket server during the start-up of the web server (Tomcat in this case):
package org.listeners;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.sockets.KKMultiServer;
public class ApplicationListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
KKMultiServer kKMultiServer = new KKMultiServer();
Thread serverThread = new Thread(kKMultiServer);
serverThread.start();
event.getServletContext().setAttribute("PlainKKMultiServer", kKMultiServer);
}
public void contextDestroyed(ServletContextEvent event) { }
}
This is the main socket server class that listens for new connections:
public class KKMultiServer implements Runnable {
private Map<Long, KKMultiServerThread_v2> createdThreads = new HashMap<Long, KKMultiServerThread_v2>();
#Override
public void run() {
boolean listening = true;
try (ServerSocket serverSocket = new ServerSocket(5000)) {
while (listening) {
KKMultiServerThread_v2 newServerThread = new KKMultiServerThread_v2(serverSocket.accept(), this);
Thread myThread = new Thread(newServerThread);
myThread.start();
Long threadId = myThread.getId();
System.out.println("THREAD ID: " + threadId);
}
} catch (IOException e) {
System.err.println("Could not listen on port " + 5000);
System.exit(-1);
}
}
public Map<Long, KKMultiServerThread_v2> getCreatedThreads() {
return createdThreads;
}
}
And the thread class created with every single petition from each of the equipments (dispensers) to handle the socket communication:
public class KKMultiServerThread_v2 implements Runnable {
private Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
private long dispenserCode;
private KKMultiServer kKMultiServer;
public KKMultiServerThread_v2(Socket socket, KKMultiServer kKMultiServer) {
this.socket = socket;
this.kKMultiServer = kKMultiServer;
}
public void run() {
try {
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
readDataFromDispenser();
}
private void readDataFromDispenser() {
String inputLine;
try {
while ((inputLine = in.readLine()) != null) {
if (inputLine.equals("Bye")) {
break;
}
if (dispenserCode == 0) {
dispenserCode = 1111; // this code will be unique per equipment
this.kKMultiServer.getCreatedThreads().put(dispenserCode, this);
}
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendDataToDispenser(String dataToSend) {
if (!socket.isClosed() && socket.isConnected()) {
out.println(dataToSend);
} else {
this.kKMultiServer.getCreatedThreads().remove(this);
}
}
}
Now that the socket is created and alive I can use it directly from the web application to send messages back to the equipment (Struts Action in this case)
public class HelloWorldAction extends ActionSupport {
private static final long serialVersionUID = 1L;
public String sendMessageToDispenser() throws Exception {
ServletContext context = ServletActionContext.getServletContext();
KKMultiServer kKMultiServer = (KKMultiServer) context.getAttribute("PlainKKMultiServer");
Map<Long, KKMultiServerThread_v2> currentThreads = kKMultiServer.getCreatedThreads();
Iterator<Long> it = currentThreads.keySet().iterator();
while (it.hasNext()) {
Long key = (Long) it.next();
KKMultiServerThread_v2 currentThread = currentThreads.get(key);
currentThread.sendDataToDispenser("DATA TO YOU!");
}
return SUCCESS;
}
}
Do you think it's possible to perform this solution? I mean, keeping these connections open so I can access my equipments whenever necessary (without waiting for the periodically connections). What's the best approach? If you have any other suggestions please let me know.
Thank you very much.
To my mind it clearly depends on how many equipment will be connected to your system. Sockets are not always sending data so it can have low effect on the overall performance. Though, Socket are know to be a little slow, if you have a lot of data to send to/from your equipments, you should consider this.
If you want to have send data from your server to your client you have few solutions
Your server knows all your equipment after registering for example. When starting you equipment connect to the server. (be careful about local network redirection)
Your equipment and server use sockets to communicate
I don't think there is another solution but I can be wrong. If your equipment request your server every X seconds, it will never be exactly perfeclty on time.
I have a java client calling a java SOAP webservice deployed on glassfish server 4.1, that same java client also has a websocket connection to a server deployed on the same server.
Basically my architecture is this I have a java client calling a SOAP webservice, and connected to a java websocket server endpoint. Why? You can akin this to a bank, the client calls the SOAP webservice for say to check the account balance, then the websocket serves in notifying the client when a credit/debit has been made on the account.
Problem: In my main method I startup connection with the websocket, after that I call the webservice for a resource. Now the problem is I have to try several times before a call to the webservice is made. Its a very strange bug. I tried initializing the websocket on a different thread but I still get the same problem. Please see the code below
Bank webservice:
//relevant import statements
#WebService(serviceName = "BankService")
public class BankService {
private static final Map<Integer, AccountHolder> accountHolders = new ConcurrentHashMap<Integer, AccountHolder>();
private static AccountHolder customer, merchant;
public BankService(){
customer = new AccountHolder("Customer 1", 1234, 4321, 4000, null, null);
merchant = new AccountHolder("Merchant", 5678, 8765, 1000, null, null);
accountHolders.put(1234, customer);
accountHolders.put(5678, merchant);
}
#WebMethod(operationName = "getBalance")
public String getBalance(#WebParam(name = "acctNum") int acctNum) {
AccountHolder client;
synchronized(accountHolders){
client = accountHolders.get(acctNum);
}
return client.toString(); //returns a string containing balance
}
}
Bank notification websocket:
#ServerEndpoint(value="/n")
public class NotificationServer {
private Logger logger = Logger.getLogger(this.getClass().getName());
#OnOpen
public void onOpen(Session session) throws InterruptedException, IOException {
logger.info("Connected ... " + session.getId());
}
#OnMessage
public void notifyCustomer(String message, Session session) throws InterruptedException, IOException {
session.getBasicRemote().sendText(message);
}
#OnClose....
}
Client
public class Client {
static Client client;
private String getBalance(int acctNum) {
BankService_Service service = new BankWebService.BankService_Service();
BankService port = service.getBankServicePort();
return port.getBalance(acctNum);
}
private void wsc() throws InterruptedException {
BankNotificationClient bnc = new BankNotificationClient();
Thread thread = new Thread(bnc);
thread.start();
}
public static void main(String[] args) throws InterruptedException {
client = new Client();
client.wsc();
Scanner scanner = new Scanner(System.in);
while(!"quit".equals(scanner.next())){
if(scanner.next().equals("check")){
System.out.println(client.getBalance(1234));
}
}
}
}
Bank notification client:
//necessary import statements
import javax.*;
import org.glassfish.tyrus.client.ClientManager;
#ClientEndpoint
public class BankNotificationClient implements Runnable {
private static Session clientSession;
private Logger logger = Logger.getLogger(this.getClass().getName());
ClientManager clientws;
#OnOpen
public void onOpen(Session session) throws InterruptedException {
logger.info("Connected ... " + session.getId());
clientSession = session;
}
#OnMessage
public void onMessage(String message, Session session) {
....
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
logger.info(String.format("Session %s has closed because %s", session.getId(), closeReason));
}
public void run() {
try {
client = ClientManager.createClient();
client.connectToServer(BankNotificationClient.class, new URI("ws://localhost:8080/BankService/n"));
System.out.println("working" + client.getClass());
} catch (DeploymentException | URISyntaxException e) {
logger.info("Something has gone wrong initializing the socket");
}
}
}
I am using netbeans 8.0.2 as my development IDE. Having deployed the webservice and websocket to glassfish server 4.1. Running this would give the following output
Output:
Aug 05, 2015 11:31:18 PM BankNotificationHandler.BankNotificationClient onOpen
INFO: Connected ... 981de7b4-91b9-43b6-beaf-c219500ee77a
check
check
Dear Customer Customer 1, your account balance is 4000.
check
check
Dear Customer Customer 1, your account balance is 4000.
check
check
Dear Customer Customer 1, your account balance is 4000.
As you can see from the output I have to type and enter "check" twice before I get a result from the webservice. Using a debugger on the first entry of check the code suddenly halts at the if statement in the while loop, then I have to enter "check" again. On entering check it continues from where it stopped in the if statement line and proceeds to give me the desired result.
Please let me know if there is any other information I have left out.
Cheers.
I'm currently using Struts 2 as my framework and I need to have a Websocket feature so I can communicate with my client that is accessing it through HTML Websocket.
I have tried to use Java Websocket API (JSR 356) with Java application running on Tomcat 7.0.56. However, when I try it with Struts 2 framework, it does not work.
Some researches that I did suggested that it could have been because of the way Struts 2 maps the URL, but to no avail, I am still unable to communicate with the Websocket endpoint on my server.
Do anyone have any idea how to implement Websocket with Struts 2 framework?
The code that I used for the websocket is as follow:
#ServerEndpoint("/mssendpoint")
public class MSSEndpoint {
public static Logger logger = Logger.getLogger(MSSEndpoint.class);
/* Queue for all open WebSocket sessions */
static Queue<Session> queue = new ConcurrentLinkedQueue<Session>();
static Set<WebsocketListener> listeners = new HashSet<WebsocketListener>();
public static void send(String msg) {
try {
/* Send updates to all open WebSocket sessions */
for (Session session : queue) {
session.getBasicRemote().sendText(msg);
logger.info("Sent: " + msg);
}
}
catch (IOException e) {
logger.error(e.toString());
}
}
#OnOpen
public void openConnection(Session session) {
/* Register this connection in the queue */
queue.add(session);
logger.info("Connection opened.");
}
#OnClose
public void closedConnection(Session session) {
/* Remove this connection from the queue */
queue.remove(session);
logger.info("Connection closed.");
}
#OnError
public void error(Session session, Throwable t) {
/* Remove this connection from the queue */
queue.remove(session);
logger.info(t.toString());
logger.info("Connection error.");
}
#OnMessage
public void onMessage(String message, Session session) {
if (queue.contains(session)) {
notifyListener(message);
}
}
public static void addListener(WebsocketListener listener){
listeners.add(listener);
}
public static void removeListener(WebsocketListener listener){
listeners.remove(listener);
}
public void notifyListener(String message){
for (WebsocketListener listener : listeners) {
listener.onMessage(message);
}
}
}
I have used this exact same code on normal Java Servlet application running on Tomcat 7.0.56 and with a client, I could connect to it.
I used 'Simple Websocket Client' chrome extension as the client.
All I need was to connect to ws://localhost/myprojectname/mssendpoint and it will connect directly.
EDIT2:
I forgot to mention that the error was that when I tried to connect, it will simply say undefined when I use the Websocket Client. Assuming that my Struts 2 project is called cms, by right I should just need to access ws://localhost/myprojectname/mssendpoint. But then it produces that undefined message.