I got a load-test setup of spring websocket server (based on Jetty and spring version 4.3.2.RELEASE) and client, that generates many connections (based on spring's sample java websocket client). The code below sends data to given websocket session: the snippet exploits the case where sessionId can be used instead of User ID (Spring WebSocket #SendToSession: send message to specific session). I may execute this code very often, every 2-3 milliseconds. I use SimpleMessageBroker.
public void publishToSessionUsingTopic(String sessionId, String subscriptionTopic, Map<String, CacheRowModel> payload) {
String subscriptionTopicWithoutUser = subscriptionTopic.replace(USER_ENDPOINT, "");
// necessary message headers for per-session send
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
simpMessagingTemplate.convertAndSendToUser(sessionId, subscriptionTopicWithoutUser, Collections.singletonList(payload), headerAccessor.getMessageHeaders());
}
When this code is executed very frequently (every 2-3 milliseconds) for ~100 sessions, while I see in my logs that it was run and called the convertAndSendToUser, some of the sessions won't receive the message. I appreciate any suggestions about how this could be cleared.
Well, I think your problem is with the:
#Bean
public ThreadPoolTaskExecutor clientOutboundChannelExecutor() {
TaskExecutorRegistration reg = getClientOutboundChannelRegistration().getOrCreateTaskExecRegistration();
ThreadPoolTaskExecutor executor = reg.getTaskExecutor();
executor.setThreadNamePrefix("clientOutboundChannel-");
return executor;
}
where it uses this config for the Executor:
protected ThreadPoolTaskExecutor getTaskExecutor() {
ThreadPoolTaskExecutor executor = (this.taskExecutor != null ? this.taskExecutor : new ThreadPoolTaskExecutor());
executor.setCorePoolSize(this.corePoolSize);
executor.setMaxPoolSize(this.maxPoolSize);
executor.setKeepAliveSeconds(this.keepAliveSeconds);
executor.setQueueCapacity(this.queueCapacity);
executor.setAllowCoreThreadTimeOut(true);
return executor;
}
See, there is no RejectedExecutionHandler configured. And by default it is like:
private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
So, when you have enough many messages and tasks for them exceed the ThreadPool, any extra are just aborted.
To fix the issue you should implement WebSocketMessageBrokerConfigurer and override its configureClientOutboundChannel() to provide some custom taskExecutor(ThreadPoolTaskExecutor taskExecutor) for example with the new ThreadPoolExecutor.CallerRunsPolicy().
Related
we are using spring boot with websocket.
#Slf4j
#Component
public class MyWebsocketConnector extends TextWebSocketHandler {
//....
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// do something...
}
}
But now I found in our traces, that different messages are handled by different threads.
Therefore, I ask myself how I can guarantee that messages of a session that arrive one after the other are also processed one after the other.
Unfortunately, I can't find anything about a guaranteed order or synchronisation.
Does anyone know more about this?
The only thing I found was this: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-ordered-messages
But this is the STOMP Implemantiation - we are not using STOMP :-(
even though I used STOMP and set the flag "setPreservePublishOrder" to true it couldn't guarantee the order. The only solution I could find that works was to set the number of threads for the client inbound channel to 1.
#Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.initialize();
registration.taskExecutor(executor);
}
I have a scenario where the spring-boot application have to download a file from downstream application and pass it to the client. The API also needs to update a read flag in the database without blocking the response(main-thread).
A basic async use-case is what I thought of and implemented in the respective API. But, I am getting a behavioral issue with #Async. The annotation is able to spawn a new thread , but its blocking the main-thread and holding the response. The expectation was to return without holding the main-thread.
Actually, the async update is the last operation of main-thread, and I guess due to that #Async is blocking the main-thread.
Can anyone please suggest a better solution of this scenario.
Calling class
ResponseEntity<byte[]> parsedResponse = retrieverService.retrieve(id,"html");
retrieverService.update(id);
return parsedResponse;
Async method
#Override
#Async("updateTaskExecutor")
public void update(String id) {
LOG.info("Updating data for metaTagId: {}", id);
db.updateReadFlag(id);
}
Async Config
#Configuration
#EnableAsync
public class AsyncConfiguration {
#Bean(name = "updateTaskExecutor")
public Executor updateTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("UpdateTaskClient-");
executor.initialize();
return executor;
}
}
The Configurations were correct. I was using debugger to check the parallelism. As suggested by #M. Deinum, its not the correct way to check parallelism. After using Thread.sleep() , I could see that asynchronous calls are working as expected. I am able to send the response back, while performing an update query asynchronously.
How would one utilize poison pill to stop message handlers (and message suppliers) with spring-integration?
I have a setup of N producers (subclassed Supplier) and M consumers (subclasses GenericHandler). They are connected via unbounded queue.
Producers should send K messages each, then send poison pill (I think each producer should send M/N poison pills, that is).
I would like then, to stop producers altogether (they are controlled by TaskExecutor)
#Bean(name = "supplierExecutor")
Executor supplierExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(x);
executor.setMaxPoolSize(y);
executor.setKeepAliveSeconds(0);
executor.setQueueCapacity(z);
executor.setThreadNamePrefix("supplier-");
executor.initialize();
return executor;
}
Also, I would like to stop consumers altogether, but gracefully. (They are controlled by their own TaskScheduler)
#Bean(name = "consumerScheduler")
TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(a);
taskScheduler.setThreadNamePrefix("temp-consumer");
taskScheduler.initialize();
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
return taskScheduler;
}
Right now, in my supplier get() method, I have the following snippet
public Foo get() {
if (reachedMaxSendLimit()) {
incrementMsgSentCount();
return POISON_PILL;
} else if (surpassedMaxSentLimit()) {
return null;
} else {
return handlePayload(payload);
}
}
Is there a well-defined way to achieve the behaviour I'm trying to accomplish?
I am aware how I'd do it without spring with regular Runnables, but I'm a bit clueless here.
I don't know what is poison pill and it's not clear to me what you are talking about here, but if the story is about stop, then you definitely should consider existing org.springframework.context.Lifecycle control in Spring. In this case all the Spring Integration endpoints are of that type and you simply can use their stop().
For the management purposes Spring Integration provides Control Bus component. For that you can send a command message with the stop() expression to really stop particular endpoint.
Note: it's good practice to stop producers before consumers. This way you won't lose messages for which there is no already consumers to process.
I have developed a simple client for receiving messages from a rabbitMQ queue.
#RabbitListener(queues = "#{'${rabbitmq.queues}'.split(',')}")
public void receiveMessage(Message message, #Header(AmqpHeaders.CONSUMER_QUEUE) String queue) {
logger.debug("message payload as string {}", new String(message.getBody()));
I am having a standard SSL handshake error at the moment because I haven't installed my private key.
Since there is an error, the message is not consumed, it stays in the queue.
The problem is that my client continues to try to consume the same message indefinitely. Could you advice me with a solution to this?
What I want is, in case of a serious global error, to stop consuming messages for a "while". Maybe a Thread sleep? Is there a spring solution to this? Am I missing something here?
By default, the listener container will attempt to reconnect every 5 seconds.
You can reconfigure the container(s), by adding a BackOff to the listener container factory; something like...
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrentConsumers(3);
final ExponentialBackOff backOff = new ExponentialBackOff();
backOff.setInitialInterval(5_000);
backOff.setMultiplier(1.5);
backOff.setMaxInterval(30_000);
backOff.setMaxElapsedTime(120_000);
factory.setRecoveryBackOff(backOff);
return factory;
}
I have a Spring Boot REST service that sometimes call third party services as a part of a request. I would like to set a timeout on all my resources (let's say 5 seconds), so that if any request handling (the whole chain, from incoming to response) takes longer than 5 seconds my controllers responds with HTTP 503 instead of the actual response. It would be awesome if this was just a Spring property, for example setting
spring.mvc.async.request-timeout=5000
but I haven't had any luck with that. I've also tried extending WebMvcConfigurationSupport and overriding configureAsyncSupport:
#Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(5000);
configurer.registerCallableInterceptors(timeoutInterceptor());
}
#Bean
public TimeoutCallableProcessingInterceptor timeoutInterceptor() {
return new TimeoutCallableProcessingInterceptor();
}
without any luck.
I suspect I have to manually time all my third party calls, and if they take too long, throw a timeout exception. Is that right? Or is there any easier, holistic solution that covers all my request endpoints?
You need to return a Callable<> if you want spring.mvc.async.request-timeout=5000 to work.
#RequestMapping(method = RequestMethod.GET)
public Callable<String> getFoobar() throws InterruptedException {
return new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(8000); //this will cause a timeout
return "foobar";
}
};
}
The #Transactional annotation takes a timeout parameter where you can specify timeout in seconds for a specific method in the #RestController
#RequestMapping(value = "/method",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
#Transactional(timeout = 120)
A fresh answer for Spring Boot 2.2 is required as server.connection-timeout=5000 is deprecated. Each server behaves differently, so server specific properties are recommended instead.
SpringBoot embeds Tomcat by default, if you haven't reconfigured it with Jetty or something else. Use server specific application properties like server.tomcat.connection-timeout or server.jetty.idle-timeout.
As commented by Wilkinson:
Setting the connection timeout will only result in a timeout when the
client connects but is then too slow to send its request. If you want
the client to wait for a maximum of 30 seconds for a response you will
have to configure that on the client-side. If you want the server-side
to only spend a maximum of 30 seconds handling the request there is no
way to guarantee that as you cannot force the thread that is handling
the request to stop.
You can also try setting spring.mvc.async.request-timeout
I would suggest you have a look at the Spring Cloud Netflix Hystrix starter to handle potentially unreliable/slow remote calls. It implements the Circuit Breaker pattern, that is intended for precisely this sorta thing.
See offcial docs for more information.
You can try server.connection-timeout=5000 in your application.properties. From the official documentation:
server.connection-timeout= # Time in milliseconds that connectors will
wait for another HTTP request before closing the connection. When not
set, the connector's container-specific default will be used. Use a
value of -1 to indicate no (i.e. infinite) timeout.
On the other hand, you may want to handle timeouts on the client side using Circuit Breaker pattern as I have already described in my answer here: https://stackoverflow.com/a/44484579/2328781
if you are using RestTemplate than you should use following code to implement timeouts
#Bean
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(2000);
factory.setConnectTimeout(2000);
return factory;
}}
The xml configuration
<bean class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"
p:readTimeout="2000"
p:connectTimeout="2000" />
</constructor-arg>
In Spring properties files, you can't just specify a number for this property. You also need to specify a unit. So you can say spring.mvc.async.request-timeout=5000ms or spring.mvc.async.request-timeout=5s, both of which will give you a 5-second timeout.
I feel like none of the answers really solve the issue. I think you need to tell the embedded server of Spring Boot what should be the maximum time to process a request. How exactly we do that is dependent on the type of the embedded server used.
In case of Undertow, one can do this:
#Component
class WebServerCustomizer : WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
override fun customize(factory: UndertowServletWebServerFactory) {
factory.addBuilderCustomizers(UndertowBuilderCustomizer {
it.setSocketOption(Options.READ_TIMEOUT, 5000)
it.setSocketOption(Options.WRITE_TIMEOUT, 25000)
})
}
}
Spring Boot official doc: https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/html/howto.html#howto-configure-webserver
You can configure the Async thread executor for your Springboot REST services. The setKeepAliveSeconds() should consider the execution time for the requests chain. Set the ThreadPoolExecutor's keep-alive seconds. Default is 60. This setting can be modified at runtime, for example through JMX.
#Bean(name="asyncExec")
public Executor asyncExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("AsynchThread-");
executor.setAllowCoreThreadTimeOut(true);
executor.setKeepAliveSeconds(10);
executor.initialize();
return executor;
}
Then you can define your REST endpoint as follows
#Async("asyncExec")
#PostMapping("/delayedService")
public CompletableFuture<String> doDelay()
{
String response = service.callDelayedService();
return CompletableFuture.completedFuture(response);
}