Prevent blocking DeferredResult - spring

I have a method which blocks the thread and then returns a value. In order to prevent the web server from getting exhausted with blocked threads, I planned to use DeferredResult. I tried to demonstrate it by setting the server.tomcat.max-threads=1 in application.properties file. But when I hit URL multiple times from my browser, the behavior is still blocking. I am aware of the #Async annotation, which I can use on my blocking method, but what happens is that the request ends without any result.
My expectation is, the web server must entertain multiple requests even if the max-thread is set to 1. Maybe I am executing it incorrectly. So, I would request you to correct me. Following is my code. I have started three Http Request at the same time but the behavior appears synchronous. I am using Spring Boot v2.3.3
UPDATE 1:
I have changed my testing procedure. Instead of hitting 3 requests, I have hit 10. Now, after 4 requests are fulfilled by the web server synchronously, the remaining requests are entertained by the web server in a work stealing fashion. This is good, but I want the work stealing behavior to start immediately instead of waiting for multiple requests.
I have also tried Spring Boot's default and a custom AsyncTaskExecutor supplied to the CompletableFuture but the behavior remained the same when no TaskExecutor is provided.
TaskExecutor:
#Bean(name = "threadPoolExecutor")
public AsyncTaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
Controller:
#RestController
public class DeferredController {
private static final Logger LOGGER = LoggerFactory.getLogger(DeferredController.class);
#Autowired
private MyBlockingService blockingService;
#Autowired
#Qualifier("threadPoolExecutor")
private AsyncTaskExecutor taskExecutor;
#GetMapping("/home")
public DeferredResult<String> home() {
int randomId = (int) (Math.random() * 1000);
LOGGER.info("Request received for id: {}", randomId);
DeferredResult<String> result = new DeferredResult<>(6000L);
result.onTimeout(() -> result.setErrorResult("Timeout"));
CompletableFuture
.supplyAsync(() -> blockingService.execute(randomId), taskExecutor)
.whenCompleteAsync((output, throwable) -> {
if (output != null || !output.equals("")) {
result.setResult(output);
} else {
result.setErrorResult(throwable);
}
});
LOGGER.info("Servlet Thread released for id: {}", randomId);
return result;
}
}
Service:
#Service
public class MyBlockingService {
private static final Logger LOGGER = LoggerFactory.getLogger(MyBlockingService.class);
public String execute(int id) {
LOGGER.info("Blocking Service start: {}", id);
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOGGER.info("---- Blocking Service end: {} ----", id);
return "Done: " + id;
}
}
Logs:
2022-04-12 10:45:02.146 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 844
2022-04-12 10:45:02.149 INFO [onPool-worker-1] c.s.d.s.c.MyBlockingService : Blocking Service start: 844
2022-04-12 10:45:02.150 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 844
2022-04-12 10:45:07.151 INFO [onPool-worker-1] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 844 ----
2022-04-12 10:45:07.214 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 624
2022-04-12 10:45:07.214 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 624
2022-04-12 10:45:07.214 INFO [onPool-worker-1] c.s.d.s.c.MyBlockingService : Blocking Service start: 624
2022-04-12 10:45:12.223 INFO [onPool-worker-1] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 624 ----
2022-04-12 10:45:12.240 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 711
2022-04-12 10:45:12.240 INFO [onPool-worker-1] c.s.d.s.c.MyBlockingService : Blocking Service start: 711
2022-04-12 10:45:12.240 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 711
2022-04-12 10:45:17.245 INFO [onPool-worker-1] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 711 ----
Logs based on UPDATE 1:
2022-04-12 12:53:16.781 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 782
2022-04-12 12:53:16.783 INFO [ MyExecutor-1] c.s.d.s.c.MyBlockingService : Blocking Service start: 782
2022-04-12 12:53:16.783 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 782
2022-04-12 12:53:21.795 INFO [ MyExecutor-1] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 782 ----
2022-04-12 12:53:21.836 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 774
2022-04-12 12:53:21.836 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 774
2022-04-12 12:53:21.836 INFO [ MyExecutor-2] c.s.d.s.c.MyBlockingService : Blocking Service start: 774
2022-04-12 12:53:26.837 INFO [ MyExecutor-2] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 774 ----
2022-04-12 12:53:26.851 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 381
2022-04-12 12:53:26.852 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 381
2022-04-12 12:53:26.853 INFO [ MyExecutor-3] c.s.d.s.c.MyBlockingService : Blocking Service start: 381
2022-04-12 12:53:31.870 INFO [ MyExecutor-3] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 381 ----
2022-04-12 12:53:31.883 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 296
2022-04-12 12:53:31.883 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 296
2022-04-12 12:53:31.883 INFO [ MyExecutor-4] c.s.d.s.c.MyBlockingService : Blocking Service start: 296
2022-04-12 12:53:36.892 INFO [ MyExecutor-4] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 296 ----
2022-04-12 12:53:36.904 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 149
2022-04-12 12:53:36.904 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 149
2022-04-12 12:53:36.904 INFO [ MyExecutor-5] c.s.d.s.c.MyBlockingService : Blocking Service start: 149
2022-04-12 12:53:38.989 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 132
2022-04-12 12:53:38.990 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 132
2022-04-12 12:53:38.991 INFO [ MyExecutor-6] c.s.d.s.c.MyBlockingService : Blocking Service start: 132
2022-04-12 12:53:39.431 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 119
2022-04-12 12:53:39.431 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 119
2022-04-12 12:53:39.432 INFO [ MyExecutor-7] c.s.d.s.c.MyBlockingService : Blocking Service start: 119
2022-04-12 12:53:39.872 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 308
2022-04-12 12:53:39.872 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 308
2022-04-12 12:53:39.872 INFO [ MyExecutor-1] c.s.d.s.c.MyBlockingService : Blocking Service start: 308
2022-04-12 12:53:40.294 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 734
2022-04-12 12:53:40.294 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 734
2022-04-12 12:53:40.294 INFO [ MyExecutor-2] c.s.d.s.c.MyBlockingService : Blocking Service start: 734
2022-04-12 12:53:40.838 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 601
2022-04-12 12:53:40.839 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 601
2022-04-12 12:53:40.839 INFO [ MyExecutor-3] c.s.d.s.c.MyBlockingService : Blocking Service start: 601
2022-04-12 12:53:41.916 INFO [ MyExecutor-5] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 149 ----
2022-04-12 12:53:41.931 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 773
2022-04-12 12:53:41.931 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 773
2022-04-12 12:53:41.931 INFO [ MyExecutor-4] c.s.d.s.c.MyBlockingService : Blocking Service start: 773
2022-04-12 12:53:44.000 INFO [ MyExecutor-6] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 132 ----
2022-04-12 12:53:44.016 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 98
2022-04-12 12:53:44.016 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 98
2022-04-12 12:53:44.016 INFO [ MyExecutor-5] c.s.d.s.c.MyBlockingService : Blocking Service start: 98
2022-04-12 12:53:44.436 INFO [ MyExecutor-7] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 119 ----
2022-04-12 12:53:44.448 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Request received for id: 456
2022-04-12 12:53:44.449 INFO [nio-8080-exec-1] c.s.d.s.c.DeferredController : Servlet Thread released for id: 456
2022-04-12 12:53:44.449 INFO [ MyExecutor-6] c.s.d.s.c.MyBlockingService : Blocking Service start: 456
2022-04-12 12:53:44.887 INFO [ MyExecutor-1] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 308 ----
2022-04-12 12:53:45.311 INFO [ MyExecutor-2] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 734 ----
2022-04-12 12:53:45.841 INFO [ MyExecutor-3] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 601 ----
2022-04-12 12:53:46.933 INFO [ MyExecutor-4] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 773 ----
2022-04-12 12:53:49.022 INFO [ MyExecutor-5] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 98 ----
2022-04-12 12:53:49.458 INFO [ MyExecutor-6] c.s.d.s.c.MyBlockingService : ---- Blocking Service end: 456 ----

