英文:
Correct way to implement a 'heartbeat' service in SpringFramework
问题 {#heading}
我有一个运行在Tomcat上的Java Web应用程序。我想创建一个"心跳"方法,只返回一个200状态代码,以便我可以用来验证服务的状态。我创建了一个简单的控制器类,代码如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.function.ServerResponse;
@CrossOrigin(origins = "*")
@Controller
@RequestMapping(path = "/status")
public class StatusController {
private static final Logger logger = LoggerFactory.getLogger(StatusController.class);
@GetMapping(path = "/heartbeat")
public ServerResponse getHeartbeat() {
logger.info("checking heartbeat");
return ServerResponse.ok().build();
}
@GetMapping(path = "/hello", produces = "text/html")
@ResponseBody
public String getHello() {
return "Hello World";
}
}
如果我部署我的应用程序,我可以调用mysite.com/myapp/status/hello
并得到返回值"Hello World"。然而,如果我尝试调用心跳服务(mysite.com/myapp/status/heartbeat
),我会得到一个404错误。
我不太确定我在这里做错了什么。如何正确地从服务调用中返回状态代码? 英文:
I have a java web application running on tomcat. I wanted to create a 'heartbeat' method that will simply return a 200 status code that I can use to verify the status of the service. I created a simple controller class that looks like this:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.function.ServerResponse;
@CrossOrigin(origins = \"\*\")
@Controller
@RequestMapping(path = \"/status\")
public class StatusController {
private static final Logger logger = LoggerFactory.getLogger(StatusController.class);
@CrossOrigin(origins = "*")
@GetMapping(path = "/heartbeat")
public ServerResponse getHeartbeat()
{
logger.info("checking heartbeat");
return ServerResponse.ok().build();
}
@CrossOrigin(origins = "*")
@GetMapping(path = "/hello", produces = "text/html")
@ResponseBody
public String getHello()
{
return "Hello World";
}
`}
`
If I deploy my application, I can call mysite.com/myapp/status/hello and get a return value of 'Hello World'. However, if I try to call the heartbeat service ( mysite.com/myapp/status/heartbeat ) I get a 404 error.
I'm not quite sure what I'm doing wrong here. What is the correct way to simply return a status code from a service call?
答案1 {#1}
得分: 2
为了补充Abhinav的答案,正如他所说,不要在这里使用ServerResponse;这不是适当的响应类型。
如果你只想在一切正常时返回200,可以这样做:
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE;
...
@CrossOrigin(origins = "*")
@GetMapping(path = "/heartbeat")
public ResponseEntity<?> getHeartbeat() {
logger.info("checking heartbeat");
if (isEverythingKosher()) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.status(SERVICE_UNAVAILABLE).build();
}
}
但是,正如前面的回答所述,我建议使用Actuator,因为它已经内置支持大多数你想要使用的检查。你甚至可以通过定义一个扩展AbstractHealthIndicator的类来扩展它以使用你自己的健康检查。
@Component
public class MyHealthCheck extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
// 在这里执行你的检查,根据状态设置构建器为up或down
if (isEverythingKosher()) {
builder.up();
} else {
builder.down();
}
}
}
然后当你调用actuator/health端点时,它将贡献到你的应用程序的健康状态。
使用actuator的输出如下(我将自定义健康指示器设置为DOWN):
{
"status": "DOWN",
"components": {
"diskSpace": {
"status": "UP",
"details": {
"total": 1000240963584,
"free": 472888266752,
"threshold": 10485760,
"path": "/Users/lane.maxwell/git/actuator-demo/.",
"exists": true
}
},
"mongo": {
"status": "UP",
"details": {
"maxWireVersion": 13
}
},
"myHealthCheck": {
"status": "DOWN"
},
"ping": {
"status": "UP"
}
}
}
英文:
To add to Abhinav's answer, as he stated don't use ServerResponse here; it's not the appropriate response type.
If you just want to return a 200 if everything is ok, you can do something like this:
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE;
...
`@CrossOrigin(origins = "*")
@GetMapping(path = "/heartbeat")
public ResponseEntity<?> getHeartbeat()
{
logger.info("checking heartbeat");
if (isEverythingKosher()) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.status(SERVICE_UNAVAILABLE).build();
}
}
`
But, as stated in the previous answer, I would leverage Actuator as it's already got built-in support for most checks that you'll want to use. You can even extend it with your own health checks if you want to by defining a class that extends AbstractHealthIndicator
@Component
public class MyHealthCheck extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
// Do your checks here, set the builder up or down depending on the status
if (isEverythingKosher()) {
builder.up();
} else {
builder.down();
}
}
}
Then when you call the actuator/health endpoint, it will contribute to the health status of your application.
Here is what the output would look like using the actuator (I set the custom health indicator to indicate down).
{
"status": "DOWN",
"components": {
"diskSpace": {
"status": "UP",
"details": {
"total": 1000240963584,
"free": 472888266752,
"threshold": 10485760,
"path": "/Users/lane.maxwell/git/actuator-demo/.",
"exists": true
}
},
"mongo": {
"status": "UP",
"details": {
"maxWireVersion": 13
}
},
"myHealthCheck": {
"status": "DOWN"
},
"ping": {
"status": "UP"
}
}
}
答案2 {#2}
得分: 1
ServerResponse 是来自Spring Reactive框架的,用于Spring webflux。
所以,对于简单的健康检查/心跳实现,如果您正在构建Spring Web项目并部署在Tomcat服务器上,那么您的第二个方法getHello()
构造将很好地完成任务,因为您的第二个方法的响应状态也将是 200 OK。
附注:我看到您已经将您的问题标记为spring-boot
。所以,如果您正在构建一个Spring Boot应用程序,我建议您使用它的spring-boot-actuator,它添加了许多生产级的功能,其中包括我在我们的企业应用程序中使用过的两个功能,即健康检查 和关闭
http://localhost:8080/actuator/health
{"status":"UP"}
`http://localhost:8080/actuator/shutdown
{ "message" : "Shutting down, bye..."}
`
所以,\actuatot\health
端点也非常适合心跳实现。
英文:
The ServerResponse is from Spring Reactive framework, used in Spring webflux.
So for simple healthcheck/heartbeat implementation, your second method getHello()
construct will do just fine, if you are building spring web project and deploying in a tomcat server. Because your second method's response status will be 200 OK as well.
Side-note: I've seen you've tagged your question as a spring-boot
. So, if you are building a spring-boot app, then i'll suggest to use it's spring-boot-actuator which adds many production grade capabilities, out of which, two which i've used in our enterprise apps are health check and shutdown
http://localhost:8080/actuator/health
{"status":"UP"}
`http://localhost:8080/actuator/shutdown
{ "message" : "Shutting down, bye..."}
`
So, \actuatot\health
endpoint is also good fit for heartbeat implementation