pomelo-citrus 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|