Chào bạn,
Trong bài học trước, chúng ta đã khám phá Message Events như một phương tiện giao tiếp một-một (one-to-one) mạnh mẽ giữa các quy trình, tương tự như việc gửi một email trực tiếp đến một người nhận cụ thể. Bạn đã học cách sử dụng Message Correlation để đảm bảo tin nhắn được chuyển đến đúng process instance đang chờ đợi.
Tuy nhiên, trong nhiều kịch bản thực tế, đặc biệt là trong kiến trúc microservices, chúng ta cần một cơ chế thông báo rộng hơn. Điều gì sẽ xảy ra nếu bạn muốn gửi một thông báo đến tất cả các quy trình đang hoạt động cùng một lúc, giống như một thông báo khẩn cấp trên loa phát thanh? Đó chính là lúc Signal Events phát huy tác dụng.
Mục tiêu của bài học hôm nay là implement Signal Events cho giao tiếp broadcast (một-nhiều). Bạn sẽ học cách mô hình hóa và triển khai một cơ chế giao tiếp mạnh mẽ, cho phép một sự kiện có thể ảnh hưởng đến nhiều quy trình nghiệp vụ đồng thời.
1. Sự khác biệt cốt lõi: Signal (Broadcast) và Message (Direct)
Điểm khác biệt cơ bản và quan trọng nhất giữa Signal Event và Message Event nằm ở cách chúng phân phối thông điệp:
- Message Event (Tin nhắn trực tiếp): Giao tiếp một-một. Một tin nhắn được gửi đi và phải được tương quan (correlate) để đến chính xác một người nhận đang chờ. Nếu không tìm thấy người nhận hoặc tìm thấy nhiều hơn một, hệ thống sẽ báo lỗi.
- Signal Event (Tín hiệu quảng bá): Giao tiếp một-nhiều (broadcast). Một tín hiệu được "phát" đi trong toàn bộ engine. Bất kỳ process instance nào đang lắng nghe tín hiệu đó đều sẽ nhận được. Nó có thể có từ 0 đến N người nhận. Nếu không có ai lắng nghe, tín hiệu vẫn được coi là gửi thành công.
Với nền tảng là một developer, bạn có thể hình dung:
- Message Event tương tự như một lời gọi
RPChoặc một requestHTTPđến một endpoint cụ thể. - Signal Event tương tự như mô hình publish-subscribe (pub/sub). Một process "publish" một tín hiệu lên một "topic" (chính là tên của signal), và nhiều process khác "subscribe" vào topic đó để nhận thông báo.
Để hiểu rõ hơn về sự khác biệt và các trường hợp sử dụng, hãy xem đoạn video sau.
BPMN Intermediate Events and How to Use Them In Camunda
Video này từ kênh Camunda sẽ giải thích rõ ràng bản chất của Signal Event như một cơ chế broadcast và đưa ra các ví dụ thực tế rất hữu ích.
Hãy xem từ 16:40 đến 19:45. Chú ý các điểm chính: Signal Event là một broadcast (0 đến N người nhận) và không có các ràng buộc chặt chẽ như Message Event. Trường hợp sử dụng Start Event: Kích hoạt đồng loạt nhiều quy trình báo cáo sau khi xử lý dữ liệu xong. Trường hợp sử dụng Intermediate Event: Gửi tín hiệu cập nhật đến tất cả các process instance đang chạy liên quan đến cùng một khách hàng.
2. Các loại Signal Event và cách hoạt động
Tương tự như các loại event khác, Signal Event có thể được dùng để ném (throwing) hoặc bắt (catching) một tín hiệu tại các điểm khác nhau trong quy trình.

