I have been trying to use the timeout feature of the async context. But the behavior is highly intermittent. Sometimes the timeout happens, and many a times it doesn't. I am pasting my code here.
#WebServlet(name = "TestServlet", urlPatterns = {"/test"},asyncSupported = true)
public class TestServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
private static PriorityBlockingQueue<Runnable> pq = new PriorityBlockingQueue<Runnable>(1000);
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,1,10, TimeUnit.SECONDS,pq);
public void service(final ServletRequest servletRequest, final ServletResponse response)
throws ServletException, IOException {
TestListener listener = new TestListener();
final AsyncContext asyncContext = servletRequest.startAsync();
asyncContext.addListener(listener);
asyncContext.setTimeout(100);
Handler handler = new Handler(asyncContext);
threadPoolExecutor.execute(handler);
}
}
The listener and the handler code is included below.
public class TestListener implements AsyncListener {
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("Event completed");
}
public void onError(AsyncEvent event) throws IOException {
event.getAsyncContext().complete();
}
public void onStartAsync(AsyncEvent event) throws IOException {
// TODO Auto-generated method stub
}
public void onTimeout(AsyncEvent event){
System.out.println("Timeout ");
event.getAsyncContext().complete();
}
}
public class Handler implements Runnable {
private AsyncContext asyncContext;
public Handler(AsyncContext asyncContext){
this.asyncContext = asyncContext;
}
public void run(){
try {
long currtime = System.currentTimeMillis();
Thread.sleep(500);
System.out.println("slept for " + (System.currentTimeMillis() - currtime));
} catch (InterruptedException e) {
System.out.println("Error in thread ");
}
try{
if(asyncContext != null){
System.out.println("Completing async context " + " timeout is " + asyncContext.getTimeout());
asyncContext.complete();
}
}catch (Exception e){
System.out.println("Exception in completing async context ");
}
}
}
And the output is intermittent. Including the same here -
[ops#root combinedlogs]$ time curl "http://localhost:9001/mockresponse/test"
real 0m0.506s
user 0m0.001s
sys 0m0.003s
[ops#root combinedlogs]$ time curl "http://localhost:9001/mockresponse/test"
real 0m0.159s
user 0m0.001s
sys 0m0.003s
Catalina logs -
slept for 500
Completing async context timeout is 100
Event completed
Timeout
Event completed
slept for 500
Exception in completing async context
I don't understand why this is happening. Please help! Thanks for your time.
PS: The tomcat version is 7.0.37
Try increasing the timeout and the sleep interval to more than 1 sec. For ex: Try a timeout interval of 2sec and a sleep for 5sec. It is possible that the servlet container does not detect timeouts less than 1 sec consistently. There were couple of bugs (marginally) related to such sub-second timeouts earlier in tomcat, like this one. I understand you are using a later version of tomcat than mentioned in that bug, still its worth the try.
Related
I have singleton client with the below contract
public interface MQPublisher {
void publish(String message) throws ClientConnectionException, ClientErrorException;
void start() throws ClientException;
void stop();
}
The class which is using this publisher is as below :
public class MessagePublisher {
#Autowired
private MQPublisher publisher;
private AtomicBoolean isPublisherRunning;
public void startPublisher() {
if (!isPublisherRunning.get()) {
publisher.start();
isPublisherRunning.compareAndSet(false, true);
}
}
#Retry(RETRY_MSG_UPLOAD)
public void sendMessage(String msg) {
try {
startPublisher();
publisher.publish(msg); // when multiple requests fail with the same exception, what will happen??
} catch (Exception e) {
log.error("Exception while publishing message : {}", msg, e);
publisher.stop();
isPublisherRunning.compareAndSet(true, false);
throw e;
}
}
We are using resilience4j retry functionality to retry the sendMessage method. This works fine in case of a single request. Consider a case when multiple requests are processed parallely and all of them fails with an exception. In this case, these requests will be retried and there is a chance that one thread will start the publisher while the other will stop it and it will throw exceptions again. How to handle this scenario in a cleaner way?
It isn't clear why the whole publisher should be stopped in case of failure. Nevertheless, if there are real reasons for that, I would change the stop method to use an atomic timer that will restart on each message sending and stop the publisher only after at least 5 seconds (or the time needed for a message to be successfully sent) have passed from the message sending.
Something like that:
#Slf4j
public class MessagePublisher {
private static final int RETRY_MSG_UPLOAD = 10;
#Autowired
private MQPublisher publisher;
private AtomicBoolean isPublisherRunning;
private AtomicLong publishStart;
public void startPublisher() {
if (!isPublisherRunning.get()) {
publisher.start();
isPublisherRunning.compareAndSet(false, true);
}
}
#Retryable(maxAttempts = RETRY_MSG_UPLOAD)
public void sendMessage(String msg) throws InterruptedException {
try {
startPublisher();
publishStart.set(System.nanoTime());
publisher.publish(msg); // when multiple requests fail with the same exception, what will happen??
} catch (Exception e) {
log.error("Exception while publishing message : {}", msg, e);
while (System.nanoTime() < publishStart.get() + 5000000000L) {
Thread.sleep(1000);
}
publisher.stop();
isPublisherRunning.compareAndSet(true, false);
throw e;
}
}
}
I think it is important to mention (as you just did) that this is a terrible design, and that such calculations should be done by the publisher implementer and not by the caller.
I'm trying to build a scenario which follows theses steps:
(on Init) Flux publisher created
(on Init) Subscribers subscribe
(on user action) publisher start streaming/publishing events
Web controller subscriber consumes and caches last BUFFER_SIZE events
Based on http://projectreactor.io/docs/core/release/reference/#advanced-parallelizing-parralelflux and https://www.baeldung.com/reactor-core I'm trying to use create and publish to do this, and the issue I'm having is that the thread that calls flux.connect is trapped in the while loop inside the publisher.
Here is a minimal working example using spring-boot-starter-webflux:
private ConnectableFlux<Integer> flux;
private Scheduler scheduler;
private int nextRead = 0;
private static final int BUFFERSIZE = 100;
private List<Integer> sink = new LinkedList<Integer>() ;
#PostConstruct
public void Init() {
this.scheduler = Schedulers.newSingle("Streamer");
flux = Flux.<Integer>create(fluxSink -> {
while (true) {
fluxSink.next(nextRead++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).publishOn(scheduler).publish();
}
#GetMapping("/subscribe")
public void subscribe(){
this.flux.subscribeOn(scheduler,false).subscribe(new CoreSubscriber<Integer>() {
#Override
public Context currentContext() {
return null;
}
#Override
public void onSubscribe(Subscription subscription) {
subscription.request(Long.MAX_VALUE);
}
#Override
public void onNext(Integer e) {
while (sink.size() >= BUFFERSIZE) sink.remove(0);
sink.add(e);
logger.debug("sink event: " + e);
}
#Override
public void onError(Throwable t) {}
#Override
public void onComplete() {}
});
}
#GetMapping("/start")
public void startStream(){
logger.debug("EventStreamSimulator startStream before connect");
this.flux.connect();
logger.debug("EventStreamSimulator startStream after connect");
}
#GetMapping("/values")
public Flux<Integer> getEvents(){
return Flux.fromIterable(sink);
}
Based on this code, the web request on /start will start the streaming but the http thread will be stuck in the emitter infinite loop. requests on /values and logging shows that it is working fine though (but the original http request to /start never finishes/returns)
Sample logs:
2018-10-09 18:12:54.798 DEBUG 6024 --- [ctor-http-nio-2] com.example.FluxPocController : emmit event: 0
2018-10-09 18:12:54.798 DEBUG 6024 --- [ Streamer-1] com.example.FluxPocController : sink event: 0
Then, here is the question: is the publishOn directive supported for these async way of using Flux.create? if so, how to use it?
I want to display a default custom page until my server starts completely.
Problem Statement : I am doing few setup things when the server starts, so it takes a very long time for the server to start. So in the meantime server stats if some user hits the home page url, so he should see some default page showing server is starting..blah blah...
Now the issue is if server is not started completely, neither my application context is loaded fully, and the response for the hit is "aborted", which is not a HTTP response, so how can i catch this response and show some error page.
Server can be jetty, tomcat any
A quick and dirty example:
public class DeferringServlet extends HttpServlet {
private static final Logger logger = Logger.getLogger(DeferringServlet.class);
private MyApp myApp;
private Thread appInitializerThread;
public void init() {
myApp = new MyApp();
appInitializerThread = new Thread(new Runnable() {
public void run() {
myApp.init();
}
});
appInitializerThread.start();
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
if(myApp.isInitialized()) {
myApp.doGet(req, resp);
} else {
myApp.setStatus(503); //Service unavailable
myApp.getWriter.println("Please wait. App is loading");
}
}
public void destroy() {
if(appInitializerThread.isAlive()) {
appInitializerThread.interrupt();
}
try {
appInitializerThread.join();
} catch (InterruptedException ex) {
logger.warn("Interrupted before app initializer could finish");
}
}
}
I have an web application which accept some data from user to create a task, then the task should be executed.
Since the execution of the Task is to download something from the internet which will cost some time, so I tried to create a new Thread to do the job.
This is my idea:
create a LoaderThread used to download data. And the LoaderThread hold a field of ArrayList used for put the Task.
A Servlet to handle the request and response.
When the Servlet startup, start the LoaderThread
During the servlet run, add task to the LoaderThread.
This is the code(some of them is omitted):
public class RwdServlet extends HttpServlet {
private StaticMapLoader loader;
#Override
public void init() throws ServletException {
super.init();
loader = new StaticMapLoader();
loader.startRunning();
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Task t=createTask(req);
loader.addTask(t);
}
#Override
public void destroy() {
loader.stopRunning();
}
}
public class StaticMapLoader extends Thread {
private List<Task> tasks = new ArrayList<Task>();
private boolean running = false;
#Override
public void run() {
while (running) {
if (tasks.size() > 0) {
Task t = tasks.get(0);
log.info(t);
if (t != null && t.status == Status.waiting) {
tasks.remove(0);
t.status = Status.running;
downLoad(t);
}
}
}
}
private void downLoad(Task t) {
//download file
}
public void addTask(Task t) {
tasks.add(t);
}
public void startRunning() {
running = true;
this.start();
}
public void stopRunning() {
running = false;
this.interrupt();
}
}
The above code worked, but I found that even the tasks were empty and there are no new task added, the loop will keep running.
So I though if I can make the LoaderThread suspend when there are no tasks, and notify it when new task come out.
So I tried this:
#Override
public void run() {
while (running) {
if (tasks.size() > 0) {
Task t = tasks.get(0);
log.info(t);
if (t != null && t.status == Status.waiting) {
tasks.remove(0);
t.status = Status.running;
downLoad(t);
}
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I tried to call the wait() if the tasks is empty.
But I do not know how to wake up it?
Also, is there anything I should know to improve the application?
BWT, is it possible that more than one LoaderThread instance will be created? If so , how to avoid it?
It seems that I can use other implementation, but I wonder if my case is refactor-able?
Since I want to learn some thing I have missed. :) Thanks.
Your requirements are the standard usage of ExecutorService, so I would recommend you to use ExecutorService and not reinvent the wheel.
Base on code you provided, your servlet should look like this:
public class RwdServlet extends HttpServlet {
private ExecutorService loader;
#Override
public void init() throws ServletException {
super.init();
loader = Executors.newCachedThreadPool();//or use some other executor, google about difference between them
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Task t=createTask(req); //assume that task implements Runnable or Callable
loader.submit(t); // submit a task to executor after this line your task will start execution in another thread
}
#Override
public void destroy() {
loader.shutdown();//this will destroy executor service but before that it will wait until all already submitted tasks will be executed
}
}
See link with example
Your use case calls for an ExecutorService and you have started reimplementing it from scratch. Better stop now and use the finished, bug-free, flexible, and powerful product from the standard library.
Is it possible to take an HTTPServletRequest away from its thread, dissolve this thread (i.e. bring it back to the pool), but keep the underlying connection with the browser working, until I get the results from a time-consuming operation (say, processing an image)? When the return data are processed, another method should be called asynchronously, and be given the request as well as the data as parameters.
Usually, long pooling functions in a pretty blocking fashion, where the current thread is not dissolved, which reduces the scalability of the server-side app, in terms of concurrent connections.
Yes, you can do this with Servlet 3.0
Below is the sample to write the alert every 30 secs(not tested).
#WebServlet(async =“true”)
public class AsyncServlet extends HttpServlet {
Timer timer = new Timer("ClientNotifier");
public void doGet(HttpServletRequest req, HttpServletResponse res) {
AsyncContext aCtx = request.startAsync(req, res);
// Suspend request for 30 Secs
timer.schedule(new TimerTask(aCtx) {
public void run() {
try{
//read unread alerts count
int unreadAlertCount = alertManager.getUnreadAlerts(username);
// write unread alerts count
response.write(unreadAlertCount);
}
catch(Exception e){
aCtx.complete();
}
}
}, 30000);
}
}
Below is the sample to write based on an event. The alertManager has to be implemented which notifies AlertNotificationHandler when client has to be alerted.
#WebServlet(async=“true”)
public class AsyncServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) {
final AsyncContext asyncCtx = request.startAsync(req, res);
alertManager.register(new AlertNotificationHandler() {
public void onNewAlert() { // Notified on new alerts
try {
int unreadAlertCount =
alertManager.getUnreadAlerts();
ServletResponse response = asyncCtx.getResponse();
writeResponse(response, unreadAlertCount);
// Write unread alerts count
} catch (Exception ex) {
asyncCtx.complete();
// Closes the response
}
}
});
}
}
Yes, it's possible using Servlet spec ver. 3.0. Implementation I can recommend is Jetty server. See here.