Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 40

Giao tiếp Broadcast với Signal Events

Recommended resources

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 RPC hoặc một request HTTP đế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.

Camunda BPMN Signal Event Configuration
Ví dụ về việc cấu hình một Intermediate Throwing Signal Event trong Camunda Modeler. Tên của signal ('rateChange') đóng vai trò như tên của "kênh" mà tín hiệu sẽ được phát đi.

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ặc non-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"

  1. 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.
  2. 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 task Thẩ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 Task Cập nhật lãi suất.
    • Nối Cập nhật lãi suất quay trở lại task Thẩm định hồ sơ.
    • Nối Thẩm định hồ sơ đến End Event cho luồng xử lý thông thường.
  3. Cấu hình các phần tử:

    • Chọn Signal Boundary Event.
    • Trong Properties Panel, tab General, mục Event:
      • Signal Name: Nhập rate-changed. Đây là tên tín hiệu mà sự kiện sẽ lắng nghe.
    • Chọn Service Task Cập nhật lãi suất.
    • Trong Properties Panel, tab General:
      • Implementation: Java Class
      • Java 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.

  1. Deploy quy trình và khởi chạy ứng dụng Spring Boot.

  2. 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ơ.
  3. 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 raw và JSON):
    {
        "name": "rate-changed",
        "variables": {
            "newRate" : {
                "value" : 4.5,
                "type": "Double"
            }
        }
    }
    
Sending a Camunda Signal Event via Postman
Đây là ví dụ cấu trúc request body trong Postman để phát một tín hiệu. Chúng ta chỉ cần chỉ định tên tín hiệu (`name`) và các biến (`variables`) muốn gửi kèm.
  1. Nhấn Send.

Quan sát kết quả:

  1. 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 qua Cập nhật lãi suất và quay trở lại Thẩm định hồ sơ.
  2. 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.
  3. Vào tab Variables của một trong các instance, bạn sẽ thấy biến interestRate đã được tạo hoặc cập nhật thành 4.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 Events là 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.