anycable 0.4.6 → 0.5.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -1
- data/CHANGELOG.md +32 -1
- data/Makefile +2 -1
- data/README.md +29 -2
- data/Rakefile +3 -1
- data/anycable.gemspec +6 -4
- data/benchmarks/.gitignore +1 -0
- data/benchmarks/HowTo.md +23 -0
- data/benchmarks/ansible.cfg +9 -0
- data/benchmarks/benchmark.yml +40 -0
- data/benchmarks/hosts +5 -0
- data/benchmarks/servers.yml +29 -0
- data/lib/anycable.rb +19 -3
- data/lib/anycable/config.rb +18 -6
- data/lib/anycable/handler/exceptions_handling.rb +24 -10
- data/lib/anycable/pubsub.rb +6 -1
- data/lib/anycable/rpc/{rpc.rb → rpc_pb.rb} +4 -0
- data/lib/anycable/rpc/{rpc_services.rb → rpc_services_pb.rb} +0 -2
- data/lib/anycable/rpc_handler.rb +19 -11
- data/lib/anycable/server.rb +24 -11
- data/lib/anycable/socket.rb +1 -0
- data/lib/anycable/version.rb +2 -1
- data/protos/rpc.proto +4 -0
- metadata +35 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c34f2dcdd663a7a4197be6ae56359d8e1f15fd7
|
4
|
+
data.tar.gz: 43eebb1490b11a6fdbf3d7aa65658f8cc25cd48f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a684670f4fe590b9cc9da89c92c94bb42e3168cb531e75ff4a21cda4c4fb39683041bd40da4bbaaaaf8b331910c00453971c8571e302521b016116d1747d868
|
7
|
+
data.tar.gz: e0f67df078fa371f6d989ec03d8deced4908e61a1f37c206b1a77ecbff92e675c80f604dc7fa22dc45fbfb9fe3d4227fe505f55bc787c8d37279942325cf96d2
|
data/.rubocop.yml
CHANGED
@@ -9,14 +9,21 @@ AllCops:
|
|
9
9
|
- 'spec/dummy/**/*'
|
10
10
|
- 'tmp/**/*'
|
11
11
|
- 'bench/**/*'
|
12
|
+
- 'vendor/**/*'
|
12
13
|
- 'lib/anycable/rpc/**/*'
|
14
|
+
- 'Gemfile'
|
15
|
+
- 'Rakefile'
|
16
|
+
- '*.gemspec'
|
13
17
|
DisplayCopNames: true
|
14
18
|
StyleGuideCopsOnly: false
|
15
|
-
TargetRubyVersion: 2.
|
19
|
+
TargetRubyVersion: 2.4
|
16
20
|
|
17
21
|
Style/AccessorMethodName:
|
18
22
|
Enabled: false
|
19
23
|
|
24
|
+
Style/PercentLiteralDelimiters:
|
25
|
+
Enabled: false
|
26
|
+
|
20
27
|
Style/TrivialAccessors:
|
21
28
|
Enabled: false
|
22
29
|
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## 0.5.0 (master)
|
4
|
+
|
5
|
+
- [#2](https://github.com/anycable/anycable/issues/2) Add support for [Redis Sentinel](https://redis.io/topics/sentinel). ([@accessd](https://github.com/accessd))
|
6
|
+
|
7
|
+
- [#28](https://github.com/anycable/anycable/issues/28) Support arbitrary headers. ([@palkan][])
|
8
|
+
|
9
|
+
Previously we hardcoded only "Cookie" header. Now we add all passed headers by WebSocket server to request env.
|
10
|
+
|
11
|
+
- [#27](https://github.com/anycable/anycable/issues/27) Add `error_msg` to RPC responses. ([@palkan][])
|
12
|
+
|
13
|
+
Now RPC responses has 3 statuses:
|
14
|
+
|
15
|
+
- `SUCCESS` – successful request, operation succeed
|
16
|
+
- `FAILURE` – successful request, operation failed (e.g. authentication failed)
|
17
|
+
- `ERROR` – request failed (exception raised).
|
18
|
+
|
19
|
+
We provide `error_msg` only when request status is `ERROR`.
|
20
|
+
|
21
|
+
- [#25](https://github.com/anycable/anycable/issues/25) Improve logging and exceptions handling. ([@palkan][])
|
22
|
+
|
23
|
+
Default logger logs to STDOUT with `info` level by default but can be configured to log to file with
|
24
|
+
any severity.
|
25
|
+
|
26
|
+
GRPC logging is turned off by default (can be turned on through `log_grpc` configuration parameter).
|
27
|
+
|
28
|
+
`ANYCABLE_DEBUG=1` acts as a shortcut to set `debug` level and turn on GRPC logging.
|
29
|
+
|
30
|
+
Now it's possible to add custom exception handlers (e.g. to notify external exception tracking services).
|
31
|
+
|
32
|
+
More on [Wiki](https://github.com/anycable/anycable/wiki/Logging-&-Exceptions-Handling).
|
33
|
+
|
3
34
|
## 0.4.6 (2017-05-20)
|
4
35
|
|
5
36
|
- Add `Anycable::Server#stop` method. ([@sadovnik][])
|
@@ -53,4 +84,4 @@ All Rails specifics now live here https://github.com/anycable/anycable-rails.
|
|
53
84
|
Implement `Disconnect` handler, which invokes `Connection#disconnect` (along with `Channel#unsubscribed` for each subscription).
|
54
85
|
|
55
86
|
[@palkan]: https://github.com/palkan
|
56
|
-
[@sadovnik]: https://github.com/sadovnik
|
87
|
+
[@sadovnik]: https://github.com/sadovnik
|
data/Makefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
all: build
|
2
2
|
|
3
3
|
build:
|
4
|
-
|
4
|
+
grpc_tools_ruby_protoc -I ./protos --ruby_out=./lib/anycable/rpc --grpc_out=./lib/anycable/rpc ./protos/rpc.proto
|
5
|
+
sed -i '' '/'rpc_pb'/d' ./lib/anycable/rpc/rpc_services_pb.rb
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
[![GitPitch](https://gitpitch.com/assets/badge.svg)](https://gitpitch.com/anycable/anycable/master?grs=github) [![Gem Version](https://badge.fury.io/rb/anycable.svg)](https://rubygems.org/gems/anycable) [![Build Status](https://travis-ci.org/anycable/anycable.svg?branch=master)](https://travis-ci.org/anycable/anycable) [![Circle CI](https://circleci.com/gh/anycable/anycable/tree/master.svg?style=svg)](https://circleci.com/gh/anycable/anycable/tree/master)
|
2
|
+
[![Dependency Status](https://dependencyci.com/github/anycable/anycable/badge)](https://dependencyci.com/github/anycable/anycable)
|
2
3
|
[![Gitter](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/anycable/Lobby)
|
3
4
|
|
4
5
|
# Anycable
|
@@ -7,7 +8,7 @@ AnyCable allows you to use any WebSocket server (written in any language) as a r
|
|
7
8
|
|
8
9
|
AnyCable uses ActionCable protocol, so you can use ActionCable [JavaScript client](https://www.npmjs.com/package/actioncable) without any monkey-patching.
|
9
10
|
|
10
|
-
**NOTE**: Since version 0.4.0 this repository contains only core functionality and
|
11
|
+
**NOTE**: Since version 0.4.0 this repository contains only core functionality and cannot be used separately as is.
|
11
12
|
Rails plug-n-play integration has been extracted to [anycable-rails](https://github.com/anycable/anycable-rails) gem.
|
12
13
|
|
13
14
|
<a href="https://evilmartians.com/">
|
@@ -40,11 +41,22 @@ Read our [Wiki](https://github.com/anycable/anycable/wiki) for more.
|
|
40
41
|
|
41
42
|
Anycable uses [anyway_config](https://github.com/palkan/anyway_config), thus it is also possible to set configuration variables through `secrets.yml` or environment vars.
|
42
43
|
|
44
|
+
### Example with redis sentinel
|
45
|
+
|
46
|
+
```yaml
|
47
|
+
rpc_host: "localhost:50123"
|
48
|
+
redis_url: "redis://redis-1-1:6379/2"
|
49
|
+
redis_sentinels:
|
50
|
+
- { host: 'redis-1-1', port: 26379 }
|
51
|
+
- { host: 'redis-1-2', port: 26379 }
|
52
|
+
- { host: 'redis-1-3', port: 26379 }
|
53
|
+
```
|
54
|
+
|
43
55
|
## ActionCable Compatibility
|
44
56
|
|
45
57
|
This is the compatibility list for the AnyCable gem, not for AnyCable servers (which may not support some of the features yet).
|
46
58
|
|
47
|
-
Feature | Status
|
59
|
+
Feature | Status
|
48
60
|
-------------------------|--------
|
49
61
|
Connection Identifiers | +
|
50
62
|
Connection Request (cookies, params) | +
|
@@ -58,6 +70,21 @@ Streaming | +
|
|
58
70
|
[Custom stream callbacks](http://edgeapi.rubyonrails.org/classes/ActionCable/Channel/Streams.html) | -
|
59
71
|
Broadcasting | +
|
60
72
|
|
73
|
+
## Build
|
74
|
+
|
75
|
+
- Install required GRPC gems:
|
76
|
+
|
77
|
+
```
|
78
|
+
gem install grpc
|
79
|
+
gem install grpc-tools
|
80
|
+
```
|
81
|
+
|
82
|
+
- Re-generate GRPC files (if necessary):
|
83
|
+
|
84
|
+
```
|
85
|
+
make
|
86
|
+
```
|
87
|
+
|
61
88
|
## Contributing
|
62
89
|
|
63
90
|
Bug reports and pull requests are welcome on GitHub at https://github.com/anycable/anycable.
|
data/Rakefile
CHANGED
data/anycable.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'anycable/version'
|
@@ -17,14 +18,15 @@ Gem::Specification.new do |spec|
|
|
17
18
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
19
|
spec.require_paths = ["lib"]
|
19
20
|
|
20
|
-
spec.add_dependency "anyway_config", "~>
|
21
|
-
spec.add_dependency "grpc", "~> 1.
|
22
|
-
spec.add_dependency "redis", "
|
21
|
+
spec.add_dependency "anyway_config", "~> 1.1"
|
22
|
+
spec.add_dependency "grpc", "~> 1.6"
|
23
|
+
spec.add_dependency "redis", ">= 3.2"
|
23
24
|
|
24
25
|
spec.add_development_dependency "bundler", "~> 1"
|
25
26
|
spec.add_development_dependency "rake", ">= 10.0"
|
26
27
|
spec.add_development_dependency "rack", "~> 2.0"
|
27
28
|
spec.add_development_dependency "rspec", ">= 3.5"
|
29
|
+
spec.add_development_dependency "rubocop", ">= 0.50"
|
28
30
|
spec.add_development_dependency "simplecov", ">= 0.3.8"
|
29
31
|
spec.add_development_dependency "pry-byebug"
|
30
32
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
*.retry
|
data/benchmarks/HowTo.md
ADDED
@@ -0,0 +1,23 @@
|
|
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 --extra-vars="prepare=True" 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.
|
@@ -0,0 +1,40 @@
|
|
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.22.14'
|
9
|
+
hostname: ws-bench-client
|
10
|
+
local_ips: ['172.31.21.241', '172.31.21.242', '172.31.21.243', '172.31.21.244']
|
11
|
+
local_ips_str: '-l 172.31.21.241 -l 172.31.21.242 -l 172.31.21.243 -l 172.31.21.244'
|
12
|
+
steps: 10
|
13
|
+
step_size: 1000
|
14
|
+
sample_size: 40
|
15
|
+
prepare: False
|
16
|
+
tasks:
|
17
|
+
- name: Prepare the machine
|
18
|
+
when: prepare
|
19
|
+
block:
|
20
|
+
- hostname:
|
21
|
+
name: "{{ hostname }}"
|
22
|
+
- lineinfile:
|
23
|
+
path: /etc/hosts
|
24
|
+
line: '127.0.0.1 {{ hostname }}'
|
25
|
+
state: present
|
26
|
+
- shell: ip addr add {{ item }}/20 dev eth0
|
27
|
+
with_items: "{{ local_ips }}"
|
28
|
+
- name: Action Cable benchmark
|
29
|
+
become_user: deplo
|
30
|
+
shell: bin/websocket-bench broadcast {{ local_ips_str }} --concurrent 4 --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding 200 --total-steps {{ steps }} --origin http://0.0.0.0 ws://{{ server_host }}:3334/cable --server-type=actioncable
|
31
|
+
register: bench
|
32
|
+
tags: action_cable
|
33
|
+
args:
|
34
|
+
chdir: /webapps/anycable_bench
|
35
|
+
ignore_errors: yes
|
36
|
+
|
37
|
+
- name: Benchmark results
|
38
|
+
debug: var=bench.stdout_lines
|
39
|
+
tags:
|
40
|
+
- action_cable
|
data/benchmarks/hosts
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
---
|
2
|
+
- name: Benchmark Servers
|
3
|
+
hosts: servers
|
4
|
+
sudo: yes
|
5
|
+
remote_user: ubuntu
|
6
|
+
gather_facts: False
|
7
|
+
vars:
|
8
|
+
rails_webc: 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
|
+
with_items:
|
16
|
+
- "3334"
|
17
|
+
ignore_errors: true
|
18
|
+
- name: Run Action Cable
|
19
|
+
become_user: deplo
|
20
|
+
tags: action_cable
|
21
|
+
shell: WEB_CONCURRENCY={{ rails_webc }} bundle exec rails s -p 3334 -e production
|
22
|
+
args:
|
23
|
+
chdir: /webapps/anycable_bench/ruby/action-cable-server
|
24
|
+
- name: Run Anycable Go
|
25
|
+
become_user: deplo
|
26
|
+
tags: anycable
|
27
|
+
shell: bundle exec anycable
|
28
|
+
args:
|
29
|
+
chdir: /webapps/anycable_bench/ruby/action-cable-server/bin
|
data/lib/anycable.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "anycable/version"
|
3
4
|
require "anycable/config"
|
4
|
-
require "anycable/server"
|
5
|
-
require "anycable/pubsub"
|
6
5
|
require "logger"
|
7
6
|
|
8
7
|
# Anycable allows to use any websocket service (written in any language) as a replacement
|
@@ -14,12 +13,21 @@ require "logger"
|
|
14
13
|
# Broadcasting messages to WS is done through Redis Pub/Sub.
|
15
14
|
module Anycable
|
16
15
|
class << self
|
16
|
+
# Provide connection factory which
|
17
|
+
# is a callable object with build
|
18
|
+
# a Connection object
|
19
|
+
attr_accessor :connection_factory
|
20
|
+
|
17
21
|
def logger=(logger)
|
18
22
|
@logger = logger
|
19
23
|
end
|
20
24
|
|
21
25
|
def logger
|
22
|
-
@logger
|
26
|
+
return @logger if instance_variable_defined?(:@logger)
|
27
|
+
log_output = Anycable.config.log_file || STDOUT
|
28
|
+
@logger = Logger.new(log_output).tap do |logger|
|
29
|
+
logger.level = Anycable.config.log_level
|
30
|
+
end
|
23
31
|
end
|
24
32
|
|
25
33
|
def config
|
@@ -30,6 +38,11 @@ module Anycable
|
|
30
38
|
yield(config) if block_given?
|
31
39
|
end
|
32
40
|
|
41
|
+
def error_handlers
|
42
|
+
return @error_handlers if instance_variable_defined?(:@error_handlers)
|
43
|
+
@error_handlers = []
|
44
|
+
end
|
45
|
+
|
33
46
|
def pubsub
|
34
47
|
@pubsub ||= PubSub.new
|
35
48
|
end
|
@@ -40,3 +53,6 @@ module Anycable
|
|
40
53
|
end
|
41
54
|
end
|
42
55
|
end
|
56
|
+
|
57
|
+
require "anycable/server"
|
58
|
+
require "anycable/pubsub"
|
data/lib/anycable/config.rb
CHANGED
@@ -1,15 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require "anyway_config"
|
3
4
|
|
4
5
|
module Anycable
|
5
|
-
# Anycable configuration
|
6
|
+
# Anycable configuration.
|
6
7
|
class Config < Anyway::Config
|
7
8
|
config_name :anycable
|
8
9
|
|
9
|
-
attr_config :
|
10
|
-
rpc_host: "localhost:50051",
|
10
|
+
attr_config rpc_host: "localhost:50051",
|
11
11
|
redis_url: "redis://localhost:6379/5",
|
12
|
-
|
13
|
-
|
12
|
+
redis_sentinels: [],
|
13
|
+
redis_channel: "__anycable__",
|
14
|
+
log_file: nil,
|
15
|
+
log_level: :info,
|
16
|
+
log_grpc: false,
|
17
|
+
debug: false # Shortcut to enable GRPC logging and debug level
|
18
|
+
|
19
|
+
def initialize(*)
|
20
|
+
super
|
21
|
+
# Set log params if debug is true
|
22
|
+
return unless debug
|
23
|
+
self.log_level = :debug
|
24
|
+
self.log_grpc = true
|
25
|
+
end
|
14
26
|
end
|
15
27
|
end
|
@@ -1,29 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Anycable
|
4
|
-
module Handler
|
4
|
+
module Handler # :nodoc:
|
5
5
|
# Handle app-level errors
|
6
6
|
module ExceptionsHandling
|
7
7
|
def connect(*)
|
8
8
|
super
|
9
|
-
rescue StandardError =>
|
10
|
-
|
11
|
-
Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR)
|
9
|
+
rescue StandardError => ex
|
10
|
+
handle_exception(ex)
|
11
|
+
Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
|
12
12
|
end
|
13
13
|
|
14
14
|
def disconnect(*)
|
15
15
|
super
|
16
|
-
rescue StandardError =>
|
17
|
-
|
18
|
-
Anycable::DisconnectResponse.new(status: Anycable::Status::ERROR)
|
16
|
+
rescue StandardError => ex
|
17
|
+
handle_exception(ex)
|
18
|
+
Anycable::DisconnectResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
|
19
19
|
end
|
20
20
|
|
21
21
|
def command(*)
|
22
22
|
super
|
23
|
-
rescue StandardError =>
|
24
|
-
|
25
|
-
Anycable::CommandResponse.new(status: Anycable::Status::ERROR)
|
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
|
26
38
|
end
|
27
39
|
end
|
40
|
+
|
41
|
+
Anycable.error_handlers << proc { |e| Anycable.logger.error(e.message) }
|
28
42
|
end
|
29
43
|
end
|
data/lib/anycable/pubsub.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "redis"
|
3
4
|
require "json"
|
4
5
|
|
@@ -8,7 +9,11 @@ module Anycable
|
|
8
9
|
attr_reader :redis_conn
|
9
10
|
|
10
11
|
def initialize
|
11
|
-
|
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)
|
12
17
|
end
|
13
18
|
|
14
19
|
def broadcast(channel, payload)
|
@@ -12,6 +12,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
12
12
|
optional :status, :enum, 1, "anycable.Status"
|
13
13
|
optional :identifiers, :string, 2
|
14
14
|
repeated :transmissions, :string, 3
|
15
|
+
optional :error_msg, :string, 4
|
15
16
|
end
|
16
17
|
add_message "anycable.CommandMessage" do
|
17
18
|
optional :command, :string, 1
|
@@ -25,6 +26,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
25
26
|
optional :stop_streams, :bool, 3
|
26
27
|
repeated :streams, :string, 4
|
27
28
|
repeated :transmissions, :string, 5
|
29
|
+
optional :error_msg, :string, 6
|
28
30
|
end
|
29
31
|
add_message "anycable.DisconnectRequest" do
|
30
32
|
optional :identifiers, :string, 1
|
@@ -34,10 +36,12 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
34
36
|
end
|
35
37
|
add_message "anycable.DisconnectResponse" do
|
36
38
|
optional :status, :enum, 1, "anycable.Status"
|
39
|
+
optional :error_msg, :string, 2
|
37
40
|
end
|
38
41
|
add_enum "anycable.Status" do
|
39
42
|
value :ERROR, 0
|
40
43
|
value :SUCCESS, 1
|
44
|
+
value :FAILURE, 2
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
data/lib/anycable/rpc_handler.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'anycable/socket'
|
3
|
-
require 'anycable/rpc/
|
4
|
-
require 'anycable/rpc/
|
4
|
+
require 'anycable/rpc/rpc_pb'
|
5
|
+
require 'anycable/rpc/rpc_services_pb'
|
5
6
|
|
6
7
|
require 'anycable/handler/exceptions_handling'
|
7
8
|
|
@@ -18,12 +19,12 @@ module Anycable
|
|
18
19
|
|
19
20
|
socket = build_socket(env: rack_env(request))
|
20
21
|
|
21
|
-
connection = factory.
|
22
|
+
connection = factory.call(socket)
|
22
23
|
|
23
24
|
connection.handle_open
|
24
25
|
|
25
26
|
if socket.closed?
|
26
|
-
Anycable::ConnectionResponse.new(status: Anycable::Status::
|
27
|
+
Anycable::ConnectionResponse.new(status: Anycable::Status::FAILURE)
|
27
28
|
else
|
28
29
|
Anycable::ConnectionResponse.new(
|
29
30
|
status: Anycable::Status::SUCCESS,
|
@@ -38,7 +39,7 @@ module Anycable
|
|
38
39
|
|
39
40
|
socket = build_socket(env: rack_env(request))
|
40
41
|
|
41
|
-
connection = factory.
|
42
|
+
connection = factory.call(
|
42
43
|
socket,
|
43
44
|
identifiers: request.identifiers,
|
44
45
|
subscriptions: request.subscriptions
|
@@ -47,7 +48,7 @@ module Anycable
|
|
47
48
|
if connection.handle_close
|
48
49
|
Anycable::DisconnectResponse.new(status: Anycable::Status::SUCCESS)
|
49
50
|
else
|
50
|
-
Anycable::DisconnectResponse.new(status: Anycable::Status::
|
51
|
+
Anycable::DisconnectResponse.new(status: Anycable::Status::FAILURE)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
@@ -56,7 +57,7 @@ module Anycable
|
|
56
57
|
|
57
58
|
socket = build_socket
|
58
59
|
|
59
|
-
connection = factory.
|
60
|
+
connection = factory.call(
|
60
61
|
socket,
|
61
62
|
identifiers: message.connection_identifiers
|
62
63
|
)
|
@@ -68,7 +69,7 @@ module Anycable
|
|
68
69
|
)
|
69
70
|
|
70
71
|
Anycable::CommandResponse.new(
|
71
|
-
status: result ? Anycable::Status::SUCCESS : Anycable::Status::
|
72
|
+
status: result ? Anycable::Status::SUCCESS : Anycable::Status::FAILURE,
|
72
73
|
disconnect: socket.closed?,
|
73
74
|
stop_streams: socket.stop_streams?,
|
74
75
|
streams: socket.streams,
|
@@ -87,24 +88,31 @@ module Anycable
|
|
87
88
|
'PATH_INFO' => uri.path,
|
88
89
|
'SERVER_PORT' => uri.port.to_s,
|
89
90
|
'HTTP_HOST' => uri.host,
|
90
|
-
'HTTP_COOKIE' => request.headers['Cookie'],
|
91
91
|
# Hack to avoid Missing rack.input error
|
92
92
|
'rack.request.form_input' => '',
|
93
93
|
'rack.input' => '',
|
94
94
|
'rack.request.form_hash' => {}
|
95
|
-
}
|
95
|
+
}.merge(build_headers(request.headers))
|
96
96
|
end
|
97
97
|
|
98
98
|
def build_socket(**options)
|
99
99
|
Anycable::Socket.new(**options)
|
100
100
|
end
|
101
101
|
|
102
|
+
def build_headers(headers)
|
103
|
+
headers.each_with_object({}) do |(k, v), obj|
|
104
|
+
k = k.upcase
|
105
|
+
k.tr!('-', '_')
|
106
|
+
obj["HTTP_#{k}"] = v
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
102
110
|
def logger
|
103
111
|
Anycable.logger
|
104
112
|
end
|
105
113
|
|
106
114
|
def factory
|
107
|
-
Anycable.
|
115
|
+
Anycable.connection_factory
|
108
116
|
end
|
109
117
|
end
|
110
118
|
end
|
data/lib/anycable/server.rb
CHANGED
@@ -1,31 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'grpc'
|
3
4
|
require 'anycable/rpc_handler'
|
4
5
|
|
5
|
-
# Set GRPC logger
|
6
|
-
module GRPC
|
7
|
-
def self.logger
|
8
|
-
Anycable.logger
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
6
|
module Anycable
|
13
7
|
# Wrapper over GRPC server
|
14
8
|
module Server
|
15
9
|
class << self
|
16
|
-
|
10
|
+
attr_reader :grpc_server
|
17
11
|
|
18
12
|
def start
|
19
|
-
|
20
|
-
grpc_server
|
21
|
-
|
13
|
+
log_grpc! if Anycable.config.log_grpc
|
14
|
+
@grpc_server ||= build_server
|
15
|
+
|
22
16
|
Anycable.logger.info "RPC server is listening on #{Anycable.config.rpc_host}"
|
23
17
|
grpc_server.run_till_terminated
|
24
18
|
end
|
25
19
|
|
26
20
|
def stop
|
21
|
+
return unless running?
|
27
22
|
@grpc_server.stop
|
28
23
|
end
|
24
|
+
|
25
|
+
def running?
|
26
|
+
grpc_server&.running_state == :running
|
27
|
+
end
|
28
|
+
|
29
|
+
# Enable GRPC logging
|
30
|
+
def log_grpc!
|
31
|
+
GRPC.define_singleton_method(:logger) { Anycable.logger }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def build_server
|
37
|
+
GRPC::RpcServer.new.tap do |server|
|
38
|
+
server.add_http2_port(Anycable.config.rpc_host, :this_port_is_insecure)
|
39
|
+
server.handle(Anycable::RPCHandler)
|
40
|
+
end
|
41
|
+
end
|
29
42
|
end
|
30
43
|
end
|
31
44
|
end
|
data/lib/anycable/socket.rb
CHANGED
data/lib/anycable/version.rb
CHANGED
data/protos/rpc.proto
CHANGED
@@ -11,6 +11,7 @@ service RPC {
|
|
11
11
|
enum Status {
|
12
12
|
ERROR = 0;
|
13
13
|
SUCCESS = 1;
|
14
|
+
FAILURE = 2;
|
14
15
|
}
|
15
16
|
|
16
17
|
message ConnectionRequest {
|
@@ -22,6 +23,7 @@ message ConnectionResponse {
|
|
22
23
|
Status status = 1;
|
23
24
|
string identifiers = 2;
|
24
25
|
repeated string transmissions = 3;
|
26
|
+
string error_msg = 4;
|
25
27
|
}
|
26
28
|
|
27
29
|
message CommandMessage {
|
@@ -37,6 +39,7 @@ message CommandResponse {
|
|
37
39
|
bool stop_streams = 3;
|
38
40
|
repeated string streams = 4;
|
39
41
|
repeated string transmissions = 5;
|
42
|
+
string error_msg = 6;
|
40
43
|
}
|
41
44
|
|
42
45
|
message DisconnectRequest {
|
@@ -48,4 +51,5 @@ message DisconnectRequest {
|
|
48
51
|
|
49
52
|
message DisconnectResponse {
|
50
53
|
Status status = 1;
|
54
|
+
string error_msg = 2;
|
51
55
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: anycable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: anyway_config
|
@@ -16,42 +16,42 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '1.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '1.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: grpc
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.6'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
40
|
+
version: '1.6'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: redis
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
47
|
+
version: '3.2'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
54
|
+
version: '3.2'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '3.5'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.50'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.50'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: simplecov
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,7 +179,13 @@ files:
|
|
165
179
|
- assets/cpu_chart.gif
|
166
180
|
- assets/cpu_chart2.gif
|
167
181
|
- assets/evlms.png
|
182
|
+
- benchmarks/.gitignore
|
168
183
|
- benchmarks/2017-02-12.md
|
184
|
+
- benchmarks/HowTo.md
|
185
|
+
- benchmarks/ansible.cfg
|
186
|
+
- benchmarks/benchmark.yml
|
187
|
+
- benchmarks/hosts
|
188
|
+
- benchmarks/servers.yml
|
169
189
|
- bin/console
|
170
190
|
- bin/setup
|
171
191
|
- circle.yml
|
@@ -173,8 +193,8 @@ files:
|
|
173
193
|
- lib/anycable/config.rb
|
174
194
|
- lib/anycable/handler/exceptions_handling.rb
|
175
195
|
- lib/anycable/pubsub.rb
|
176
|
-
- lib/anycable/rpc/
|
177
|
-
- lib/anycable/rpc/
|
196
|
+
- lib/anycable/rpc/rpc_pb.rb
|
197
|
+
- lib/anycable/rpc/rpc_services_pb.rb
|
178
198
|
- lib/anycable/rpc_handler.rb
|
179
199
|
- lib/anycable/server.rb
|
180
200
|
- lib/anycable/socket.rb
|
@@ -195,12 +215,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
195
215
|
version: '0'
|
196
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
217
|
requirements:
|
198
|
-
- - "
|
218
|
+
- - ">"
|
199
219
|
- !ruby/object:Gem::Version
|
200
|
-
version:
|
220
|
+
version: 1.3.1
|
201
221
|
requirements: []
|
202
222
|
rubyforge_project:
|
203
|
-
rubygems_version: 2.6.
|
223
|
+
rubygems_version: 2.6.13
|
204
224
|
signing_key:
|
205
225
|
specification_version: 4
|
206
226
|
summary: Polyglot replacement for ActionCable server
|