socky-server 0.5.0.beta1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 / 2012-02-16
4
+
5
+ - Performance fixes - sending messages should be now 3-5 times faster(thanks to @piotrmarat)
6
+
3
7
  ## 0.5.0.beta1 / 2011-08-01
4
8
 
5
9
  Socky was rewritten from scratch. From this version it's Rack application and is based on
@@ -105,4 +109,4 @@ And many more - please check [Socky website](http://socky.org) for more or check
105
109
 
106
110
  ## 0.0.6 / 2010-05-24
107
111
 
108
- - initial release
112
+ - initial release
data/README.md CHANGED
@@ -2,21 +2,25 @@
2
2
 
3
3
  ## Installation
4
4
 
5
- $ gem install socky-server --pre
5
+ ``` bash
6
+ $ gem install socky-server --pre
7
+ ```
6
8
 
7
9
  ## Usage
8
10
 
9
11
  Socky server provides two Rack middlewares - WebSocket and HTTP. Each one of them can be used separately, but they can be also used in one process. Example Rackup file could look like that:
10
12
 
11
- require 'socky/server'
12
-
13
- map '/websocket' do
14
- run Socky::Server::WebSocket.new
15
- end
16
-
17
- map '/http' do
18
- run Socky::Server::HTTP.new
19
- end
13
+ ``` ruby
14
+ require 'socky/server'
15
+
16
+ map '/websocket' do
17
+ run Socky::Server::WebSocket.new
18
+ end
19
+
20
+ map '/http' do
21
+ run Socky::Server::HTTP.new
22
+ end
23
+ ```
20
24
 
21
25
  ## Configuration
22
26
 
@@ -26,7 +30,9 @@ Both middlewares accept options as hash. Currently available options are:
26
30
 
27
31
  Hash of supported applications. Each key is application name, each value is application secret. You can use as much applications as you want - each of them will have separate application address created by mixing hostname, middleware address and applicatio name. So i.e. for app "my_app" WebSocket application uri will be:
28
32
 
29
- http://example.org/websocket/my_app
33
+ ```
34
+ http://example.org/websocket/my_app
35
+ ```
30
36
 
31
37
  ### :debug [Boolean]
32
38
 
@@ -40,28 +46,32 @@ Path to YAML config file. Config file should contain hash with exactly the same
40
46
 
41
47
  Create file 'config.ru':
42
48
 
43
- require 'socky/server'
49
+ ``` ruby
50
+ require 'socky/server'
44
51
 
45
- options = {
46
- :debug => true,
47
- :applications => {
48
- :my_app => 'my_secret',
49
- :other_app => 'other_secret'
50
- }
51
- }
52
+ options = {
53
+ :debug => true,
54
+ :applications => {
55
+ :my_app => 'my_secret',
56
+ :other_app => 'other_secret'
57
+ }
58
+ }
52
59
 
53
- map '/websocket' do
54
- run Socky::Server::WebSocket.new options
55
- end
60
+ map '/websocket' do
61
+ run Socky::Server::WebSocket.new options
62
+ end
56
63
 
57
- map '/http' do
58
- use Rack::CommonLogger
59
- run Socky::Server::HTTP.new options
60
- end
64
+ map '/http' do
65
+ use Rack::CommonLogger
66
+ run Socky::Server::HTTP.new options
67
+ end
68
+ ```
61
69
 
62
70
  Run file using Thin:
63
71
 
64
- $ thin -R config.ru -p3001 start
72
+ ``` bash
73
+ $ thin -R config.ru -p3001 start
74
+ ```
65
75
 
66
76
  ## Setting other options
67
77
 
data/Rakefile CHANGED
@@ -2,6 +2,7 @@ require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
4
  require 'rspec/core/rake_task'
5
+ require File.expand_path(File.join(File.dirname(__FILE__), 'tasks/performance'))
5
6
 
6
7
  RSpec::Core::RakeTask.new do |t|
7
8
  t.rspec_opts = ["-c", "-f progress"]
@@ -10,14 +10,15 @@ module Socky
10
10
  module Server
11
11
  ROOT = File.expand_path(File.dirname(__FILE__))
12
12
 
13
- autoload :Application, "#{ROOT}/server/application"
14
- autoload :Channel, "#{ROOT}/server/channel"
15
- autoload :Config, "#{ROOT}/server/config"
16
- autoload :Connection, "#{ROOT}/server/connection"
17
- autoload :HTTP, "#{ROOT}/server/http"
18
- autoload :Logger, "#{ROOT}/server/logger"
19
- autoload :Message, "#{ROOT}/server/message"
20
- autoload :Misc, "#{ROOT}/server/misc"
21
- autoload :WebSocket, "#{ROOT}/server/websocket"
13
+ autoload :Application, "#{ROOT}/server/application"
14
+ autoload :Channel, "#{ROOT}/server/channel"
15
+ autoload :Config, "#{ROOT}/server/config"
16
+ autoload :Connection, "#{ROOT}/server/connection"
17
+ autoload :HTTP, "#{ROOT}/server/http"
18
+ autoload :Logger, "#{ROOT}/server/logger"
19
+ autoload :Message, "#{ROOT}/server/message"
20
+ autoload :Misc, "#{ROOT}/server/misc"
21
+ autoload :WebSocket, "#{ROOT}/server/websocket"
22
+ autoload :CachedJsonHash, "#{ROOT}/server/cached_json_hash"
22
23
  end
23
- end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Socky
2
+ module Server
3
+ class CachedJsonHash < Hash
4
+
5
+ def to_json
6
+ @json ||= super
7
+ end
8
+
9
+ def remove_cache
10
+ @json = nil
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+
@@ -34,8 +34,9 @@ module Socky
34
34
  end
35
35
 
36
36
  def send_data(data, except = nil)
37
+ cached_json_data = CachedJsonHash[data]
37
38
  self.subscribers.each do |subscriber_id, subscriber|
38
- subscriber['connection'].send_data(data) unless subscriber_id == except || !subscriber['read']
39
+ subscriber['connection'].send_data(cached_json_data) unless subscriber_id == except || !subscriber['read']
39
40
  end
40
41
  end
41
42
 
@@ -51,7 +52,7 @@ module Socky
51
52
 
52
53
  def deliver(connection, message)
53
54
  return unless connection.nil? || (subscribers[connection.id] && subscribers[connection.id]['write'])
54
- send_data({ 'event' => message.event, 'channel' => self.name, 'data' => message.user_data })
55
+ send_data('event' => message.event, 'channel' => self.name, 'data' => message.user_data)
55
56
  end
56
57
 
57
58
  protected
@@ -1,5 +1,5 @@
1
1
  module Socky
2
2
  module Server
3
- VERSION = '0.5.0.beta1'
3
+ VERSION = '0.5.0'
4
4
  end
5
5
  end
@@ -11,9 +11,9 @@ Gem::Specification.new do |s|
11
11
  s.homepage = "http://socky.org"
12
12
  s.summary = %q{Socky is a WebSocket server and client for Ruby}
13
13
  s.description = %q{Socky is a WebSocket server and client for Ruby}
14
-
15
- s.add_dependency 'websocket-rack', ">= 0.2.1"
16
- s.add_dependency 'socky-authenticator', '~> 0.5.0.beta5'
14
+
15
+ s.add_dependency 'websocket-rack', ">= 0.3.2"
16
+ s.add_dependency 'socky-authenticator', '~> 0.5.0'
17
17
  s.add_dependency 'json'
18
18
  s.add_development_dependency 'rspec', '~> 2.0'
19
19
 
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'performance_helper'
3
+ require 'benchmark'
4
+
5
+ describe 'Connecting Performance' do
6
+
7
+ it "For x listeners" do
8
+ [1, 10, 100].each do |count|
9
+ results = []
10
+ 5.times do
11
+ @application = Socky::Server::Application.new('test_application', 'test_secret')
12
+ @channel = "presence"
13
+ results << Benchmark.realtime {count.times { add_listener }}
14
+ clean_application
15
+ end
16
+ print_result count, results
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+
23
+
24
+
25
+
26
+
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'performance_helper'
3
+ require 'benchmark'
4
+
5
+ describe 'Sending Many Messages Performance' do
6
+
7
+ it "For 1 listener and x messages" do
8
+ [1, 10, 100].each do |count|
9
+ results = []
10
+ 5.times do
11
+ @channel = "private"
12
+ @application = application_with_listeners(1)
13
+ results << Benchmark.realtime {send_messages(count)}
14
+ clean_application
15
+ end
16
+ print_result count, results
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+
23
+
24
+
25
+
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'performance_helper'
3
+ require 'benchmark'
4
+
5
+ describe 'Sending Message To Many Listeners Performance' do
6
+
7
+ it "For x listeners and 1 message" do
8
+ [1, 10, 100].each do |count|
9
+ results = []
10
+ 5.times do
11
+ @channel = "private"
12
+ @application = application_with_listeners(count)
13
+ results << Benchmark.realtime {send_messages(1)}
14
+ clean_application
15
+ end
16
+ print_result count, results
17
+ end
18
+ end
19
+ end
20
+
21
+
22
+
23
+
24
+
25
+
@@ -0,0 +1,55 @@
1
+ Socky::Server::Channel::Private.class_eval do
2
+ protected
3
+
4
+ def check_auth(connection, message)
5
+ true
6
+ end
7
+
8
+ end
9
+
10
+
11
+ def application_with_listeners count
12
+ @application = Socky::Server::Application.new('test_application', 'test_secret')
13
+ count.times { add_listener }
14
+ add_writer
15
+ @application
16
+ end
17
+
18
+ def add_listener
19
+ websocket = mock_websocket(@application.name)
20
+ env = { 'PATH_INFO' => '/websocket/' + @application.name }
21
+ websocket.on_open(env)
22
+ data = {'event' => 'socky:subscribe', 'channel' => "#{@channel}-channel", 'auth' => 'test_auth'}
23
+ websocket.on_message(env, data)
24
+ websocket
25
+ end
26
+
27
+ def add_writer
28
+ @writer_ws = mock_websocket(@application.name)
29
+ env = { 'PATH_INFO' => '/websocket/' + @application.name }
30
+ @writer_ws.on_open(env)
31
+ data = {'event' => 'socky:subscribe', 'channel' => "#{@channel}-channel", 'write' => 'true', 'auth' => 'test_auth'}
32
+ @writer_ws.on_message(env, data)
33
+ @writer_ws
34
+ end
35
+
36
+ def send_messages count
37
+ env = { 'PATH_INFO' => '/websocket/' + @application.name }
38
+ data = {'event' => 'send', 'channel' => "#{@channel}-channel", 'user_data' => "test_message"}
39
+ count.times { @writer_ws.on_message(env, data) }
40
+ end
41
+
42
+ def clean_application
43
+ @application.connections.values.each {|c| c.destroy}
44
+ Socky::Server::Channel::Private.list['test_application'] = Hash.new
45
+ Socky::Server::Channel::Private.list['test_application'] = Hash.new
46
+ Socky::Server::Application.list.delete('test_application')
47
+ end
48
+
49
+
50
+ def print_result label, results
51
+ avg = results.inject(0.0) { |sum, el| sum + el } / results.size
52
+ puts ["#{label}:".ljust(15), avg].join
53
+ end
54
+
55
+
@@ -8,13 +8,16 @@ def mock_websocket(application)
8
8
  env = {}
9
9
  env['PATH_INFO'] = '/websocket/' + application
10
10
  connection = Socky::Server::WebSocket.new.call(env)
11
- connection.stub!(:send_data)
11
+ send_data_method = connection.method(:send_data)
12
+ connection.stub!(:send_data).and_return do |*args|
13
+ a = send_data_method.call(*args)
14
+ end
12
15
  connection
13
16
  end
14
17
 
15
18
  def mock_connection(application)
16
19
  websocket = mock_websocket(application)
17
-
20
+
18
21
  env = { 'PATH_INFO' => '/websocket/' + application }
19
22
  websocket.on_open(env)
20
23
  end
@@ -24,4 +27,4 @@ def auth_token(socket, channel_name, remains = {})
24
27
  'connection_id' => socket.connection.id,
25
28
  'channel' => channel_name
26
29
  }.merge(remains), :allow_changing_rights => true, :secret => 'test_secret')['auth']