Related

Unable to fetch the view file

On making a GET Request which returns a ModelAndView Object I am getting the following error
: GET "/tweet2?email=tim#gmail.com", parameters={masked} 2022-03-08
11:04:45.459 DEBUG 46576 --- [nio-8080-exec-3]
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to
com.example.demo.RestAPIExample#getTweetsByEmail(String) 2022-03-08
11:04:45.464 DEBUG 46576 --- [nio-8080-exec-3]
o.s.w.s.v.ContentNegotiatingViewResolver : Selected '/' given [/]
2022-03-08 11:04:45.464 DEBUG 46576 --- [nio-8080-exec-3]
o.s.w.servlet.view.InternalResourceView : View name 'tweets', model
{tweets=[com.example.demo.Tweet#3a7a85cb]} 2022-03-08 11:04:45.465
DEBUG 46576 --- [nio-8080-exec-3]
o.s.w.servlet.view.InternalResourceView : Forwarding to [tweets]
2022-03-08 11:04:45.467 DEBUG 46576 --- [nio-8080-exec-3]
o.s.web.servlet.DispatcherServlet : "FORWARD" dispatch for GET
"/tweets?email=tim#gmail.com", parameters={masked} 2022-03-08
11:04:45.470 DEBUG 46576 --- [nio-8080-exec-3]
o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to
ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath
[resources/], classpath [static/], classpath [public/], ServletContext
[/]] 2022-03-08 11:04:45.472 DEBUG 46576 --- [nio-8080-exec-3]
o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2022-03-08 11:04:45.473 DEBUG 46576 --- [nio-8080-exec-3]
o.s.web.servlet.DispatcherServlet : Exiting from "FORWARD"
dispatch, status 404 2022-03-08 11:04:45.473 DEBUG 46576 ---
[nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed
404 NOT_FOUND 2022-03-08 11:04:45.474 DEBUG 46576 ---
[nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : "ERROR"
dispatch for GET "/error?email=tim#gmail.com", parameters={masked}
2022-03-08 11:04:45.475 DEBUG 46576 --- [nio-8080-exec-3]
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to
org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2022-03-08 11:04:45.482 DEBUG 46576 --- [nio-8080-exec-3]
o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json',
given [/] and supported [application/json, application/+json,
application/json, application/+json] 2022-03-08 11:04:45.483 DEBUG
46576 --- [nio-8080-exec-3] o.s.w.s.m.m.a.HttpEntityMethodProcessor :
Writing [{timestamp=Tue Mar 08 11:04:45 IST 2022, status=404,
error=Not Found, path=/tweet2}] 2022-03-08 11:04:45.497 DEBUG 46576
--- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404
Here is the code I wrote:
#GetMapping("/tweet2")
public ModelAndView getTweetsByEmail(#RequestParam String email) {
ModelAndView modelAndView = new ModelAndView("tweets");
List<Tweet> tweets = tweetMap.get(email);
modelAndView.getModel().put("tweets",tweets);
return modelAndView;
}
And there is a tweets.mustache file under the resources folder. Not sure why its unable to detect it

application shutting down in spring boot

In a spring boot project in configuration file there is a task executor whose code goes like this
#Bean(name = "asyncExec")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("CashFlowThread-");
executor.initialize();
return executor;
}
I am deploying an API which download from s3 bucket and create 4 pdf and store it in target folder . while the api is called console shows error that asyncExec is shutting down .
Stack trace for it shows
Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-12-01 17:04:30.174 INFO 3680 --- [nio-5000-exec-2] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-12-01 17:04:30.179 INFO 3680 --- [nio-5000-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
2020-12-01 17:04:30.185 INFO 3680 --- [nio-5000-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-17 - Starting...
2020-12-01 17:04:35.767 INFO 3680 --- [nio-5000-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-17 - Start completed.
File is created!
Successfully obtained bytes from an S3 object
2020-12-01 17:04:43.907 INFO 3680 --- [ Thread-174] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'asyncExec'
2020-12-01 17:04:43.907 INFO 3680 --- [ Thread-174] com.zaxxer.hikari.HikariDataSource : HikariPool-17 - Shutdown initiated...

Spring Cloud Gateway in Docker Compose returns ERR_NAME_NOT_RESOLVED

I'm building a microservices app and I've run into problem with configuring the Spring Cloud gateway to proxy the calls to the API from frontend running on Nginx server.
When I make a POST request to /users/login, I get this response: OPTIONS http://28a41511677e:8082/login net::ERR_NAME_NOT_RESOLVED.
The string 28a41511677e is the services docker container ID. When I call another service (using GET method), it returns data just fine.
I'm using Eureka discovery server which seems to find all the services correctly. The service in question is registered as 28a41511677e:users-service:8082
Docker compose:
version: "3.7"
services:
db:
build: db/
expose:
- 5432
registry:
build: registryservice/
expose:
- 8761
ports:
- 8761:8761
gateway:
build: gatewayservice/
expose:
- 8080
depends_on:
- registry
users:
build: usersservice/
expose:
- 8082
depends_on:
- registry
- db
timetable:
build: timetableservice/
expose:
- 8081
depends_on:
- registry
- db
ui:
build: frontend/
expose:
- 80
ports:
- 80:80
depends_on:
- gateway
Gateway implementation:
#EnableDiscoveryClient
#SpringBootApplication
public class GatewayserviceApplication {
#Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
return builder.routes()
.route("users-service", p -> p.path("/user/**")
.uri("lb://users-service"))
.route("timetable-service", p -> p.path("/routes/**")
.uri("lb://timetable-service"))
.build();
}
public static void main(String[] args) {
SpringApplication.run(GatewayserviceApplication.class, args);
}
}
Gateway settings:
spring:
application:
name: gateway-service
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
eureka:
client:
service-url:
defaultZone: http://registry:8761/eureka
Users service controller:
#RestController
#CrossOrigin
#RequestMapping("/user")
public class UserController {
private UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#PostMapping(path = "/login")
ResponseEntity<Long> login(#RequestBody LoginDto loginDto) {
logger.info("Logging in user");
Long uid = userService.logIn(loginDto);
return new ResponseEntity<>(uid, HttpStatus.OK);
}
}
Edit:
This also happens on NPM dev server. I tried changing the lb://users-service to http://users:8082, with no success, still getting ERR_NAME_NOT_RESOLVED.
I however found that when I call the endpoint, the following output can be seen in log:
gateway_1 | 2019-05-19 23:55:10.842 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Disable delta property : false
gateway_1 | 2019-05-19 23:55:10.866 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Single vip registry refresh property : null
gateway_1 | 2019-05-19 23:55:10.867 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Force full registry fetch : false
gateway_1 | 2019-05-19 23:55:10.868 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Application is null : false
gateway_1 | 2019-05-19 23:55:10.868 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Registered Applications size is zero : true
gateway_1 | 2019-05-19 23:55:10.869 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Application version is -1: false
gateway_1 | 2019-05-19 23:55:10.871 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
gateway_1 | 2019-05-19 23:55:11.762 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : The response status is 200
users_1 | 2019-05-19 21:55:19.268 INFO 1 --- [nio-8082-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
users_1 | 2019-05-19 21:55:19.273 INFO 1 --- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
users_1 | 2019-05-19 21:55:19.513 INFO 1 --- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 239 ms
users_1 | 2019-05-19 21:55:20.563 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Disable delta property : false
users_1 | 2019-05-19 21:55:20.565 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Single vip registry refresh property : null
users_1 | 2019-05-19 21:55:20.565 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Force full registry fetch : false
users_1 | 2019-05-19 21:55:20.566 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Application is null : false
users_1 | 2019-05-19 21:55:20.566 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Registered Applications size is zero : true
users_1 | 2019-05-19 21:55:20.566 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Application version is -1: false
users_1 | 2019-05-19 21:55:20.567 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
users_1 | 2019-05-19 21:55:20.958 INFO 1 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : The response status is 200
Edit 2:
I enabled logging for the gateway service and this is the output whenever I call /user/login. According to the logs, the gateway matches the /users/login/ correctly, but then starts using just /login for some reason.
2019-05-20 12:58:47.002 DEBUG 1 --- [or-http-epoll-2] r.n.http.server.HttpServerOperations : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] New http connection, requesting read
2019-05-20 12:58:47.025 DEBUG 1 --- [or-http-epoll-2] reactor.netty.channel.BootstrapHandlers : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] Initialized pipeline DefaultChannelPipeline{(BootstrapHandlers$BootstrapInitializerHandler#0 = reactor.netty.channel.BootstrapHandlers$BootstrapInitializerHandler), (reactor.left.httpCodec = io.netty.handler.codec.http.HttpServerCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.server.HttpTrafficHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}
2019-05-20 12:58:47.213 DEBUG 1 --- [or-http-epoll-2] r.n.http.server.HttpServerOperations : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] Increasing pending responses, now 1
2019-05-20 12:58:47.242 DEBUG 1 --- [or-http-epoll-2] reactor.netty.http.server.HttpServer : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter#575e590e
2019-05-20 12:58:47.379 TRACE 1 --- [or-http-epoll-2] o.s.c.g.f.WeightCalculatorWebFilter : Weights attr: {}
2019-05-20 12:58:47.817 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition CompositeDiscoveryClient_USERS-SERVICE applying {pattern=/USERS-SERVICE/**} to Path
2019-05-20 12:58:47.952 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition CompositeDiscoveryClient_USERS-SERVICE applying filter {regexp=/USERS-SERVICE/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2019-05-20 12:58:47.960 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition matched: CompositeDiscoveryClient_USERS-SERVICE
2019-05-20 12:58:47.961 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition CompositeDiscoveryClient_GATEWAY-SERVICE applying {pattern=/GATEWAY-SERVICE/**} to Path
2019-05-20 12:58:47.964 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition CompositeDiscoveryClient_GATEWAY-SERVICE applying filter {regexp=/GATEWAY-SERVICE/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2019-05-20 12:58:47.968 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.r.RouteDefinitionRouteLocator : RouteDefinition matched: CompositeDiscoveryClient_GATEWAY-SERVICE
2019-05-20 12:58:47.979 TRACE 1 --- [or-http-epoll-2] o.s.c.g.h.p.RoutePredicateFactory : Pattern "/user/**" matches against value "/user/login"
2019-05-20 12:58:47.980 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: users-service
2019-05-20 12:58:47.981 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: POST http://gateway:8080/user/login] to Route{id='users-service', uri=lb://users-service, order=0, predicate=org.springframework.cloud.gateway.support.ServerWebExchangeUtils$$Lambda$333/0x000000084035ac40#276b060f, gatewayFilters=[]}
2019-05-20 12:58:47.981 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.h.RoutePredicateHandlerMapping : [ff6d8305] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler#4faea64b
2019-05-20 12:58:47.994 DEBUG 1 --- [or-http-epoll-2] o.s.c.g.handler.FilteringWebHandler : Sorted gatewayFilterFactories: [OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter#773f7880}, order=-2147482648}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter#65a4798f}, order=-1}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter#4c51bb7}, order=0}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter#878452d}, order=10000}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.LoadBalancerClientFilter#4f2613d1}, order=10100}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter#83298d7}, order=2147483646}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter#6d24ffa1}, order=2147483647}, OrderedGatewayFilter{delegate=GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter#426b6a74}, order=2147483647}]
2019-05-20 12:58:47.996 TRACE 1 --- [or-http-epoll-2] o.s.c.g.filter.RouteToRequestUrlFilter : RouteToRequestUrlFilter start
2019-05-20 12:58:47.999 TRACE 1 --- [or-http-epoll-2] o.s.c.g.filter.LoadBalancerClientFilter : LoadBalancerClientFilter url before: lb://users-service/user/login
2019-05-20 12:58:48.432 INFO 1 --- [or-http-epoll-2] c.netflix.config.ChainedDynamicProperty : Flipping property: users-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2019-05-20 12:58:48.492 INFO 1 --- [or-http-epoll-2] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-users-service
2019-05-20 12:58:48.496 INFO 1 --- [or-http-epoll-2] c.netflix.loadbalancer.BaseLoadBalancer : Client: users-service instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=users-service,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2019-05-20 12:58:48.506 INFO 1 --- [or-http-epoll-2] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater
2019-05-20 12:58:48.543 INFO 1 --- [or-http-epoll-2] c.netflix.config.ChainedDynamicProperty : Flipping property: users-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2019-05-20 12:58:48.555 INFO 1 --- [or-http-epoll-2] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client users-service initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=users-service,current list of Servers=[157e1f567371:8082],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:157e1f567371:8082; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 01:00:00 CET 1970; First connection made: Thu Jan 01 01:00:00 CET 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList#3cd9b0bf
2019-05-20 12:58:48.580 TRACE 1 --- [or-http-epoll-2] o.s.c.g.filter.LoadBalancerClientFilter : LoadBalancerClientFilter url chosen: http://157e1f567371:8082/user/login
2019-05-20 12:58:48.632 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : Creating new client pool [proxy] for 157e1f567371:8082
2019-05-20 12:58:48.646 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439] Created new pooled channel, now 0 active connections and 1 inactive connections
2019-05-20 12:58:48.651 DEBUG 1 --- [or-http-epoll-2] reactor.netty.channel.BootstrapHandlers : [id: 0xa9634439] Initialized pipeline DefaultChannelPipeline{(BootstrapHandlers$BootstrapInitializerHandler#0 = reactor.netty.channel.BootstrapHandlers$BootstrapInitializerHandler), (SimpleChannelPool$1#0 = io.netty.channel.pool.SimpleChannelPool$1), (reactor.left.httpCodec = io.netty.handler.codec.http.HttpClientCodec), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}
2019-05-20 12:58:48.673 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] onStateChange(PooledConnection{channel=[id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082]}, [connected])
2019-05-20 12:58:48.679 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] onStateChange(GET{uri=/, connection=PooledConnection{channel=[id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082]}}, [configured])
2019-05-20 12:58:48.682 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Registering pool release on close event for channel
2019-05-20 12:58:48.690 DEBUG 1 --- [or-http-epoll-2] r.netty.http.client.HttpClientConnect : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Handler is being applied: {uri=http://157e1f567371:8082/user/login, method=POST}
2019-05-20 12:58:48.701 DEBUG 1 --- [or-http-epoll-2] r.n.channel.ChannelOperationsHandler : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] New sending options
2019-05-20 12:58:48.720 DEBUG 1 --- [or-http-epoll-2] r.n.channel.ChannelOperationsHandler : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Writing object DefaultHttpRequest(decodeResult: success, version: HTTP/1.1)
POST /user/login HTTP/1.1
content-length: 37
accept-language: cs-CZ,cs;q=0.9,en;q=0.8
referer: http://localhost/user/login
cookie: JSESSIONID=6797219EB79F6026BD8F19E9C46C09DB
accept: application/json, text/plain, */*
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36
content-type: application/json;charset=UTF-8
origin: http://gateway:8080
accept-encoding: gzip, deflate, br
Forwarded: proto=http;host="gateway:8080";for="172.19.0.7:42958"
X-Forwarded-For: 172.19.0.1,172.19.0.7
X-Forwarded-Proto: http,http
X-Forwarded-Port: 80,8080
X-Forwarded-Host: localhost,gateway:8080
host: 157e1f567371:8082
2019-05-20 12:58:48.751 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Channel connected, now 1 active connections and 0 inactive connections
2019-05-20 12:58:48.759 DEBUG 1 --- [or-http-epoll-2] r.n.channel.ChannelOperationsHandler : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Writing object
2019-05-20 12:58:48.762 DEBUG 1 --- [or-http-epoll-2] reactor.netty.channel.FluxReceive : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] Subscribing inbound receiver [pending: 1, cancelled:false, inboundDone: true]
2019-05-20 12:58:48.808 DEBUG 1 --- [or-http-epoll-2] r.n.channel.ChannelOperationsHandler : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Writing object EmptyLastHttpContent
2019-05-20 12:58:48.809 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] onStateChange(POST{uri=/user/login, connection=PooledConnection{channel=[id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082]}}, [request_sent])
2019-05-20 12:58:49.509 INFO 1 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: users-service.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2019-05-20 12:58:49.579 DEBUG 1 --- [or-http-epoll-2] r.n.http.client.HttpClientOperations : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Received response (auto-read:false) : [Set-Cookie=JSESSIONID=7C47A99C1F416F910AB554F4617247D6; Path=/; HttpOnly, X-Content-Type-Options=nosniff, X-XSS-Protection=1; mode=block, Cache-Control=no-cache, no-store, max-age=0, must-revalidate, Pragma=no-cache, Expires=0, X-Frame-Options=DENY, Location=http://157e1f567371:8082/login, Content-Length=0, Date=Mon, 20 May 2019 10:58:49 GMT]
2019-05-20 12:58:49.579 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] onStateChange(POST{uri=/user/login, connection=PooledConnection{channel=[id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082]}}, [response_received])
2019-05-20 12:58:49.581 TRACE 1 --- [or-http-epoll-2] o.s.c.g.filter.NettyWriteResponseFilter : NettyWriteResponseFilter start
2019-05-20 12:58:49.586 DEBUG 1 --- [or-http-epoll-2] reactor.netty.channel.FluxReceive : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Subscribing inbound receiver [pending: 0, cancelled:false, inboundDone: false]
2019-05-20 12:58:49.586 DEBUG 1 --- [or-http-epoll-2] r.n.http.client.HttpClientOperations : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Received last HTTP packet
2019-05-20 12:58:49.593 DEBUG 1 --- [or-http-epoll-2] r.n.channel.ChannelOperationsHandler : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] Writing object DefaultFullHttpResponse(decodeResult: success, version: HTTP/1.1, content: EmptyByteBufBE)
HTTP/1.1 302 Found
Set-Cookie: JSESSIONID=7C47A99C1F416F910AB554F4617247D6; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Location: http://157e1f567371:8082/login
Date: Mon, 20 May 2019 10:58:49 GMT
content-length: 0
2019-05-20 12:58:49.595 DEBUG 1 --- [or-http-epoll-2] r.n.http.server.HttpServerOperations : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] Detected non persistent http connection, preparing to close
2019-05-20 12:58:49.595 DEBUG 1 --- [or-http-epoll-2] r.n.http.server.HttpServerOperations : [id: 0xff6d8305, L:/172.19.0.4:8080 - R:/172.19.0.7:42958] Last Http packet was sent, terminating channel
2019-05-20 12:58:49.598 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] onStateChange(POST{uri=/user/login, connection=PooledConnection{channel=[id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082]}}, [disconnecting])
2019-05-20 12:58:49.598 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Releasing channel
2019-05-20 12:58:49.598 DEBUG 1 --- [or-http-epoll-2] r.n.resources.PooledConnectionProvider : [id: 0xa9634439, L:/172.19.0.4:59624 - R:157e1f567371/172.19.0.5:8082] Channel cleaned, now 0 active connections and 1 inactive connections
I managed to fix it. The problem was actually not in the gateway, it was in the users service. It had improper security configuration and required a login when accessing its endpoints. So, when I called any endpoint, it got redirected to /login.
I added the following code to the service and it works properly now.
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/").permitAll();
httpSecurity.cors().and().csrf().disable();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
That's probably not a proper solution, but on a non production code it gets the job done.

