pomelo-citrus 0.0.1
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 +7 -0
- data/README.md +20 -0
- data/Rakefile +0 -0
- data/citrus.gemspec +35 -0
- data/lib/citrus.rb +18 -0
- data/lib/citrus/application.rb +237 -0
- data/lib/citrus/citrus.rb +27 -0
- data/lib/citrus/common/remote/backend/msg_remote.rb +57 -0
- data/lib/citrus/common/remote/frontend/channel_remote.rb +73 -0
- data/lib/citrus/common/remote/frontend/session_remote.rb +108 -0
- data/lib/citrus/common/service/backend_session_service.rb +265 -0
- data/lib/citrus/common/service/channel_service.rb +485 -0
- data/lib/citrus/common/service/connection_service.rb +71 -0
- data/lib/citrus/common/service/filter_service.rb +92 -0
- data/lib/citrus/common/service/handler_service.rb +63 -0
- data/lib/citrus/common/service/session_service.rb +446 -0
- data/lib/citrus/components/backend_session.rb +32 -0
- data/lib/citrus/components/channel.rb +33 -0
- data/lib/citrus/components/component.rb +19 -0
- data/lib/citrus/components/connection.rb +48 -0
- data/lib/citrus/components/connector.rb +265 -0
- data/lib/citrus/components/master.rb +40 -0
- data/lib/citrus/components/monitor.rb +48 -0
- data/lib/citrus/components/proxy.rb +195 -0
- data/lib/citrus/components/push_scheduler.rb +74 -0
- data/lib/citrus/components/remote.rb +71 -0
- data/lib/citrus/components/server.rb +61 -0
- data/lib/citrus/components/session.rb +41 -0
- data/lib/citrus/connectors/commands/handshake.rb +22 -0
- data/lib/citrus/connectors/commands/heartbeat.rb +22 -0
- data/lib/citrus/connectors/commands/kick.rb +22 -0
- data/lib/citrus/connectors/common/coder.rb +21 -0
- data/lib/citrus/connectors/common/handler.rb +21 -0
- data/lib/citrus/connectors/ws_connector.rb +110 -0
- data/lib/citrus/connectors/ws_socket.rb +75 -0
- data/lib/citrus/filters/handler/handler_filter.rb +19 -0
- data/lib/citrus/filters/handler/too_busy.rb +16 -0
- data/lib/citrus/filters/rpc/rpc_filter.rb +19 -0
- data/lib/citrus/filters/rpc/too_busy.rb +16 -0
- data/lib/citrus/master/master.rb +60 -0
- data/lib/citrus/master/starter.rb +73 -0
- data/lib/citrus/master/watchdog.rb +83 -0
- data/lib/citrus/modules/console.rb +45 -0
- data/lib/citrus/modules/console_module.rb +35 -0
- data/lib/citrus/modules/master_watcher.rb +88 -0
- data/lib/citrus/modules/monitor_watcher.rb +86 -0
- data/lib/citrus/monitor/monitor.rb +61 -0
- data/lib/citrus/push_schedulers/buffer.rb +16 -0
- data/lib/citrus/push_schedulers/direct.rb +76 -0
- data/lib/citrus/server/server.rb +327 -0
- data/lib/citrus/util/app_util.rb +203 -0
- data/lib/citrus/util/constants.rb +19 -0
- data/lib/citrus/util/countdown_latch.rb +42 -0
- data/lib/citrus/util/events.rb +14 -0
- data/lib/citrus/util/module_util.rb +68 -0
- data/lib/citrus/util/path_util.rb +50 -0
- data/lib/citrus/util/utils.rb +49 -0
- data/lib/citrus/version.rb +7 -0
- metadata +241 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 24 July 2014
|
4
|
+
|
5
|
+
module Citrus
|
6
|
+
# Common
|
7
|
+
#
|
8
|
+
#
|
9
|
+
module Common
|
10
|
+
# Remote
|
11
|
+
#
|
12
|
+
#
|
13
|
+
module Remote
|
14
|
+
# Frontend
|
15
|
+
#
|
16
|
+
#
|
17
|
+
module Frontend
|
18
|
+
# SessionRemote
|
19
|
+
#
|
20
|
+
#
|
21
|
+
class SessionRemote
|
22
|
+
# Create a new remote session service
|
23
|
+
#
|
24
|
+
# @param [Object] app
|
25
|
+
def initialize app
|
26
|
+
@app = app
|
27
|
+
end
|
28
|
+
|
29
|
+
# Bind the session with a user id
|
30
|
+
#
|
31
|
+
# @param [Integer] sid
|
32
|
+
# @param [String] uid
|
33
|
+
def bind sid, uid, &block
|
34
|
+
@app.session_service.bind sid, uid, &block
|
35
|
+
end
|
36
|
+
|
37
|
+
# Unbind the session with a user id
|
38
|
+
#
|
39
|
+
# @param [Integer] sid
|
40
|
+
# @param [String] uid
|
41
|
+
def unbind sid, uid, &block
|
42
|
+
@app.session_service.unbind sid, uid, &block
|
43
|
+
end
|
44
|
+
|
45
|
+
# Push the key/value into session
|
46
|
+
#
|
47
|
+
# @param [Integer] sid
|
48
|
+
# @param [String] key
|
49
|
+
# @param [Hash] value
|
50
|
+
def push sid, key, value, &block
|
51
|
+
@app.session_service.import sid, key, value, &block
|
52
|
+
end
|
53
|
+
|
54
|
+
# Push new value for the existed session
|
55
|
+
#
|
56
|
+
# @param [Integer] sid
|
57
|
+
# @param [Hash] settings
|
58
|
+
def pushAll sid, settings, &block
|
59
|
+
@app.session_service.import_all sid, settings, &block
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get session information with session id
|
63
|
+
#
|
64
|
+
# @param [Integer] sid
|
65
|
+
def getBackendSessionBySid sid, &block
|
66
|
+
session = @app.session_service.get sid
|
67
|
+
unless session
|
68
|
+
block_given? and yield
|
69
|
+
return
|
70
|
+
end
|
71
|
+
block_given? and yield nil, session.to_frontend_session.export
|
72
|
+
end
|
73
|
+
|
74
|
+
# Get all the session information the specified user id
|
75
|
+
#
|
76
|
+
# @param [String] uid
|
77
|
+
def getBackendSessionByUid uid, &block
|
78
|
+
sessions = @app.session_service.get_by_uid uid
|
79
|
+
unless session
|
80
|
+
block_given? and yield
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
res = []
|
85
|
+
sessions.each { |session|
|
86
|
+
res << session.to_frontend_session.export
|
87
|
+
}
|
88
|
+
block_given? and yield nil, res
|
89
|
+
end
|
90
|
+
|
91
|
+
# Kick a session by session id
|
92
|
+
#
|
93
|
+
# @param [Integer] sid
|
94
|
+
def kickBySid sid, &block
|
95
|
+
@app.session_service.kick_by_sid sid, &block
|
96
|
+
end
|
97
|
+
|
98
|
+
# Kick sessions by user id
|
99
|
+
#
|
100
|
+
# @param [String] uid
|
101
|
+
def kickByUid uid, &block
|
102
|
+
@app.session_service.kick uid, &block
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 28 July 2014
|
4
|
+
|
5
|
+
module Citrus
|
6
|
+
# Common
|
7
|
+
#
|
8
|
+
#
|
9
|
+
module Common
|
10
|
+
# Service
|
11
|
+
#
|
12
|
+
#
|
13
|
+
module Service
|
14
|
+
# BackendSessionService
|
15
|
+
#
|
16
|
+
#
|
17
|
+
class BackendSessionService
|
18
|
+
#
|
19
|
+
#
|
20
|
+
#
|
21
|
+
EXPORTED_FIELDS = ['id', 'frontend_id', 'uid', 'settings']
|
22
|
+
|
23
|
+
# Initialize the service
|
24
|
+
#
|
25
|
+
# @param [Object] app
|
26
|
+
def initialize app
|
27
|
+
@app = app
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create a new backend session
|
31
|
+
#
|
32
|
+
# @param [Hash] args
|
33
|
+
def create args={}
|
34
|
+
if args.empty?
|
35
|
+
throw Exception.new 'args should not be empty'
|
36
|
+
end
|
37
|
+
BackendSession.new args, self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get backend session by frontend server id and session id
|
41
|
+
#
|
42
|
+
# @param [String] frontend_id
|
43
|
+
# @param [Integer] sid
|
44
|
+
def get frontend_id, sid, &block
|
45
|
+
namespace = 'sys'
|
46
|
+
service = 'sessionRemote'
|
47
|
+
method = 'getBackendSessionBySid'
|
48
|
+
args = [sid]
|
49
|
+
rpc_invoke(frontend_id, namespace, service, method,
|
50
|
+
args, &backend_session_cb.bind(nil, block))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get backend sessions by frontend server id and user id
|
54
|
+
#
|
55
|
+
# @param [String] frontend_id
|
56
|
+
# @param [String] uid
|
57
|
+
def get_by_uid frontend_id, uid, &block
|
58
|
+
namespace = 'sys'
|
59
|
+
service = 'sessionRemote'
|
60
|
+
method = 'getBackendSessionByUid'
|
61
|
+
args = [uid]
|
62
|
+
rpc_invoke(server_id, namespace, service, method,
|
63
|
+
args, &backend_session_cb.bind(nil, block))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Kick a session by session id
|
67
|
+
#
|
68
|
+
# @param [String] frontend_id
|
69
|
+
# @param [Integer] sid
|
70
|
+
def kick_by_sid frontend_id, sid, &block
|
71
|
+
namespace = 'sys'
|
72
|
+
service = 'sessionRemote'
|
73
|
+
method = 'kickBySid'
|
74
|
+
args = [sid]
|
75
|
+
rpc_invoke(frontend_id, namespace, service, method, args, &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Kick sessions by user id
|
79
|
+
#
|
80
|
+
# @param [String] frontend_id
|
81
|
+
# @param [String] uid
|
82
|
+
def kick_by_uid frontend_id, uid, &block
|
83
|
+
namespace = 'sys'
|
84
|
+
service = 'sessionRemote'
|
85
|
+
method = 'kickByUid'
|
86
|
+
args = [uid]
|
87
|
+
rpc_invoke(frontend_id, namespace, service, method, args, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Bind the session with the specified user id
|
91
|
+
#
|
92
|
+
# @param [String] frontend_id
|
93
|
+
# @param [Integer] sid
|
94
|
+
# @param [String] uid
|
95
|
+
def bind frontend_id, sid, uid, &block
|
96
|
+
namespace = 'sys'
|
97
|
+
service = 'sessionRemote'
|
98
|
+
method = 'bind'
|
99
|
+
args = [sid, uid]
|
100
|
+
rpc_invoke(frontend_id, namespace, service, method, args, &block)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Unbind the session with the specified user id
|
104
|
+
#
|
105
|
+
# @param [String] frontend_id
|
106
|
+
# @param [Integer] sid
|
107
|
+
# @param [String] uid
|
108
|
+
def unbind frontend_id, sid, uid, &block
|
109
|
+
namespace = 'sys'
|
110
|
+
service = 'sessionRemote'
|
111
|
+
method = 'unbind'
|
112
|
+
args = [sid, uid]
|
113
|
+
rpc_invoke(frontend_id, namespace, service, method, args, &block)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Push the specified customized change to the frontend internal session
|
117
|
+
#
|
118
|
+
# @param [String] frontend_id
|
119
|
+
# @param [Integer] sid
|
120
|
+
# @param [String] key
|
121
|
+
# @param [Hash] value
|
122
|
+
def push frontend_id, sid, key, value, &block
|
123
|
+
namespace = 'sys'
|
124
|
+
service = 'sessionRemote'
|
125
|
+
method = 'push'
|
126
|
+
args = [sid, key, value]
|
127
|
+
rpc_invoke(frontend_id, namespace, service, method, args, &block)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Push all the customized changes to the frontend internal session
|
131
|
+
#
|
132
|
+
# @param [String] frontend_id
|
133
|
+
# @param [Integer] sid
|
134
|
+
# @param [Hash] settings
|
135
|
+
def push_all frontend_id, sid, settings, &block
|
136
|
+
namespace = 'sys'
|
137
|
+
service = 'sessionRemote'
|
138
|
+
method = 'pushAll'
|
139
|
+
args = [sid, settings]
|
140
|
+
rpc_invoke(frontend_id, namespace, service, method, args, &block)
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# Backend session callback
|
146
|
+
#
|
147
|
+
# @param [#call] block
|
148
|
+
# @param [Object] err
|
149
|
+
# @param [Hash, Array] sinfos
|
150
|
+
#
|
151
|
+
# @private
|
152
|
+
def backend_session_cb block, err, sinfos
|
153
|
+
if err
|
154
|
+
block.respond_to? :call and block.call err
|
155
|
+
return
|
156
|
+
end
|
157
|
+
|
158
|
+
unless sinfos
|
159
|
+
block.respond_to? :call and block.call
|
160
|
+
return
|
161
|
+
end
|
162
|
+
sessions = []
|
163
|
+
if sinfos.instance_of? Array
|
164
|
+
# get_by_uid
|
165
|
+
sinfos.each { |sinfo| sessions << create(sinfo) }
|
166
|
+
else
|
167
|
+
# get
|
168
|
+
sessions = create sinfos
|
169
|
+
end
|
170
|
+
block.respond_to? :call and block.call nil, sessions
|
171
|
+
end
|
172
|
+
|
173
|
+
# Rpc invoke
|
174
|
+
#
|
175
|
+
# @param [String] frontend_id
|
176
|
+
# @param [String] namespace
|
177
|
+
# @param [String] service
|
178
|
+
# @param [String] method
|
179
|
+
# @param [Array] args
|
180
|
+
#
|
181
|
+
# @private
|
182
|
+
def rpc_invoke frontend_id, namespace, service, method, args, &block
|
183
|
+
@app.rpc_invoke(frontend_id, {
|
184
|
+
:namespace => namespace,
|
185
|
+
:service => service,
|
186
|
+
:method => method,
|
187
|
+
:args => args
|
188
|
+
}, &block)
|
189
|
+
end
|
190
|
+
|
191
|
+
# BackendSession
|
192
|
+
#
|
193
|
+
#
|
194
|
+
class BackendSession
|
195
|
+
# Create a new backend session
|
196
|
+
#
|
197
|
+
# @param [Hash] args
|
198
|
+
# @param [Object] service
|
199
|
+
def initialize args={}, service
|
200
|
+
args.each_pair { |key, value|
|
201
|
+
instance_eval %Q{ @#{key} = value }
|
202
|
+
}
|
203
|
+
@session_service = service
|
204
|
+
end
|
205
|
+
|
206
|
+
# Bind current session with the user id
|
207
|
+
#
|
208
|
+
# @param [String] uid
|
209
|
+
def bind uid, &block
|
210
|
+
@session_service.bind(@frontend_id, @id, uid) { |err|
|
211
|
+
@uid = uid unless err
|
212
|
+
block_given? and yield err
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
# Unbind current session with the user id
|
217
|
+
#
|
218
|
+
# @param [String] uid
|
219
|
+
def unbind uid, &block
|
220
|
+
@session_service.unbind(@frontend_id, @id, uid) { |err|
|
221
|
+
@uid = nil unless err
|
222
|
+
block_given? and yield err
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
# Set the key/value into backend session
|
227
|
+
#
|
228
|
+
# @param [String] key
|
229
|
+
# @param [Hash] value
|
230
|
+
def set key, value
|
231
|
+
@settings[key] = value
|
232
|
+
end
|
233
|
+
|
234
|
+
# Get the value from backend session by key
|
235
|
+
#
|
236
|
+
# @param [String] key
|
237
|
+
def get key
|
238
|
+
@settings[key]
|
239
|
+
end
|
240
|
+
|
241
|
+
# Push the key/value in backend session to the front internal session
|
242
|
+
#
|
243
|
+
# @param [String] key
|
244
|
+
def push key, &block
|
245
|
+
@session_service.push @frontend_id, @id, key, get(key), &block
|
246
|
+
end
|
247
|
+
|
248
|
+
# Push all the key/values in backend session to the frontend internal session
|
249
|
+
def push_all &block
|
250
|
+
@session_service.push_all @frontend_id, @id, @settings, &block
|
251
|
+
end
|
252
|
+
|
253
|
+
# Export the key/values for serialization
|
254
|
+
def export
|
255
|
+
res = {}
|
256
|
+
EXPORTED_FIELDS.each { |field|
|
257
|
+
instance_eval %Q{ res['#{field}'] = @#{field} }
|
258
|
+
}
|
259
|
+
res
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,485 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 29 July 2014
|
4
|
+
|
5
|
+
require 'citrus/common/remote/frontend/channel_remote'
|
6
|
+
require 'citrus/util/countdown_latch'
|
7
|
+
|
8
|
+
module Citrus
|
9
|
+
# Common
|
10
|
+
#
|
11
|
+
#
|
12
|
+
module Common
|
13
|
+
# Service
|
14
|
+
#
|
15
|
+
#
|
16
|
+
module Service
|
17
|
+
# ChannelService
|
18
|
+
#
|
19
|
+
#
|
20
|
+
class ChannelService
|
21
|
+
# Util
|
22
|
+
#
|
23
|
+
#
|
24
|
+
module Util
|
25
|
+
private
|
26
|
+
|
27
|
+
# Add uid and server id into group
|
28
|
+
#
|
29
|
+
# @param [String] uid
|
30
|
+
# @param [String] sid
|
31
|
+
# @param [Hash] groups
|
32
|
+
#
|
33
|
+
# @private
|
34
|
+
def add uid, sid, groups
|
35
|
+
unless sid
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
group = groups[sid]
|
40
|
+
group = []; groups[sid] = group unless group
|
41
|
+
|
42
|
+
group << uid; true
|
43
|
+
end
|
44
|
+
|
45
|
+
# Delete element from array
|
46
|
+
#
|
47
|
+
# @param [String] uid
|
48
|
+
# @param [String] sid
|
49
|
+
# @param [Array] group
|
50
|
+
#
|
51
|
+
# @private
|
52
|
+
def delete_from uid, sid, group
|
53
|
+
return true unless group
|
54
|
+
|
55
|
+
group.each { |e|
|
56
|
+
group.delete e; return true if e == uid
|
57
|
+
}
|
58
|
+
return false
|
59
|
+
end
|
60
|
+
|
61
|
+
# Push message by group
|
62
|
+
#
|
63
|
+
# @param [Object] service
|
64
|
+
# @param [String] route
|
65
|
+
# @param [Hash] msg
|
66
|
+
# @param [Hash] groups
|
67
|
+
# @param [Hash] args
|
68
|
+
#
|
69
|
+
# @private
|
70
|
+
def send_message_by_group service, route, msg, groups, args, &block
|
71
|
+
app = service.app
|
72
|
+
namespace = 'sys'
|
73
|
+
service = 'channelRemote'
|
74
|
+
method = 'pushMessage'
|
75
|
+
count = groups.length
|
76
|
+
success_flag = false
|
77
|
+
fail_ids = []
|
78
|
+
|
79
|
+
block_given? and yield if count == 0
|
80
|
+
|
81
|
+
latch = Utils::CountDownLatch.new(count) {
|
82
|
+
unless success_flag
|
83
|
+
block_given? and yield Exception.new 'all uids push message fail'
|
84
|
+
return
|
85
|
+
end
|
86
|
+
block_given? and yield nil, fail_ids
|
87
|
+
}
|
88
|
+
|
89
|
+
rpc_cb = Proc.new { |server_id|
|
90
|
+
Proc.new { |err, fails|
|
91
|
+
if err
|
92
|
+
latch.done
|
93
|
+
return
|
94
|
+
end
|
95
|
+
fail_ids += fails if fails
|
96
|
+
success_flag = true
|
97
|
+
latch.done
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
args = { :type => 'push', :user_args => args || {} }
|
102
|
+
|
103
|
+
send_message = Proc.new { |sid|
|
104
|
+
if sid == app.server_id
|
105
|
+
service.channelRemote.send method, route, msg, groups[sid], args, &rpc_cb.call
|
106
|
+
else
|
107
|
+
app.rpc_invoke(sid, {
|
108
|
+
:namespace => namespace,
|
109
|
+
:service => service,
|
110
|
+
:method => method,
|
111
|
+
:args => [route, msg, groups[sid], args]
|
112
|
+
}, &rpc_cb.call(sid))
|
113
|
+
end
|
114
|
+
}
|
115
|
+
|
116
|
+
groups.each_with_index { |group, sid|
|
117
|
+
if group && group.length > 0
|
118
|
+
send_message sid
|
119
|
+
else
|
120
|
+
EM.next_tick { rpc_cb.call.call }
|
121
|
+
end
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
# Restore channel
|
126
|
+
#
|
127
|
+
# @param [Object] service
|
128
|
+
#
|
129
|
+
# @private
|
130
|
+
def restore_channel service, &block
|
131
|
+
if service.store
|
132
|
+
block_given? and yield
|
133
|
+
return
|
134
|
+
end
|
135
|
+
|
136
|
+
load_all_from_store(service, gen_key(service)) { |err, list|
|
137
|
+
if err
|
138
|
+
block_given? and yield err
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
unless (list.instance_of? Array) && list.lenth > 0
|
143
|
+
block_given? and yield
|
144
|
+
return
|
145
|
+
end
|
146
|
+
|
147
|
+
load_p = Proc.new { |key|
|
148
|
+
load_all_from_store(service, key) { |err, items|
|
149
|
+
items.each { |item|
|
150
|
+
sid, uid = item.split ':'
|
151
|
+
channel = service.channels[name]
|
152
|
+
if add uid, sid, channel.groups
|
153
|
+
channel.records[uid] = { :sid => sid, :uid => uid }
|
154
|
+
end
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
list.each_index { |index|
|
160
|
+
name = list[index][gen_key(service).length+1..-1]
|
161
|
+
service.channels[name] = Channel.new name, service
|
162
|
+
load_p.call list[index]
|
163
|
+
}
|
164
|
+
block_given? and yield
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
# Add to store
|
169
|
+
#
|
170
|
+
# @param [Object] service
|
171
|
+
# @param [String] key
|
172
|
+
# @param [Hash] value
|
173
|
+
#
|
174
|
+
# @private
|
175
|
+
def add_to_store service, key, value
|
176
|
+
if service.store
|
177
|
+
service.store.add(key, value) { |err|
|
178
|
+
if err
|
179
|
+
end
|
180
|
+
}
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Remove from store
|
185
|
+
#
|
186
|
+
# @param [Object] service
|
187
|
+
# @param [String] key
|
188
|
+
# @param [Hash] value
|
189
|
+
#
|
190
|
+
# @private
|
191
|
+
def remove_from_store service, key, value
|
192
|
+
if service.store
|
193
|
+
service.store.remove(key, value) { |err|
|
194
|
+
if err
|
195
|
+
end
|
196
|
+
}
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Load all from store
|
201
|
+
#
|
202
|
+
# @param [Object] service
|
203
|
+
# @param [String] key
|
204
|
+
#
|
205
|
+
# @private
|
206
|
+
def load_all_from_store service, key, &block
|
207
|
+
if service.store
|
208
|
+
service.store.load(key) { |err, list|
|
209
|
+
if err
|
210
|
+
block_given? and yield err
|
211
|
+
else
|
212
|
+
block_given? and yield nil, list
|
213
|
+
end
|
214
|
+
}
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Remove all from store
|
219
|
+
#
|
220
|
+
# @param [Object] service
|
221
|
+
# @param [String] key
|
222
|
+
#
|
223
|
+
# @private
|
224
|
+
def remove_all_from_store service, key
|
225
|
+
if service.store
|
226
|
+
service.store.remove_all(key) { |err|
|
227
|
+
if err
|
228
|
+
end
|
229
|
+
}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Generate key
|
234
|
+
#
|
235
|
+
# @param [Object] service
|
236
|
+
# @param [String] name
|
237
|
+
#
|
238
|
+
# @private
|
239
|
+
def gen_key service, name=''
|
240
|
+
unless name.empty?
|
241
|
+
service.prefix + ':' + service.app.server_id + ':' + name
|
242
|
+
else
|
243
|
+
service.prefix + ':' + service.app.server_id
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Generate value
|
248
|
+
#
|
249
|
+
# @param [String] sid
|
250
|
+
# @param [String] uid
|
251
|
+
#
|
252
|
+
# @private
|
253
|
+
def gen_value sid, uid
|
254
|
+
sid + ':' + uid
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
include Util
|
259
|
+
|
260
|
+
attr_reader :app, :channels, :prefix, :store, :channel_remote
|
261
|
+
|
262
|
+
# Initialize the service
|
263
|
+
#
|
264
|
+
# @param [Object] app
|
265
|
+
# @param [Hash] args
|
266
|
+
def initialize app, args={}
|
267
|
+
@app = app
|
268
|
+
@channels = {}
|
269
|
+
@prefix = args[:prefix]
|
270
|
+
@store = args[:store]
|
271
|
+
@broadcast_filter = args[:broadcast_filter]
|
272
|
+
@channel_remote = Remote::Frontend::ChannelRemote.new app
|
273
|
+
end
|
274
|
+
|
275
|
+
# Start the service
|
276
|
+
def start &block
|
277
|
+
restore_channel self, &block
|
278
|
+
end
|
279
|
+
|
280
|
+
# Create channel with name
|
281
|
+
#
|
282
|
+
# @param [String] name
|
283
|
+
def create_channel name
|
284
|
+
return @channels[name] if @channels[name]
|
285
|
+
|
286
|
+
c = Channel.new name
|
287
|
+
add_to_store self, gen_key(self), gen_key(self, name)
|
288
|
+
@channels[name] = c
|
289
|
+
c
|
290
|
+
end
|
291
|
+
|
292
|
+
# Get channel by name
|
293
|
+
#
|
294
|
+
# @param [String] name
|
295
|
+
# @param [Boolean] create
|
296
|
+
def get_channel name, create=false
|
297
|
+
channel = @channels[name]
|
298
|
+
if !channel && create
|
299
|
+
channel = @channels[name] = Channel.new name
|
300
|
+
add_to_store self, gen_key(self), gen_key(self, name)
|
301
|
+
end
|
302
|
+
channel
|
303
|
+
end
|
304
|
+
|
305
|
+
# Destroy channel by name
|
306
|
+
#
|
307
|
+
# @param [String] name
|
308
|
+
def destroy_channel name
|
309
|
+
@channels.delete name
|
310
|
+
remove_from_store self, gen_key(self), gen_key(self, name)
|
311
|
+
remove_all_from_store self, gen_key(self, name)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Push message by uids
|
315
|
+
#
|
316
|
+
# @param [String] route
|
317
|
+
# @param [Hash] msg
|
318
|
+
# @param [Array] uids
|
319
|
+
# @param [Hash] args
|
320
|
+
def push_message_by_uids route, msg, uids, args, &block
|
321
|
+
unless uids && uids.length != 0
|
322
|
+
block_given? and yield Exception.new 'uids should not be empty'
|
323
|
+
return
|
324
|
+
end
|
325
|
+
|
326
|
+
groups = {}
|
327
|
+
uids.each { |record| add record[:uid], record[:sid], groups }
|
328
|
+
|
329
|
+
send_message_by_group self, route, msg, groups, args, &block
|
330
|
+
end
|
331
|
+
|
332
|
+
# Broadcast message to all the connected clients
|
333
|
+
#
|
334
|
+
# @param [String] server_type
|
335
|
+
# @param [String] route
|
336
|
+
# @param [Hash] message
|
337
|
+
# @param [Hash] args
|
338
|
+
def broadcast server_type, route, msg, args, &block
|
339
|
+
namespace = 'sys'
|
340
|
+
service = 'channelRemote'
|
341
|
+
method = 'broadcast'
|
342
|
+
servers = @app.get_servers_by_type server_type
|
343
|
+
|
344
|
+
unless servers && servers.length != 0
|
345
|
+
# server list is empty
|
346
|
+
block_given? and yield
|
347
|
+
end
|
348
|
+
|
349
|
+
count = servers.length
|
350
|
+
success_flag = false
|
351
|
+
|
352
|
+
latch = Utils::CountDownLatch.new(count) {
|
353
|
+
unless success_flag
|
354
|
+
block_given? and yield Exception.new 'broadcast failed'
|
355
|
+
return
|
356
|
+
end
|
357
|
+
block_given? and yield nil
|
358
|
+
}
|
359
|
+
|
360
|
+
gen_cb = Proc.new { |server_id|
|
361
|
+
Proc.new { |err|
|
362
|
+
if err
|
363
|
+
latch.done
|
364
|
+
return
|
365
|
+
end
|
366
|
+
success_flag = true
|
367
|
+
latch.done
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
send_message = Proc.new { |server_id|
|
372
|
+
if server_id == @app.server_id
|
373
|
+
@channel_remote.send method, route, msg, args, &gen_cb.call
|
374
|
+
else
|
375
|
+
@app.rpc_invoke(server_id, {
|
376
|
+
:namespace => namespace,
|
377
|
+
:service => service,
|
378
|
+
:method => method,
|
379
|
+
:args => [route, msg, args]
|
380
|
+
}, &gen_cb.call(server_id))
|
381
|
+
end
|
382
|
+
}
|
383
|
+
|
384
|
+
args = { :type => 'broadcast', :user_args => args || {} }
|
385
|
+
|
386
|
+
servers.each { |server|
|
387
|
+
send_message server[:server_id]
|
388
|
+
}
|
389
|
+
end
|
390
|
+
|
391
|
+
# Channel
|
392
|
+
#
|
393
|
+
#
|
394
|
+
class Channel
|
395
|
+
include Util
|
396
|
+
|
397
|
+
attr_reader :groups, :user_amount
|
398
|
+
|
399
|
+
# Create a new channel
|
400
|
+
#
|
401
|
+
# @param [String] name
|
402
|
+
# @param [Object] service
|
403
|
+
def initialize name, service
|
404
|
+
@name = name
|
405
|
+
@groups = {}
|
406
|
+
@records = {}
|
407
|
+
@channel_service = service
|
408
|
+
@state = :state_inited
|
409
|
+
@user_amount = 0
|
410
|
+
end
|
411
|
+
|
412
|
+
# Add user to channel
|
413
|
+
#
|
414
|
+
# @param [String] uid
|
415
|
+
# @param [String] sid
|
416
|
+
def add uid, sid
|
417
|
+
return false unless @state == :state_inited
|
418
|
+
|
419
|
+
res = add uid, sid, @groups
|
420
|
+
if res
|
421
|
+
@records[uid] = { :sid => sid, :uid => uid }
|
422
|
+
@user_amount += 1
|
423
|
+
end
|
424
|
+
|
425
|
+
add_to_store @channel_service, gen_key(@channel_service, @name), gen_value(sid, uid)
|
426
|
+
res
|
427
|
+
end
|
428
|
+
|
429
|
+
# Remove user from channel
|
430
|
+
#
|
431
|
+
# @param [String] uid
|
432
|
+
# @param [String] sid
|
433
|
+
def leave uid, sid
|
434
|
+
return unless uid && sid
|
435
|
+
|
436
|
+
@records.delete uid
|
437
|
+
@user_amount -= 1
|
438
|
+
@user_amout = 0 if @user_amount < 0
|
439
|
+
|
440
|
+
remove_from_store @channel_service, gen_key(@channel_service, @name), gen_value(sid, uid)
|
441
|
+
|
442
|
+
res = delete_from uid, sid, @groups[sid]
|
443
|
+
if @groups[sid] && @groups[sid].length == 0
|
444
|
+
@groups.delete sid
|
445
|
+
end
|
446
|
+
res
|
447
|
+
end
|
448
|
+
|
449
|
+
# Get channel members
|
450
|
+
def get_members
|
451
|
+
res = []
|
452
|
+
@groups.each { |group| group.each { |e| res << e } }
|
453
|
+
res
|
454
|
+
end
|
455
|
+
|
456
|
+
# Get member info
|
457
|
+
#
|
458
|
+
# @param [String] uid
|
459
|
+
def get_member uid
|
460
|
+
@records[uid]
|
461
|
+
end
|
462
|
+
|
463
|
+
# Destroy channel
|
464
|
+
def destroy
|
465
|
+
@state = :state_destroyed
|
466
|
+
@channel_service.destroy_channel @name
|
467
|
+
end
|
468
|
+
|
469
|
+
# Push message to all the members in the channel
|
470
|
+
#
|
471
|
+
# @param [String] route
|
472
|
+
# @param [Hash] msg
|
473
|
+
# @param [Hash] args
|
474
|
+
def push_message route, msg, args, &block
|
475
|
+
unless @state == :state_inited
|
476
|
+
block_given? and yield Exception.new 'channel is not running now'
|
477
|
+
return
|
478
|
+
end
|
479
|
+
send_message_by_group @channel_service, route, msg, @groups, args, &block
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|