2016-03-30 7 views
1

Установка: Так у меня есть RESTfull API написан на Java, используя spring-boot и spring-hates для добавления ссылки на ресурс (гипермедиа-Driven RESTful Web Service). Все у меня есть стандартный и никаких дополнительных настроек или изменения не сделаныSpring HATEOAS ControllerLinkBuilder methodOn раз увеличение отклика значительно

Проблема

  1. Дело: нет ссылки на ресурс - Chrome TTFB Avg. (10 прогонов) 400 мс для 1000 наименований
  2. Корпус: 1 ссылка самообслуживания на ресурсе - Chrome TTFB сред. (10 трасс) 1500ms на 1000 пунктов

Я использую this official guide

Вопрос

Почему добавлять только 1 ссылку на мой ресурс добавляет еще 1 секунду для обработки запроса. Мне потребуется около 5-7 ссылок на каждый ресурс, и каждый ресурс имеет дополнительные встроенные функции?

Для всего 9000 предметов с 1 ссылкой на элемент (включая вложенные), я должен ждать 30 секунд для ответа и без ссылок ~ 400 мс.

P.S. Дополнительный код не имеет значения, потому что я просто добавляю код из учебника, который резко влияет на производительность.

Edit 1

Как предложил я добавляю пример кода из моего TextItem конструктора

add(linkTo(methodOn(TestController.class).getTestItems()).withRel("testLink")); 

Edit 2

Так в следующем примере предложен из @Mathias Dpunkt работает абсолютно совершенен

private Method method = ReflectionUtils.findMethod(TestController.class, "getOne", Integer.class); 

@Override 
public Resource<Item> process(Resource<Item> resource) { 
    resource.add(linkTo(method, resource.getContent().getId()).withSelfRel()); 
    return resource; 
} 

Новая проблема

Контроллер:

@RestController 
@RequestMapping("items") 
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) 
public class TestController { 

    private final ItemResourceProcessor resourceProcessor; 

    @RequestMapping(method = GET) 
    public ResponseEntity<List<Resource<Item>>> getAll() { 
     List<Resource<Item>> items = new ArrayList<>(100); 
     for (int i = 0; i < 100; i++) { 
      items.add(resourceProcessor.process(
        new Resource<>(new Item(i, UUID.randomUUID().toString())))); 
     } 

     return ResponseEntity.ok(items); 
    } 

    @RequestMapping(method = GET, path = "/{id}") 
    public ResponseEntity<Resource<Item>> getOne(@PathVariable Integer id, @RequestParam boolean test1, @RequestParam boolean test2) { 
     return null; 
    } 
} 

Если метод контроллера принимает @RequestParam посланного решение не добавляет его к ссылке. Когда я звоню

private Method method = ReflectionUtils.findMethod(TestController.class, "getOne", Integer.class);  

@Override 
public Resource<Item> process(Resource<Item> resource) { 
    resource.add(linkTo(method, resource.getContent().getId(), true, true).withSelfRel()); 
    return resource; 
} 
+1

Профиль вашего приложения. – Kayaman

+0

@ Кайаман, даже если мой вопрос основан на конкретном приложении - случай - это то, что происходит в каждом весеннем приложении, которое у меня есть - я обнаружил его только сейчас, когда размер ответа взорвался, но это не проблема, не так ли. . –

+0

Добавление ссылки не вызывает проблемы. Замедление, скорее всего, связано с использованием метода methodOn. Но пока вы считаете, что нет необходимости показывать свой код, мы не можем вам помочь. – zeroflagL

ответ

6

EDIT:

Обновленный пост использовать улучшенные версии - Spring HATEOAS 0.22 и Spring Framework 4.3.5/5.0 M4.

Это очень интересно.Я имел взгляд на исходный код для ControllerLinkBuilder методов linkTo и methodOn и там происходит много для простой ссылки:

  • строит АОП propxy для контроллера, который регистрирует взаимодействия и завладеет метода и параметры для построения ссылки для
  • он обнаруживает отображения этого метода для построения по ссылке

ControllerLinkBuilder очень удобно, так как позволяет избежать дублирования логики, которая уже содержится в вашем отображении.

Я придумал простой пример приложения и очень простой тест для измерения и сравнения производительности ссылку строитель

Он основан на простой контроллер - это просто возвращается 100 простых объектов - каждый несет одну само- ссылка.

@RestController 
@RequestMapping("items") 
@RequiredArgsConstructor(onConstructor = @__(@Autowired)) 
public class TestController { 

    private final ItemResourceProcessor resourceProcessor; 

    @RequestMapping(method = GET) 
    public ResponseEntity<List<Resource<Item>>> getAll() { 
     List<Resource<Item>> items = new ArrayList<>(100); 
     for (int i = 0; i < 100; i++) { 
      items.add(resourceProcessor.process(
        new Resource<>(new Item(i, UUID.randomUUID().toString())))); 
     } 

     return ResponseEntity.ok(items); 
    } 

