Skip to content

15.3.4 Tạo các endpoint tùy chỉnh

Thoạt nhìn, bạn có thể nghĩ rằng các endpoint của Actuator chỉ đơn giản được triển khai như các controller của Spring MVC. Nhưng như bạn sẽ thấy trong chương 17, các endpoint cũng được hiển thị dưới dạng JMX MBeans cũng như thông qua các yêu cầu HTTP. Do đó, các endpoint này chắc chắn phải có điều gì đó hơn là chỉ là một lớp controller.

Thực tế, các endpoint của Actuator được định nghĩa khá khác so với các controller. Thay vì một lớp được chú thích với @Controller hoặc @RestController, các endpoint của Actuator được định nghĩa với các lớp được chú thích bằng @Endpoint.

Hơn nữa, thay vì sử dụng các annotation gắn với HTTP như @GetMapping, @PostMapping hay @DeleteMapping, các thao tác trên endpoint của Actuator được định nghĩa bởi các phương thức được chú thích với @ReadOperation, @WriteOperation@DeleteOperation. Những annotation này không ám chỉ bất kỳ cơ chế truyền thông cụ thể nào và trên thực tế, cho phép Actuator giao tiếp bằng nhiều cơ chế truyền thông khác nhau, bao gồm cả HTTP và JMX ngay từ đầu. Để minh họa cách viết một endpoint Actuator tùy chỉnh, hãy xem NotesEndpoint trong đoạn mã sau.

Listing 15.7 Một endpoint tùy chỉnh để ghi chú

java
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;
  }
}

Endpoint này là một endpoint đơn giản cho việc ghi chú, nơi người dùng có thể gửi một ghi chú với thao tác ghi (write), đọc danh sách ghi chú với thao tác đọc (read), và xóa ghi chú với thao tác xóa (delete). Phải thừa nhận rằng endpoint này không quá hữu ích nếu so với các endpoint có sẵn của Actuator. Nhưng khi bạn nhận ra rằng các endpoint mặc định của Actuator đã bao phủ rất nhiều chức năng, thì việc nghĩ ra một ví dụ thực tế cho một endpoint tùy chỉnh là điều không đơn giản.

Dù sao đi nữa, lớp NotesEndpoint được chú thích với @Component để Spring có thể tự động phát hiện thông qua quá trình quét component và khởi tạo như một bean trong context của ứng dụng Spring. Nhưng điều quan trọng hơn trong chủ đề này là nó cũng được chú thích với @Endpoint, biến nó thành một endpoint Actuator với ID là notes. Endpoint này được kích hoạt mặc định nên bạn không cần phải bật nó thủ công bằng cách thêm vào thuộc tính cấu hình management.web.endpoints.web.exposure.include.

Như bạn thấy, NotesEndpoint cung cấp mỗi loại thao tác một lần:

  • Phương thức notes() được chú thích với @ReadOperation. Khi được gọi, nó sẽ trả về danh sách các ghi chú hiện có. Theo nghĩa HTTP, điều này có nghĩa là nó sẽ xử lý một yêu cầu HTTP GET đến /actuator/notes và phản hồi bằng một danh sách ghi chú ở dạng JSON.

  • Phương thức addNote() được chú thích với @WriteOperation. Khi được gọi, nó sẽ tạo một ghi chú mới từ đoạn văn bản được cung cấp và thêm nó vào danh sách. Theo HTTP, nó xử lý một yêu cầu POST với phần thân yêu cầu là một đối tượng JSON chứa thuộc tính text. Sau đó, nó phản hồi với trạng thái hiện tại của danh sách ghi chú.

  • Phương thức deleteNote() được chú thích với @DeleteOperation. Khi được gọi, nó sẽ xóa ghi chú tại vị trí chỉ định. Theo HTTP, endpoint này xử lý các yêu cầu DELETE trong đó chỉ số (index) được truyền dưới dạng tham số yêu cầu.

Để thấy điều này hoạt động, bạn có thể sử dụng curl để thử nghiệm endpoint mới này. Trước tiên, thêm vài ghi chú bằng cách gửi hai yêu cầu POST riêng biệt như sau:

bash
$ curl localhost:8080/actuator/notes \
            -d'{"text":"Bring home milk"}' \
            -H"Content-type: application/json"
[{"time":"2020-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":"2021-07-03T12:39:13.058+0000","text":"Bring home milk"},
{"time":"2021-07-03T12:39:16.012+0000","text":"Take dry cleaning"}]

Như bạn thấy, mỗi khi một ghi chú mới được gửi lên, endpoint sẽ phản hồi bằng danh sách ghi chú vừa được cập nhật. Nếu sau đó bạn muốn xem danh sách các ghi chú, bạn có thể thực hiện một yêu cầu GET đơn giản như sau:

bash
$ curl localhost:8080/actuator/notes
[{"time":"2021-07-03T12:39:13.058+0000","text":"Bring home milk"},
{"time":"2021-07-03T12:39:16.012+0000","text":"Take dry cleaning"}]
`

Nếu bạn muốn xóa một trong các ghi chú, một yêu cầu DELETE với tham số index, như bên dưới, sẽ thực hiện điều đó:

bash
$ curl localhost:8080/actuator/notes?index=1 -X DELETE
[{"time":"2021-07-03T12:39:13.058+0000","text":"Bring home milk"}]

Điều quan trọng cần lưu ý là mặc dù ở đây tôi chỉ trình bày cách tương tác với endpoint bằng HTTP, nhưng nó cũng sẽ được hiển thị như một MBean và có thể được truy cập bằng bất kỳ client JMX nào bạn chọn. Nhưng nếu bạn muốn giới hạn nó chỉ hiển thị như một endpoint HTTP, bạn có thể chú thích lớp endpoint với @WebEndpoint thay vì @Endpoint như sau:

java
@Component
@WebEndpoint(id="notes", enableByDefault=true)
public class NotesEndpoint {
  ...
}

Tương tự, nếu bạn muốn một endpoint chỉ dành riêng cho JMX, hãy chú thích lớp với @JmxEndpoint.

Released under the MIT License.