Phần trước gửi toàn chuỗi. Ứng dụng thật gửi đối tượng, và event bus của Vert.x không tự biết cách đóng gói chúng.
Không có codec thì không gửi được
eb.send("don", new DonHang("DH-1", 250000, "..."));
IllegalArgumentException: No message codec for type: class lab.Codec$DonHang
Ném ngay, đồng bộ, tại chỗ gửi. Đây là một trong số ít lỗi của Vert.x nói thẳng vấn đề là gì — tận dụng điều đó và đừng vội tìm cách đi vòng.
Ba cách đi tiếp, và chúng khác nhau nhiều hơn vẻ ngoài.
Cách 1: chuyển sang JsonObject
eb.send("don", JsonObject.mapFrom(don));
Không cần khai gì. Đây là cách gần như ai cũng chọn đầu tiên, và với phần lớn ứng dụng thì nó đủ tốt.
Cách 2: codec nhị phân tự viết
class NhiPhan implements MessageCodec<DonHang, DonHang> {
public void encodeToWire(Buffer b, DonHang d) {
b.appendInt(d.ma().length()).appendString(d.ma());
b.appendInt(d.tien());
...
}
public DonHang decodeFromWire(int pos, Buffer b) { ... }
public DonHang transform(DonHang d) { return d; }
public String name() { return "nhi-phan"; }
public byte systemCodecID() { return -1; }
}
eb.registerDefaultCodec(DonHang.class, new NhiPhan());
Đo trên một bản ghi mẫu:
| Kích thước trên dây | Thông lượng (giao đủ 200 000) | |
|---|---|---|
JsonObject |
86 byte | 2 749 569 /giây |
| Codec nhị phân | 65 byte (76%) | 3 429 083 /giây |
Gọn hơn 24%, nhanh hơn 25%. Đáng làm không? Với hầu hết hệ thống thì không — cả hai đều ở mức hàng triệu thông điệp mỗi giây, cao hơn nhiều lần thông lượng HTTP mà phần 16 đo được. Bạn đang tối ưu thứ không phải nút thắt.
Codec tự viết đáng làm khi thông điệp lớn hoặc rất nhiều, hoặc khi bạn cần một định dạng cố định để nói chuyện với hệ thống khác.
Cách 3: codec cục bộ, và điều nó thật sự làm
class CucBo implements MessageCodec<DonHang, DonHang> {
public void encodeToWire(Buffer b, DonHang d) { throw new UnsupportedOperationException(); }
public DonHang decodeFromWire(int pos, Buffer b) { throw new UnsupportedOperationException(); }
public DonHang transform(DonHang d) { return d; } // tra ve CHINH doi tuong do
...
}
bên nhận có đúng ĐỐI TƯỢNG đó không: CÓ (cùng tham chiếu)
Không tuần tự hoá, không sao chép, không cấp phát. transform là phương thức Vert.x gọi khi người gửi và người nhận ở cùng JVM, và trả về chính đối tượng đó nghĩa là thông điệp đi qua bus với chi phí bằng một lời gọi hàm.
Dùng codec cục bộ thì object phải bất biến. Với record của Java thì việc đó gần như miễn phí — và đó là lý do mọi ví dụ trong bài này dùng record.
Thêm một điểm nữa: encodeToWire ném ngoại lệ nghĩa là thông điệp không đi qua mạng được. Ngày ai đó bật cụm (bài 21), mọi thông điệp phải sang node khác sẽ hỏng — và nó hỏng lúc chạy, không phải lúc biên dịch.
Chọn cách nào
| Tình huống | Cách |
|---|---|
| Bình thường, chưa đo thấy vấn đề | JsonObject |
| Nhiều verticle trong cùng JVM, object bất biến | Codec cục bộ |
| Thông điệp lớn, hoặc chạy trên cụm, hoặc cần định dạng cố định | Codec nhị phân |
| Cần nói chuyện với hệ thống viết bằng ngôn ngữ khác | JsonObject — hoặc một định dạng chuẩn |
Dòng cuối đáng nhấn: sê-ri RabbitMQ đo được rằng gắn tên class Java vào thông điệp trói hai dịch vụ vào nhau. Codec tự viết ở đây có cùng rủi ro nếu bạn coi nó là hợp đồng giữa các dịch vụ — hãy giữ nó cho giao tiếp bên trong một ứng dụng.
Bài sau: so event bus với gọi HTTP nội bộ, bằng cùng một phép đo.
Thử ba mươi giây
// kiem xem codec cua ban co thuc su khong sao chep khong
eb.consumer("thu", (Message<DonHang> m) ->
System.out.println("cung tham chieu: " + (m.body() == donGoc)));
Ra true nghĩa là bạn đang chia sẻ object giữa hai verticle. Chấp nhận được nếu nó bất biến — và là một lỗi đang chờ xảy ra nếu không.