27
- end
30
+ end
@@ -0,0 +1,11 @@
1
+ require 'rspec/core'
2
+ require 'rspec/core/rake_task'
3
+
4
+ namespace :spec do
5
+ desc "Run the performance tests"
6
+ RSpec::Core::RakeTask.new("performance") do |t|
7
+ t.pattern = "./spec/**/*_performance.rb"
8
+ t.rspec_opts = ["-c", "-f documentation"]
9
+ end
10
+ end
11
+
metadata CHANGED
@@ -1,42 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: socky-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.beta1
5
- prerelease: 6
4
+ version: 0.5.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Bernard Potocki
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-01 00:00:00.000000000 +02:00
13
- default_executable:
12
+ date: 2012-02-16 00:00:00.000000000Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: websocket-rack
17
- requirement: &70321806627380 !ruby/object:Gem::Requirement
16
+ requirement: &70158364026200 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ! '>='
21
20
  - !ruby/object:Gem::Version
22
- version: 0.2.1
21
+ version: 0.3.2
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *70321806627380
24
+ version_requirements: *70158364026200
26
25
  - !ruby/object:Gem::Dependency
27
26
  name: socky-authenticator
28
- requirement: &70321806626880 !ruby/object:Gem::Requirement
27
+ requirement: &70158364024620 !ruby/object:Gem::Requirement
29
28
  none: false
