Làm thế nào để xử lý trạng thái ban đầu trong một kiến ​​trúc hướng sự kiện?


33

Trong kiến trúc hướng sự kiện, mỗi thành phần chỉ hoạt động khi một sự kiện được gửi qua hệ thống.

Hãy tưởng tượng một chiếc xe giả thuyết với bàn đạp phanh và đèn phanh.

  • Phanh lượt ánh sáng trên khi nó nhận được một brake_on sự kiện, và tắt khi nó nhận được một brake_off sự kiện.
  • Bàn đạp phanh sẽ gửi một sự kiện phanh_on khi nó được nhấn xuống và một sự kiện phanh_off khi nó được phát hành.

Đây là tất cả tốt và tốt, cho đến khi bạn có tình huống xe được bật với bàn đạp phanh đã được nhấn xuống . Vì đèn phanh không bao giờ nhận được sự kiện phanh_on , nên nó sẽ tắt - rõ ràng là một tình huống không mong muốn. Bật đèn phanh theo mặc định chỉ đảo ngược tình huống.

Có thể làm gì để giải quyết 'vấn đề trạng thái ban đầu' này?

EDIT: Cảm ơn bạn cho tất cả các câu trả lời. Câu hỏi của tôi không phải là về một chiếc xe thực tế. Trong xe hơi, họ đã giải quyết vấn đề này bằng cách liên tục gửi trạng thái - do đó không có vấn đề khởi động trong miền đó. Trong miền phần mềm của tôi, giải pháp đó sẽ sử dụng nhiều chu kỳ CPU không cần thiết.

EDIT 2: Ngoài câu trả lời của @ gbjbaanb , tôi sẽ tìm một hệ thống trong đó:

  • bàn đạp phanh giả định, sau khi khởi tạo, sẽ gửi một sự kiện với trạng thái của nó
  • đèn phanh giả định, sau khi khởi tạo, sẽ gửi một sự kiện yêu cầu một sự kiện trạng thái từ bàn đạp phanh.

Với giải pháp này, không có sự phụ thuộc giữa các thành phần, không có điều kiện cuộc đua, không có hàng đợi tin nhắn để cũ và không có thành phần 'chính chủ'.


2
Điều đầu tiên xuất hiện trong đầu là tạo ra một sự kiện "tổng hợp" (gọi nó initialize) có chứa dữ liệu cảm biến cần thiết.
msw

Không nên đạp bàn đạp gửi một sự kiện phanh_pedal_on và phanh thực tế gửi sự kiện phanh_on? Tôi sẽ không muốn đèn phanh của mình bật sáng nếu phanh không hoạt động.
bdsl

3
Tôi đã đề cập đến nó là một ví dụ giả thuyết? :-) Nó được đơn giản hóa rất nhiều để giữ câu hỏi ngắn gọn và đi vào vấn đề.
Frank Kuster 12/2/2015

Câu trả lời:


32

Có nhiều cách để làm điều này, nhưng tôi thích giữ một hệ thống dựa trên thông điệp càng tách rời càng tốt. Điều này có nghĩa là hệ thống tổng thể không thể đọc trạng thái của bất kỳ thành phần nào, cũng như bất kỳ thành phần nào đọc trạng thái của bất kỳ thành phần nào khác (vì cách đó nằm trong quan hệ spaghetti của các phụ thuộc).

Vì vậy, trong khi hệ thống đang chạy sẽ tự chăm sóc, chúng ta cần một cách để bảo mỗi thành phần tự khởi động và chúng ta đã có một thứ như vậy trong đăng ký thành phần, tức là khi khởi động, hệ thống cốt lõi phải thông báo cho từng thành phần rằng nó là hiện đã đăng ký (hoặc sẽ yêu cầu mỗi thành phần trả về chi tiết của nó để có thể đăng ký). Đây là giai đoạn mà thành phần có thể thực hiện các tác vụ khởi động của nó và có thể gửi tin nhắn như nó sẽ làm trong hoạt động bình thường.

Vì vậy, bàn đạp phanh, khi đánh lửa được khởi động, sẽ nhận được thông báo đăng ký / kiểm tra từ ban quản lý xe và nó sẽ trả lại không chỉ tin nhắn "Tôi ở đây và đang làm việc", nhưng sau đó sẽ kiểm tra trạng thái của chính nó và gửi tin nhắn cho trạng thái đó (ví dụ như một tin nhắn chán nản bàn đạp).

