设置:所以我有一个用java编写的RESTfull API,使用spring-hates
和TextItem
添加资源链接(超媒体驱动的RESTful Web服务)。我拥有的一切都是标准的,没有进行额外的设置或更改
问题
我正在使用this official guide
问题
为什么只向我的资源添加1个链接会为处理请求添加额外的1秒。每个资源需要大约5-7个链接,每个资源都有其他嵌入的链接?
对于9000个项目,每个项目只有1个链接(包括嵌套的项目),我必须等待30秒才能获得响应,并且没有链接~400毫秒。
P.S。附加代码是无关紧要的,因为我只是从教程中添加了一个能够显着影响性能的代码。
修改1
正如所建议的那样,我正在添加add(linkTo(methodOn(TestController.class).getTestItems()).withRel("testLink"));
构造函数
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;
}
修改2
所以@Mathias Dpunkt提出的以下示例非常完美
@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;
}
,则发布的解决方案不会将其附加到链接。当我打电话
fileInputs
答案 0 :(得分:11)
更新帖子以使用改进版本 - Spring HATEOAS 0.22和Spring Framework 4.3.5 / 5.0 M4。
这非常有趣。我查看了ControllerLinkBuilder
方法linkTo
和methodOn
的源代码,并且有很多内容用于简单链接:
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。没有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
。此实现不执行任何控制器映射的内省,因此是参考基准测试的良好候选者。
@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测试不是正确的基准 - 但结果可以重复并显示相同的结果比较替代品 - 所以他们至少可以提供差异的维度的暗示在表现)