30
29
  requirements:
31
30
  - - ~>
32
31
  - !ruby/object:Gem::Version
33
- version: 0.5.0.beta5
32
+ version: 0.5.0
34
33
  type: :runtime
35
34
  prerelease: false
36
- version_requirements: *70321806626880
35
+ version_requirements: *70158364024620
37
36
  - !ruby/object:Gem::Dependency
38
37
  name: json
39
- requirement: &70321806626500 !ruby/object:Gem::Requirement
38
+ requirement: &70158364024140 !ruby/object:Gem::Requirement
40
39
  none: false
41
40
  requirements:
42
41
  - - ! '>='
@@ -44,10 +43,10 @@ dependencies:
44
43
  version: '0'
45
44
  type: :runtime
46
45
  prerelease: false
47
- version_requirements: *70321806626500
46
+ version_requirements: *70158364024140
48
47
  - !ruby/object:Gem::Dependency
49
48
  name: rspec
50
- requirement: &70321806625940 !ruby/object:Gem::Requirement
49
+ requirement: &70158364022880 !ruby/object:Gem::Requirement
51
50
  none: false
52
51
  requirements:
53
52
  - - ~>
@@ -55,7 +54,7 @@ dependencies:
55
54
  version: '2.0'
56
55
  type: :development
57
56
  prerelease: false
