anycable 0.5.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +166 -2
- data/MIT-LICENSE +1 -1
- data/README.md +19 -60
- data/bin/anycable +13 -0
- data/bin/anycabled +30 -0
- data/lib/anycable.rb +69 -18
- data/lib/anycable/broadcast_adapters.rb +33 -0
- data/lib/anycable/broadcast_adapters/redis.rb +42 -0
- data/lib/anycable/cli.rb +329 -0
- data/lib/anycable/config.rb +93 -17
- data/lib/anycable/exceptions_handling.rb +37 -0
- data/lib/anycable/health_server.rb +52 -31
- data/lib/anycable/middleware.rb +19 -0
- data/lib/anycable/middleware_chain.rb +58 -0
- data/lib/anycable/rpc/rpc_pb.rb +1 -1
- data/lib/anycable/rpc/rpc_services_pb.rb +1 -1
- data/lib/anycable/rpc_handler.rb +77 -32
- data/lib/anycable/server.rb +132 -39
- data/lib/anycable/socket.rb +1 -1
- data/lib/anycable/version.rb +2 -2
- metadata +34 -59
- data/.gitignore +0 -40
- data/.hound.yml +0 -3
- data/.rubocop.yml +0 -71
- data/.travis.yml +0 -13
- data/Gemfile +0 -8
- data/Makefile +0 -5
- data/PITCHME.md +0 -139
- data/PITCHME.yaml +0 -1
- data/Rakefile +0 -8
- data/anycable.gemspec +0 -32
- data/assets/Memory3.png +0 -0
- data/assets/Memory5.png +0 -0
- data/assets/RTT3.png +0 -0
- data/assets/RTT5.png +0 -0
- data/assets/Scheme1.png +0 -0
- data/assets/Scheme2.png +0 -0
- data/assets/cpu_chart.gif +0 -0
- data/assets/cpu_chart2.gif +0 -0
- data/assets/evlms.png +0 -0
- data/benchmarks/.gitignore +0 -1
- data/benchmarks/2017-02-12.md +0 -308
- data/benchmarks/2018-03-04.md +0 -192
- data/benchmarks/2018-05-27-rpc-bench.md +0 -57
- data/benchmarks/HowTo.md +0 -23
- data/benchmarks/ansible.cfg +0 -9
- data/benchmarks/benchmark.yml +0 -67
- data/benchmarks/hosts +0 -5
- data/benchmarks/servers.yml +0 -36
- data/circle.yml +0 -8
- data/etc/bug_report_template.rb +0 -76
- data/lib/anycable/handler/exceptions_handling.rb +0 -43
- data/lib/anycable/pubsub.rb +0 -26
- data/protos/rpc.proto +0 -55
@@ -1,57 +0,0 @@
|
|
1
|
-
# AnyCable RPC benchmarks
|
2
|
-
|
3
|
-
AnyCable RPC server benchmarks.
|
4
|
-
|
5
|
-
The benchmark is pretty simple:
|
6
|
-
- Create N concurrent _clients_ (authenticate and subscribe to `BenchmarkChannel` within the RPC app)
|
7
|
-
- Every client starts performing an action (`echo`) in a loop
|
8
|
-
- Every call is collected (request time)
|
9
|
-
- Wait for the specified amount of seconds and aggragate the results.
|
10
|
-
|
11
|
-
Measuring **requests per seconds**.
|
12
|
-
|
13
|
-
**NOTE:** this benchmark shows performance characteristics for both _clients_ and the server.
|
14
|
-
The purpose of this benchmark is to measure the thoughout of RPC connection between AnyCable services.
|
15
|
-
|
16
|
-
**NOTE 2:** "Noop RPC" is just a gRPC handling doing nothing except from responding with success result.
|
17
|
-
|
18
|
-
Sources:
|
19
|
-
- [Golang](https://github.com/anycable/anycable-go/blob/chore/benchmarking/cmd/rpc-bench/main.go)
|
20
|
-
- [Erlang](https://github.com/anycable/simple-cable-app/tree/master/benchmarks)
|
21
|
-
|
22
|
-
## Erlang single connection
|
23
|
-
|
24
|
-
RPC type \ Number of clients | 1 | 10 | 50
|
25
|
-
--------------------------------|------|------|----
|
26
|
-
AnyCable RPC | 100 | 110 | 130
|
27
|
-
Noop RPC | 100 | 115 | 125
|
28
|
-
|
29
|
-
Erlang client definitely has problems(
|
30
|
-
|
31
|
-
Looks like HTTP2 streaming is working in _blocking mode_.
|
32
|
-
|
33
|
-
Requires investigation.
|
34
|
-
|
35
|
-
## Erlycable connection pool (5-50)
|
36
|
-
|
37
|
-
RPC type \ Number of clients | 1 | 10 | 50
|
38
|
-
--------------------------------|------|------|----
|
39
|
-
AnyCable RPC | 100 | 800 | 1700
|
40
|
-
Noop RPC | 100 | 900 | 2700
|
41
|
-
|
42
|
-
## Golang single connection
|
43
|
-
|
44
|
-
RPC type \ Number of clients | 1 | 10 | 50
|
45
|
-
--------------------------------|------|------|----
|
46
|
-
AnyCable RPC | 1600 | 2200 | 2400
|
47
|
-
Noop RPC with connection object building\* | 2200 | 3500 | 4000
|
48
|
-
Noop RPC | 3000 | 4600 | 5500
|
49
|
-
|
50
|
-
\* Only build Action Cable connection object without performing an action.
|
51
|
-
|
52
|
-
## Golang connection pool (5-50)
|
53
|
-
|
54
|
-
RPC type \ Number of clients | 1 | 10 | 50
|
55
|
-
--------------------------------|------|------|----
|
56
|
-
AnyCable RPC | 1400 | 1500 | 1700
|
57
|
-
Noop RPC | 2100 | 3200 | 4000
|
data/benchmarks/HowTo.md
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
## Hot to run benchmarks
|
2
|
-
|
3
|
-
### Installation
|
4
|
-
|
5
|
-
- Install [Ansible](http://ansible.com) (`brew install ansible`)
|
6
|
-
|
7
|
-
- Launch two EC2 instances from `anycable-benchmark-xxx` AMI
|
8
|
-
|
9
|
-
- Attach more local network interfaces to the _client_ instance
|
10
|
-
|
11
|
-
- Update `hosts` file and playbooks with required information (IPs, etc)
|
12
|
-
|
13
|
-
- Prepare the _client_ instance: `ansible-playbook --tags prepare benchmark.yml`
|
14
|
-
|
15
|
-
### Running benchmarks
|
16
|
-
|
17
|
-
To run a server, e.g. Action Cable: `ansible-playbook --tags action_cable servers.yml`
|
18
|
-
|
19
|
-
To run a benchmark against it: `ansible-playbook --tags action_cable benchmark.yml`
|
20
|
-
|
21
|
-
You can also specify benchmark parameters: `ansible-playbook --extra-vars "step_size=1000 steps=10 sample_size=10" benchmark.yml`.
|
22
|
-
|
23
|
-
**NOTE**: Ansible doesn't support command output streaming, so we can only see the results at the end of the run.
|
data/benchmarks/ansible.cfg
DELETED
data/benchmarks/benchmark.yml
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
---
|
2
|
-
- name: Benchmark Client
|
3
|
-
hosts: benchmark
|
4
|
-
sudo: yes
|
5
|
-
remote_user: ubuntu
|
6
|
-
gather_facts: False
|
7
|
-
vars:
|
8
|
-
server_host: '172.31.17.82'
|
9
|
-
hostname: ws-bench-client
|
10
|
-
local_ips: ['172.31.17.191', '172.31.17.192', '172.31.17.193', '172.31.17.194']
|
11
|
-
local_ips_str: '-l 172.31.17.191 -l 172.31.17.192 -l 172.31.17.193 -l 172.31.17.194'
|
12
|
-
steps: 20
|
13
|
-
step_size: 1000
|
14
|
-
sample_size: 100
|
15
|
-
concurrency: 8
|
16
|
-
payload_size: 200
|
17
|
-
prepare: False
|
18
|
-
tasks:
|
19
|
-
- name: Prepare the machine
|
20
|
-
tags: prepare
|
21
|
-
block:
|
22
|
-
- hostname:
|
23
|
-
name: "{{ hostname }}"
|
24
|
-
- lineinfile:
|
25
|
-
path: /etc/hosts
|
26
|
-
line: '127.0.0.1 {{ hostname }}'
|
27
|
-
state: present
|
28
|
-
- shell: ip addr add {{ item }}/20 dev eth0
|
29
|
-
with_items: "{{ local_ips }}"
|
30
|
-
|
31
|
-
- name: Print Action Cable command
|
32
|
-
debug: msg="bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} --origin http://0.0.0.0 ws://{{ server_host }}:3334/cable --server-type=actioncable"
|
33
|
-
tags: action_cable
|
34
|
-
|
35
|
-
- name: Action Cable benchmark
|
36
|
-
become_user: deplo
|
37
|
-
shell: bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} --origin http://0.0.0.0 ws://{{ server_host }}:3334/cable --server-type=actioncable
|
38
|
-
register: bench
|
39
|
-
tags: action_cable
|
40
|
-
args:
|
41
|
-
chdir: /webapps/anycable_bench
|
42
|
-
ignore_errors: yes
|
43
|
-
|
44
|
-
- name: Print Plezi command
|
45
|
-
debug: msg="bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} ws://{{ server_host }}:3334/cable"
|
46
|
-
tags: plezi
|
47
|
-
|
48
|
-
- name: Plezi benchmark
|
49
|
-
become_user: deplo
|
50
|
-
shell: bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} ws://{{ server_host }}:3334/cable
|
51
|
-
register: bench
|
52
|
-
tags: plezi
|
53
|
-
args:
|
54
|
-
chdir: /webapps/anycable_bench
|
55
|
-
ignore_errors: yes
|
56
|
-
|
57
|
-
- name: Benchmark results (stdout)
|
58
|
-
debug: var=bench.stdout_lines
|
59
|
-
tags:
|
60
|
-
- action_cable
|
61
|
-
- plezi
|
62
|
-
|
63
|
-
- name: Benchmark results (stderr)
|
64
|
-
debug: var=bench.stderr_lines
|
65
|
-
tags:
|
66
|
-
- action_cable
|
67
|
-
- plezi
|
data/benchmarks/hosts
DELETED
data/benchmarks/servers.yml
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
---
|
2
|
-
- name: Benchmark Servers
|
3
|
-
hosts: servers
|
4
|
-
sudo: yes
|
5
|
-
remote_user: ubuntu
|
6
|
-
gather_facts: False
|
7
|
-
vars:
|
8
|
-
web_concurrency: 8
|
9
|
-
tasks:
|
10
|
-
- name: Kill servers
|
11
|
-
shell: pid=$(lsof -i:{{item}} -t); kill -TERM $pid || kill -KILL $pid
|
12
|
-
tags:
|
13
|
-
- action_cable
|
14
|
-
- anycable
|
15
|
-
- plezi
|
16
|
-
with_items:
|
17
|
-
- "3334"
|
18
|
-
ignore_errors: true
|
19
|
-
- name: Run Action Cable
|
20
|
-
become_user: deplo
|
21
|
-
tags: action_cable
|
22
|
-
shell: WEB_CONCURRENCY={{ web_concurrency }} bundle exec rails s -p 3334 -e production
|
23
|
-
args:
|
24
|
-
chdir: /webapps/anycable_bench/ruby/action-cable-server
|
25
|
-
- name: Run Anycable Go
|
26
|
-
become_user: deplo
|
27
|
-
tags: anycable
|
28
|
-
shell: ANYCABLE_GO_BIN="anycable-go-0.6.0" ANYCABLE_PORT="3334" bundle exec bin/anycable
|
29
|
-
args:
|
30
|
-
chdir: /webapps/anycable_bench/ruby/action-cable-server
|
31
|
-
- name: Run Iodine/Plezi
|
32
|
-
become_user: deplo
|
33
|
-
tags: plezi
|
34
|
-
shell: iodine -p 3334
|
35
|
-
args:
|
36
|
-
chdir: /webapps/anycable_bench/ruby/plezi-iodine
|
data/circle.yml
DELETED
data/etc/bug_report_template.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bundler/inline"
|
4
|
-
|
5
|
-
# This reproduction script is based on `anyt` gem
|
6
|
-
# (https://github.com/anycable/anyt).
|
7
|
-
#
|
8
|
-
# See more test examples here:
|
9
|
-
# https://github.com/anycable/anyt/tree/master/lib/anyt/tests
|
10
|
-
|
11
|
-
gemfile(true) do
|
12
|
-
source "https://rubygems.org"
|
13
|
-
|
14
|
-
gem "anyt"
|
15
|
-
end
|
16
|
-
|
17
|
-
require "anyt"
|
18
|
-
require "anyt/cli"
|
19
|
-
require "anycable-rails"
|
20
|
-
|
21
|
-
# WebSocket server url
|
22
|
-
ENV['ANYT_TARGET_URL'] ||= "ws://localhost:8080/cable"
|
23
|
-
|
24
|
-
# Command to launch WebSocket server.
|
25
|
-
# Comment this line if you want to run WebSocket server manually
|
26
|
-
ENV['ANYT_COMMAND'] ||= "anycable-go"
|
27
|
-
|
28
|
-
ActionCable.server.config.logger = Rails.logger = Anycable.logger
|
29
|
-
|
30
|
-
# Test scenario
|
31
|
-
feature "issue_xyz" do
|
32
|
-
# This block defines an anonymous channel to test against
|
33
|
-
channel do
|
34
|
-
# def subscribed
|
35
|
-
# stream_from "a"
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# def perform(data)
|
39
|
-
# # ...
|
40
|
-
# end
|
41
|
-
end
|
42
|
-
|
43
|
-
# You can use minitest/spec features here
|
44
|
-
before do
|
45
|
-
# `channel` contains identifier of the anonymous channel defined above
|
46
|
-
subscribe_request = { command: "subscribe", identifier: { channel: channel }.to_json }
|
47
|
-
|
48
|
-
# `client` represents a websocket client connected to a server
|
49
|
-
client.send(subscribe_request)
|
50
|
-
|
51
|
-
ack = {
|
52
|
-
"identifier" => { channel: channel }.to_json, "type" => "confirm_subscription"
|
53
|
-
}
|
54
|
-
|
55
|
-
# `receive` method returns the first message from the incoming messages queue;
|
56
|
-
# waits 5s when no messages available
|
57
|
-
assert_equal ack, client.receive
|
58
|
-
end
|
59
|
-
|
60
|
-
# describe you bug scenario here
|
61
|
-
scenario %{
|
62
|
-
Should work
|
63
|
-
} do
|
64
|
-
# ...
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# Required setup/teardown
|
69
|
-
begin
|
70
|
-
Anyt::RPC.start
|
71
|
-
Anyt::Command.run if Anyt.config.command
|
72
|
-
Anyt::Tests.run
|
73
|
-
ensure
|
74
|
-
Anyt::Command.stop if Anyt.config.command
|
75
|
-
Anyt::RPC.stop
|
76
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Anycable
|
4
|
-
module Handler # :nodoc:
|
5
|
-
# Handle app-level errors
|
6
|
-
module ExceptionsHandling
|
7
|
-
def connect(*)
|
8
|
-
super
|
9
|
-
rescue StandardError => ex
|
10
|
-
handle_exception(ex)
|
11
|
-
Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
|
12
|
-
end
|
13
|
-
|
14
|
-
def disconnect(*)
|
15
|
-
super
|
16
|
-
rescue StandardError => ex
|
17
|
-
handle_exception(ex)
|
18
|
-
Anycable::DisconnectResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
|
19
|
-
end
|
20
|
-
|
21
|
-
def command(*)
|
22
|
-
super
|
23
|
-
rescue StandardError => ex
|
24
|
-
handle_exception(ex)
|
25
|
-
Anycable::CommandResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
|
26
|
-
end
|
27
|
-
|
28
|
-
def handle_exception(ex)
|
29
|
-
Anycable.error_handlers.each do |handler|
|
30
|
-
begin
|
31
|
-
handler.call(ex)
|
32
|
-
rescue StandardError => ex
|
33
|
-
Anycable.logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
|
34
|
-
Anycable.logger.error ex
|
35
|
-
Anycable.logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
Anycable.error_handlers << proc { |e| Anycable.logger.error(e.message) }
|
42
|
-
end
|
43
|
-
end
|
data/lib/anycable/pubsub.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "redis"
|
4
|
-
require "json"
|
5
|
-
|
6
|
-
module Anycable
|
7
|
-
# PubSub for broadcasting
|
8
|
-
class PubSub
|
9
|
-
attr_reader :redis_conn
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
redis_config = { url: Anycable.config.redis_url }
|
13
|
-
unless Anycable.config.redis_sentinels.empty?
|
14
|
-
redis_config[:sentinels] = Anycable.config.redis_sentinels
|
15
|
-
end
|
16
|
-
@redis_conn = Redis.new(redis_config)
|
17
|
-
end
|
18
|
-
|
19
|
-
def broadcast(channel, payload)
|
20
|
-
redis_conn.publish(
|
21
|
-
Anycable.config.redis_channel,
|
22
|
-
{ stream: channel, data: payload }.to_json
|
23
|
-
)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/protos/rpc.proto
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
syntax = "proto3";
|
2
|
-
|
3
|
-
package anycable;
|
4
|
-
|
5
|
-
service RPC {
|
6
|
-
rpc Connect (ConnectionRequest) returns (ConnectionResponse) {}
|
7
|
-
rpc Command (CommandMessage) returns (CommandResponse) {}
|
8
|
-
rpc Disconnect (DisconnectRequest) returns (DisconnectResponse) {}
|
9
|
-
}
|
10
|
-
|
11
|
-
enum Status {
|
12
|
-
ERROR = 0;
|
13
|
-
SUCCESS = 1;
|
14
|
-
FAILURE = 2;
|
15
|
-
}
|
16
|
-
|
17
|
-
message ConnectionRequest {
|
18
|
-
string path = 1;
|
19
|
-
map<string,string> headers = 2;
|
20
|
-
}
|
21
|
-
|
22
|
-
message ConnectionResponse {
|
23
|
-
Status status = 1;
|
24
|
-
string identifiers = 2;
|
25
|
-
repeated string transmissions = 3;
|
26
|
-
string error_msg = 4;
|
27
|
-
}
|
28
|
-
|
29
|
-
message CommandMessage {
|
30
|
-
string command = 1;
|
31
|
-
string identifier = 2;
|
32
|
-
string connection_identifiers = 3;
|
33
|
-
string data = 4;
|
34
|
-
}
|
35
|
-
|
36
|
-
message CommandResponse {
|
37
|
-
Status status = 1;
|
38
|
-
bool disconnect = 2;
|
39
|
-
bool stop_streams = 3;
|
40
|
-
repeated string streams = 4;
|
41
|
-
repeated string transmissions = 5;
|
42
|
-
string error_msg = 6;
|
43
|
-
}
|
44
|
-
|
45
|
-
message DisconnectRequest {
|
46
|
-
string identifiers = 1;
|
47
|
-
repeated string subscriptions = 2;
|
48
|
-
string path = 3;
|
49
|
-
map<string,string> headers = 4;
|
50
|
-
}
|
51
|
-
|
52
|
-
message DisconnectResponse {
|
53
|
-
Status status = 1;
|
54
|
-
string error_msg = 2;
|
55
|
-
}
|