Phần 19 đo event bus trong cùng tiến trình và kết bằng một lời hứa: con số 11 micro giây sẽ đổi thế nào khi thông điệp phải đi qua mạng. Bài này trả lời, và câu trả lời đủ để đổi cách bạn thiết kế.

Dựng cụm

ClusterManager cm = new HazelcastClusterManager();
Vertx vertx = Vertx.builder().withClusterManager(cm).buildClustered()
        .toCompletionStage().toCompletableFuture().get();

Không có gì khác trong mã ứng dụng. Cùng eb.request(...), cùng địa chỉ — chỉ khác là consumer có thể đang ở tiến trình khác.

[tho] gia nhập cụm sau 2,9 s | số node = 1
[do]  gia nhập cụm sau 0,8 s | số node = 2

Node đầu tiên mất gần ba giây để dựng cụm; node thứ hai vào cụm có sẵn chỉ mất 0,8 giây. Đây là thời gian cộng thêm vào khởi động ứng dụngphần 2 đo Vert.x thường khởi động trong 0,18 giây, nên bật cụm làm nó chậm đi khoảng mười lần.

Đo lại

Cùng JVM Qua cụm HTTP loopback
Độ trễ p50 11,3 µs 66,3 µs 56,5 µs
p95 25,5 µs 134,7 µs 121,4 µs
p99 44,3 µs 202,8 µs 176,3 µs
Thông lượng 1 096 025/s 199 259/s 95 838/s
Nhìn hai cột phải. Event bus qua cụm chậm hơn HTTP trên loopback về độ trễ — 66,3 so với 56,5 µs. Lợi thế tốc độ mà phần 19 đo được đến hoàn toàn từ việc không đi qua mạng; một khi đã qua mạng thì nó biến mất.

Thông lượng vẫn gấp đôi HTTP (199 nghìn so với 96 nghìn), vì event bus dùng một kết nối TCP dùng chung và khung dữ liệu gọn hơn. Nhưng đây không còn là khác biệt hạng nặng — đây là cùng một hạng.

Kết luận thực dụng vẫn như phần 19, chỉ chắc chắn hơn: chọn event bus vì kiến trúc, đừng chọn vì tốc độ.

send không ưu tiên node của bạn

Đây là phần tôi nghĩ nhiều người đoán sai. Đăng ký consumer cho cùng một địa chỉ ở cả hai node, rồi gửi 2 000 request từ node thứ hai:

2 000 request -> consumer CÙNG node: 1000 | consumer node KHÁC: 1000

Chia đôi tuyệt đối. Vert.x không ưu tiên consumer cục bộ — nó chia vòng tròn trên toàn cụm như thể mọi consumer đều ở cùng khoảng cách.

Nghĩa là trong một cụm ba node, hai phần ba lời gọi của bạn đi qua mạng dù ngay trong tiến trình đã có sẵn người xử lý. Với bảng ở trên, đó là chênh lệch giữa 11 µs và 66 µs cho hai phần ba lưu lượng.

Nếu bạn muốn ưu tiên cục bộ, phải nói rõ:

eb.request("dv", than, new DeliveryOptions().setLocalOnly(true));

Nhưng cân nhắc kỹ: setLocalOnly cũng có nghĩa là không có cân bằng tải toàn cụm nữa, và nếu node đó không có consumer thì bạn nhận NO_HANDLERS như phần 20 đo được, thay vì được phục vụ bởi node khác.

Cụm cho bạn gì để đổi

Cân bằng tải không cần cấu hình. Thêm một node là nó tự nhận phần việc của mình, không cần đăng ký dịch vụ, không cần bộ cân bằng tải.

Cùng một API. Mã không đổi một dòng giữa chạy một node và chạy mười node.

Và một phụ thuộc mới. Hazelcast là một hệ thống phân tán đầy đủ chạy bên trong ứng dụng của bạn: nó có bầu cử, có phân vùng mạng, có trạng thái riêng. Bài 42 của sê-ri sẽ đo cụm ba node và chuyện gì xảy ra khi mất một node — và đó là chỗ cái giá thật của việc bật cụm hiện ra.

Khi nào nên bật

Khi bạn thật sự cần nhiều node nói chuyện với nhau qua event bus. Nếu các bản sao của bạn độc lập — mỗi cái phục vụ request riêng, chia sẻ trạng thái qua CSDL — thì cụm chỉ thêm phụ thuộc và thêm ba giây khởi động.

Đừng bật cụm chỉ để "sẵn sàng mở rộng". Bảng ở trên là cái giá bạn trả ngay, còn lợi ích chỉ đến khi thật sự có nhiều node cần trao đổi.

Bài sau: mở event bus ra trình duyệt qua SockJS, và một dòng cấu hình biến nó thành cửa sau.

Thử ba mươi giây

System.out.println("so node trong cum: " + clusterManager.getNodes().size());
vertx.eventBus().request("dv", "x", new DeliveryOptions().setLocalOnly(true))
     .onFailure(t -> System.out.println("khong co consumer cuc bo: " + t.getMessage()));

Nếu lời gọi setLocalOnly thất bại trong khi lời gọi thường vẫn chạy, mọi request của bạn đang đi qua mạng — và bảng trên cho biết cái giá.