![JAR search and dependency download from the Maven repository](/logo.png)
com.netflix.hystrix.contrib.sample.stream.HystrixSampleSseServlet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hystrix-metrics-event-stream Show documentation
Show all versions of hystrix-metrics-event-stream Show documentation
hystrix-metrics-event-stream developed by Netflix
The newest version!
/**
* Copyright 2016 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.hystrix.contrib.sample.stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.schedulers.Schedulers;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
/**
*/
public abstract class HystrixSampleSseServlet extends HttpServlet {
protected final Observable sampleStream;
private static final Logger logger = LoggerFactory.getLogger(HystrixSampleSseServlet.class);
//wake up occasionally and check that poller is still alive. this value controls how often
protected static final int DEFAULT_PAUSE_POLLER_THREAD_DELAY_IN_MS = 500;
private final int pausePollerThreadDelayInMs;
/* Set to true upon shutdown, so it's OK to be shared among all SampleSseServlets */
private static volatile boolean isDestroyed = false;
protected HystrixSampleSseServlet(Observable sampleStream) {
this.sampleStream = sampleStream;
this.pausePollerThreadDelayInMs = DEFAULT_PAUSE_POLLER_THREAD_DELAY_IN_MS;
}
protected HystrixSampleSseServlet(Observable sampleStream, int pausePollerThreadDelayInMs) {
this.sampleStream = sampleStream;
this.pausePollerThreadDelayInMs = pausePollerThreadDelayInMs;
}
protected abstract int getMaxNumberConcurrentConnectionsAllowed();
protected abstract int getNumberCurrentConnections();
protected abstract int incrementAndGetCurrentConcurrentConnections();
protected abstract void decrementCurrentConcurrentConnections();
/**
* Handle incoming GETs
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (isDestroyed) {
response.sendError(503, "Service has been shut down.");
} else {
handleRequest(request, response);
}
}
/**
* WebSphere won't shutdown a servlet until after a 60 second timeout if there is an instance of the servlet executing
* a request. Add this method to enable a hook to notify Hystrix to shutdown. You must invoke this method at
* shutdown, perhaps from some other servlet's destroy() method.
*/
public static void shutdown() {
isDestroyed = true;
}
@Override
public void init() throws ServletException {
isDestroyed = false;
}
/**
* Handle servlet being undeployed by gracefully releasing connections so poller threads stop.
*/
@Override
public void destroy() {
/* set marker so the loops can break out */
isDestroyed = true;
super.destroy();
}
/**
* - maintain an open connection with the client
* - on initial connection send latest data of each requested event type
* - subsequently send all changes for each requested event type
*
* @param request incoming HTTP Request
* @param response outgoing HTTP Response (as a streaming response)
* @throws javax.servlet.ServletException
* @throws java.io.IOException
*/
private void handleRequest(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
final AtomicBoolean moreDataWillBeSent = new AtomicBoolean(true);
Subscription sampleSubscription = null;
/* ensure we aren't allowing more connections than we want */
int numberConnections = incrementAndGetCurrentConcurrentConnections();
try {
int maxNumberConnectionsAllowed = getMaxNumberConcurrentConnectionsAllowed(); //may change at runtime, so look this up for each request
if (numberConnections > maxNumberConnectionsAllowed) {
response.sendError(503, "MaxConcurrentConnections reached: " + maxNumberConnectionsAllowed);
} else {
/* initialize response */
response.setHeader("Content-Type", "text/event-stream;charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
response.setHeader("Pragma", "no-cache");
final PrintWriter writer = response.getWriter();
//since the sample stream is based on Observable.interval, events will get published on an RxComputation thread
//since writing to the servlet response is blocking, use the Rx IO thread for the write that occurs in the onNext
sampleSubscription = sampleStream
.observeOn(Schedulers.io())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
logger.error("HystrixSampleSseServlet: ({}) received unexpected OnCompleted from sample stream", getClass().getSimpleName());
moreDataWillBeSent.set(false);
}
@Override
public void onError(Throwable e) {
moreDataWillBeSent.set(false);
}
@Override
public void onNext(String sampleDataAsString) {
if (sampleDataAsString != null) {
writer.print("data: " + sampleDataAsString + "\n\n");
// explicitly check for client disconnect - PrintWriter does not throw exceptions
if (writer.checkError()) {
moreDataWillBeSent.set(false);
}
writer.flush();
}
}
});
while (moreDataWillBeSent.get() && !isDestroyed) {
try {
Thread.sleep(pausePollerThreadDelayInMs);
//in case stream has not started emitting yet, catch any clients which connect/disconnect before emits start
writer.print("ping: \n\n");
// explicitly check for client disconnect - PrintWriter does not throw exceptions
if (writer.checkError()) {
moreDataWillBeSent.set(false);
}
writer.flush();
} catch (InterruptedException e) {
moreDataWillBeSent.set(false);
}
}
}
} finally {
decrementCurrentConcurrentConnections();
if (sampleSubscription != null && !sampleSubscription.isUnsubscribed()) {
sampleSubscription.unsubscribe();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy