Spring HATEOAS ControllerLinkBuilder方法大大增加了响应时间 [英] Spring HATEOAS ControllerLinkBuilder methodOn increasing response times significantly

查看:92
本文介绍了Spring HATEOAS ControllerLinkBuilder方法大大增加了响应时间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

设置:因此,我有一个用Java编写的RESTfull API,使用spring-bootspring-hates来添加到资源的链接(超媒体驱动的RESTful Web服务).我拥有的所有东西都是标准配置,没有其他设置或更改

The setup: So I have a RESTfull API written in java, using spring-boot and spring-hates for adding links to the resource (Hypermedia-Driven RESTful Web Service). Everything I have is standard and no additional settings or changes are made

问题

  1. 案例:资源上没有链接-Chrome TTFB平均. (10次运行)400毫秒(1000个项目)
  2. 案例:资源上的 1个自我参考链接-Chrome TTFB平均. (10次运行)1500毫秒(1000个项目)
  1. Case: no links on resource - Chrome TTFB avg. (10 runs) 400ms for 1000 items
  2. Case: 1 self reference link on resource - Chrome TTFB avg. (10 runs) 1500ms for 1000 items

我正在使用此官方指南

问题

为什么仅将1个链接添加到我的资源,为什么还要增加1秒来处理请求.我将在每个资源上需要大约5-7个链接,并且每个资源都有其他嵌入式链接?

Why adding only 1 link to my resource adds additional 1 second for the processing the request. I will need around 5 - 7 links on each resource and every resource has additional embedded ones?

对于总共9000个项目,每个项目只有1个链接(包括嵌套的链接),我必须等待30秒才能得到响应,并且没有链接需要400毫秒.

For 9000 total items with only 1 link per item (included the nested ones), i have to wait 30 sec for the response and without links ~ 400 ms.

P.S.额外的代码无关紧要,因为我只是在教程中添加了一个代码,该代码会显着影响性能.

P.S. The additional code is irrelevant because I am just adding a code from the tutorial which effects the performance dramatically.

编辑1

根据建议,我要从TextItem构造函数中添加示例代码

As suggested I am adding example code from my TextItem constructor

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

编辑2

因此,@ Mathias Dpunkt提出的以下示例非常完美

So the following example proposed from @Mathias Dpunkt works absolutely perfect

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,则发布的解决方案不会将其附加到链接中.当我打电话

If the controller method takes @RequestParam the posted solution does not append it to the link. When I call

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;
}

推荐答案

更新了帖子以使用改进的版本- Spring HATEOAS 0.22 Spring Framework 4.3.5/5.0 M4 .

这很有趣.我看了ControllerLinkBuilder方法linkTomethodOn的源代码,并且有很多事情可以做一个简单的链接:

This is very interesting. I had a look at the source code for the ControllerLinkBuilder methods linkToand methodOn and there is a lot going on for a simple link:

  • 为控制器构建aop代理,以记录交互并获取方法和参数以建立链接
  • 发现此方法的映射以构建链接

ControllerLinkBuilder非常方便,因为它避免了映射中已经包含的重复逻辑.

The ControllerLinkBuilder is very convenient because it avoids duplicating logic that is already contained in your mapping.

我想出了一个简单的示例应用程序和一个非常基本的基准来测量和比较链接生成器的性能

I came up with a simple sample application and a very basic benchmark to measure and compare link builder performance

它基于一个简单的控制器-仅返回100个简单的对象-每个对象都带有一个自链接.

It is based on a simple controller - it is just returning 100 simple objects - each is carrying one self-link.

@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添加了一个简单的自链接,我尝试并测量了三种不同的替代方法:

The ItemResourceProcessor adds a simple self-link and I tried and measured three different alternatives:

1.具有linkTo(methodOn)

此处ControllerLinkBuilder用于检查控制器和方法上的映射-对于生成的每个链接都需要aop代理.

Here ControllerLinkBuilder is used to inspect the mapping on controller and method - which needs an aop proxy for every link generated.

@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.没有methodOn()的ControllerLinkBuilder

此处避免了对methodOn()的调用,并且在资源处理器创建时就确定了方法引用,并重新用于生成链接.此版本避免了methodOn的开销,但仍会发现方法上的映射以生成链接.

Here the call the to methodOn() is avoided and the method reference is determined once at the creation of the resource processor and reused to generate the link. This version avoids the overhead of methodOn but still discovers the mapping on the method to generate the link.

@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;
    }
}

结果比第一个版本略好.优化只会给我们带来小小的好处.

The results are slightly better than for the first version. The optimization is giving us only small benefits.

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.使用BasicLinkBuilder生成链接

在这里,我们离开ControllerLinkBuilder并使用BasicLinkBuilder.此实现不会执行任何自省的控制器映射,因此是参考基准的很好的选择.

Here we move away from ControllerLinkBuilder and use BasicLinkBuilder. This implementation is not performing any introspections of controller mappings and is thus a good candidate for reference benchmark.

@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()会有开销.测试表明,与BasicLinkBuilder相比,100条链接平均花费我们不到2毫秒的时间.

Of course, there is an overhead of methodOn(). The tests show that 100 links cost us less than 2ms on average compared to BasicLinkBuilder.

因此,当渲染的链接数量不多时,ControllerLinkBuilder的便利性使其成为链接生成的不错选择.

So when the amounts of rendered links are not massive the convenience of ControllerLinkBuilder is making it a good choice for link generation.

免责声明:我知道我的wrk测试不是正确的基准测试-但是结果可以重复,并且与其他方法相比显示出相同的结果-因此它们至少可以提示差异的大小表现)

DISCLAIMER: I know my wrk tests are not proper benchmarks - but the results could be repeated and showed the same results comparing the alternatives - so they at least can provide a hint on the dimensions of differences in performance)

这篇关于Spring HATEOAS ControllerLinkBuilder方法大大增加了响应时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