Seb*_*ebi 3 spring jackson spring-boot
我有一个简单的 Spring Boot 应用程序,带有基本的 RestController(完整代码可在此处获取)。它使用 JSON 并使用 Jackson 将请求从 JSON 转换并将响应转换为 JSON。
@RestController("/")
@RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public class SomeController {
@Autowired
private SomeService someService;
@PostMapping
public ResponseEntity<SomeResponseDto> post(@RequestBody @Valid SomeRequestDto someRequestDto) {
final SomeResponseDto responseDto = new SomeResponseDto();
responseDto.setMessage(someRequestDto.getInputMessage());
responseDto.setUuid(someService.getUuid());
return ResponseEntity.ok(responseDto);
}
Run Code Online (Sandbox Code Playgroud)
启动后,第一个请求比任何后续请求慢大约 10 倍。我调试并分析了该应用程序,似乎在第一次请求时,Jackson JSON 解析器正在AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters和AbstractJackson2HttpMessageConverter中的某处进行初始化。
在后续请求中,它似乎会被重新使用。
如何在启动期间初始化 Jackson JSON 解析,以便第一个请求也很快?
我知道如何在Spring启动后触发一个方法。在PreloadComponent中,我添加了如何针对控制器执行 REST 请求的示例。
@Component
public class PreloadComponent implements ApplicationListener<ApplicationReadyEvent> {
private final Logger logger = LoggerFactory.getLogger(PreloadComponent.class);
@Autowired
private Environment environment;
@Autowired
private WebClient.Builder webClientBuilder;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// uncomment following line to directly send a REST request on app start-up
// sendRestRequest();
}
private void sendRestRequest() {
final String serverPort = environment.getProperty("local.server.port");
final String baseUrl = "http://localhost:" + serverPort;
final String warmUpEndpoint = baseUrl + "/warmup";
logger.info("Sending REST request to force initialization of Jackson...");
final SomeResponseDto response = webClientBuilder.build().post()
.uri(warmUpEndpoint)
.header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.body(Mono.just(createSampleMessage()), SomeRequestDto.class)
.retrieve()
.bodyToMono(SomeResponseDto.class)
.timeout(Duration.ofSeconds(5))
.block();
logger.info("...done, response received: " + response.toString());
}
private SomeRequestDto createSampleMessage() {
final SomeRequestDto someRequestDto = new SomeRequestDto();
someRequestDto.setInputMessage("our input message");
return someRequestDto;
}
}
Run Code Online (Sandbox Code Playgroud)
这只适用于这个玩具示例。实际上,我有许多具有复杂 DTO 的 REST 端点,并且我需要在每个“真实”端点旁边添加一个“预热”端点,因为我无法调用我的真实端点。
我添加了具有不同 DTO 的第二个端点,并在我的PreloadComponent. 这并不能解决问题。我假设为每种类型创建了一个杰克逊/任何实例。
我自动连接ObjectMapper到我的PreloadComponentJSON 并将其解析到我的 DTO。再说一遍,这并不能解决问题。
完整源代码位于: https: //github.com/steinsag/warm-me-up
事实证明杰克逊验证是问题所在。我添加了 JVM 选项
-verbose:class
Run Code Online (Sandbox Code Playgroud)
查看类何时加载。我注意到在第一个请求时,有许多 Jackson 验证类被加载。
为了证实我的假设,我重新设计了我的示例,并添加了另一个具有不同 DTO 的独立预热控制器。
该 DTO 使用与真实 DTO中一样的所有 Java 验证注释,例如@NotNull、@Min等。此外,它还有一个自定义枚举来对子类型进行验证。
在启动过程中,我现在向此预热端点发出 REST 请求,该端点不需要包含任何业务逻辑。
启动后,我的第一个请求现在仅比任何后续请求慢 2-3 倍。这是可以接受的。之前,第一个请求的速度慢了 20-40 倍。
我还评估了是否确实需要 REST 请求,或者仅进行 JSON 解析或 DTO 验证是否就足够了(请参阅PreloadComponent)。这会稍微减少第一个请求的运行时间,但仍然比适当预热慢 5-15 倍。所以我猜想还需要一个 REST 请求来加载 Spring Dispatcher 等中的其他类。
我更新了我的示例: https: //github.com/steinsag/warm-me-up
| 归档时间: |
|
| 查看次数: |
1597 次 |
| 最近记录: |