pomelo-citrus-rpc 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +20 -0
  3. data/Rakefile +0 -0
  4. data/citrus-rpc.gemspec +32 -0
  5. data/example/client.rb +43 -0
  6. data/example/remote/test/service.rb +9 -0
  7. data/example/server.rb +20 -0
  8. data/lib/citrus-rpc.rb +16 -0
  9. data/lib/citrus-rpc/rpc-client/client.rb +328 -0
  10. data/lib/citrus-rpc/rpc-client/mailboxes/ws_mailbox.rb +164 -0
  11. data/lib/citrus-rpc/rpc-client/mailstation.rb +363 -0
  12. data/lib/citrus-rpc/rpc-client/proxy.rb +37 -0
  13. data/lib/citrus-rpc/rpc-client/router.rb +63 -0
  14. data/lib/citrus-rpc/rpc-server/acceptors/ws_acceptor.rb +143 -0
  15. data/lib/citrus-rpc/rpc-server/dispatcher.rb +36 -0
  16. data/lib/citrus-rpc/rpc-server/gateway.rb +58 -0
  17. data/lib/citrus-rpc/rpc-server/server.rb +92 -0
  18. data/lib/citrus-rpc/util/constants.rb +20 -0
  19. data/lib/citrus-rpc/util/utils.rb +42 -0
  20. data/lib/citrus-rpc/version.rb +7 -0
  21. data/spec/mock-remote/area/add_one_remote.rb +13 -0
  22. data/spec/mock-remote/area/add_three_remote.rb +9 -0
  23. data/spec/mock-remote/area/who_am_i_remote.rb +9 -0
  24. data/spec/mock-remote/connector/who_am_i_remote.rb +9 -0
  25. data/spec/rpc-client/client_spec.rb +166 -0
  26. data/spec/rpc-client/mailstaion_spec.rb +235 -0
  27. data/spec/rpc-client/proxy_spec.rb +8 -0
  28. data/spec/rpc-client/router_spec.rb +8 -0
  29. data/spec/rpc-client/ws_mailbox_spec.rb +144 -0
  30. data/spec/rpc-server/client/mock-tcp-client.rb +6 -0
  31. data/spec/rpc-server/client/mock-ws-client.rb +48 -0
  32. data/spec/rpc-server/dispatcher_spec.rb +88 -0
  33. data/spec/rpc-server/gateway_spec.rb +206 -0
  34. data/spec/rpc-server/server_spec.rb +79 -0
  35. data/spec/rpc-server/ws_acceptor_spec.rb +138 -0
  36. data/spec/spec_helper.rb +25 -0
  37. metadata +179 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: db99b005f4a5fb8e4c52b4667cf4224dff50551c