Vấn đề sau đó trở thành một trong những phụ thuộc khởi động, vì nếu đèn phanh chưa được đăng ký thì nó sẽ không nhận được tin nhắn, nhưng điều này dễ dàng được giải quyết bằng cách xếp hàng tất cả các tin nhắn này cho đến khi hệ thống cốt lõi hoàn thành quy trình khởi động, đăng ký và kiểm tra .

Lợi ích lớn nhất là không có mã đặc biệt cần thiết để xử lý khởi tạo ngoại trừ việc bạn đã phải viết (ok, nếu việc gửi tin nhắn của bạn cho các sự kiện bàn đạp phanh nằm trong trình xử lý bàn đạp phanh, bạn cũng sẽ phải gọi nó trong quá trình khởi tạo , nhưng đó thường không phải là vấn đề trừ khi bạn viết mã đó gắn chặt với logic xử lý) và không có tương tác giữa các thành phần ngoại trừ các thành phần mà chúng đã gửi cho nhau như bình thường. Kiến trúc truyền thông điệp rất tốt vì điều này!


1
Tôi thích câu trả lời của bạn, vì nó giữ cho tất cả các thành phần tách rời - đó là lý do quan trọng nhất để chọn kiến ​​trúc này. Tuy nhiên, hiện tại không có thành phần 'chính' thực sự nào quyết định hệ thống ở trạng thái 'khởi tạo' - mọi thứ chỉ bắt đầu chạy. Với vấn đề trong câu hỏi của tôi là kết quả. Khi chủ quyết định hệ thống đang chạy, nó có thể gửi một sự kiện 'khởi tạo hệ thống' tới tất cả các thành phần, sau đó mọi thành phần bắt đầu phát trạng thái của nó. Vấn đề được giải quyết. Cảm ơn bạn! (Bây giờ tôi chỉ còn lại vấn đề làm thế nào để quyết định xem hệ thống có được khởi tạo không ...)
Frank Kuster

Làm thế nào về việc người điều phối cập nhật trạng thái theo dõi bản cập nhật mới nhất nhận được từ mỗi đối tượng và bất cứ khi nào nhận được yêu cầu đăng ký mới, họ có gửi cho người đăng ký mới những cập nhật gần đây nhất mà họ nhận được từ các nguồn sự kiện đã đăng ký không?
supercat

Trong trường hợp đó, bạn cũng phải theo dõi khi nào sự kiện hết hạn. Không phải tất cả các sự kiện đều có thể giữ được mãi mãi cho bất kỳ thành phần mới nào có thể đăng ký.
Frank Kuster

@spaceknarf tốt, trong trường hợp "mọi thứ chỉ bắt đầu chạy", bạn không thể xây dựng sự phụ thuộc vào các bộ phận để bàn đạp bắt đầu sau ánh sáng, bạn sẽ phải khởi động chúng theo thứ tự đó, mặc dù tôi tưởng tượng một cái gì đó bắt đầu chúng chạy, vì vậy hãy chạy chúng theo thứ tự 'phải' (ví dụ: tập lệnh khởi động linux trước systemd nơi dịch vụ bắt đầu trước được gọi là 1.xxx và thứ 2 được gọi là 2.xxx, v.v.).
gbjbaanb

Các kịch bản với một thứ tự như vậy là mong manh. Nó chứa rất nhiều phụ thuộc ngầm. Thay vào đó, tôi đã suy nghĩ nếu bạn có một thành phần 'chính', có một danh sách các thành phần được cấu hình tĩnh nên chạy (như được đề cập bởi @Lie Ryan), thì nó có thể phát sự kiện 'sẵn sàng' khi tất cả các thành phần đó được tải. Để đáp ứng điều đó, tất cả các thành phần phát sóng trạng thái ban đầu của họ.
Frank Kuster 12/2/2015

4

Bạn có thể có một sự kiện khởi tạo để thiết lập trạng thái phù hợp khi tải / khởi động. Điều này có thể được mong muốn đối với các hệ thống hoặc chương trình đơn giản không bao gồm nhiều phần cứng, tuy nhiên đối với các hệ thống phức tạp hơn có nhiều thành phần vật lý khi bạn gặp rủi ro giống như không khởi tạo được - nếu sự kiện "phanh trên" bị mất hoặc mất trong quá trình giao tiếp của bạn hệ thống (ví dụ: hệ thống dựa trên CAN) bạn có thể vô tình đặt hệ thống của mình về phía sau như thể bạn khởi động nó với phanh bị đè nén. Bạn càng có nhiều bộ điều khiển, chẳng hạn như với một chiếc ô tô, khả năng bị bỏ sót càng cao.

