anycable 0.5.2 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
}
|