Các loại chính bao gồm:
- Signal Start Event (Catching): Bắt đầu một process instance mới mỗi khi một tín hiệu cụ thể được phát đi. Bạn có thể có nhiều process definition cùng bắt đầu bằng một tín hiệu.
- Intermediate Signal Catch Event (Catching): Khi một quy trình đến điểm này, nó sẽ dừng lại và chờ cho đến khi nhận được tín hiệu.
- Signal Boundary Event (Catching): Gắn vào một activity (ví dụ một User Task). Nó sẽ lắng nghe một tín hiệu trong khi activity đó đang hoạt động. Nó có thể là
interrupting(hủy activity và đi theo luồng mới) hoặcnon-interrupting(thực hiện một hành động song song mà không ảnh hưởng đến activity chính). - Intermediate Signal Throw Event (Throwing): Phát đi một tín hiệu đến toàn bộ engine trong khi quy trình đang chạy.
- Signal End Event (Throwing): Phát đi một tín hiệu khi quy trình kết thúc tại điểm đó.
Để có một cái nhìn tổng quan về các loại sự kiện này và cách chúng được định nghĩa, bạn có thể tham khảo tài liệu sau.
Signal Events - EximeeBPMS documentation
Tài liệu này (từ Eximee, một nền tảng tương thích với Camunda) cung cấp một bản tóm tắt kỹ thuật chi tiết về các loại Signal Event và cách chúng hoạt động.
Hãy lướt qua các phần sau để nắm được các khái niệm: Giới thiệu chung (phần đầu tiên). Signal Start Event. Signal Intermediate Catching Event. Signal Boundary Event. Signal Intermediate Throwing Event. Signal End Event. Bạn không cần đọc chi tiết phần mã XML, chỉ cần hiểu chức năng của từng loại.
3. Thực hành: Broadcast thông báo thay đổi lãi suất
Bây giờ chúng ta sẽ đi vào phần quan trọng nhất: thực hành.
Kịch bản:
Một ngân hàng có nhiều hồ sơ vay vốn đang trong giai đoạn "Thẩm định". Đột nhiên, ngân hàng trung ương thay đổi lãi suất cơ bản. Chúng ta cần một cơ chế để phát đi tín hiệu rate-changed đến tất cả các hồ sơ đang thẩm định, buộc chúng phải cập nhật lại thông tin lãi suất mới và được xem xét lại.
Bước 1: Mô hình hóa Quy trình "Xét duyệt hồ sơ vay"
-
Trong Camunda Modeler, tạo một quy trình mới và thiết kế như sau:
- Start Event.
- User Task:
Nhập thông tin hồ sơ. - User Task:
Thẩm định hồ sơ. - Service Task:
Cập nhật lãi suất. - End Event:
Hoàn tất thẩm định.
-
Thêm Signal Boundary Event:
- Từ thanh công cụ, chọn biểu tượng
Boundary Event(vòng tròn nét đứt) và kéo thả nó lên trên taskThẩm định hồ sơ. - Click chuột phải vào Boundary Event, chọn
Change Type->Signal Boundary Event. Đảm bảo nó là loại Interrupting (nét liền). - Đặt tên cho sự kiện này là
Nhận tín hiệu thay đổi lãi suất. - Nối
Signal Boundary Eventđến Service TaskCập nhật lãi suất. - Nối
Cập nhật lãi suấtquay trở lại taskThẩm định hồ sơ. - Nối
Thẩm định hồ sơđếnEnd Eventcho luồng xử lý thông thường.
- Từ thanh công cụ, chọn biểu tượng
-
Cấu hình các phần tử:
- Chọn Signal Boundary Event.
- Trong Properties Panel, tab
General, mụcEvent:- Signal Name: Nhập
rate-changed. Đây là tên tín hiệu mà sự kiện sẽ lắng nghe.
- Signal Name: Nhập
- Chọn Service Task
Cập nhật lãi suất. - Trong Properties Panel, tab
General:Implementation:Java ClassJava Class:com.example.camunda.delegates.UpdateRateDelegate
Quy trình của bạn sẽ trông giống như sau:
Sơ đồ quy trình xét duyệt hồ sơ vay. Signal Boundary Event được gắn vào task "Thẩm định hồ sơ" để bắt tín hiệu thay đổi lãi suất.
Bước 2: Viết mã Java Delegate và truyền biến
Khi phát tín hiệu, chúng ta thường muốn gửi kèm dữ liệu. Trong trường hợp này là lãi suất mới.
Signal Events - EximeeBPMS documentation
Để biết cách truyền dữ liệu khi phát tín hiệu, hãy đọc phần "Passing Variables" trong tài liệu này. Nó giải thích cách sử dụng camunda:in để ánh xạ biến từ nơi phát tín hiệu đến nơi nhận tín hiệu.
Đọc phần 'Passing Variables'. Chú ý đến cách sử dụng camunda:in và thuộc tính sourceExpression hoặc source để truyền biến.
Bây giờ, hãy tạo lớp UpdateRateDelegate. Delegate này sẽ lấy biến newRate được gửi kèm theo tín hiệu và cập nhật vào biến của quy trình.
package com.example.camunda.delegates;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component("UpdateRateDelegate")
public class UpdateRateDelegate implements JavaDelegate {
private final Logger LOGGER = LoggerFactory.getLogger(UpdateRateDelegate.class);
@Override
public void execute(DelegateExecution execution) throws Exception {
// Biến newRate được truyền vào từ payload của tín hiệu.
Double newRate = (Double) execution.getVariable("newRate");
String processInstanceId = execution.getProcessInstanceId();
LOGGER.info(">>> [{}]: Received rate change signal. Updating rate to: {}", processInstanceId, newRate);
// Cập nhật biến lãi suất của quy trình
execution.setVariable("interestRate", newRate);
}
}
Bước 3: Deploy và Kiểm thử bằng REST API
Đây là lúc chúng ta thấy sức mạnh của broadcast.
-
Deploy quy trình và khởi chạy ứng dụng Spring Boot.
-
Tạo nhiều process instance:
- Vào Tasklist, bắt đầu 3 process instance của quy trình "Xét duyệt hồ sơ vay".
- Hoàn thành task
Nhập thông tin hồ sơcho cả 3 instance. - Bây giờ, trong Cockpit, bạn sẽ thấy 3 process instance đang cùng hoạt động và dừng tại task
Thẩm định hồ sơ.
-
Phát tín hiệu bằng Postman:
- Mở Postman và tạo một request mới để phát tín hiệu
rate-changed. - Method:
POST - URL:
http://localhost:8080/rest/signal - Headers:
Content-Type:application/json - Body (chọn
rawvàJSON):
{ "name": "rate-changed", "variables": { "newRate" : { "value" : 4.5, "type": "Double" } } } - Mở Postman và tạo một request mới để phát tín hiệu