Để giải thích cho vấn đề này, bạn có thể liên tục gửi logic "phanh trên". Có lẽ cứ sau 1/100 giây hay gì đó. Mã của bạn có chứa bộ não có thể lắng nghe những sự kiện này và kích hoạt "phanh" trong khi nó đang nhận được chúng. Sau 1/10 giây không nhận được tín hiệu "phanh", nó sẽ kích hoạt sự kiện "phanh_off" bên trong.

Các sự kiện khác nhau sẽ có yêu cầu thời gian khác nhau đáng kể. Trong xe hơi, đèn phanh của bạn cần nhanh hơn nhiều so với đèn báo kiểm tra nhiên liệu của bạn (trong đó độ trễ nhiều giây có thể chấp nhận được) hoặc các hệ thống ít quan trọng khác.

Sự phức tạp của hệ thống vật lý của bạn sẽ quyết định phương pháp nào phù hợp hơn. Cho ví dụ của bạn là một chiếc xe, bạn có thể sẽ muốn một cái gì đó tương tự như sau này.

Dù bằng cách nào, với một hệ thống vật lý, bạn KHÔNG muốn dựa vào một sự kiện duy nhất được nhận / xử lý chính xác. Các bộ vi điều khiển được kết nối trên một hệ thống nối mạng thường có thời gian chờ "Tôi còn sống" vì lý do này.


trong một hệ thống vật lý, bạn sẽ chạy một dây và sử dụng logic nhị phân: CAO bị hãm phanh và THẤP là phanh không bị
đè nén

@ratchetfreak có rất nhiều khả năng cho loại điều này. Có lẽ một công tắc có thể xử lý đó. Có rất nhiều sự kiện hệ thống khác không được xử lý đơn giản.
enderland

1

Trong trường hợp này, tôi sẽ không mô hình phanh là bật / tắt đơn giản. Thay vào đó, tôi sẽ gửi các sự kiện "áp lực phanh". Ví dụ, áp suất bằng 0 sẽ biểu thị và áp suất 100 sẽ bị giảm hoàn toàn. Hệ thống (nút) sẽ liên tục gửi các sự kiện áp suất ngắt (tại một khoảng thời gian nhất định) đến (các) bộ điều khiển khi cần thiết.

Khi hệ thống được khởi động, nó sẽ bắt đầu nhận các sự kiện áp lực cho đến khi nó bị tắt.


1

Nếu phương tiện duy nhất của bạn để truyền thông tin trạng thái là thông qua các sự kiện, thì bạn đang gặp rắc rối. Thay vào đó, bạn cần có khả năng cả hai:

  1. truy vấn trạng thái hiện tại của bàn đạp phanh và
  2. đăng ký các sự kiện "thay đổi trạng thái" từ bàn đạp phanh.

Đèn phanh có thể được xem như một người quan sát bàn đạp phanh. Nói cách khác, bàn đạp phanh không biết gì về đèn phanh và có thể hoạt động mà không có nó. (Điều này có nghĩa là bất kỳ khái niệm nào về bàn đạp phanh chủ động gửi một sự kiện "trạng thái ban đầu" đến đèn phanh đều không được hình thành.)

Khi khởi động hệ thống, đèn phanh đăng ký với bàn đạp phanh để nhận thông báo phanh và cũng đọc trạng thái hiện tại của bàn đạp phanh và tự bật hoặc tắt.

Sau đó, các thông báo phanh có thể được thực hiện theo một trong ba cách sau:

  1. như các sự kiện "không thay đổi trạng thái bàn đạp phanh"
  2. như một cặp "sự kiện bàn đạp phanh hiện đang chán nản" và sự kiện "bàn đạp phanh hiện được phát hành"
  3. như một sự kiện "trạng thái bàn đạp mới" với thông số "chán nản" hoặc "được giải phóng".

Tôi thích cách tiếp cận đầu tiên, có nghĩa là khi nhận được thông báo, đèn phanh sẽ chỉ đơn giản là làm những gì nó đã biết: đọc trạng thái hiện tại của bàn đạp phanh và tự bật hoặc tắt.