58
- version_requirements: *70321806625940
57
+ version_requirements: *70158364022880
59
58
  description: Socky is a WebSocket server and client for Ruby
60
59
  email:
61
60
  - bernard.potocki@imanel.org
@@ -73,6 +72,7 @@ files:
73
72
  - example/config.yml
74
73
  - lib/socky/server.rb
75
74
  - lib/socky/server/application.rb
75
+ - lib/socky/server/cached_json_hash.rb
76
76
  - lib/socky/server/channel.rb
77
77
  - lib/socky/server/channel/base.rb
78
78
  - lib/socky/server/channel/presence.rb
@@ -93,13 +93,17 @@ files:
93
93
  - spec/integration/ws_connection_spec.rb
94
94
  - spec/integration/ws_presence_spec.rb
95
95
  - spec/integration/ws_rights_spec.rb
96
+ - spec/performance/connectin_performance.rb
97
+ - spec/performance/send_many_messages_performance.rb
98
+ - spec/performance/send_message_to_many_listeners_performance.rb
99
+ - spec/performance_helper.rb
96
100
  - spec/spec_helper.rb
97
101
  - spec/support/websocket_application.rb
98
102
  - spec/unit/socky/server/application_spec.rb
99
103
  - spec/unit/socky/server/config_spec.rb
100
104
  - spec/unit/socky/server/connection_spec.rb
101
105
  - spec/unit/socky/server/message_spec.rb
102
- has_rdoc: true
106
+ - tasks/performance.rb
103
107
  homepage: http://socky.org
104
108
  licenses: []
105
109
  post_install_message:
@@ -115,12 +119,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
119
  required_rubygems_version: !ruby/object:Gem::Requirement
116
120
  none: false
117
121
  requirements:
118
- - - ! '>'
122
+ - - ! '>='
119
123
  - !ruby/object:Gem::Version
120
- version: 1.3.1
124
+ version: '0'
121
125
  requirements: []
122
126
  rubyforge_project:
123
- rubygems_version: 1.6.2
127
+ rubygems_version: 1.8.11
124
128
  signing_key:
125
129
  specification_version: 3
126
130
  summary: Socky is a WebSocket server and client for Ruby
@@ -130,6 +134,10 @@ test_files:
130
134
  - spec/integration/ws_connection_spec.rb
131
135
  - spec/integration/ws_presence_spec.rb
132
136
  - spec/integration/ws_rights_spec.rb
137
+ - spec/performance/connectin_performance.rb
138
+ - spec/performance/send_many_messages_performance.rb
139
+ - spec/performance/send_message_to_many_listeners_performance.rb
140
+ - spec/performance_helper.rb
133
141
  - spec/spec_helper.rb
134
142
  - spec/support/websocket_application.rb
135
143
  - spec/unit/socky/server/application_spec.rb