0

Giới thiệu về Celluloid - Part 3

celluloid_logo


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?


GotenTrunks 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 GotenTrunks lại, sau đó link GotenGoku. Hãy thử xem GotenGoku 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 GotenTrunks). 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 newlink) để 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

FirstFriendAnotherFriend sẽ được khởi tạo và được đăng ký trong Celluloid registry dưới cái tên Celluloid::Actor[:first_friend]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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí