opentable / otj-server

OpenTable Java server template component
Apache License 2.0
1 stars 16 forks source link

Reactive Rest Http Server not working with JAX-RS style annotations #51

Closed wso-ot closed 6 years ago

wso-ot commented 6 years ago

We are working on this: https://github.com/opentable/restaurant-service

We tried to use the JAX-RS style annotations (@Path, @Get, etc) in the RestaurantResource but it's not loading the controller. It works when we switch to the Spring REST style annotations with "@RestController" and "@RequestMapping".

Maybe something about the Resteasy context not being loaded in the Reactive init path?

mikebell90 commented 6 years ago

Try returning a CompleteableFuture return mono.toFuture ?

If that works, a Provider could be created to adapt this.

wso-ot commented 6 years ago

Doesn't work... Here's what I have at the resource:


    @Component
    @Slf4j
    //@RequestMapping("/v9/restaurants")
    @Path("/v9/restaurants")
    public class RestaurantsResource {

        @Autowired private RestaurantService restaurantService;

        @GET
        @Path("/{rid}")
        @Produces(MediaType.APPLICATION_JSON_UTF8_VALUE)
        public CompletableFuture<Restaurant> getRestaurtant(@PathParam("rid") int rid){

            return restaurantService.getRestaurantMono(rid).toFuture();

        }

    //    @RequestMapping(value = "/{rid}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
    //    @GET
    //    public Mono<Restaurant> getRestaurtant(@PathVariable("rid") int rid){
    //
    //        return restaurantService.getRestaurantMono(rid);
    //
    //    }

    }
mikebell90 commented 6 years ago

Any more info than "doesn't work"?

wso-ot commented 6 years ago

The request is sent with this: curl -X GET \ http://localhost:8900/v9/restaurants/1811 \ -H 'cache-control: no-cache' \ -H 'content-type: application/json' \ -H 'postman-token: a3e8e1f7-ea89-f59a-36dd-17e8a0fae685'

And the result:

{
    "timestamp": 1526413378294,
    "path": "/v9/restaurants/1811",
    "status": 404,
    "error": "Not Found",
    "message": "No matching handler"
}
wso-ot commented 6 years ago

The restaurantService forwards the request to a ReactiveCrudRepository. The method returns a Mono. It remains a Mono until the code above

wso-ot commented 6 years ago

The application configuration class:

@ComponentScan
@EnableDiscoveryClient
@EnableAsync
@EnableScheduling
@EnableWebFlux
@RestReactiveHttpServer
@EnableReactiveMongoRepositories
@EnableAutoConfiguration
public class ServiceConfiguration {
}
wso-ot commented 6 years ago

The main application class:

/**
 * The main application class
 */
@Configuration
@Import(value = {
        MongoConfiguration.class,
        ServiceConfiguration.class
})
public class RestaurantServiceApplication {

    public static void main(final String[] args) {

        OTApplication.run(RestaurantServiceApplication.class, args);

    }

    @Bean
    public ServiceInfo serviceInfo() {

        return () -> "restaurant-service";

    }

}
wso-ot commented 6 years ago

I'm running this in my IDE, running just the main class. With no argument.

wso-ot commented 6 years ago

And the bootstrap.yml

spring:
    cloud:
        config:
            enabled: false
            failFast: false
            uri: https://opentable:***@config-ci-uswest2.otenv.com/
    application:
        name: coresvc-restaurant-service

---
wso-ot commented 6 years ago

application.yml

ot:
  announce:
    enabled: false
    service-type: restauarnt-service
  discovery:
    mode: OT
    servers: proxy://discovery-ci-sf.otenv.com/
  graphite:
    graphite-host: carbon-qa-sf.otenv.com
  restaurant-service:
    mongo-connect-timeout: 500
    mongo-server-selection-timeout: 500

spring:
  data:
    mongodb:
      uri: mongodb://beaversUser:***@beavers-01-pp-sf.otenv.com:27017,beavers-02-pp-sf.otenv.com:27017,beavers-03-pp-sf.otenv.com:27017/Restaurants-v3?replicaSet=beavers;authSource=admin;safe=true;readPreference=primary;wtimeoutMS=180000
logging:
  level:
    org.springframework.jdbc.core.JdbcTemplate: WARN
    org.springframework.data.mongodb.core.MongoTemplate: WARN
    #root: TRACE

server:
    port: ${PORT0:8900}
mikebell90 commented 6 years ago

{ "timestamp": 1526413378294, "path": "/v9/restaurants/1811", "status": 404, "error": "Not Found", "message": "No matching handler" }

We don't generate this. Further, please run it locally and prove it actually responds on that endpoint when you manually hit it - Change it for example to return a string.

mikebell90 commented 6 years ago

Furthermore, not that there aren't valid reasons for reactive/async responses, but do you actually have metrics/evidence that you actually do need it?

wso-ot commented 6 years ago

On the 404 response:
The problem is, we can't get it to response with anything if we use @Path and @Get annotations, i.e. JAX-RS style annotations. So Jersey or Resteasy is not picking the resource up. Here's a simpler thing that I've tried:

package com.opentable.coresvc.restaurantservice.resource;

import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.util.concurrent.CompletableFuture;

@Component
//@RestController
@Slf4j
//@RequestMapping("/v9/restaurants")
@Api("Simple Test")
@Path("/simple")
public class SimpleResource {
    @GET
    @Path("/hello")
    @Produces(MediaType.APPLICATION_JSON_UTF8_VALUE)
    public CompletableFuture<String> getHello(){

        return Mono.just("Hello World").toFuture();

    }

}
wso-ot commented 6 years ago

This is the version that will work:

package com.opentable.coresvc.restaurantservice.resource;

import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.util.concurrent.CompletableFuture;

//@Component
@RestController
@Slf4j
@RequestMapping("/simple")
@Api("Simple Test")
//@Path("/simple")
public class SimpleResource {
    @RequestMapping(value = "/hello", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
    public Mono<String> getHello(){

        return Mono.just("Hello World");

    }

}
wso-ot commented 6 years ago

On why using Reactive: The API you are seeing is the simple case. We have an endpoint that will return all Restaurants from the MongoDB. That's 1/2 million restaurants. The main user is the restaurant search indexer. So we will be experimenting with this to see if performance improves. The existing Restaurant API is written in Node JS and is constantly leaking memory.

rjm-ot commented 6 years ago

Is there an issue with using the spring annotations? also if you're going to use Spring WebFlux (reactive spring) You want to return Mono and Flux rather than CompletableFuture. I'm available for a realtime conversation. you can find me on slack as "robert murray"

wso-ot commented 6 years ago

I'm gonna close this, because the reason on why things doesn't work is explained here. Let's move the conversation to there instead.

https://wiki.otcorp.opentable.com/x/6e2bAg