- Nhấn Send.
Quan sát kết quả:
- Quay lại Cockpit và refresh. Bạn sẽ thấy một điều kỳ diệu: Cả 3 process instance đã bị ngắt khỏi task
Thẩm định hồ sơ, chạy quaCập nhật lãi suấtvà quay trở lạiThẩm định hồ sơ. - Kiểm tra console log của ứng dụng, bạn sẽ thấy 3 dòng log từ
UpdateRateDelegate, mỗi dòng cho một process instance. - Vào tab
Variablescủa một trong các instance, bạn sẽ thấy biếninterestRateđã được tạo hoặc cập nhật thành4.5.
Thí nghiệm này cho thấy rõ cách một lời gọi API duy nhất đã ảnh hưởng đồng thời đến tất cả các quy trình đang lắng nghe, minh họa hoàn hảo cho tính năng broadcast.
Tổng kết
Trong bài học này, bạn đã nắm vững một công cụ giao tiếp quan trọng khác trong BPMN, bổ sung cho Message Events mà bạn đã học trước đó.
Những điểm chính cần ghi nhớ:
- Signal Events được sử dụng cho giao tiếp một-nhiều (broadcast), trái ngược với
Message Eventslà một-một. - Signal không yêu cầu
correlation, nó được phát đi toàn cục trong engine. Bất kỳ quy trình nào đăng ký lắng nghe tín hiệu có tên tương ứng đều sẽ nhận được. - Các loại Signal Event (Start, Intermediate, Boundary) cho phép bạn bắt tín hiệu tại các giai đoạn khác nhau của quy trình, mang lại sự linh hoạt cao.
- Bạn có thể phát tín hiệu và truyền dữ liệu kèm theo thông qua Camunda REST API, một kỹ năng quan trọng để tích hợp Camunda với các hệ thống bên ngoài.
Xem trước bài học tiếp theo
Chúng ta đã tìm hiểu cách xử lý các sự kiện nghiệp vụ như Timer, Message, và Signal. Nhưng trong quá trình thực thi, đặc biệt là khi gọi các hệ thống bên ngoài qua Service Task, các lỗi kỹ thuật (technical exceptions) có thể xảy ra. Trong bài học tiếp theo, chúng ta sẽ học cách sử dụng Error Boundary Event để bắt và xử lý các exception này một cách có kiểm soát, giúp quy trình của bạn trở nên mạnh mẽ và đáng tin cậy hơn.