I am using jersey to implement a SSE scenario.
The server keeps connections alive. And push data to clients periodically.
In my scenario, there is a connection limit, only a certain number of clients can subscribe to the server at the same time.
So when a new client is trying to subscribe, I do a check(EventOutput.isClosed) to see if any old connections are not active anymore, so they can make room for new connections.
But the result of EventOutput.isClosed is always false, unless the client explicitly calls close of EventSource. This means that if a client drops accidentally(power outage or internet cutoff), it's still hogging the connection, and new clients can not subscribe.
Is there a work around for this?
#CuiPengFei,
So in my travels trying to find an answer to this myself I stumbled upon a repository that explains how to handle gracefully cleaning up the connections from disconnected clients.
The encapsulate all of the SSE EventOutput logic into a Service/Manager. In this they spin up a thread that checks to see if the EventOutput has been closed by the client. If so they formally close the connection (EventOutput#close()). If not they try to write to the stream. If it throws an Exception then the client has disconnected without closing and it handles closing it. If the write is successful then the EventOutput is returned to the pool as it is still an active connection.
The repo (and the actual class) are available here. Ive also included the class without imports below in case the repo is ever removed.
Note that they bind this to a Singleton. The store should be globally unique.
public class SseWriteManager {
private final ConcurrentHashMap<String, EventOutput> connectionMap = new ConcurrentHashMap<>();
private final ScheduledExecutorService messageExecutorService;
private final Logger logger = LoggerFactory.getLogger(SseWriteManager.class);
public SseWriteManager() {
messageExecutorService = Executors.newScheduledThreadPool(1);
messageExecutorService.scheduleWithFixedDelay(new messageProcessor(), 0, 5, TimeUnit.SECONDS);
}
public void addSseConnection(String id, EventOutput eventOutput) {
logger.info("adding connection for id={}.", id);
connectionMap.put(id, eventOutput);
}
private class messageProcessor implements Runnable {
#Override
public void run() {
try {
Iterator<Map.Entry<String, EventOutput>> iterator = connectionMap.entrySet().iterator();
while (iterator.hasNext()) {
boolean remove = false;
Map.Entry<String, EventOutput> entry = iterator.next();
EventOutput eventOutput = entry.getValue();
if (eventOutput != null) {
if (eventOutput.isClosed()) {
remove = true;
} else {
try {
logger.info("writing to id={}.", entry.getKey());
eventOutput.write(new OutboundEvent.Builder().name("custom-message").data(String.class, "EOM").build());
} catch (Exception ex) {
logger.info(String.format("write failed to id=%s.", entry.getKey()), ex);
remove = true;
}
}
}
if (remove) {
// we are removing the eventOutput. close it is if it not already closed.
if (!eventOutput.isClosed()) {
try {
eventOutput.close();
} catch (Exception ex) {
// do nothing.
}
}
iterator.remove();
}
}
} catch (Exception ex) {
logger.error("messageProcessor.run threw exception.", ex);
}
}
}
public void shutdown() {
if (messageExecutorService != null && !messageExecutorService.isShutdown()) {
logger.info("SseWriteManager.shutdown: calling messageExecutorService.shutdown.");
messageExecutorService.shutdown();
} else {
logger.info("SseWriteManager.shutdown: messageExecutorService == null || messageExecutorService.isShutdown().");
}
}}
Wanted to provide an update on this:
What was happening is that the eventSource on the client side (js) never got into readyState '1' unless we did a broadcast as soon as a new subscription was added. Even in this state the client could receive data pushed from the server. Adding call to do a broadcast of a simple "OK" message helped kicking the eventSource into readyState 1.
On closing the connection from the client side; to be pro-active in cleaning up resources, just closing the eventSource on the client side doesn't help. We must make another ajax call to the server to force the server to do a broadcast. When the broadcast is forced, jersey will clean up the connections that are no longer alive and will in-turn release resources (Connections in CLOSE_WAIT). If not a connection will linger in CLOSE_WAIT till the next broadcast happens.
Related
I am using GRPC-Java 1.1.2. In an active GRPC session, I have a few bidirectional streams open. Is there a way to clean them from the client end when the client is disconnecting? When I try to disconnect, I run the following look for a fixed number of times and then disconnect but I can see the following error on the server side (not sure if its caused by another issue though):
disconnect from client
while (!channel.awaitTermination(3, TimeUnit.SECONDS)) {
// check for upper bound and break if so
}
channel.shutdown().awaitTermination(3, TimeUnit.SECONDS);
error on server
E0414 11:26:48.787276000 140735121084416 ssl_transport_security.c:439] SSL_read returned 0 unexpectedly.
E0414 11:26:48.787345000 140735121084416 secure_endpoint.c:185] Decryption error: TSI_INTERNAL_ERROR
If you want to close gRPC (server-side or bi-di) streams from the client end, you will have to attach the rpc call with a Context.CancellableContext found in package io.grpc.
Suppose you have an rpc:
service Messaging {
rpc Listen (ListenRequest) returns (stream Message) {}
}
In the client side, you will handle it like this:
public class Messaging {
private Context.CancellableContext mListenContext;
private MessagingGrpc.MessagingStub getMessagingAsyncStub() {
/* return your async stub */
}
public void listen(final ListenRequest listenRequest, final StreamObserver<Message> messageStream) {
Runnable listenRunnable = new Runnable() {
#Override
public void run() {
Messaging.this.getMessagingAsyncStub().listen(listenRequest, messageStream);
}
if (mListenContext != null && !mListenContext.isCancelled()) {
Log.d(TAG, "listen: already listening");
return;
}
mListenContext = Context.current().withCancellation();
mListenContext.run(listenRunnable);
}
public void cancelListen() {
if (mListenContext != null) {
mListenContext.cancel(null);
mListenContext = null;
}
}
}
Calling cancelListen() will emulate the error, 'CANCELLED', the connection will be closed, and onError of your StreamObserver<Message> messageStream will be invoked with throwable message: 'CANCELLED'.
If you use shutdownNow() it will more aggressively shutdown the RPC streams you have. Also, you need to call shutdown() or shutdownNow() before calling awaitTermination().
That said, a better solution would be to end all your RPCs gracefully before closing the channel.
I have a Java rich client application that registers a durable subscription on a remote HornetQ JMS Topic on startup.
However if the server restarts the connection is lost and can only be restored by restarting the client application.
This leads to confusing situations where JMS messages are not received and after a client restart a lot of messages are received at once.
A simple solution to restore connection would be to run a timer to periodically check if the connection is still valid and try to reconnect otherwise.
Alternatively the server could send a heartbeat to the client and try to reconnect if no heartbeat is received after a certain period (like mentioned in this answer).
But both seem like clumsy approaches to this problem and therefore I would like to know if there is a better solution to find out that the connection is not available anymore?
To get notified on the disconnection you have to register an ExceptionListener on your TopicConnection before you start the connection.
private void subscribe() throws JMSException {
// get context / create connection and session / etc.
TopicConnection connection = ...
connection.setExceptionListener(this::handleExceptions);
connection.start();
}
In the ExceptionListener you can check the error code of the received JMSException. (The error codes are vendor-specific)
In the case of HornetQ the error code DISCONNECT is received after the connection is lost.
private static final String HORNETQ_DISCONNECT_ERROR_CODE = "DISCONNECT";
private void handleExceptions(final JMSException jmsException) {
final String errorCode = jmsException.getErrorCode();
if (HORNETQ_DISCONNECT_ERROR_CODE.equals(errorCode)) {
tryConnect();
}
}
Then you can start a self-canceling Timer that tries to reconnect every x seconds until it succeeds.
private static final long SUBSCRIBE_RETRY_TIME_OUT_IN_MILLIS = 60000;
private void tryConnect() {
final Timer timer = new Timer("JMS-Topic-Reconnection-Timer", true);
final TimerTask timerTask = new TimerTask() {
#Override
public void run() {
try {
subscribe();
// cancel the timer, after the subscription succeeds
timer.cancel();
}
catch (final Exception e) {
logger.info("reconnect to jms topic failed: {}", e.getMessage());
}
}
};
timer.schedule(timerTask, SUBSCRIBE_RETRY_TIME_OUT_IN_MILLIS, SUBSCRIBE_RETRY_TIME_OUT_IN_MILLIS);
}
Background
I have implemented an adapter interface using the RPC protocol, but recently have been tasked with implementing the interface using a WebSocket listener. With RPC, I was easily able to start an RPC listener thread to listen for events on a separate thread, but I'm not finding it so simple when it comes to JSR356.
The Question
I'm attempting to implement a Java WebSocket ClientEndpoint that connects to a subscription URI, but I want to do so in a manner that utilizes multi-threading. I've been having a hard time finding any examples where multi-threading is needed from a client endpoint perspective. Is this even possible?
I need the WebSocket message handler to handle messages without blocking the main thread. I have not implemented the message handler yet because I'm not sure how to go about creating it in a way to accomplish what I want. Can anyone help point me in a better direction? Here's what I have so far:
EventHandler.java
#ClientEndpoint
public class EventHandler {
private URI subscriptionURI;
private Session clientSession;
public EventHandler(URI subscriptionURI) throws URISyntaxException {
this.subscriptionURI = subscriptionURI;
}
/**
* Attempts to connect to the CADI WebSocket server.
* #throws Exception
*/
public void connect() throws Exception {
// Grab the WebSocket container and attempt to connect to the subscription URI
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, subscriptionURI);
}
/**
* Closes the CADI WebSocket client session.
*/
public void close() {
try {
// Close the client session if it is open
if(clientSession != null && clientSession.isOpen())
clientSession.close();
}
catch(Exception e) {
LogMaster.getErrorLogger().error("Could not close the WebSocket client session. It may have been closed already.", e);
}
}
#OnOpen
public void socketOpened(Session session) {
this.clientSession = session;
}
}
Here is how I start a new thread to connect to the WebSocket. What are the implications of this, though? Are subsequent messages received on the WebSocket going to block the main thread still?
EventHandler eventHandler = new EventHandler(new URI("wss://localhost/Example"));
new Thread()
{
#Override
public void run() {
try {
eventHandler.connect();
}
catch (Exception e) {
LogMaster.getErrorLogger().error("Could not start EventHandler.", e);
}
}
}.start();
I'm facing this issue working with a ServerSocket inside one of my bundles, let's just call it: FooBundle.
This FooBundle has, among others, a SocketListener.java class. This class is a Thread and to make a little overview of it, I'll paste some pseudocode:
public class SocketListener implements Runnable{
ServerSocket providerSocket;
Socket connection = null;
private boolean closeIt = false;
public void run() {
try {
//Create the server socket
providerSocket = new ServerSocket(41000, 10);
} catch (IOException e1) {
//catching the exception....
}
while(!closeIt){
try{
connection = providerSocket.accept();
in = new Scanner(new InputStreamReader(onnection.getInputStream()));
while(in.hasNext() !=false)
message = message + " "+in.next();
// bla bla bla...
} catch (IOException e) {
//bla bla...
}
finally{
try{
if (message.equalsIgnoreCase("bye"))
providerSocket.close();
closeIt = true;
}
catch(IOException ioException){
//........
}
}
As you can see, it's a simple thread that waits for a connection until the message it receives from one of the SocketClients is "bye".
This is the problem I'm facing right now: When the Bundle is stopped, I do need to restart the entire OSGi framework : If I try to restart the bundle, a java.net.BindException message is thrown: "Address already in use". So, I stopped the bundle but the socket hasn't been closed.
In OSGi, you need to take care of what the stop() method inside the Activator must include, but I just can't pass any reference of an anonymous thread to the Activator.
Imagine that this is my class diagram inside the bundle:
**FooBundle**
|__FooBundleActivator
|__FooImpl
|__SocketListener (thread)
The SocketListener thread is called from the FooImpl class as an anonymous thread.
My question is: Is there any appropiate method to have such control of anonymous threads and specifically in my case, of non-closing socket ports, inside the OSGi paradigm?
Thanks in advance.
If your bundle is told to stop then assume the guy doing the stopping knows what he is doing. Yes, your protocol expects the 'bye' but shit happens, any protocol that has problems with these things is too fragile for the real world. In general, all your tasks in OSGi should have a life cycle. So this would be my code (using DS instead of activators).
#Component
public class ProtocolServer extends Thread {
volatile ServerSocket server;
volatile Socket connection;
public ProtocolServer() {
super("Protocol Server on 4100"); // to identify the thread
}
#Activate void activate() {
setDaemon(true);
start();
}
#Deactivate void deactivate() {
interrupt();
// best effort close (even if null)
try { server.close(); } catch(Exception e) {}
try { connection.close(); } catch(Exception e) {}
join(10000); // waits 10 secs until thread exits
}
public void run() {
// loop for active component
while( !isInterrupted() )
try {
doServer();
} catch( Exception e) {
log(e);
// bad error, accept failed or bind failed
// or server socket was closed. If we should remain
// active, sleep to prevent overloading the
// system by trying too often, so sleep
if ( !isInterrupted() )
try { Thread.sleep(5000); } catch(Exception e) {}
}
}
private void doServer() throws Exception {
server = new ServerSocket(4100)
try {
while( !isInterrupted() )
doConnection(server);
} finally {
server.close();
}
}
private void doConnection(ServerSocket server) throws Exception {
connection = server.accept();
try {
doMessages(connection);
// the pseudo code exits here, but that seems
// kind of weird? If desired, interrupt
// this object, this will exit the thread
} catch( Exception e) {
log(e); // the connection failed, is not uncommon
} finally {
connection.close();
connection = null;
}
}
private void doMessages(Socket connection) {
MyScanner s = new MyScanner(socket);
String msg;
while( !isInterrupted() && !"bye".equals( msg=s.getMessage()))
process(msg);
}
}
One important design consideration in OSGi is that the components keep working even if there are failures. In a network you often have transient errors that go away on their own. Even if they don't it is desirable that the server keeps on trying while you fix the problem. Your pseudo code would be a nightmare in practice since it would disappear on any error. Any system with multiple such components tends to becomes quickly unstable.
One thing that also surprised me is that you only support one connection at a time. In general it is better to not limit this and handle the messages in their own thread. In that case, you must ensure that each created handler for a connection is also closed appropriately.
Instantiate the ServerSocket outside (probably in the Activator) and pass it to the SocketListener via a constructor. You can call serverSocket.stop() in the stop function of the Activator than.
In case you call ServerSocket.stop() a SocketException will be thrown that is a subclass of IOException. Please think of handling IOException in the while iteration in the way that it will stop executing the iteration for sure.
You need to close that listening socket regardless of the message before exiting the thread function. Then what should really make a difference for you is calling setReuseAddress(true) on that socket to allow binding the port while old connection hangs in the timeout state.
And, please please please, use better indentation technique in your code ...
I got thread for server in my Android app and need to handle it properly when user decide to close it. I choose non-blocking ServerSocketChannel which accept() clients.
And got this
public class SocketServer extends Thread
{
private static final String LOG_TAG = "SocketServer";
private boolean isRunning = false;
private ServerSocketChannel listener = null;
public void _stop()
{
this.isRunning = false;
}
public void _start()
{
this.isRunning = true;
this.start();
}
private void free()
{
try
{
listener.close();
}
catch (IOException e)
{
//Error handle
}
listener = null;
}
public SocketServer(int port)
{
super();
try
{
listener = ServerSocketChannel.open();
listener.configureBlocking(false);
listener.socket().bind(new InetSocketAddress(port));
}
catch (IOException e)
{
//Error handle
}
}
public void run()
{
SocketChannel client = null;
while(isRunning)
{
try
{
client = listener.accept();//GC going mad
}
if(client != null)
Log.i(LOG_TAG, "ACCEPTED CLIENT");
catch (IOException e)
{
//Error handle
}
}
free();
}
All i'm doing is accepting new client - getting null because of no incoming connections and do it again until server is stopped.
ServerClient client is null at start and assigned to null by accept() if no connections available.
But Java's garbage collector thinks what client is somehow init by accept() or accept() somehow allocate some memory, which GC cleans after every while loop.
If comment accept() line (e.g do nothing) where will be no GC at all, so problem exactly in accept().
This quite not right in my opinion.
P.S. If there is some way to break blocking ServerSocket accept()/Socket read() state and exit properly, please tell me.
P.S. 2 Is it safe to write/ read to SocketChannel socket() as to Socket, will it block thread?
Many operations in Java create temporary objects internally to do their work.
You are much better off using a blocking SocketServer. This way the objects it creates is only on a per-accepted-Socket basis rather than a per-attempt basis.
I suggest you implement blocking NIO with a thread (or two) per connection first. If then you discover you have a performance issue with the number of threads you have, try using a Selector with non-blocking NIO.