Giới thiệu về Celluloid - Part 3
Bài đăng này đã không được cập nhật trong 9 năm
Hôm qua mình mới download Kobito về, đang muốn thử xem ra sao nên tiện tay làm một bài viết vậy (yaoming).. tiếp chủ đề Celluloid nhé (honho)
Trong phần 2, mình đã giới thiệu với các bạn về Celluloid::Supervisor
để monitor từng actors và restart những actors bị crash. Đột nhiên mình lại nhớ đến Shenron (rồng thần Namek) với năng lực hồi sinh các thanh niên thỉnh thoảng lăn ra chết như Krilin, Yamcha, Goku v.vv.. vậy. Tuyệt vời hơn ở chỗ Shenron lần nào cũng phải gọi mới ra làm việc, còn Supervisor chỉ cần gọi một lần là đủ (haha).. Nhưng ngoài Supervisor ra thì các actors bình thường sao có thể nhận biết được có một actor khác đã bị crash? Chúng ta sẽ thử tìm hiểu nhé..
Linking
Chắc ai là fan của Dragon Ball cũng nhận ra thứ tự sức mạnh của các nhân vật trong truyện như sau: Goku > boss (max level) > Vegeta > boss (low level) > nhân vật khác.. Không biết đã bao giờ Vegeta cảm thấy vui chưa, khi cứ củ hành được boss 1 chút là y như rằng bị vả ngược lại sau khi boss được nâng cấp ..
class Vegeta
include Celluloid
class FightingBossError < StandardError; end
def fight_against_boss_max_level
raise FightingBossError, "wth, why's he so strong ???"
end
end
Giờ chúng ta sẽ thử cho Vegeta đánh nhau và xem kết quả =))
pry(main)> vegeta = Vegeta.new
# => #<Celluloid::Proxy::Cell(Vegeta:0x3ffb223d7298)>
pry(main)> vegeta.async.fight_against_boss_max_level
# E, [2015-09-23T16:23:14.729009 #24234] ERROR -- : Actor crashed!
# Vegeta::FightingBossError: wth, why's he so strong ???
# => nil
pry(main)> vegeta.dead?
# => true
Vậy là Vegeta đã hy sinh, nhưng làm thế nào để Goku nhận ra điều này để ngăn chặn boss huỷ diệt Trái Đất?
class Goku
include Celluloid
trap_exit :actor_died
def actor_died(actor, reason)
p "Haizz! #{actor.inspect} has died because of a #{reason.class}"
end
end
Goku sử dụng method trap_exit
để nhận thông báo khi bất cứ actor nào link với Goku bị crash. Việc tiếp theo chúng ta cần làm chỉ là link Vegeta vào Goku mà thôi:
pry(main)> vegeta = Vegeta.new
# => #<Celluloid::Proxy::Cell(Vegeta:0x3ffb22305dc4)>
pry(main)> goku = Goku.new
# => #<Celluloid::Proxy::Cell(Goku:0x3ffb22987e90)>
pry(main)> goku.link vegeta
# => #<Celluloid::Proxy::Cell(Vegeta:0x3ffb22305dc4)>
pry(main)> vegeta.async.fight_against_boss_max_level
# E, [2015-09-23T17:24:21.388709 #24410] ERROR -- : Actor crashed!
# Vegeta::FightingBossError: wth, why's he so strong ???
# => nil
# "Haizz! #<Celluloid::Proxy::Cell(Vegeta) dead> has died because of a Vegeta::FightingBossError"
Bởi vì Goku đã được link với Vegeta, nên sau khi Vegeta chết, Vegeta's exit message
sẽ được chuyển đến cho Goku. Bằng trap_exit
, Goku bắt được các exit messages và thực thi các actions đã khai báo (trong trường hợp trên thì là in lỗi ra màn hình). Vậy nếu Goku không sử dụng trap_exit
thì mọi chuyện sẽ diễn ra thế nào?
Goten và Trunks là đôi bạn thân, thích đánh đấm do ảnh hưởng truyền thống gia đình (haha). Vì còn trẻ con nên năng lực có hạn, phải dung hợp cùng nhau (thành Gotenks) thì mới mạnh hơn được tẹo.
class Trunks
include Celluloid
class FightingBossError < StandardError; end
def fight_against_boss
raise FightingBossError, "sorry my buddy, i can't bear it anymore..."
end
end
class Goten
include Celluloid
end
Giờ chúng ta sẽ tạo liên kết giữa các actors, sao cho khi Trunks bị ăn hành, thì Goten cũng bị ăn hành ngay lập tức, và Goku sẽ nhận được tin báo từ Goten. Vậy là chúng ta sẽ link Goten và Trunks lại, sau đó link Goten và Goku. Hãy thử xem Goten và Goku sẽ phản ứng thế nào khi actor Trunks bị crash nhé
pry(main)> trunks = Trunks.new
# => #<Celluloid::Proxy::Cell(Trunks:0x3fed6bcbd610)>
pry(main)> goten = Goten.new
# => #<Celluloid::Proxy::Cell(Goten:0x3fed6bc699fc)>
pry(main)> goku = Goku.new
# => #<Celluloid::Proxy::Cell(Goku:0x3fed6ae8d4dc)>
pry(main)> goten.link trunks
# => #<Celluloid::Proxy::Cell(Trunks:0x3fed6bcbd610)>
pry(main)> goku.link goten
# => #<Celluloid::Proxy::Cell(Goten:0x3fed6bc699fc)>
pry(main)> trunks.async.fight_against_boss
# => nil
# E, ERROR -- : Actor crashed!
# Trunks::FightingBossError: sorry my buddy, i can't bear it anymore...
# "Haizz! #<Celluloid::Proxy::Cell(Goten) dead> has died because of a Trunks::FightingBossError"
pry(main)> goten.dead?
# => true
pry(main)> goku.dead?
# => false
Vậy là sau khi Trunks bị trùm hạ gục, Goten đã chịu chung số phận (hoho). Vì Goku có khai báo xử lý bắt exit event của Goten nên đã nhận được thông báo về cái chết của Goten.
Vì các actors được link
với nhau có thể truyền error messages cho nhau, nên ngoại trừ các actors có sử dụng trap_exit
thì tất cả sẽ cùng chết với nhau (như trường hợp của Goten và Trunks). Chức năng này giúp chúng ta có thể tác động đến nhiều actors cùng lúc khi gặp vấn đề. Khi có error phát sinh tại một actor, nó ko chỉ gây crash cho actor đó mà sẽ khiến một nhóm actors liên quan bị crash cùng. Kết hợp với supervisor
ở phần trước, ta có thể tạo ra một supervisor chịu trách nhiệm quản lý một actor đặc biệt. Actor đặc biệt này sử dụng new_link
(một combo giữa new
và link
) để kết nối với các actors trong một nhóm liên quan với nhau. Khi đó, nếu có error phát sinh tại bất cứ một actor nào, thì tất cả sẽ bị kill, và toàn bộ sẽ được restart lại nhờ supervisor. (honho)
Bonus: các bạn có thể sử dụng method unlink
để xoá bỏ link giữa các actors.
Supervision Groups
Supervision groups
cung cấp cho chúng ta một phương pháp để supervise nhiều actors cùng lúc. Có thể start, stop một groups các actors chỉ với một dòng lệnh, và chắc chắn rằng chúng có thể được restart tự động nếu có bất kỳ thành viên trong đó bị crash. Groups có thể supervise các groups khác, giúp chúng ta xây dựng được các supervision trees
cho tất cả các components trong hệ thống.
Để sử dụng, hãy khai báo kế thừa từ Celluloid::SupervisionGroup
class FriendWithBenefit < Celluloid::SupervisionGroup
supervise FirstFriend, as: :first_friend
supervise AnotherFriend, as: :another_friend, args: [{is_in_relationship: true}]
pool MyFriend, as: :my_friend_pool, size: 5
end
Để khởi động group trên, sử dụng câu lệnh sau:
FriendWithBenefit.run
FirstFriend
và AnotherFriend
sẽ được khởi tạo và được đăng ký trong Celluloid registry dưới cái tên Celluloid::Actor[:first_friend]
và Celluloid::Actor[:another_friend]
. Ngoài ra thì một pool gồm 5 MyFriend
sẽ được start và đăng ký dưới tên Celluloid::Actor[:my_friend_pool]
. Chi tiết về Pools
sẽ được mình giới thiệu sau vào một ngày đẹp trời nào đó.
Để khởi động group dưới background, sử dụng:
FriendWithBenefit.run!
All rights reserved