    @RequestMapping(method = GET, path = "/{id}") 
    public ResponseEntity<Resource<Item>> getOne(@PathVariable Integer id) { 
     return null; 
    } 
} 

ItemResourceProcessor добавляет простой самостоятельной ссылке, и я пытался и измерил три различных варианта:

1. ControllerLinkBuilder с linkTo (methodOn)

Здесь ControllerLinkBuilder используется для проверки отображения на контроллере и методе - которому нужен прокси-сервер aop для каждой созданной ссылки.

@Component 
public class ItemResourceProcessor implements ResourceProcessor<Resource<Item>> { 

    @Override 
    public Resource<Item> process(Resource<Item> resource) { 
     resource.add(linkTo(methodOn(TestController.class).getOne(resource.getContent().getId())).withSelfRel()); 
     return resource; 
    } 
} 

Результаты этого варианта таковы:

wrk -t2 -c5 -d30s http://localhost:8080/items 

    Running 30s test @ http://localhost:8080/items 
    2 threads and 5 connections 
    Thread Stats Avg  Stdev  Max +/- Stdev 
    Latency  4.77ms 0.93ms 25.57ms 83.97% 
    Req/Sec 420.87  48.63 500.00  71.33% 
    25180 requests in 30.06s, 305.70MB read 
Requests/sec: 837.63 

2. ControllerLinkBuilder без methodOn()

Здесь Вызвать, чтобы избежать methodOn() и эталонный метод определяется один раз при создании процессора ресурсов и повторного использования для генерации ссылки. Эта версия позволяет избежать накладных расходов методаOn, но все же обнаруживает сопоставление метода для создания ссылки.

@Component 
public class ItemResourceProcessor implements ResourceProcessor<Resource<Item>> { 

    private Method method = ReflectionUtils.findMethod(TestController.class, "getOne", Integer.class); 

    @Override 
    public Resource<Item> process(Resource<Item> resource) { 
    resource.add(linkTo(method, resource.getContent().getId()).withSelfRel()); 
    return resource; 
    } 
} 

Результаты немного лучше, чем для первой версии. Оптимизация дает нам лишь небольшие преимущества.

wrk -t2 -c5 -d30s http://localhost:8080/items 

Running 30s test @ http://localhost:8080/items 
    2 threads and 5 connections 
    Thread Stats Avg  Stdev  Max +/- Stdev 
    Latency  4.02ms 477.64us 13.80ms 84.01% 
    Req/Sec 499.42  18.24 540.00  65.50% 
    29871 requests in 30.05s, 365.50MB read 
Requests/sec: 994.03 

3. Генерация Link с помощью BasicLinkBuilder

Здесь мы отходим от ControllerLinkBuilder и использовать BasicLinkBuilder. Эта реализация не выполняет никаких интроспекций сопоставлений контроллеров и, следовательно, является хорошим кандидатом для эталонного теста.

@Component 
public class ItemResourceProcessor implements ResourceProcessor<Resource<Item>> { 

    private ControllerLinkBuilder baseLink; 

    @Override 
    public Resource<Item> process(Resource<Item> resource) { 
     resource.add(BasicLinkBuilder.linkToCurrentMapping() 
      .slash("items") 
      .slash(resource.getContent().getId()).withSelfRel()); 
     return resource; 
    } 
} 

Результаты снова лучше, чем предыдущий

wrk -t2 -c5 -d30s http://localhost:8080/items 

Running 30s test @ http://localhost:8080/items 
    2 threads and 5 connections 
    Thread Stats Avg  Stdev  Max +/- Stdev 
    Latency  3.05ms 683.71us 12.84ms 72.12% 
    Req/Sec 658.31  87.79 828.00  66.67% 
    39349 requests in 30.03s, 458.91MB read 
Requests/sec: 1310.14 

Резюме

Конечно, есть накладные расходы methodOn().Тесты показывают, что 100 ссылок стоили нам менее 2 мс в среднем по сравнению с BasicLinkBuilder.

Поэтому, когда количество отображаемых ссылок не является массовым, удобство ControllerLinkBuilder делает его хорошим выбором для генерации ссылок.

ОТКАЗ: Я знаю, что мои Wrk тесты не соответствующие тесты - но результаты могут быть повторены и показал те же результаты сравнения альтернатив - так они, по крайней мере, могут дать намек на размерах разницы в производительности)

+0

Я попробую его как можно скорее :) Спасибо за предложение –

+0

@ DanielDonev, пожалуйста, посмотрите мои результаты тестов. –

+0

Извините, но я не смог бы его проверить, но ваш результат кажется многообещающим. Я попытаюсь найти время, чтобы сделать это в выходные дни и поделится своими результатами на основе той же настройки, которая использовалась, когда я задал вопрос. Еще раз спасибо за помощь :) –