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.

Và đó cũng là cái bẫy. Bên nhận cầm chính object của bên gửi. Nếu nó sửa một trường, bên gửi thấy thay đổi đó — hai verticle chạy trên hai event loop khác nhau vừa chia sẻ trạng thái, đúng thứ mà mô hình nhắn tin sinh ra để tránh. Mọi phép suy luận "verticle của tôi chỉ một luồng chạm vào" sụp đổ tại đây.

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.