乍一看,您可能认为 Actuator 的端点实现,和 Spring MVC 的 Controller 一样。正如您将在第 18 章中看到的,端点可以作为 JMX MBean,还可以通过 HTTP 访问。因此,对于这些端点来说,并不仅仅是 Controller 类。
事实上,Actuator 端点的定义与 Controller 完全不同。不同于用 @Controller 或 @RestController 注解的类,Actuator 端点使用带有 @Endpoint 注解的类定义。
更重要的是,不再使用 HTTP 命名注解,诸如 @GetMapping、@PostMapping 或 @DeleteMapping。Actuator 端点用 @ReadOperation、@WriteOperation 和 @DeleteOperation 注解。这些注解并不意味着任何特定的通信机制,事实上,允许 Actuator 通过各种通信机制进行通信,HTTP 和 JMX 开箱即用。
为了演示如何编写自定义 Actuator 端点,请参考如下的 NotesEndpoint。
复制 package tacos . ingredients ;
import java . util . ArrayList ;
import java . util . Date ;
import java . util . List ;
import org . springframework . boot . actuate . endpoint . annotation . DeleteOperation ;
import org . springframework . boot . actuate . endpoint . annotation . Endpoint ;
import org . springframework . boot . actuate . endpoint . annotation . ReadOperation ;
import org . springframework . boot . actuate . endpoint . annotation . WriteOperation ;
import org . springframework . stereotype . Component ;
import lombok . Getter ;
import lombok . RequiredArgsConstructor ;
@ Component
@ Endpoint (id = "notes" , enableByDefault = true )
public class NotesEndpoint {
private List < Note > notes = new ArrayList <>();
@ ReadOperation
public List < Note > notes () {
return notes;
}
@ WriteOperation
public List < Note > addNote ( String text) {
notes . add ( new Note(text) );
return notes;
}
@ DeleteOperation
public List < Note > deleteNote ( int index) {
if (index < notes . size ()) {
notes . remove (index);
}
return notes;
}
@ RequiredArgsConstructor
private class Note {
@ Getter
private Date time = new Date() ;
@ Getter
private final String text;
}
}
该端点是一个简单的便笺端点。在该端点中,用户可以使用写入操作增加一个便笺,使用读取操作读取便笺列表,然后使用删除操作删除便笺。诚然,这个端点对于 Actuator 来说不是很有用,但是当您考虑开箱即用的 Actuator 端点会涉及太多领域时,很难想出一个实用的自定义示例。
可以看到,NotesEndpoint 类合使用 @Component 注解,以便通过 Spring 的组件扫描获取,并在 Spring 中实例化为 bean。它还添加了注解 @Endpoint,使其成为 ID 为 notes 的 Actuator 端点。并且它是默认启用的,这样您就不需要通过将其包含在 management.web.endpoints.web.exposure.include
中来显式启用它。
如您所见,NotesEndpoint 提供了如下操作:
notes()
方法用 @ReadOperation 注解。调用时,它将返回可用的便笺列表。使用 HTTP 时,这意味着它将处理对 /exactor/notes
的 HTTP GET 请求,并响应 JSON 形式的便笺列表。
addNote()
方法用 @WriteOperation 注解。当被调用时,它将根据给定文本创建新便笺,并将其添加到列表中。使用 HTTP 时,
它处理 POST 请求,其中请求体是包含文本属性的 JSON 对象。处理完成后返回列表的当前状态。
deleteNote()
方法用 @DeleteOperation 注解。调用时,它将删除给定索引的便笺。使用 HTTP 时,该端点处理 DELETE 请求,在请求参数中给出便笺索引。
要查看实际运行效果,可以使用 curl 测试新端点。首先,添加几个便笺,使用两个单独的 POST 请求:
复制 $ curl localhost:8080/actuator/notes \
-d '{"text":"Bring home milk"}' \
-H "Content-type: application/json"
[{ "time" : "2018-06-08T13:50:45.085+0000" , "text" : "Bring home milk" }]
$ curl localhost:8080/actuator/notes \
-d '{"text":"Take dry cleaning"}' \
-H "Content-type: application/json"
[{ "time" : "2018-06-08T13:50:45.085+0000" , "text" : "Bring home milk" },
{ "time" : "2018-06-08T13:50:48.021+0000" , "text" : "Take dry cleaning" }]
如您所见,每次发布新便笺时,端点都会返回最新便笺列表。如果以后要查看便笺列表,可以执行以下操作:
复制 $ curl localhost:8080/actuator/notes
[{ "time" : "2018-06-08T13:50:45.085+0000" , "text" : "Bring home milk" },
{ "time" : "2018-06-08T13:50:48.021+0000" , "text" : "Take dry cleaning" }]
`
如果您决定删除其中一个便笺,则发送带有索引参数的 DELETE 请求:
复制 $ curl localhost:8080/actuator/notes?index= 1 -X DELETE
[{ "time" : "2018-06-08T13:50:45.085+0000" , "text" : "Bring home milk" }]
需要注意的是,尽管这里只演示了使用 HTTP 如何与端点交互,但它还作为 MBean 公开了,可以使用任何您熟悉的 JMX 客户端进行访问。如果您想将其限制为仅公开 HTTP 端点,您可以使用 @WebEndpoint 而不是 @endpoint 注解端点类:
复制 @ Component
@ WebEndpoint (id = "notes" , enableByDefault = true )
public class NotesEndpoint {
...
}
同样,如果您喜欢只使用 MBean 的端点,请使用 @JmxEndpoint 注解。