pomelo-citrus-rpc 0.0.2
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-rpc.gemspec +32 -0
- data/example/client.rb +43 -0
- data/example/remote/test/service.rb +9 -0
- data/example/server.rb +20 -0
- data/lib/citrus-rpc.rb +16 -0
- data/lib/citrus-rpc/rpc-client/client.rb +328 -0
- data/lib/citrus-rpc/rpc-client/mailboxes/ws_mailbox.rb +164 -0
- data/lib/citrus-rpc/rpc-client/mailstation.rb +363 -0
- data/lib/citrus-rpc/rpc-client/proxy.rb +37 -0
- data/lib/citrus-rpc/rpc-client/router.rb +63 -0
- data/lib/citrus-rpc/rpc-server/acceptors/ws_acceptor.rb +143 -0
- data/lib/citrus-rpc/rpc-server/dispatcher.rb +36 -0
- data/lib/citrus-rpc/rpc-server/gateway.rb +58 -0
- data/lib/citrus-rpc/rpc-server/server.rb +92 -0
- data/lib/citrus-rpc/util/constants.rb +20 -0
- data/lib/citrus-rpc/util/utils.rb +42 -0
- data/lib/citrus-rpc/version.rb +7 -0
- data/spec/mock-remote/area/add_one_remote.rb +13 -0
- data/spec/mock-remote/area/add_three_remote.rb +9 -0
- data/spec/mock-remote/area/who_am_i_remote.rb +9 -0
- data/spec/mock-remote/connector/who_am_i_remote.rb +9 -0
- data/spec/rpc-client/client_spec.rb +166 -0
- data/spec/rpc-client/mailstaion_spec.rb +235 -0
- data/spec/rpc-client/proxy_spec.rb +8 -0
- data/spec/rpc-client/router_spec.rb +8 -0
- data/spec/rpc-client/ws_mailbox_spec.rb +144 -0
- data/spec/rpc-server/client/mock-tcp-client.rb +6 -0
- data/spec/rpc-server/client/mock-ws-client.rb +48 -0
- data/spec/rpc-server/dispatcher_spec.rb +88 -0
- data/spec/rpc-server/gateway_spec.rb +206 -0
- data/spec/rpc-server/server_spec.rb +79 -0
- data/spec/rpc-server/ws_acceptor_spec.rb +138 -0
- data/spec/spec_helper.rb +25 -0
- metadata +179 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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)
|
data/Rakefile
ADDED
File without changes
|
data/citrus-rpc.gemspec
ADDED
@@ -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
|
data/example/client.rb
ADDED
@@ -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
|
data/example/server.rb
ADDED
@@ -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
|
data/lib/citrus-rpc.rb
ADDED
@@ -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
|