4
+ data.tar.gz: d531706c0fa76c8794e9f3d413908de4e83c39b2
5
+ SHA512:
6
+ metadata.gz: dfa5cc94a9eb7f2414a7f846b8e53e62fede90200c8222d513e543a1aa6f582f4b0350a22b0e3743228820471adcd377b235707ee8170dda943fd4ae30b8293f
7
+ data.tar.gz: bb25d5530f8224e71647fa3331d21b17c01ed5c0e9536f1f689af4d37ee0a0dce81cde677b9ab46c6f76d2fdaf62b119fa4f1a44fea192f54f5f8fdf882fc540
@@ -0,0 +1,20 @@
1
+ ## Welcome to pomelo-citrus-rpc
2
+
3
+ pomelo-citrus-rpc is a simple clone of pomelo-rpc written in Ruby using EventMachine.
4
+
5
+ ## Motivation
6
+
7
+ Since NodeJS is influenced by Ruby EventMachine and Python's Twisted model, Ruby should also be able to have its own game server framework like pomelo.
8
+
9
+ Ruby is a very expressive and eloquent programming language. I was an RoR programmer before and I really like Ruby, When developing this project, I have used many skills like meta-programming and they give me the real pleasures.
10
+
11
+ Recently, I would focus on my daily work, so I open source this project and hope to have more people participate in this project.
12
+
13
+ ## Todo
14
+
15
+ This gem is the very first gem I have done in my whole work, it needs to be improved but it already has the ablity to provide the basic infrastructure to other gems, other gems exist in my own repository.
16
+
17
+ ## Links
18
+
19
+ * [EventMachine](https://github.com/eventmachine/eventmachine)
20
+ * [pomelo-rpc](https://github.com/NetEase/pomelo-rpc)
File without changes
@@ -0,0 +1,32 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 8 July 2014
4
+
5
+ $:.push File.expand_path('../lib', __FILE__)
6
+
7
+ require 'citrus-rpc/version'
8
+
9
+ Gem::Specification.new do |spec|
10
+ spec.name = 'pomelo-citrus-rpc'
11
+ spec.version = CitrusRpc::VERSION
12
+ spec.platform = Gem::Platform::RUBY
13
+ spec.authors = ['MinixLi']
14
+ spec.email = 'MinixLi1986@gmail.com'
15
+ spec.description = %q{pomelo-citrus-rpc is a simple clone of pomelo-rpc, it provides the infrastructure of RPC between multi-server processes}
16
+ spec.summary = %q{pomelo-rpc clone written in Ruby using EventMachine}
17
+ spec.homepage = 'https://github.com/minixli/pomelo-citrus-rpc'
18
+ spec.license = 'MIT'
19
+
20
+ spec.files = `git ls-files`.split($/)
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_dependency('eventmachine', '~> 0')
26
+ spec.add_dependency('json', '~> 0')
27
+ spec.add_dependency('websocket-eventmachine-client', '~> 0')
28
+ spec.add_dependency('websocket-eventmachine-server', '~> 0')
29
+
30
+ spec.add_dependency('pomelo-citrus-loader', '~> 0')
31
+ spec.add_dependency('pomelo-citrus-logger', '~> 0')
32
+ end
@@ -0,0 +1,43 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require File.expand_path('../../lib/citrus-rpc', __FILE__)
6
+
7
+ Client = CitrusRpc::RpcClient::Client
8
+
9
+ dirname = File.expand_path File.dirname(__FILE__)
10
+ record = {
11
+ :namespace => 'user',
12
+ :server_type => 'test',
13
+ :path => dirname + '/remote/test'
14
+ }
15
+
16
+ servers = [
17
+ { :id => 'test-server-1', :server_type => 'test', :host => '127.0.0.1', :port => 3333 }
18
+ ]
19
+
20
+ context = { :server_id => 'test-server-1' }
21
+
22
+ route_context = servers
23
+
24
+ router = proc{ |route_param, msg, route_context, &block|
25
+ block.call nil, route_context[0][:id] if block
26
+ }
27
+
28
+ client = Client.new :context => context, :route_context => route_context, :router => router
29
+
30
+ client.add_proxy record
31
+ client.add_servers servers
32
+
33
+ EM.run do
34
+ client.start do |err|
35
+ puts 'rpc client started'
36
+
37
+ route_param = nil
38
+ client.proxies.user.test.serviceRemote.echo(route_param, 'hello') do |err, resp|
39
+ puts err if err
40
+ puts resp
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ class ServiceRemote < Citrus::AppRemote
6
+ def echo msg, &block
7
+ block.call nil, 'echo ' + msg
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require File.expand_path('../../lib/citrus-rpc', __FILE__)
6
+
7
+ Server = CitrusRpc::RpcServer::Server
8
+
9
+ dirname = File.expand_path File.dirname(__FILE__)
10
+ records = [
11
+ { :namespace => 'user', :path => dirname + '/remote/test' }
12
+ ]
13
+ port = 3333
14
+
15
+ server = Server.new :records => records, :port => port
16
+
17
+ EM.run do
18
+ server.start
19
+ puts 'rpc server started'
20
+ end
@@ -0,0 +1,16 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 4 July 2014
4
+
5
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+
7
+ require 'eventmachine'
8
+ require 'json'
9
+ require 'ostruct'
10
+
11
+ require 'citrus-loader'
12
+ require 'citrus-logger'
13
+
14
+ require 'citrus-rpc/util/utils'
15
+ require 'citrus-rpc/rpc-client/client'
16
+ require 'citrus-rpc/rpc-server/server'
@@ -0,0 +1,328 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 4 July 2014
4
+
5
+ require 'citrus-rpc/rpc-client/mailstation'
6
+ require 'citrus-rpc/rpc-client/proxy'
7
+ require 'citrus-rpc/rpc-client/router'
8
+
9
+ module CitrusRpc
10
+ # RpcClient
11
+ #
12
+ #
13
+ module RpcClient
14
+ # Client
15
+ #
16
+ # @example
17
+ #
18
+ # Create a new rpc client
19
+ #
20
+ # client = CitrusRpc::RpcClient::Client.new
21
+ #
22
+ # Add a proxy
23
+ #
24
+ # dirname = File.expand_path File.dirname(__FILE__)
25
+ # client.add_proxy(
26
+ # :namespace => 'user',
27
+ # :server_type => 'test',
28
+ # :path => dirname + '/remote/test' # remote service interface path
29
+ # )
30
+ #
31
+ # Add a remote server
32
+ #
33
+ # client.add_server(
34
+ # :server_id => 'test-server-1',
35
+ # :server_type => 'test',
36
+ # :host => '127.0.0.1',
37
+ # :port => 3333
38
+ # )
39
+ #
40
+ # Do the rpc invoke
41
+ #
42
+ # client.start do |err|
43
+ # client.proxies.sys.connector.WhoAmIRemote.do(nil, 'hello') do |err, resp|
44
+ # ...
45
+ # end
46
+ # end
47
+ #
48
+ class Client
49
+ include CitrusLoader
50
+
51
+ include Proxy
52
+ include Router
53
+
54
+ attr_reader :proxies
55
+
56
+ # Create a new rpc client
57
+ #
58
+ # @param [Hash] args Options
59
+ #
60
+ # @option args [Object] context
61
+ # @option args [Object] route_context
62
+ # @option args [#call] router
63
+ # @option args [String] router_type
64
+ def initialize args={}
65
+ @args = args
66
+
67
+ @context = @args[:context]
68
+ @route_context = @args[:route_context]
69
+
70
+ @router = @args[:router] || method(:df_route)
71
+ @router_type = @args[:router_type]
72
+
73
+ @proxies = OpenStruct.new
74
+ @station = MailStation.new args
75
+
76
+ @state = :state_inited
77
+ end
78
+
79
+ # Start the rpc client which would try to connect the remote servers
80
+ def start
81
+ unless @state == :state_inited
82
+ block_given? and yield Exception.new 'rpc client has started'
83
+ return
84
+ end
85
+
86
+ @station.start { |err|
87
+ if err
88
+ block_given? and yield err
89
+ return
90
+ end
91
+ @state = :state_started
92
+ block_given? and yield
93
+ }
94
+ end
95
+
96
+ # Stop the rpc client
97
+ #
98
+ # @param [Boolean] force
99
+ def stop force=false
100
+ unless @state == :state_started
101
+ return
102
+ end
103
+ @state = :state_closed
104
+ @station.stop force
105
+ end
106
+
107
+ # Add a new proxy to the rpc client which would override the proxy under
108
+ # the same key
109
+ #
110
+ # @param [Hash] record
111
+ def add_proxy record
112
+ return unless record
113
+
114
+ proxy = generate_proxy record
115
+ return unless proxy
116
+
117
+ insert_proxy @proxies, record[:namespace], record[:server_type], proxy
118
+ end
119
+
120
+ # Batch version for add_proxy
121
+ #
122
+ # @param [Array] records
123
+ def add_proxies records
124
+ if records && records.length > 0
125
+ records.each { |record| add_proxy record }
126
+ end
127
+ end
128
+
129
+ # Add new remote server to the rpc client
130
+ #
131
+ # @param [Hash] server
132
+ def add_server server
133
+ @station.add_server server
134
+ end
135
+
136
+ # Batch version for add new remote server
137
+ #
138
+ # @param [Array] servers
139
+ def add_servers servers
140
+ @station.add_servers servers
141
+ end
142
+
143
+ # Remove remote server from the rpc client
144
+ #
145
+ # @param [String] id
146
+ def remove_server id
147
+ @station.remove_server id
148
+ end
149
+
150
+ # Batch version for remove remote server
151
+ #
152
+ # @param [Array] server_ids
153
+ def remove_servers ids
154
+ @station.remove_servers ids
155
+ end
156
+
157
+ # Replace remote servers
158
+ #
159
+ # @param [Array] servers
160
+ def replace_servers servers
161
+ @station.replace_servers servers
162
+ end
163
+
164
+ # Do the rpc invoke directly
165
+ #
166
+ # @param [String] server_id
167
+ # @param [Hash] msg
168
+ def rpc_invoke server_id, msg, &block
169
+ unless @state == :state_started
170
+ block_given? and yield Exception.new 'fail to do rpc invoke for client is not running'
171
+ return
172
+ end
173
+ @station.dispatch server_id, msg, @args, block
174
+ end
175
+
176
+ # Add rpc before filter
177
+ #
178
+ # @param [#call] filter
179
+ def before filter
180
+ @station.before filter
181
+ end
182
+
183
+ # Add rpc after filter
184
+ #
185
+ # @param [#call] filter
186
+ def after filter
187
+ @station.after filter
188
+ end
189
+
190
+ # Add rpc filter
191
+ #
192
+ # @param [#call] filter
193
+ def filter filter
194
+ @station.filter filter
195
+ end
196
+
197
+ # Set rpc filter error handler
198
+ #
199
+ # @param [#call] handler
200
+ def set_error_handler handler
201
+ @station.error_handler = handler
202
+ end
203
+
204
+ private
205
+
206
+ # Generate proxies for remote servers
207
+ #
208
+ # @param [Hash] record
209
+ #
210
+ # @private
211
+ def generate_proxy record
212
+ res = OpenStruct.new;
213
+ remotes = load_app_remote record[:path]
214
+ remotes.each { |service, remote|
215
+ res[service] = create_proxy(
216
+ :remote => remote, :service => service, :attach => record, :proxy_cb => method(:proxy_cb)
217
+ )
218
+ }
219
+ res
220
+ end
221
+
222
+ # Proxy callback
223
+ #
224
+ # @param [String] service
225
+ # @param [Symbol] method
226
+ # @param [Hash] attach
227
+ # @param [Boolean] is_to_specified_server
228
+ #
229
+ # @private
230
+ def proxy_cb service, method, attach, is_to_specified_server, *args, &block
231
+ unless @state == :state_started
232
+ return
233
+ end
234
+ unless args.length > 0
235
+ return
236
+ end
237
+
238
+ route_param = args.shift
239
+ server_type = attach[:server_type]
240
+ msg = { :namespace => attach[:namespace], :server_type => server_type,
241
+ :service => service, :method => method, :args => args }
242
+
243
+ if is_to_specified_server
244
+ rpc_to_specified_server msg, server_type, route_param, &block
245
+ else
246
+ get_route_target(server_type, msg, route_param) { |err, server_id|
247
+ if err
248
+ block_given? and block.call err
249
+ else
250
+ rpc_invoke server_id, msg, &block
251
+ end
252
+ }
253
+ end
254
+ end
255
+
256
+ # Calculate remote target server id for rpc client
257
+ #
258
+ # @param [String] server_type
259
+ # @param [Hash] msg
260
+ # @param [Object] route_param
261
+ #
262
+ # @private
263
+ def get_route_target server_type, msg, route_param, &block
264
+ if @router_type
265
+ router = case @router_type
266
+ when :roundrobin
267
+ method :rr_route
268
+ when :weight_roundrobin
269
+ method :wrr_route
270
+ when :least_active
271
+ method :la_route
272
+ when :consistent_hash
273
+ method :ch_route
274
+ else
275
+ method :rd_route
276
+ end
277
+ router.call(self, server_type, msg) { |err, server_id|
278
+ block_given? and yield err, server_id
279
+ }
280
+ else
281
+ unless @router.respond_to? :call
282
+ block_given? and yield Exception.new 'invalid router method'
283
+ return
284
+ end
285
+ @router.call(route_param, msg, @route_context) { |err, server_id|
286
+ block_given? and yield err, server_id
287
+ }
288
+ end
289
+ end
290
+
291
+ # Rpc to specified server id or servers
292
+ #
293
+ # @param [Hash] msg
294
+ # @param [String] server_type
295
+ # @param [String] route_param
296
+ #
297
+ # @private
298
+ def rpc_to_specified_server msg, server_type, route_param, &block
299
+ unless route_param.instance_of? String
300
+ return
301
+ end
302
+
303
+ if route_param == '*'
304
+ @station.servers.each { |server_id, server|
305
+ if server[:server_type] == server_type
306
+ rpc_invoke server_id, msg, &block
307
+ end
308
+ }
309
+ else
310
+ rpc_invoke route_param, msg, &block
311
+ end
312
+ end
313
+
314
+ # Add proxy into array
315
+ #
316
+ # @param [Object] proxies
317
+ # @param [String] namespace
318
+ # @param [String] server_type
319
+ # @param [Object] proxy
320
+ #
321
+ # @private
322
+ def insert_proxy proxies, namespace, server_type, proxy
323
+ proxies[namespace] ||= OpenStruct.new
324
+ proxies[namespace][server_type] = proxy
325
+ end
326
+ end
327
+ end
328
+ end