Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[BRUG Minsk, May 2017] Action Cable. Making Better

[BRUG Minsk, May 2017] Action Cable. Making Better

Vladimir Dementyev

May 13, 2017
Tweet

More Decks by Vladimir Dementyev

Other Decks in Programming

Transcript

  1. View Slide

  2. View Slide

  3. Evil Martians

    View Slide

  4. Evil Martians

    View Slide

  5. What is
    Action Cable?

    View Slide

  6. Action Cable
    RailsConf 2015 April, 21

    View Slide

  7. Action Cable
    RailsConf 2015

    View Slide

  8. Action Cable in a Nutshell
    Server
    Client
    WebSocket
    Client
    WebSocket
    stream C
    Broadcaster
    stream B
    stream A
    stream B
    channel X
    channel Y
    channel Z

    View Slide

  9. Action Cable Channel
    class AnswersChannel < ApplicationCable::Channel
    def subscribed
    reject_subscription unless current_user.admin?
    end
    def follow(params)
    stream_from "questions/#{params['id']}"
    end
    end

    View Slide

  10. The Good Parts

    View Slide

  11. Good Parts
    • Easy configuration

    View Slide

  12. Good Parts
    • Easy configuration
    • Application logic access through
    channels

    View Slide

  13. Good Parts
    • Easy configuration
    • Application logic access through
    channels
    • JavaScript client that just works

    View Slide

  14. Bad Parts?

    View Slide

  15. Performance

    View Slide

  16. Memory Cable

    View Slide

  17. Memory Cable
    20k connections
    MB
    0
    375
    750
    1 125
    1 500
    1 875
    2 250
    2 625
    3 000
    Go Erlang ActionCable (2x) ActionCable (16x)

    View Slide

  18. WebSocket Shootout

    View Slide

  19. WebSocket Shootout
    Client
    Server
    broadcast
    to all
    message
    send message back

    View Slide

  20. WebSocket Shootout
    Broadcast RTT
    0s
    1s
    2s
    3s
    4s
    5s
    6s
    7s
    8s
    9s
    10s
    Number of connections
    1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
    Go Erlang ActionCable
    Running on AWS EC2 c3.4xlarge (16 vCPU, 30 GiB RAM)

    View Slide

  21. WebSocket Shootout

    View Slide

  22. Testing

    View Slide

  23. Testing Cable

    View Slide

  24. Extensibility

    View Slide

  25. • Server Abstraction
    Extended Cable

    View Slide

  26. • Server Abstraction
    • Protocol Extensions (e.g.
    serialization, authentication,
    presence feature)
    Extended Cable

    View Slide

  27. Other
    • Fallbacks / Transport
    • Rails-ness
    • Consistency

    View Slide

  28. Consistency

    View Slide

  29. Consistency?

    View Slide

  30. How to Make it
    Better?

    View Slide

  31. Testing Cable
    , 2016

    View Slide

  32. Testing Cable
    class ServiceTest < ActionCable::TestCase
    def test_assert_transmited_message
    Service.broadcast 'messages', 'hi!'
    assert_broadcast_on('messages', text: 'hi!')
    end
    def test_assert_broadcast_on_again
    assert_broadcast_on('messages','h!') do
    Service.broadcast 'messages', text: 'h!'
    end
    end
    end

    View Slide

  33. Testing Cable
    class ChannelTest < ActionCable::Channel::TestCase
    def test_subscribed_with_room_number
    subscribe room_number: 1
    assert subscription.confirmed?
    assert "chat_1", streams.last
    end
    def test_perform_speak
    subscribe room_number: 1
    perform :speak, message: "Hello"
    assert_equal "Hello",
    transmissions.last["message"]["text"]
    end
    end

    View Slide

  34. PRs Cable

    View Slide

  35. PRs Cable

    View Slide

  36. AnyCable

    View Slide

  37. View Slide

  38. Server
    Channels
    Streams
    Client (Protocol)
    Action Cable AnyCable

    View Slide

  39. Server
    Channels
    Streams
    Client (Protocol)
    Separation of Concerns
    Action Cable AnyCable

    View Slide

  40. AnyCable

    View Slide

  41. gRPC

    View Slide

  42. gRPC =
    Google RPC

    View Slide

  43. gRPC =
    Universal RPC
    Framework

    View Slide

  44. gRPC =
    protobuf + HTTP/2

    View Slide

  45. AnyCable Servers
    anycable-go
    erlycable

    View Slide

  46. Memory Cable
    20k connections
    MB
    0
    500
    1 000
    1 500
    2 000
    2 500
    3 000
    3 500
    4 000
    anycable-go erlycable ActionCable

    View Slide

  47. Action Cable CPU

    View Slide

  48. anycable-go CPU

    View Slide

  49. erlycable CPU

    View Slide

  50. WebSocket Shootout
    Broadcast RTT
    0s
    1s
    2s
    3s
    4s
    5s
    6s
    7s
    8s
    9s
    10s
    Number of connections
    1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
    ActionCable anycable-go erlycable
    Running on AWS EC2 c3.4xlarge (16 vCPU, 30 GiB RAM)

    View Slide

  51. Compatibility
    Feature Status
    Connection Identifiers +
    Connection Request (cookies, params) +
    Disconnect Handling +
    Subscribe to channels +
    Parameterized subscriptions +
    Unsubscribe from channels +
    Subscription Instance Variables -
    Performing Channel Actions +
    Streaming +
    Custom stream callbacks -
    Broadcasting +

    View Slide

  52. Action Cable Channel
    class ChatChannel < ApplicationCable::Channel
    def subscribed
    stream_from "chat_#{chat_id}" do |msg|
    if msg['sent_at'].present?
    delay = Time.now.to_f - msg['sent_at']
    logger.info "Message delay #{delay}s"
    end
    transmit msg
    end
    end
    end

    View Slide

  53. PRs Cable

    View Slide

  54. WebSocket Shootout
    Broadcast RTT
    0s
    1s
    2s
    3s
    4s
    5s
    6s
    7s
    8s
    9s
    10s
    Number of connections
    1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
    ActionCable Patch 1
    Running on AWS EC2 c3.4xlarge (16 vCPU, 30 GiB RAM)

    View Slide

  55. Where To
    anycable.io
    github.com/anycable
    twitter.com/any_cable
    gitter.im/anycable

    View Slide

  56. PRs Cable

    View Slide

  57. Server Adapter
    # config/application.rb

    # specify server adapter
    config.action_cable.server = :plezi
    # or
    config.action_cable.server = :any_cable

    View Slide

  58. plezi.io
    github.com/boazsegev/iodine
    Plezi

    View Slide

  59. LiteCable

    View Slide

  60. • Rails-free Action Cable
    • No deps (even ActiveSupport)
    • Compatible with AnyCable
    LiteCable

    View Slide

  61. LiteCable
    module Chat
    # ...
    class Channel < LiteCable::Channel::Base
    identifier :chat
    def subscribed
    stream_from "chat_#{chat_id}"
    end
    end
    end
    run Rack::Builder.new do
    map '/cable' do
    use LiteCable::Server::Middleware,
    connection_class: Chat::Connection
    run proc { |_| [200, {}, ['']] }
    end
    end

    View Slide

  62. LiteCable
    github.com/palkan/litecable

    View Slide

  63. • Testing Support PRs
    • AnyCable for Performance
    • LiteCable for non-Rails
    • ACLI for development/testing
    What We Have

    View Slide

  64. • Action Cable CLI
    • mRuby
    • Download'n'Run
    ACLI

    View Slide

  65. ACLI

    View Slide

  66. What's Next?

    View Slide

  67. History Stream
    class ChatChannel < ApplicationCable::Channel
    def subscribed
    stream_from "chat_#{id}",
    history: { time: 5.minutes }
    end
    end
    class ChatChannel < ApplicationCable::Channel
    def subscribed
    stream_from "chat_#{chat_id}",
    history: { size: 100 }
    end
    end

    View Slide

  68. Presence
    class ChatChannel < ApplicationCable::Channel
    def subscribed
    stream_from "chat_#{id}",
    presence: { user_id: user_id }
    end
    end
    // js client
    cable.subscriptions.create(
    { channel: 'chat', id: roomId },
    {
    presenceUpdated: function(diff) { … }
    }
    )

    View Slide

  69. cult of martians.com

    View Slide

  70. Let's make Action Cable
    suck less!
    Vladimir Dementyev
    evilmartians.com
    @palkan
    @palkan_tula

    View Slide