Spring 5 Web Reactive - Hot Publishing - How to use EmitterProcessor to bridge a MessageListener to an event stream

A sample project is located here: https://github.com/codependent/spring5-playground
I would like to bridge a message received from a JMS queue into a Reactive Controller that would publish the messages as an event stream.
I don't want the messages to be replayed, that is, if a message arrives and there isn't any subscriber I don't want them to be sent later when any subsbribes, so I am using an EmitterProcessor:
#Component
public class AlertEmitterProcessor {
private Logger logger = LoggerFactory.getLogger(getClass());
private EmitterProcessor<Alert> processor;
public AlertEmitterProcessor(){
processor = EmitterProcessor.<Alert>create();
processor.connect();
}
public EmitterProcessor<Alert> getProcessor() {
return processor;
}
public void onNext(Alert alert){
logger.info("onNext [{}]", alert);
processor.onNext(alert);
}
public void onComplete(){
logger.info("onComplete");
processor.onComplete();
}
public void onError(Throwable t){
logger.error("onError", t);
processor.onError(t);
}
}
This is my MessageListener:
#Component
public class AlertMessageListener implements MessageListener{
private Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private AlertEmitterProcessor alertProcessor;
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#Override
public void onMessage(Message message) {
logger.info("Message received: [{}]", message);
TextMessage tm = (TextMessage)message;
try {
Alert alert = jacksonMessageConverter.getObjectMapper().readValue(tm.getText(), Alert.class);
alertProcessor.onNext(alert);
} catch (IOException | JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
And finally my Rest Controller:
#Autowired
private AlertEmitterProcessor alertTopicProcessor;
#Autowired
private AlertMessageListener messageListener;
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#GetMapping(value="/accounts/{id}/alerts/live2", produces="text/event-stream")
public Flux<Alert> getAccountAlertsStreaming2(#PathVariable Integer id) {
return alertTopicProcessor.getProcessor()
.log().filter( a -> a.getAccountId().equals(id) );
}
To test its behaviour I have added this controller method to simulate inserting in the queue:
#GetMapping(value="/mock/accounts/{id}/alerts/put", produces="text/event-stream")
public void putAlert(#PathVariable Integer id) throws JsonProcessingException {
Alert alert = new Alert(id, (long)Math.round(Math.random()*10), "Message");
String alertStr = jacksonMessageConverter.getObjectMapper().writeValueAsString(alert);
TextMessage tm = new MockTextMessage(alertStr);
messageListener.onMessage(tm);
}
Right after starting the application I load http://localhost:8080/accounts/1/alerts/live2 and the browser waits for data.
2016-10-03 13:43:38.755 DEBUG 12800 --- [nio-8080-exec-1] o.s.web.reactive.DispatcherHandler : Processing GET request for [http://localhost:8080/accounts/1/alerts/live2]
2016-10-03 13:43:38.770 DEBUG 12800 --- [nio-8080-exec-1] s.w.r.r.m.a.RequestMappingHandlerMapping : Looking up handler method for path /accounts/1/alerts/live2
2016-10-03 13:43:38.778 DEBUG 12800 --- [nio-8080-exec-1] s.w.r.r.m.a.RequestMappingHandlerMapping : Returning handler method [public reactor.core.publisher.Flux<com.codependent.spring5.playground.reactive.dto.Alert> com.codependent.spring5.playground.reactive.web.AccountsRestController.getAccountAlertsStreaming2(java.lang.Integer)]
2016-10-03 13:43:38.779 DEBUG 12800 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'accountsRestController'
2016-10-03 13:43:38.800 INFO 12800 --- [nio-8080-exec-1] reactor.unresolved : onSubscribe(reactor.core.publisher.FluxPeek$PeekSubscriber#54d4fb36)
2016-10-03 13:43:38.802 INFO 12800 --- [nio-8080-exec-1] reactor.unresolved : request(unbounded)
2016-10-03 13:43:38.803 INFO 12800 --- [nio-8080-exec-1] reactor.unresolved : onNext(1)
2016-10-03 13:43:38.822 INFO 12800 --- [nio-8080-exec-1] reactor.Flux.EmitterProcessor.2 : onSubscribe(reactor.core.publisher.EmitterProcessor$EmitterSubscriber#227405f2)
2016-10-03 13:43:38.822 INFO 12800 --- [nio-8080-exec-1] reactor.Flux.EmitterProcessor.2 : request(1)
2016-10-03 13:43:38.823 INFO 12800 --- [nio-8080-exec-1] reactor.unresolved : onComplete()
Then I publish some messages http://localhost:8080/mock/accounts/1/alerts/put.
2016-10-03 13:43:43.063 DEBUG 12800 --- [nio-8080-exec-2] o.s.web.reactive.DispatcherHandler : Processing GET request for [http://localhost:8080/mock/accounts/1/alerts/put]
2016-10-03 13:43:43.063 DEBUG 12800 --- [nio-8080-exec-2] s.w.r.r.m.a.RequestMappingHandlerMapping : Looking up handler method for path /mock/accounts/1/alerts/put
2016-10-03 13:43:43.068 DEBUG 12800 --- [nio-8080-exec-2] s.w.r.r.m.a.RequestMappingHandlerMapping : Returning handler method [public void com.codependent.spring5.playground.reactive.web.AccountsRestController.putAlert(java.lang.Integer) throws com.fasterxml.jackson.core.JsonProcessingException]
2016-10-03 13:43:43.069 DEBUG 12800 --- [nio-8080-exec-2] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'accountsRestController'
2016-10-03 13:43:43.071 INFO 12800 --- [nio-8080-exec-2] reactor.unresolved : onSubscribe(reactor.core.publisher.FluxPeek$PeekSubscriber#2ba7d09c)
2016-10-03 13:43:43.071 INFO 12800 --- [nio-8080-exec-2] reactor.unresolved : request(unbounded)
2016-10-03 13:43:43.072 INFO 12800 --- [nio-8080-exec-2] reactor.unresolved : onNext(1)
2016-10-03 13:43:43.112 INFO 12800 --- [nio-8080-exec-2] c.c.s.p.r.message.AlertMessageListener : Message received: [com.codependent.spring5.playground.reactive.message.MockTextMessage#37262c9e]
2016-10-03 13:43:43.145 INFO 12800 --- [nio-8080-exec-2] c.c.s.p.r.message.AlertEmitterProcessor : onNext [Alert [alertId=3, message=Message, accountId=1]]
2016-10-03 13:43:43.146 INFO 12800 --- [nio-8080-exec-2] reactor.Flux.EmitterProcessor.2 : onNext(Alert [alertId=3, message=Message, accountId=1])
2016-10-03 13:43:43.177 INFO 12800 --- [nio-8080-exec-2] reactor.unresolved : onComplete()
2016-10-03 13:43:43.177 DEBUG 12800 --- [nio-8080-exec-2] o.s.h.s.r.ServletHttpHandlerAdapter : Successfully completed request
But none get to the browser. This eventyally finishes with a 500 error (no log).
After some manual retries it starts receiving data...
2016-10-03 13:45:07.726 DEBUG 12800 --- [nio-8080-exec-8] s.w.r.r.m.a.RequestMappingHandlerMapping : Returning handler method [public reactor.core.publisher.Flux<com.codependent.spring5.playground.reactive.dto.Alert> com.codependent.spring5.playground.reactive.web.AccountsRestController.getAccountAlertsStreaming2(java.lang.Integer)]
2016-10-03 13:45:07.726 DEBUG 12800 --- [nio-8080-exec-8] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'accountsRestController'
2016-10-03 13:45:07.727 INFO 12800 --- [nio-8080-exec-8] reactor.unresolved : onSubscribe(reactor.core.publisher.FluxPeek$PeekSubscriber#909f06f)
2016-10-03 13:45:07.727 INFO 12800 --- [nio-8080-exec-8] reactor.unresolved : request(unbounded)
2016-10-03 13:45:07.727 INFO 12800 --- [nio-8080-exec-8] reactor.unresolved : onNext(1)
2016-10-03 13:45:07.729 INFO 12800 --- [nio-8080-exec-8] reactor.Flux.EmitterProcessor.9 : onSubscribe(reactor.core.publisher.EmitterProcessor$EmitterSubscriber#7ce1f3e)
2016-10-03 13:45:07.729 INFO 12800 --- [nio-8080-exec-8] reactor.Flux.EmitterProcessor.9 : request(1)
2016-10-03 13:45:07.729 INFO 12800 --- [nio-8080-exec-8] reactor.Flux.EmitterProcessor.9 : onNext(Alert [alertId=4, message=Message, accountId=1])
2016-10-03 13:45:07.730 INFO 12800 --- [nio-8080-exec-8] reactor.unresolved : onComplete()
2016-10-03 13:45:07.747 INFO 12800 --- [nio-8080-exec-8] reactor.Flux.EmitterProcessor.9 : request(1)
2016-10-03 13:45:07.747 INFO 12800 --- [nio-8080-exec-8] reactor.Flux.EmitterProcessor.9 : onNext(Alert [alertId=0, message=Message, accountId=1])
2016-10-03 13:45:07.748 INFO 12800 --- [nio-8080-exec-8] reactor.Flux.EmitterProcessor.9 : request(1)
... but many other times it doesn't get any.
It was resolved in Spring 5 M3:
https://jira.spring.io/browse/SPR-14803
https://jira.spring.io/browse/SPR-14772

spring-cloud sidecar route not working

I create a spring-boot app with #EnableSidecar and I see that a route for the registered eureka clients "offers" and "customers" is created (Mapped URL path [/customers/**] onto ...) and the routes show up on the http://localhost:9000/routes endpoint:
{
"_links":{
"self":{
"href":"http://localhost:8090/routes"
}
},
"/customers/**":"customers",
"/zuul-proxy/**":"zuul-proxy",
"/offers/**":"offers"
}
When accessing http://localhost:9000/customers on the zuul-proxy in the browser I get a 404 thought.
My application.yml
spring:
application:
name: zuul-proxy
server:
port: 9000
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
ZuulProxyApplication.java:
#EnableSidecar
#SpringBootApplication
public class ZuulProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulProxyApplication.class, args);
}
}
Log output:
2015-09-11 23:33:09.236 INFO 42750 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8090 (http)
2015-09-11 23:33:09.237 INFO 42750 --- [ main] c.n.e.EurekaDiscoveryClientConfiguration : Registering application zuul-proxy with eureka with status UP
2015-09-11 23:33:09.238 INFO 42750 --- [ main] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [current=UP, previous=STARTING]
2015-09-11 23:33:09.241 INFO 42750 --- [ main] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/customers/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2015-09-11 23:33:09.242 INFO 42750 --- [ main] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/offers/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2015-09-11 23:33:09.245 INFO 42750 --- [ main] ch.local.zuul.ZuulProxyApplication : Started ZuulProxyApplication in 8.637 seconds (JVM running for 9.26)
2015-09-11 23:33:09.245 INFO 42750 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_ZUUL-PROXY/192.168.0.108: registering service...
2015-09-11 23:33:09.294 INFO 42750 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_ZUUL-PROXY/192.168.0.108 - registration status: 204
2015-09-11 23:33:14.545 INFO 42750 --- [nio-8090-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2015-09-11 23:33:14.545 INFO 42750 --- [nio-8090-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2015-09-11 23:33:14.572 INFO 42750 --- [nio-8090-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 27 ms
2015-09-11 23:33:14.616 INFO 42750 --- [nio-8090-exec-1] o.s.c.n.zuul.filters.ProxyRouteLocator : Finding route for path: /customers
2015-09-11 23:33:14.626 INFO 42750 --- [nio-8090-exec-1] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext#7442808: startup date [Fri Sep 11 23:33:14 CEST 2015]; parent: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#3a60c416
2015-09-11 23:33:14.646 INFO 42750 --- [nio-8090-exec-1] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2015-09-11 23:33:14.759 INFO 42750 --- [nio-8090-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: customers.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2015-09-11 23:33:14.802 INFO 42750 --- [nio-8090-exec-1] c.n.u.concurrent.ShutdownEnabledTimer : Shutdown hook installed for: NFLoadBalancer-PingTimer-customers
2015-09-11 23:33:14.829 INFO 42750 --- [nio-8090-exec-1] c.netflix.loadbalancer.BaseLoadBalancer : Client:customers instantiated a LoadBalancer:DynamicServerListLoadBalancer:{NFLoadBalancer:name=customers,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2015-09-11 23:33:14.855 INFO 42750 --- [nio-8090-exec-1] c.netflix.config.ChainedDynamicProperty : Flipping property: customers.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2015-09-11 23:33:14.858 INFO 42750 --- [nio-8090-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client customers initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=customers,current list of Servers=[192.168.0.108:customers - 8083, 192.168.0.108:customers - 8081],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:192.168.0.108:customers - 8081; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 01:00:00 CET 1970; First connection made: Thu Jan 01 01:00:00 CET 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
, [Server:192.168.0.108:customers - 8083; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 01:00:00 CET 1970; First connection made: Thu Jan 01 01:00:00 CET 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList#55d7aafe
2015-09-11 23:33:14.941 INFO 42750 --- [nio-8090-exec-1] com.netflix.http4.ConnectionPoolCleaner : Initializing ConnectionPoolCleaner for NFHttpClient:customers
2015-09-11 23:33:15.838 INFO 42750 --- [ool-10-thread-1] c.netflix.config.ChainedDynamicProperty : Flipping property: customers.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2015-09-11 23:33:19.287 INFO 42750 --- [nio-8090-exec-2] o.s.c.n.zuul.filters.ProxyRouteLocator : Finding route for path: /customers
2015-09-11 23:33:35.780 INFO 42750 --- [pool-3-thread-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/zuul-proxy/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
I see the customers and offers instances registered in eureka. I also can access them via a RestTemplate in the zuul-proxy app like:
#RestController
public class TestController {
#Autowired
private RestTemplate restTemplate;
#RequestMapping("/test")
public Customer test() {
return restTemplate.getForObject("http://customers/customers/1", Customer.class);
}
}
This is working when I call http://localhost:9000/test
What could be the problem that I get a 404 for the registered routes http://localhost:9000/customers and http://localhost:9000/offers?

Resources