gpt4 book ai didi

java - Spring HATEOAS ControllerLinkBuilder 方法显着增加响应时间

转载 作者:塔克拉玛干 更新时间:2023-11-01 21:37:55 29 4
gpt4 key购买 nike

设置:所以我有一个用 java 编写的 RESTfull API,使用 spring-bootspring-hates 添加链接到资源(超媒体驱动的 RESTful Web 服务)。我拥有的一切都是标准的,没有进行额外的设置或更改

问题

  1. 案例:资源上没有链接 - Chrome TTFB 平均。 (10 次运行)1000 件 400 毫秒
  2. 案例:资源上的 1 个 self 引用链接 - Chrome TTFB 平均。 (10 次运行)1500 毫秒用于 1000 件元素

我正在使用 this official guide

问题

为什么只添加 1 个链接到我的资源会额外增加 1 秒来处理请求。我将需要每个资源上大约 5 - 7 个链接,并且每个资源都有额外的嵌入式链接?

对于总共 9000 个项目,每个项目只有 1 个链接(包括嵌套的链接),我必须等待 30 秒的响应,没有链接约 400 毫秒。

附言额外的代码无关紧要,因为我只是添加了教程中的代码,它会显着影响性能。

编辑 1

按照建议,我从我的 TextItem 构造函数中添加示例代码

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

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

新问题

Controller :

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

如果 Controller 方法采用 @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;
}

最佳答案

编辑:

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

回答:

这很有趣。我查看了 ControllerLinkBuilder 方法 linkTomethodOn 的源代码,一个简单的链接包含很多内容:

  • 为 Controller 构建一个 aop propxy,记录交互并获取方法和参数以构建链接
  • 它发现该方法的映射以构建链接

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

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

它基于一个简单的 Controller - 它只返回 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。带有 linkTo(methodOn) 的 ControllerLinkBuilder

这里 ControllerLinkBuilder 用于检查 Controller 和方法上的映射 - 它需要为每个生成的链接使用 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。没有 methodOn() 的 ControllerLinkBuilder

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

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

这里我们不再使用 ControllerLinkBuilder,而是使用 BasicLinkBuilder。此实现不执行 Controller 映射的任何内省(introspection),因此非常适合作为引用基准。

@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 毫秒。

因此,当呈现的链接数量不是很大时,ControllerLinkBuilder 的便利性使其成为链接生成的不错选择。

免责声明:我知道我的 wrk 测试不是适当的基准 - 但结果可以重复并显示相同的结果比较替代方案 - 所以它们至少可以提供差异维度的提示在性能上)

关于java - Spring HATEOAS ControllerLinkBuilder 方法显着增加响应时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36303642/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com