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,37 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 4 July 2014
4
+
5
+ module CitrusRpc
6
+ # RpcClient
7
+ #
8
+ #
9
+ module RpcClient
10
+ # Proxy
11
+ #
12
+ #
13
+ module Proxy
14
+ private
15
+
16
+ # Create proxy
17
+ #
18
+ # @param [Hash] args Options
19
+ #
20
+ # @option args [Class] remote
21
+ # @option args [Hash] attach
22
+ # @option args [#call] proxy_cb
23
+ #
24
+ # @private
25
+ def create_proxy args={}
26
+ res = Object.new
27
+ methods = args[:remote].instance_methods
28
+ methods.each { |method|
29
+ res.define_singleton_method method, proc{ |*inner_args, &block|
30
+ args[:proxy_cb].call args[:service], method, args[:attach], false, *inner_args, &block
31
+ }
32
+ }
33
+ res
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,63 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ module CitrusRpc
6
+ # RpcClient
7
+ #
8
+ #
9
+ module RpcClient
10
+ # Router
11
+ #
12
+ #
13
+ module Router
14
+ # Calculate route info and return an appropriate server id
15
+ #
16
+ # @param [Object] session
17
+ # @param [Hash] msg
18
+ # @param [Object] context
19
+ def df_route session, msg, context, &block
20
+ end
21
+
22
+ # Random algorithm for calculating server id
23
+ #
24
+ # @param [Object] client
25
+ # @param [String] server_type
26
+ # @param [Hash] msg
27
+ def rd_route client, server_type, msg, &block
28
+ end
29
+
30
+ # Round-Robin algorithm for calculating server id
31
+ #
32
+ # @param [Object] client
33
+ # @param [String] server_type
34
+ # @param [Hash] msg
35
+ def rr_route client, server_type, msg, &block
36
+ end
37
+
38
+ # Weight-Round-Robin algorithm for calculating server id
39
+ #
40
+ # @param [Object] client
41
+ # @param [String] server_type
42
+ # @param [Hash] msg
43
+ def wrr_route client, server_type, msg, &block
44
+ end
45
+
46
+ # Least-Active algorithm for calculating server id
47
+ #
48
+ # @param [Object] client
49
+ # @param [String] server_type
50
+ # @param [Hash] msg
51
+ def la_route client, server_type, msg, &block
52
+ end
53
+
54
+ # Consistent-Hash algorithm for calculating server id
55
+ #
56
+ # @param [Object] client
57
+ # @param [String] server_type
58
+ # @param [Hash] msg
59
+ def ch_route client, server_type, msg, &block
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,143 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require 'websocket-eventmachine-server'
6
+
7
+ module CitrusRpc
8
+ # RpcServer
9
+ #
10
+ #
11
+ module RpcServer
12
+ # WsAcceptor
13
+ #
14
+ #
15
+ class WsAcceptor
16
+ include Utils::EventEmitter
17
+
18
+ # Create a new websocket acceptor
19
+ #
20
+ # @param [Hash] args Options
21
+ #
22
+ # @option args [Boolean] buffer_msg
23
+ # @option args [Integer] interval
24
+ def initialize args={}, &block
25
+ @buffer_msg = args[:buffer_msg]
26
+ @interval = args[:interval]
27
+
28
+ @server = nil
29
+ @wss = {}
30
+
31
+ @msg_queues = {}
32
+ @callback = block
33
+
34
+ @listening = false
35
+ @closed = false
36
+ end
37
+
38
+ # Listen on port
39
+ #
40
+ # @param [Integer] port
41
+ def listen port
42
+ raise RuntimeError 'acceptor double listen' if @listening
43
+
44
+ begin
45
+ @server = WebSocket::EventMachine::Server.start(:host => '0.0.0.0', :port => port) { |ws|
46
+ ws.onopen {
47
+ @wss[ws.signature] = ws
48
+ peer_port, peer_host = Socket.unpack_sockaddr_in ws.get_peername
49
+ emit :connection, { :id => ws.signature, :ip => peer_host }
50
+ }
51
+
52
+ ws.onmessage { |msg, type|
53
+ begin
54
+ pkg = JSON.parse msg
55
+ if pkg.instance_of? Array
56
+ process_msgs ws, pkg
57
+ else
58
+ process_msg ws, pkg
59
+ end
60
+ rescue => err
61
+ end
62
+ }
63
+
64
+ ws.onclose {
65
+ @wss.delete ws.signature
66
+ @msg_queues.delete ws.signature
67
+ }
68
+ ws.onerror { |err| emit :error, err }
69
+ }
70
+ rescue => err
71
+ emit :error, err
72
+ end
73
+
74
+ on(:connection) { |obj| ip_filter obj }
75
+ @listening = true
76
+ end
77
+
78
+ # Close the acceptor
79
+ def close
80
+ return unless @listening && !@closed
81
+ EM.stop_server @server
82
+ @closed = true
83
+ emit :closed
84
+ end
85
+
86
+ private
87
+
88
+ # Clone error
89
+ #
90
+ # @private
91
+ def clone_error origin
92
+ { 'msg' => origin.message, 'stack' => nil }
93
+ end
94
+
95
+ # Process message
96
+ #
97
+ # @param [Object] ws
98
+ # @param [Hash] pkg
99
+ #
100
+ # @private
101
+ def process_msg ws, pkg
102
+ @callback.call pkg['msg'] { |*args|
103
+ args.each_with_index { |arg, index|
104
+ args[index] = clone_error arg if arg.is_a? Exception
105
+ }
106
+
107
+ resp = { 'id' => pkg['id'], 'resp' => args }
108
+ if @buffer_msg
109
+ enqueue ws, resp
110
+ else
111
+ ws.send resp.to_json
112
+ end
113
+ }
114
+ end
115
+
116
+ # Batch version for process_msg
117
+ #
118
+ # @param [Object] ws
119
+ # @param [Array] pkgs
120
+ #
121
+ # @private
122
+ def process_msgs ws, pkgs
123
+ pkgs.each { |pkg| process_msg ws, pkg }
124
+ end
125
+
126
+ # Enqueue the response
127
+ #
128
+ # @param [Object] ws
129
+ #
130
+ # @private
131
+ def enqueue ws, resp
132
+ end
133
+
134
+ # ip filter
135
+ #
136
+ # @param [Object] obj
137
+ #
138
+ # @private
139
+ def ip_filter obj
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,36 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ module CitrusRpc
6
+ # RpcServer
7
+ #
8
+ #
9
+ module RpcServer
10
+ # Dispatcher
11
+ #
12
+ #
13
+ module Dispatcher
14
+ # Dispatch message to appropriate service object
15
+ #
16
+ # @param [Hash] msg
17
+ # @param [Hash] services
18
+ def dispatch msg, services, &block
19
+ unless namespace = services[msg['namespace']]
20
+ block.call Exception.new 'no such namespace: ' + msg['namespace']
21
+ return
22
+ end
23
+ unless service = namespace[msg['service']]
24
+ block.call Exception.new 'no such service: ' + msg['service']
25
+ return
26
+ end
27
+ unless service.respond_to? msg['method']
28
+ block.call Exception.new 'no such method: ' + msg['method']
29
+ return
30
+ end
31
+
32
+ service.send msg['method'], *msg['args'], &block
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,58 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ require 'citrus-rpc/rpc-server/acceptors/ws_acceptor'
6
+ require 'citrus-rpc/rpc-server/dispatcher'
7
+
8
+ module CitrusRpc
9
+ # RpcServer
10
+ #
11
+ #
12
+ module RpcServer
13
+ # Gateway
14
+ #
15
+ #
16
+ class Gateway
17
+ include Dispatcher
18
+ include Utils::EventEmitter
19
+
20
+ # Create a gateway
21
+ #
22
+ # @param [Hash] args Options
23
+ #
24
+ # @option args [Integer] port
25
+ # @option args [Class] acceptor_class
26
+ # @option args [Hash] services
27
+ def initialize args={}
28
+ @port = args[:port] || 3050
29
+ @started = false
30
+ @stoped = false
31
+
32
+ @acceptor_class = args[:acceptor_class] || WsAcceptor
33
+ @services = args[:services]
34
+
35
+ @acceptor = @acceptor_class.new(args) { |msg, &block|
36
+ dispatch msg, @services, &block
37
+ }
38
+ end
39
+
40
+ # Start the gateway
41
+ def start
42
+ raise RuntimeError 'gateway already started' if @started
43
+ @started = true
44
+
45
+ @acceptor.on(:error) { |*args| emit :error, *args }
46
+ @acceptor.on(:closed) { |*args| emit :closed, *args }
47
+ @acceptor.listen @port
48
+ end
49
+
50
+ # Stop the gateway
51
+ def stop
52
+ return unless @started && !@stoped
53
+ @stoped = true
54
+ @acceptor.close
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,92 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 4 July 2014
4
+
5
+ require 'citrus-rpc/rpc-server/gateway'
6
+
7
+ module CitrusRpc
8
+ # RpcServer
9
+ #
10
+ #
11
+ module RpcServer
12
+ # Server
13
+ #
14
+ # @example
15
+ #
16
+ # Specifiy remote service interface paths
17
+ #
18
+ # dirname = File.expand_path File.dirname(__FILE__)
19
+ # records = [
20
+ # { :namespace => 'user', :path => dirname + '/remote/test' }
21
+ # ]
22
+ #
23
+ # Create a new rpc server and start it
24
+ #
25
+ # Server.new(:records => records, :port => 3333).start
26
+ #
27
+ class Server
28
+ include CitrusLoader
29
+ include Utils::EventEmitter
30
+
31
+ # Create a new rpc server
32
+ #
33
+ # @param [Hash] args Options
34
+ #
35
+ # @option args [Integer] port
36
+ # @option args [Array] records
37
+ def initialize args={}
38
+ raise ArgumentError, 'server port empty' unless args[:port]
39
+ raise ArgumentError, 'server port must be bigger than zero' unless args[:port] > 0
40
+ raise ArgumentError, 'records empty' unless args[:records]
41
+ raise ArgumentError, 'records must be an array' unless args[:records].respond_to? :to_a
42
+
43
+ @services = {}
44
+
45
+ create_namespace args[:records]
46
+ load_remote_services args[:records], args[:context]
47
+
48
+ args[:services] = @services
49
+
50
+ @gateway = Gateway.new args
51
+ @gateway.on(:error) { |*args| emit :error, *args }
52
+ @gateway.on(:closed) { |*args| emit :closed, *args }
53
+ end
54
+
55
+ # Start the rpc server
56
+ def start
57
+ @gateway.start
58
+ end
59
+
60
+ # Stop the rpc server
61
+ def stop
62
+ @gateway.stop
63
+ end
64
+
65
+ private
66
+
67
+ # Create remote services namespace
68
+ #
69
+ # @param [Array] records
70
+ #
71
+ # @private
72
+ def create_namespace records
73
+ records.each { |record| @services[record[:namespace]] ||= {} }
74
+ end
75
+
76
+ # Load remote services
77
+ #
78
+ # @param [Array] records
79
+ # @param [Object] context
80
+ #
81
+ # @private
82
+ def load_remote_services records, context
83
+ records.each { |record|
84
+ remotes = load_app_remote record[:path]
85
+ remotes.each { |service, remote|
86
+ @services[record[:namespace]][service] = remote.new context
87
+ }
88
+ }
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,20 @@
1
+ # Author:: MinixLi (gmail: MinixLi1986)
2
+ # Homepage:: http://citrus.inspawn.com
3
+ # Date:: 5 July 2014
4
+
5
+ module CitrusRpc
6
+ # Constants
7
+ #
8
+ #
9
+ module Constants
10
+ # DefaultParams
11
+ #
12
+ #
13
+ module DefaultParams
14
+ GraceTimeout = 3
15
+ Interval = 5
16
+ PendingSize = 1000
17
+ Timeout = 30
18
+ end
19
+ end
20
+ end