0

Trong một hệ thống hướng sự kiện (mà tôi hiện đang sử dụng và yêu thích), tôi thấy điều quan trọng là phải giữ mọi thứ tách rời nhất có thể. Vì vậy, với ý tưởng đó trong đầu, hãy đi sâu vào.

Điều quan trọng là có một số trạng thái mặc định. Đèn phanh của bạn sẽ ở trạng thái 'tắt' mặc định và bàn đạp phanh của bạn sẽ ở trạng thái mặc định là 'lên'. Bất kỳ thay đổi sau đó sẽ là một sự kiện.

Bây giờ để giải quyết câu hỏi của bạn. Hãy tưởng tượng bàn đạp phanh của bạn được khởi tạo và nhấn xuống, sự kiện sẽ kích hoạt, nhưng vẫn chưa có đèn phanh để nhận sự kiện. Tôi đã thấy dễ dàng nhất để phân tách việc tạo các đối tượng (nơi người nghe sự kiện sẽ được khởi tạo) như một bước riêng biệt trước khi khởi tạo bất kỳ logic nào. Điều đó sẽ ngăn chặn mọi điều kiện cuộc đua như bạn đã mô tả.

Tôi cũng thấy khó xử khi sử dụng hai sự kiện khác nhau cho những gì thực sự giống nhau . brake_offbrake_oncó thể được đơn giản hóa e_brakevới một tham số bool on. Bạn có thể đơn giản hóa các sự kiện của mình theo cách này bằng cách thêm dữ liệu hỗ trợ.


0

Những gì bạn cần là một sự kiện phát sóng và hộp thư đến. Một chương trình phát sóng là một thông điệp được công bố cho một số lượng người nghe không xác định. Một thành phần có thể đăng ký các sự kiện phát sóng để nó chỉ nhận các sự kiện mà nó quan tâm. Điều này cung cấp sự tách rời, vì người gửi không cần biết ai là người nhận. Bảng đăng ký cần được cấu hình tĩnh trong quá trình cài đặt thành phần (thay vì khi khởi tạo). Hộp thư đến là một phần của bộ định tuyến thư hoạt động như một bộ đệm để giữ thư khi thành phần đích ngoại tuyến.

Sử dụng hóa đơn mang đến một vấn đề, đó là kích thước hộp thư đến. Bạn không muốn hệ thống phải giữ lại số lượng tin nhắn ngày càng tăng cho các thành phần sẽ không bao giờ trực tuyến nữa. Điều này đặc biệt quan trọng với hệ thống nhúng với các ràng buộc bộ nhớ nghiêm ngặt. Để vượt qua giới hạn kích thước hộp thư đến, tất cả các tin nhắn được phát cần tuân theo một vài quy tắc. Các quy tắc là:

  1. mỗi sự kiện phát sóng đều cần có tên
  2. tại bất kỳ thời điểm nào, người gửi sự kiện phát sóng chỉ có thể có một chương trình phát hoạt động với một tên được chỉ định
  3. hiệu ứng gây ra bởi sự kiện này phải bình thường

Tên phát sóng cần được khai báo trong thời gian cài đặt thành phần. Nếu một thành phần gửi một lần phát thứ hai có cùng tên trước khi người nhận xử lý phần phát trước đó, thì phần phát sóng mới sẽ ghi đè lên phần trước đó. Bây giờ bạn có thể có giới hạn kích thước hộp thư đến tĩnh, có thể được đảm bảo không bao giờ vượt quá một kích thước nhất định và có thể được tính toán trước dựa trên các bảng đăng ký.

Cuối cùng, bạn cũng cần một kho lưu trữ phát sóng. Kho lưu trữ phát sóng là một bảng chứa sự kiện cuối cùng từ mỗi tên phát sóng. Các thành phần mới vừa được cài đặt sẽ có hộp thư đến được cài sẵn các thông báo từ kho lưu trữ quảng bá. Giống như hộp thư đến, kho lưu trữ quảng bá cũng có thể có kích thước tĩnh.

Ngoài ra, để đối phó với tình huống bộ định tuyến thư ngoại tuyến, bạn cũng cần có hộp thư đi. Hộp thư đi là một phần của thành phần giữ tin nhắn gửi đi tạm thời.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.