Spring Rest服务在第一个请求上为什么慢?

huangapple go评论110阅读模式
英文:

Why are Spring Rest services slow on the first request?

问题

以下是您要翻译的内容:

所以这个问题已经被问过几次了,但似乎没有人以能够帮助我的方式回答它。我目前正在为一个简单的处理产品数据的应用程序制作后端。它甚至不是使用JSP,只是一个普通的Rest后端。使用Spring的RestControllers。

“问题”是:启动后的第一个请求比所有其他请求的响应时间要长得多。我只是在Postman中测试一个简单的JPA用户实体。

一些需要考虑的事项:

  • 这可能不是数据库问题本身,因为它显然只是在第一个传入请求时初始化了一些内容,而不是在启动时。
  • 在日志中,当第一个实际请求进入时(通过Postman),它会显示“正在初始化Spring DispatcherServlet 'dispatcherServlet'”。
  • 如果我从数据库中提取所有用户(目前只有一个用户),启动后的第一个请求需要140毫秒(根据Postman的说法)。之后,相同的请求始终最多需要10毫秒。
  • 有一个标志,某个类似问题的答案建议使用:spring.mvc.servlet.load-on-startup=1。尽管这只是删除了上面提到的DispatcherServlet初始化的日志记录。
  • 看起来这是标准行为,与我实际编写实体和/或RestControllers的方式无关。

我如何使第一个请求更快/如何强制Spring在第一个请求到达之前实际初始化所有内容?

以下是您提供的一些代码:

User.java:

@Entity
@Table(name = "users")
@Data
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;

	@NonNull
	private String firstName;
	@NonNull
	private String lastName;

	@NonNull
	@OneToOne(cascade = CascadeType.ALL)
	private Address billingAddress;

	//一些其他内容,包括列表和另一个地址
}

UserController.java:

@RestController
@RequestMapping("users")
public class UserController {

	private final UserRepository userRepository;

	@Autowired
	public UserController(UserRepository userRepository) {
		this.userRepository = userRepository;
	}

	@GetMapping()
	public List<User> getAllUsers() {
		return (List<User>) userRepository.findAll();
	}

	//更多内容
}
英文:

so this question has been asked a few times already but seems like no one answered it in a way that could help me. I'm currently making a backend for a simple application handling product data. It's not even with JSP, just a plain Rest backend. Using RestControllers by Spring.

The "problem" is: the first request after startup takes way longer to get an answer from the server than all others. (I'm just testing with Postman with a simple JPA User Entity)

Some things to consider:

  • it's probably not a database problem in itself since it clearly just initializes something upon first incoming request instead of on startup
  • In the log, it says "Initializing Spring DispatcherServlet 'dispatcherServlet'" when the first actual request comes in (via Postman).
  • If I pull all users from the database (which currently is only one user), the first request after startup takes 140ms (according to Postman). After that, the same request needs always 10ms at max.
  • There's a flag some answer to a similar question suggested: spring.mvc.servlet.load-on-startup=1. Though this only removes the logging (of the initialization of the DispatcherServlet) mentioned above.
  • It seems like this is standard behavior which has nothing to do with how I actually coded my entities and/or RestControllers.

How do I make the first request faster / how do I force Spring to actually initialize everything before the first request comes in?

Some code anyways:

User.java:

@Entity
@Table(name = &quot;users&quot;)
@Data
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;

	@NonNull
	private String firstName;
	@NonNull
	private String lastName;

	@NonNull
	@OneToOne(cascade = CascadeType.ALL)
	private Address billingAddress;

	//a bit more. a list and another address
}

UserController.java:

@RestController
@RequestMapping(&quot;users&quot;)
public class UserController {

	private final UserRepository userRepository;

	@Autowired
	public UserController(UserRepository userRepository) {
		this.userRepository = userRepository;
	}

	@GetMapping()
	public List&lt;User&gt; getAllUsers() {
		return (List&lt;User&gt;) userRepository.findAll();
	}

	//more stuff
}

答案1

得分: 6

你自己回答了这个问题。Spring 中有很多东西是为了性能和资源消耗的优化而进行懒加载的。一个用户真的会注意到一个请求需要 140 毫秒还是 10 毫秒吗?很可能不会。请注意,这不是每个用户的一个请求,而是每个启动路径的一个请求。

尽管如此... 你也回答了如何“修复”它。在部署之后(我假设是通过 CI/CD 自动化完成的),你可以提交一个或多个请求(作为你的 CI/CD 的一部分),触发所有需要的路径的初始化。例如,如果你有 5 个数据库连接,可能需要提交 5 个请求来初始化所有内容。

这是完全可接受的,这个过程被称为“系统预热”。

英文:

You answered the question yourself. A lot of stuff in Spring is lazy init for performance and resource consumption optimizations. Is a user really going to even notice that ONE request took 140ms vs 10ms. Probably not. And mind you, that's not one request per user, that's one request per init path per start up.

All that being said... you also answered how to "fix" it. After you deploy (which I assume is automated through CI/CD), you submit a request or requests (part of your CI/CD) that will trigger the initialization down all the paths you need. I.e. if you have 5 db connections, you might need to submit 5 requests to initialize everything.

This is perfectly acceptable and is a process known as "warming up the system".

huangapple
  • 本文由 发表于 2020年7月22日 01:05:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/63019528.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定