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,327 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 29 July 2014
|
4
|
+
|
5
|
+
require 'citrus/common/service/filter_service'
|
6
|
+
require 'citrus/common/service/handler_service'
|
7
|
+
require 'citrus/util/path_util'
|
8
|
+
|
9
|
+
module Citrus
|
10
|
+
# Server
|
11
|
+
#
|
12
|
+
#
|
13
|
+
module Server
|
14
|
+
# Server
|
15
|
+
#
|
16
|
+
#
|
17
|
+
class Server
|
18
|
+
include CitrusLoader
|
19
|
+
include Utils::PathUtil
|
20
|
+
|
21
|
+
# Create a new server
|
22
|
+
#
|
23
|
+
# @param [Object] app
|
24
|
+
def initialize app
|
25
|
+
@app = app
|
26
|
+
|
27
|
+
@global_filter_service = nil
|
28
|
+
@filter_service = nil
|
29
|
+
@handler_service = nil
|
30
|
+
|
31
|
+
@crons = []
|
32
|
+
@jobs = {}
|
33
|
+
@state = :state_inited
|
34
|
+
|
35
|
+
@app.on(:add_crons) { |crons| add_crons crons }
|
36
|
+
@app.on(:remove_crons) { |crons| remove_crons crons }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Start the server
|
40
|
+
def start
|
41
|
+
return unless @state == :state_inited
|
42
|
+
|
43
|
+
@global_filter_service = init_filter true
|
44
|
+
@filter_service = init_filter false
|
45
|
+
@handler_service = init_handler
|
46
|
+
|
47
|
+
@state = :state_started
|
48
|
+
end
|
49
|
+
|
50
|
+
# After the sever start
|
51
|
+
def after_start
|
52
|
+
end
|
53
|
+
|
54
|
+
# Stop the server
|
55
|
+
def stop
|
56
|
+
@state = :state_stoped
|
57
|
+
end
|
58
|
+
|
59
|
+
# Global handler
|
60
|
+
#
|
61
|
+
# @param [Hash] msg
|
62
|
+
# @param [Object] session
|
63
|
+
def global_handle msg, session, &block
|
64
|
+
unless @state == :state_started
|
65
|
+
block_given? and yield Exception.new 'server not started'
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
route_record = parse_route msg['route']
|
70
|
+
unless route_record
|
71
|
+
block_given? and yield Exception.new 'meet unknown route message'
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
dispatch = Proc.new { |err, resp, args|
|
76
|
+
if err
|
77
|
+
handle_error(true, err, msg, session, resp, args) { |err, resp, args|
|
78
|
+
response true, err, msg, session, resp, args, &block
|
79
|
+
}
|
80
|
+
return
|
81
|
+
end
|
82
|
+
|
83
|
+
unless @app.server_type == route_record['server_type']
|
84
|
+
do_forward(msg, session, route_record) { |err, resp, args|
|
85
|
+
response true, err, msg, session, resp, args, &block
|
86
|
+
}
|
87
|
+
else
|
88
|
+
do_handle(msg, session, route_record) { |err, resp, args|
|
89
|
+
response true, err, msg, session, resp, args, &block
|
90
|
+
}
|
91
|
+
end
|
92
|
+
}
|
93
|
+
before_filter true, msg, session, &dispatch
|
94
|
+
end
|
95
|
+
|
96
|
+
# Handle request
|
97
|
+
#
|
98
|
+
# @param [Hash] msg
|
99
|
+
# @param [Object] session
|
100
|
+
def handle msg, session, &block
|
101
|
+
unless @state == :state_started
|
102
|
+
block_given? and yield Exception.new 'server not started'
|
103
|
+
return
|
104
|
+
end
|
105
|
+
|
106
|
+
route_record = parse_route msg['route']
|
107
|
+
do_handle msg, session, route_record, &block
|
108
|
+
end
|
109
|
+
|
110
|
+
# Add crons at runtime
|
111
|
+
#
|
112
|
+
# @param [Array] crons
|
113
|
+
def add_crons crons
|
114
|
+
end
|
115
|
+
|
116
|
+
# Remove crons at runtime
|
117
|
+
#
|
118
|
+
# @param [Array] crons
|
119
|
+
def remove_crons crons
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# Init filter service
|
125
|
+
#
|
126
|
+
# @param [Boolean] is_global
|
127
|
+
#
|
128
|
+
# @private
|
129
|
+
def init_filter is_global
|
130
|
+
service = Common::Service::FilterService.new
|
131
|
+
|
132
|
+
if is_global
|
133
|
+
befores = @app.global_befores
|
134
|
+
afters = @app.global_afters
|
135
|
+
else
|
136
|
+
befores = @app.befores
|
137
|
+
afters = @app.afters
|
138
|
+
end
|
139
|
+
|
140
|
+
befores.each { |before| service.before before }
|
141
|
+
afters.each { |after| service.after after }
|
142
|
+
|
143
|
+
service
|
144
|
+
end
|
145
|
+
|
146
|
+
# Init handler service
|
147
|
+
#
|
148
|
+
# @private
|
149
|
+
def init_handler
|
150
|
+
Common::Service::HandlerService.new @app, load_handlers
|
151
|
+
end
|
152
|
+
|
153
|
+
# Load handlers from current application
|
154
|
+
#
|
155
|
+
# @private
|
156
|
+
def load_handlers
|
157
|
+
handlers = {}
|
158
|
+
path = get_handler_path @app.base, @app.server_type
|
159
|
+
if path
|
160
|
+
klasses = load_app_handler path
|
161
|
+
klasses.each { |klass|
|
162
|
+
handler = klass.name
|
163
|
+
handler[0] = handler[0].downcase
|
164
|
+
handlers[handler] = klass.new @app
|
165
|
+
}
|
166
|
+
end
|
167
|
+
handlers
|
168
|
+
end
|
169
|
+
|
170
|
+
# Fire before filter chain if any
|
171
|
+
#
|
172
|
+
# @param [Boolean] is_global
|
173
|
+
# @param [Hash] msg
|
174
|
+
# @param [Object] session
|
175
|
+
#
|
176
|
+
# @private
|
177
|
+
def before_filter is_global, msg, session, &block
|
178
|
+
if is_global
|
179
|
+
fm = @global_filter_service
|
180
|
+
else
|
181
|
+
fm = @filter_service
|
182
|
+
end
|
183
|
+
if fm
|
184
|
+
fm.before_filter msg, session, &block
|
185
|
+
else
|
186
|
+
block_given? and yield
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Fire after filter chain if any
|
191
|
+
#
|
192
|
+
# @param [Boolean] is_global
|
193
|
+
# @param [Object] err
|
194
|
+
# @param [Hash] msg
|
195
|
+
# @param [Object] session
|
196
|
+
# @param [Hash] resp
|
197
|
+
# @param [Hash] args
|
198
|
+
#
|
199
|
+
# @private
|
200
|
+
def after_filter is_global, err, msg, session, resp, args, &block
|
201
|
+
if is_global
|
202
|
+
fm = @global_filter_service
|
203
|
+
else
|
204
|
+
fm = @filter_service
|
205
|
+
end
|
206
|
+
if fm
|
207
|
+
if is_global
|
208
|
+
fm.after_filter(err, msg, session, resp) {}
|
209
|
+
else
|
210
|
+
fm.after_filter(err, msg, session, resp) {
|
211
|
+
block_given? and yield err, resp, args
|
212
|
+
}
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Pass err to the global error handler if specified
|
218
|
+
#
|
219
|
+
# @param [Boolean] is_global
|
220
|
+
# @param [Object] err
|
221
|
+
# @param [Hash] msg
|
222
|
+
# @param [Object] session
|
223
|
+
# @param [Hash] resp
|
224
|
+
# @param [Hash] args
|
225
|
+
#
|
226
|
+
# @private
|
227
|
+
def handle_error is_global, err, msg, session, resp, args, &block
|
228
|
+
if is_global
|
229
|
+
handler = :global_error_handler
|
230
|
+
else
|
231
|
+
handler = :err_handler
|
232
|
+
end
|
233
|
+
unless @app.respond_to? handler
|
234
|
+
block_given? and yield err, resp, args
|
235
|
+
else
|
236
|
+
@app.send handler err, msg, resp, session, args, &block
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Send response to the client and fire after filter chain if any
|
241
|
+
#
|
242
|
+
# @param [Boolean] is_global
|
243
|
+
# @param [Object] err
|
244
|
+
# @param [Hash] msg
|
245
|
+
# @param [Object] session
|
246
|
+
# @param [Hash] resp
|
247
|
+
# @param [Hash] args
|
248
|
+
#
|
249
|
+
# @private
|
250
|
+
def response is_global, err, msg, session, resp, args, &block
|
251
|
+
if is_global
|
252
|
+
block_given? and yield err, resp, args
|
253
|
+
# after filter should not interfere response
|
254
|
+
after_filter is_global, err, msg, session, resp, args, &block
|
255
|
+
else
|
256
|
+
after_filter is_global, err, msg, session, resp, args, &block
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Parse route string
|
261
|
+
#
|
262
|
+
# @param [String] route
|
263
|
+
#
|
264
|
+
# @private
|
265
|
+
def parse_route route
|
266
|
+
return nil unless route
|
267
|
+
return nil unless (ts = route.split '.').length == 3
|
268
|
+
{
|
269
|
+
'route' => route,
|
270
|
+
'server_type' => ts[0],
|
271
|
+
'handler' => ts[1],
|
272
|
+
'method' => ts[2]
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
# Forward message
|
277
|
+
#
|
278
|
+
# @param [Hash] msg
|
279
|
+
# @param [Object] session
|
280
|
+
# @param [Hash] route_record
|
281
|
+
#
|
282
|
+
# @private
|
283
|
+
def do_forward msg, session, route_record, &block
|
284
|
+
finished = false
|
285
|
+
begin
|
286
|
+
@app.sysrpc[route_record['server_type']].msgRemote.forwardMessage(
|
287
|
+
session, msg, session.export
|
288
|
+
) { |err, resp, args|
|
289
|
+
finished = true
|
290
|
+
block_given? and yield err, resp, args
|
291
|
+
}
|
292
|
+
rescue => err
|
293
|
+
block_given? and yield err unless finished
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# Handle message
|
298
|
+
#
|
299
|
+
# @param [Hash] msg
|
300
|
+
# @param [Object] session
|
301
|
+
# @param [Hash] route_record
|
302
|
+
#
|
303
|
+
# @private
|
304
|
+
def do_handle msg, session, route_record, &block
|
305
|
+
handle = Proc.new { |err, resp, args|
|
306
|
+
if err
|
307
|
+
handle_error(false, err, msg, session, resp, args) { |err, resp, args|
|
308
|
+
response false, err, msg, session, resp, args, &block
|
309
|
+
}
|
310
|
+
return
|
311
|
+
end
|
312
|
+
|
313
|
+
@handler_service.handle(route_record, msg, session) { |err, resp, args|
|
314
|
+
if err
|
315
|
+
handle_error(false, err, msg, session, resp, args) { |err, resp, args|
|
316
|
+
response false, err, msg, session, resp, args, &block
|
317
|
+
}
|
318
|
+
return
|
319
|
+
end
|
320
|
+
response false, err, msg, session, resp, args, &block
|
321
|
+
}
|
322
|
+
}
|
323
|
+
before_filter false, msg, session, &handle
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# Author:: MinixLi (gmail: MinixLi1986)
|
2
|
+
# Homepage:: http://citrus.inspawn.com
|
3
|
+
# Date:: 16 July 2014
|
4
|
+
|
5
|
+
require 'citrus/util/constants'
|
6
|
+
|
7
|
+
module Citrus
|
8
|
+
# Utils
|
9
|
+
#
|
10
|
+
#
|
11
|
+
module Utils
|
12
|
+
# AppUtil
|
13
|
+
#
|
14
|
+
#
|
15
|
+
module AppUtil
|
16
|
+
# Initialize application configuration
|
17
|
+
def default_configuration
|
18
|
+
args = parse_args
|
19
|
+
setup_env args
|
20
|
+
load_master
|
21
|
+
load_servers
|
22
|
+
process_args args
|
23
|
+
config_logger
|
24
|
+
load_lifecycle
|
25
|
+
end
|
26
|
+
|
27
|
+
# Parse command line arguments
|
28
|
+
def parse_args
|
29
|
+
args_map = {:main => $0}
|
30
|
+
ARGV.each { |arg|
|
31
|
+
sep = arg.index('=')
|
32
|
+
|
33
|
+
key = arg[0..sep-1].to_sym
|
34
|
+
val = arg[sep+1..-1]
|
35
|
+
|
36
|
+
if val == 'true'
|
37
|
+
val = true
|
38
|
+
end
|
39
|
+
if val == 'false'
|
40
|
+
val = false
|
41
|
+
end
|
42
|
+
args_map[key] = val
|
43
|
+
}
|
44
|
+
return args_map
|
45
|
+
end
|
46
|
+
|
47
|
+
# Setup enviroment
|
48
|
+
#
|
49
|
+
# @param [Hash] args
|
50
|
+
def setup_env args={}
|
51
|
+
@env = args[:env] ? args[:env].to_sym : :development
|
52
|
+
end
|
53
|
+
|
54
|
+
# Load master info from config/master.json
|
55
|
+
def load_master
|
56
|
+
@master = load_config_file Constants::Filepath::MASTER
|
57
|
+
end
|
58
|
+
|
59
|
+
# Load server info from config/servers.json
|
60
|
+
def load_servers
|
61
|
+
servers = load_config_file Constants::Filepath::SERVER
|
62
|
+
servers.each { |server_type, servers|
|
63
|
+
servers.each { |server|
|
64
|
+
server[:server_type] = server_type.to_s
|
65
|
+
@servers_map[server[:server_id]] = server
|
66
|
+
}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Process server start command
|
71
|
+
#
|
72
|
+
# @param [Hash] args
|
73
|
+
def process_args args={}
|
74
|
+
@type = args[:type] ? args[:type].to_sym : :all
|
75
|
+
@server_type = args[:server_type] ? args[:server_type] : 'master'
|
76
|
+
@server_id = args[:server_id] || @master[:server_id]
|
77
|
+
@start_id = args[:start_id]
|
78
|
+
|
79
|
+
if @server_type == 'master'
|
80
|
+
@cur_server = @master
|
81
|
+
else
|
82
|
+
@cur_server = args
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Configure logger
|
87
|
+
def config_logger
|
88
|
+
end
|
89
|
+
|
90
|
+
# Load life cycle
|
91
|
+
def load_lifecycle
|
92
|
+
end
|
93
|
+
|
94
|
+
# Load config file
|
95
|
+
#
|
96
|
+
# @param [String] filename
|
97
|
+
def load_config_file filename
|
98
|
+
origin_path = File.join @base, filename
|
99
|
+
present_path = File.join @base, Constants::Filepath::CONFIG_DIR, @env.to_s, File.basename(filename)
|
100
|
+
unless File.exists?(origin_path) && file_path = origin_path
|
101
|
+
unless File.exists? present_path && file_path = present_path
|
102
|
+
end
|
103
|
+
end
|
104
|
+
config = {}
|
105
|
+
instance_eval %Q{
|
106
|
+
config = #{File.read file_path}
|
107
|
+
}
|
108
|
+
if file_path == origin_path && config[@env]
|
109
|
+
config = config[@env]
|
110
|
+
end
|
111
|
+
return config
|
112
|
+
end
|
113
|
+
|
114
|
+
# Load default components for application
|
115
|
+
def load_default_components
|
116
|
+
if @server_type == 'master'
|
117
|
+
load Components::Master
|
118
|
+
else
|
119
|
+
load Components::Proxy
|
120
|
+
load Components::Remote if @cur_server[:port]
|
121
|
+
load Components::Connection if frontend?
|
122
|
+
load Components::Connector if frontend?
|
123
|
+
load Components::Session if frontend?
|
124
|
+
load Components::PushScheduler if frontend?
|
125
|
+
load Components::BackendSession
|
126
|
+
load Components::Channel
|
127
|
+
load Components::Server
|
128
|
+
end
|
129
|
+
load Components::Monitor
|
130
|
+
end
|
131
|
+
|
132
|
+
# Load component
|
133
|
+
#
|
134
|
+
# @param [Class] component
|
135
|
+
def load component
|
136
|
+
name = component.name
|
137
|
+
instance_eval %Q{
|
138
|
+
@components[name] = #{component}.new self, @settings[:#{name}_config] || {}
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
# Stop components
|
143
|
+
#
|
144
|
+
# @param [Array] components
|
145
|
+
# @param [Integer] index
|
146
|
+
# @param [Boolean] force
|
147
|
+
def stop_components components, index, force, &block
|
148
|
+
end
|
149
|
+
|
150
|
+
# Apply command to loaded components
|
151
|
+
#
|
152
|
+
# @param [Array] components
|
153
|
+
# @param [Symbol] method
|
154
|
+
def opt_components components, method, &block
|
155
|
+
opt_component nil, components, method, 0, &block
|
156
|
+
end
|
157
|
+
|
158
|
+
# Apply command to loaded component
|
159
|
+
#
|
160
|
+
# @param [Object] err
|
161
|
+
# @param [Array] components
|
162
|
+
# @param [Symbol] method
|
163
|
+
# @param [Integer] index
|
164
|
+
def opt_component err, components, method, index, &block
|
165
|
+
if err || index >= components.length
|
166
|
+
block_given? and yield err
|
167
|
+
return
|
168
|
+
end
|
169
|
+
component = components[index]
|
170
|
+
if component && component.respond_to?(method)
|
171
|
+
component.send method, &proc{ |err|
|
172
|
+
opt_component err, components, method, (index + 1), &block
|
173
|
+
}
|
174
|
+
else
|
175
|
+
opt_component err, components, method, (index + 1), &block
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Register console module
|
180
|
+
#
|
181
|
+
# @param [Class] module_klass
|
182
|
+
# @param [Hash] args
|
183
|
+
def register module_klass, args={}
|
184
|
+
module_id = module_klass.module_id
|
185
|
+
@modules_registered[module_id] = {
|
186
|
+
:module_id => module_id,
|
187
|
+
:module_klass => module_klass,
|
188
|
+
:args => args
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
# Check whether a string is contained in the settings
|
193
|
+
#
|
194
|
+
# @param [String] str
|
195
|
+
# @param [String] settings
|
196
|
+
def contains str, settings
|
197
|
+
return false unless settings.instance_of? String
|
198
|
+
return false if settings.empty?
|
199
|
+
settings.split('|').inject(false) { |r, t| true if str == t }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|