thrift_server 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +2 -0
- data/Makefile +27 -14
- data/README.md +142 -43
- data/circle.yml +14 -0
- data/echo_client.rb +22 -3
- data/echo_server.rb +23 -16
- data/echo_service.thrift +11 -0
- data/lib/thrift_server.rb +112 -36
- data/lib/thrift_server/instrumentation_middleware.rb +33 -0
- data/lib/thrift_server/log_subscriber.rb +60 -0
- data/lib/thrift_server/publisher.rb +25 -0
- data/lib/thrift_server/rpc_metrics_subscriber.rb +25 -0
- data/lib/thrift_server/server_metrics_subscriber.rb +32 -0
- data/lib/thrift_server/thread_pool_server.rb +115 -0
- data/lib/thrift_server/threaded_server.rb +87 -0
- data/lib/thrift_server/validation_middleware.rb +14 -0
- data/lib/thrift_server/version.rb +2 -2
- data/script/circleci/cache-image +10 -0
- data/test/log_subscriber_test.rb +86 -0
- data/test/processor_test.rb +189 -0
- data/test/rpc_metrics_subscriber_test.rb +44 -0
- data/test/server_metrics_subscriber_test.rb +51 -0
- data/test/support/log_yielder.rb +32 -0
- data/test/support/server_tests.rb +87 -0
- data/test/test_helper.rb +32 -23
- data/test/thread_pool_server_test.rb +101 -0
- data/test/threaded_server_test.rb +56 -0
- data/test/validation_middleware_test.rb +71 -0
- data/thrift_server.gemspec +2 -2
- metadata +34 -22
- data/lib/thrift_server/error_tracking_middleware.rb +0 -12
- data/lib/thrift_server/honeybadger_error_tracker.rb +0 -11
- data/lib/thrift_server/logging_middleware.rb +0 -13
- data/lib/thrift_server/metrics_middleware.rb +0 -16
- data/script/buildbox/ci +0 -5
- data/script/buildbox/step_failed +0 -5
- data/test/acceptance_test.rb +0 -225
- data/test/error_tracking_middleware_test.rb +0 -25
- data/test/honeybadger_error_tracking_test.rb +0 -12
- data/test/logging_middleware_test.rb +0 -42
- data/test/metrics_middleware_test.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c56d48273a394c80954def741cd2a44a285a019
|
4
|
+
data.tar.gz: 81f9248137da46630ed0bb415245db3121cb7087
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfb2867a32d7603d8f511bd35afe3e1df1b8f707c42829a4d9790de1b02e350586d7c237be54bd0b75b5310b6d70a75558762379c2ed13b3c8b6168f95a9f9be
|
7
|
+
data.tar.gz: 7ff824cc62f161ac6d18779b035632a081eefd9cfdb2b66d8d1e9a2745c239f242ccce628b8874fe7214c90778ba4a10afc14051849f151832efbf38f51e00e6
|
data/Dockerfile
CHANGED
data/Makefile
CHANGED
@@ -2,16 +2,22 @@ APP:=thrift_server
|
|
2
2
|
|
3
3
|
.DEFAULT_GOAL:=build
|
4
4
|
|
5
|
-
|
5
|
+
# CircleCI does not support --rm, so if the environment variable has
|
6
|
+
# value, then don't include --rm.
|
7
|
+
ifneq ($(shell echo $$CIRCLECI),)
|
8
|
+
DOCKER_RUN:=docker run -it
|
9
|
+
else
|
10
|
+
DOCKER_RUN:=docker run --rm -it
|
11
|
+
endif
|
6
12
|
|
7
13
|
gen-rb/echo_service.rb: echo_service.thrift
|
8
|
-
|
14
|
+
$(DOCKER_RUN) -v $(CURDIR):/data thrift:0.9.2 \
|
9
15
|
thrift -o /data --gen rb /data/$<
|
10
16
|
|
11
17
|
.PHONY: thrift
|
12
18
|
thrift: gen-rb/echo_service.rb
|
13
19
|
|
14
|
-
tmp/image: Dockerfile
|
20
|
+
tmp/image: Dockerfile lib/thrift_server/version.rb thrift_server.gemspec gen-rb/echo_service.rb
|
15
21
|
docker build -t $(APP) .
|
16
22
|
mkdir -p $(@D)
|
17
23
|
docker inspect -f '{{.Id}}' $(APP) >> $@
|
@@ -19,24 +25,31 @@ tmp/image: Dockerfile Gemfile lib/thrift_server/version.rb thrift_server.gemspec
|
|
19
25
|
.PHONY: build
|
20
26
|
build: tmp/image
|
21
27
|
|
28
|
+
.PHONY: test
|
29
|
+
test: tmp/image
|
30
|
+
$(DOCKER_RUN) -v $(CURDIR):/usr/src/app $(APP) bundle exec rake
|
31
|
+
|
22
32
|
.PHONY: test-unit
|
23
|
-
test-
|
24
|
-
$(
|
33
|
+
test-lib: tmp/image
|
34
|
+
$(DOCKER_RUN) $(APP) bundle exec rake
|
25
35
|
|
26
36
|
.PHONY: test-network
|
27
|
-
test-network: tmp/image gen-rb/echo_service.rb
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
37
|
+
test-network: tmp/image gen-rb/echo_service.rb clean-containers
|
38
|
+
docker run -d --name threaded_server $(APP) ruby echo_server.rb --threaded
|
39
|
+
docker run -d --name thread_pool_server $(APP) ruby echo_server.rb --thread-pool
|
40
|
+
$(DOCKER_RUN) --link threaded_server:server $(APP) ruby echo_client.rb server 9090
|
41
|
+
$(DOCKER_RUN) --link thread_pool_server:server $(APP) ruby echo_client.rb server 9090
|
32
42
|
|
33
43
|
.PHONY: test-ci
|
34
|
-
test-ci: test-
|
44
|
+
test-ci: test-lib test-network
|
45
|
+
|
46
|
+
.PHONY: clean-containers
|
47
|
+
clean-containers:
|
48
|
+
-@docker stop thread_pool_server threaded_server > /dev/null 2>&1
|
49
|
+
-@docker rm -v thread_pool_server threaded_server > /dev/null 2>&1
|
35
50
|
|
36
51
|
.PHONY: clean
|
37
|
-
clean:
|
38
|
-
-docker stop server > /dev/null 2>&1
|
39
|
-
-docker rm -v server > /dev/null 2>&1
|
52
|
+
clean: clean-containers
|
40
53
|
@mkdir -p tmp
|
41
54
|
@touch tmp/image
|
42
55
|
-cat tmp/image | xargs --no-run-if-empty docker rmi
|
data/README.md
CHANGED
@@ -3,13 +3,11 @@
|
|
3
3
|
Encapsulate bolierplate code and functionality for running Thrift
|
4
4
|
servers in Ruby. Bundled functionality:
|
5
5
|
|
6
|
-
*
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
* Framed transport
|
12
|
-
* Thread pool sever
|
6
|
+
* Metrics for server & RPCs
|
7
|
+
* Logging for server events & RPCs
|
8
|
+
* Middleware & pub-sub based approach making it easy to extend
|
9
|
+
* Deep validation on outgoing protocol messages
|
10
|
+
* `Thrift::ThreadPoolServer` & `Thrift::ThreadedServer` support
|
13
11
|
|
14
12
|
## Installation
|
15
13
|
|
@@ -27,31 +25,129 @@ Or install it yourself as:
|
|
27
25
|
|
28
26
|
## Usage
|
29
27
|
|
30
|
-
The
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
The library uses delegation to around a provided handler & Thrift
|
29
|
+
processor to implement use production server behavior. There are two
|
30
|
+
different ways to extend function: pub sub & middleware. Use pub sub
|
31
|
+
for events & middleware when you want to modify the request & response
|
32
|
+
before/after hitting the handler. Out of the box there is not extra
|
33
|
+
behavior. Here's the bare bones useful server.
|
34
34
|
|
35
|
-
|
36
|
-
logger: Logger.new($stdout),
|
37
|
-
statsd: Statsd.new,
|
38
|
-
error_tracker: ErrorTracker.new
|
39
|
-
})
|
35
|
+
server = ThriftServer.threaded EchoService, Handler.new
|
40
36
|
|
41
|
-
|
42
|
-
server.serve
|
37
|
+
server.log Logger.new($stdout)
|
43
38
|
|
44
|
-
|
45
|
-
is your implementation of the define protocol. The options hash
|
46
|
-
defines all the misc objecs and settings. The following options are
|
47
|
-
available:
|
39
|
+
server.start
|
48
40
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
41
|
+
The first arument is a module containing a generated
|
42
|
+
`Thrift::Processor` subclass or the module itself. `ThriftServer`
|
43
|
+
provides factories for building `Thrift::ThreadedServer` and
|
44
|
+
`Thrift::ThreadPoolServer` instances.
|
45
|
+
|
46
|
+
### Threaded Servers
|
47
|
+
|
48
|
+
server = ThriftServer.threaded EchoService, Handler.new
|
49
|
+
|
50
|
+
server.log Logger.new($stdout)
|
51
|
+
|
52
|
+
server.start
|
53
|
+
|
54
|
+
`ThriftServer.threaded` accepts the following options:
|
55
|
+
|
56
|
+
* `port:` - port to run server on. Defaults to `9090`.
|
57
|
+
|
58
|
+
### Thread Pool Servers
|
59
|
+
|
60
|
+
server = ThriftServer.thread_pool EchoService, Handler.new
|
61
|
+
|
62
|
+
server.log Logger.new($stdout)
|
63
|
+
|
64
|
+
server.start
|
65
|
+
|
66
|
+
`ThriftServer.threaded` accepts the following options:
|
67
|
+
|
68
|
+
* `threads:` - Pool size. Defaults to `25`.
|
69
|
+
* `port:` - port to run server on. Defaults to `9090`.
|
70
|
+
|
71
|
+
## Pub Sub
|
72
|
+
|
73
|
+
Subscriber objects may be attached. The following events are
|
74
|
+
published:
|
75
|
+
|
76
|
+
* `rpc_incoming` - published everytime the server receives an RPC
|
77
|
+
* `rpc_ok` - Everything when according to plan
|
78
|
+
* `rpc_exception` - Handler raised an exception defined in the
|
79
|
+
protocol
|
80
|
+
* `rpc_error` - Handler raised an unexpected error (useful for error
|
81
|
+
tracking)
|
82
|
+
* `server_start` - Start started
|
83
|
+
* `server_connection_opened` - Client TCP connection
|
84
|
+
* `server_connection_closed` - Client TCP disconnect
|
85
|
+
* `thread_pool_server_pool_change` - Thread pool grow/shinks
|
86
|
+
|
87
|
+
The listener should implement a method. A listener will only receive
|
88
|
+
notifications if the appropriate method is implemented. Here's an
|
89
|
+
example:
|
90
|
+
|
91
|
+
class Counter
|
92
|
+
def initialize
|
93
|
+
@counter = 0
|
94
|
+
end
|
95
|
+
|
96
|
+
def rpc_incoming(rpc)
|
97
|
+
@counter += 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
server = ThriftServer.threaded EchoService, Handler.new
|
102
|
+
server.subscribe Counter.new
|
103
|
+
|
104
|
+
### Built-in Subscribers
|
105
|
+
|
106
|
+
`ThriftServer` includes three subscribers: two for metrics and one for
|
107
|
+
logging. The `ThriftServer::LogSubscriber` uses a standard library
|
108
|
+
logger to print useful information when any of the previously
|
109
|
+
mentioned events happen. Attaching the subscriber is so important that
|
110
|
+
it has its own helper method.
|
111
|
+
|
112
|
+
server.log Logger.new($stdout)
|
113
|
+
# Same thing as above, just much longer
|
114
|
+
server.subscribe ThriftServer::LogSubscriber.new(Logger.new($stdout))
|
115
|
+
|
116
|
+
The remaining two middlware handle metrics. Each handles a different
|
117
|
+
metric granularity. `ThriftServer::ServerMetricsSubcriber` does stats
|
118
|
+
for all RPCs & server things. `ThriftServer::RpcMetricsSubscriber`
|
119
|
+
gives metrics for each individual RPC. Naturally there are important
|
120
|
+
subscribers and its highly recommend you add them. There is a shortcut
|
121
|
+
method for adding them both. They require a [statsd][] instance. You
|
122
|
+
can customize statsd prefix & postfix on that instance.
|
123
|
+
|
124
|
+
server.metrics Statsd.new
|
125
|
+
# Same thing as above, just much longer
|
126
|
+
server.subscribe ThriftServer::ServerMetricsSubscriber.new(Statsd.new)
|
127
|
+
server.subscribe ThriftServer::RpcMetricsSubscriber.new(Statsd.new)
|
128
|
+
|
129
|
+
`ThriftServer::ServerMetricsSubscriber` instruments the following
|
130
|
+
keys:
|
131
|
+
|
132
|
+
* `rpc.latency` - (timer)
|
133
|
+
* `rpc.incoming` - (counter)
|
134
|
+
* `rpc.success` - (counter) - Everything A-OK!
|
135
|
+
* `rpc.exception` - (counter) - Result was defined protocol exception
|
136
|
+
* `rpc.error` - (counter) - Uncaught errors
|
137
|
+
* `server.pool.size` - (guage) - Number of available threads
|
138
|
+
* `server.connection.active` - (guage) - Threads with active TCP connections
|
139
|
+
|
140
|
+
`ThriftServer::RpcMetricsSubscriber` produces the same metrics, but at
|
141
|
+
an individual RPC level. Assume the RPC is named `foo`.
|
142
|
+
keys:
|
143
|
+
|
144
|
+
* `rpc.foo.latency` (timer)
|
145
|
+
* `rpc.foo.incoming` (counter)
|
146
|
+
* `rpc.foo.success` (counter)
|
147
|
+
* `rpc.foo.exception` (counter)
|
148
|
+
* `rpc.foo.exception.xxx` (counter) - where `xxx` is listed in
|
149
|
+
`throws` in the Thrift IDL.
|
150
|
+
* `rpc.foo.error` (counter)
|
55
151
|
|
56
152
|
## Middleware
|
57
153
|
|
@@ -63,25 +159,26 @@ implement your own middleware easily. The middleware must respond to
|
|
63
159
|
`args`. Here's an example that dumps the `args` to stdout.
|
64
160
|
|
65
161
|
class ExampleMiddleware
|
66
|
-
|
162
|
+
include Concord.new(:app)
|
67
163
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
164
|
+
def call(rpc)
|
165
|
+
puts rpc.args.inspect
|
166
|
+
app.call rpc
|
167
|
+
end
|
72
168
|
end
|
73
169
|
|
74
170
|
New middleware can be added at build time or afterwards.
|
75
171
|
|
76
|
-
|
77
|
-
|
78
|
-
|
172
|
+
ThriftServer.threaded processor, handler, options do |stack|
|
173
|
+
stack.use ExampleMiddlware
|
174
|
+
end
|
79
175
|
|
80
176
|
Middleware can also be added after the server is built
|
81
177
|
|
82
|
-
|
83
|
-
|
84
|
-
|
178
|
+
server = ThriftServer.threaded processor, handler, options
|
179
|
+
server.use ExampleMiddleware
|
180
|
+
|
181
|
+
server.start # start it!
|
85
182
|
|
86
183
|
## Implementation
|
87
184
|
|
@@ -95,10 +192,10 @@ wrap the handler provided in `initialize` in the delegate class.
|
|
95
192
|
|
96
193
|
## Development
|
97
194
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
195
|
+
$ vagrant up
|
196
|
+
$ vagrant ssh
|
197
|
+
$ cd /vagrant
|
198
|
+
$ make test-ci
|
102
199
|
|
103
200
|
## Contributing
|
104
201
|
|
@@ -106,3 +203,5 @@ wrap the handler provided in `initialize` in the delegate class.
|
|
106
203
|
2. Commit your changes (`git commit -am 'Add some feature'`)
|
107
204
|
3. Push to the branch (`git push origin my-new-feature`)
|
108
205
|
4. Create a new Pull Request
|
206
|
+
|
207
|
+
[statsd]: https://github.com/reinh/statsd
|
data/circle.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
machine:
|
2
|
+
services:
|
3
|
+
- docker
|
4
|
+
dependencies:
|
5
|
+
cache_directories:
|
6
|
+
- "tmp/cache"
|
7
|
+
override:
|
8
|
+
- mkdir -p tmp/cache
|
9
|
+
- script/circleci/cache-image tmp/cache/ruby ruby:2.1
|
10
|
+
- script/circleci/cache-image tmp/cache/thrift thrift:0.9.2
|
11
|
+
test:
|
12
|
+
override:
|
13
|
+
- make test-ci
|
14
|
+
- make clean
|
data/echo_client.rb
CHANGED
@@ -23,13 +23,32 @@ transport.open
|
|
23
23
|
protocol = Thrift::BinaryProtocol.new transport
|
24
24
|
client = EchoService::Client.new protocol
|
25
25
|
|
26
|
-
|
26
|
+
response = client.echo "testing"
|
27
27
|
|
28
|
-
|
28
|
+
if response == 'testing'
|
29
|
+
$stdout.puts 'OK'
|
30
|
+
else
|
31
|
+
$stderr.puts 'Message not echoed'
|
32
|
+
abort
|
33
|
+
end
|
34
|
+
|
35
|
+
response = client.structEcho 'testing'
|
29
36
|
|
30
|
-
if
|
37
|
+
if response.message == 'testing'
|
31
38
|
$stdout.puts 'OK'
|
32
39
|
else
|
33
40
|
$stderr.puts 'Message not echoed'
|
34
41
|
abort
|
35
42
|
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
client.structEcho 'exception'
|
46
|
+
rescue EchoException
|
47
|
+
$stdout.puts 'OK'
|
48
|
+
else
|
49
|
+
abort 'Protocol exception lost'
|
50
|
+
end
|
51
|
+
|
52
|
+
client.ping 'hello'
|
53
|
+
|
54
|
+
transport.close
|
data/echo_server.rb
CHANGED
@@ -9,25 +9,32 @@ $LOAD_PATH << "#{__dir__}/gen-rb"
|
|
9
9
|
require 'echo_service'
|
10
10
|
|
11
11
|
class Handler
|
12
|
-
def echo(
|
13
|
-
|
12
|
+
def echo(message)
|
13
|
+
message
|
14
14
|
end
|
15
|
-
end
|
16
15
|
|
17
|
-
|
18
|
-
|
16
|
+
def structEcho(message)
|
17
|
+
case message
|
18
|
+
when /exception/
|
19
|
+
fail EchoException
|
20
|
+
else
|
21
|
+
EchoResponse.new message: message
|
22
|
+
end
|
23
|
+
end
|
19
24
|
|
25
|
+
def ping(message)
|
26
|
+
# async, into the ether!
|
20
27
|
end
|
21
28
|
end
|
22
29
|
|
23
|
-
|
24
|
-
|
25
|
-
server
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
if ARGV.include?('--threaded')
|
31
|
+
server = ThriftServer.threaded EchoService, Handler.new
|
32
|
+
server.log Logger.new($stdout)
|
33
|
+
server.start
|
34
|
+
elsif ARGV.include?('--thread-pool')
|
35
|
+
server = ThriftServer.thread_pool EchoService, Handler.new
|
36
|
+
server.log Logger.new($stdout)
|
37
|
+
server.start
|
38
|
+
else
|
39
|
+
abort 'No server option given'
|
40
|
+
end
|
data/echo_service.thrift
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
exception EchoException { }
|
2
|
+
|
3
|
+
struct EchoResponse {
|
4
|
+
1: required string message
|
5
|
+
}
|
6
|
+
|
1
7
|
service EchoService {
|
2
8
|
string echo(1: string message)
|
9
|
+
|
10
|
+
EchoResponse structEcho(1: string message)
|
11
|
+
throws (1: EchoException echo)
|
12
|
+
|
13
|
+
oneway void ping(1: string message)
|
3
14
|
}
|
data/lib/thrift_server.rb
CHANGED
@@ -1,19 +1,43 @@
|
|
1
1
|
require "thrift_server/version"
|
2
2
|
|
3
3
|
require 'thrift'
|
4
|
+
require 'thrift-validator'
|
4
5
|
require 'middleware'
|
5
6
|
require 'concord'
|
6
7
|
require 'forwardable'
|
7
|
-
require 'honeybadger'
|
8
8
|
require 'statsd-ruby'
|
9
9
|
|
10
|
-
require_relative 'thrift_server/
|
11
|
-
require_relative 'thrift_server/
|
12
|
-
require_relative 'thrift_server/error_tracking_middleware'
|
13
|
-
require_relative 'thrift_server/honeybadger_error_tracker'
|
10
|
+
require_relative 'thrift_server/instrumentation_middleware'
|
11
|
+
require_relative 'thrift_server/validation_middleware'
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
require_relative 'thrift_server/server_metrics_subscriber'
|
14
|
+
require_relative 'thrift_server/rpc_metrics_subscriber'
|
15
|
+
require_relative 'thrift_server/log_subscriber'
|
16
|
+
|
17
|
+
require_relative 'thrift_server/publisher'
|
18
|
+
|
19
|
+
require_relative 'thrift_server/thread_pool_server'
|
20
|
+
require_relative 'thrift_server/threaded_server'
|
21
|
+
|
22
|
+
module ThriftServer
|
23
|
+
RPC = Struct.new(:name, :args, :exceptions) do
|
24
|
+
def initialize(*)
|
25
|
+
super
|
26
|
+
self.exceptions ||= { }
|
27
|
+
end
|
28
|
+
|
29
|
+
def protocol_exception?(ex)
|
30
|
+
exceptions.values.include? ex.class
|
31
|
+
end
|
32
|
+
|
33
|
+
def exception_name(ex)
|
34
|
+
exceptions.invert.fetch ex.class
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
name.to_s
|
39
|
+
end
|
40
|
+
end
|
17
41
|
|
18
42
|
class MiddlewareStack < Middleware::Builder
|
19
43
|
def finalize!
|
@@ -22,7 +46,7 @@ class ThriftServer
|
|
22
46
|
end
|
23
47
|
end
|
24
48
|
|
25
|
-
class
|
49
|
+
class HandlerWrapper
|
26
50
|
class Dispatcher
|
27
51
|
include Concord.new(:app, :handler)
|
28
52
|
|
@@ -33,15 +57,14 @@ class ThriftServer
|
|
33
57
|
|
34
58
|
extend Forwardable
|
35
59
|
|
36
|
-
include Concord.new(:stack, :handler)
|
37
|
-
|
38
|
-
def_delegator :stack, :use
|
60
|
+
include Concord::Public.new(:stack, :publisher, :handler)
|
39
61
|
|
40
62
|
def call(rpc)
|
41
63
|
app.call rpc
|
42
64
|
end
|
43
65
|
|
44
66
|
private
|
67
|
+
|
45
68
|
def app
|
46
69
|
@app ||= finalize_stack!
|
47
70
|
end
|
@@ -53,54 +76,107 @@ class ThriftServer
|
|
53
76
|
end
|
54
77
|
|
55
78
|
class << self
|
56
|
-
def
|
57
|
-
stack = wrap(
|
58
|
-
|
79
|
+
def thread_pool(root, handler, options = { })
|
80
|
+
stack = wrap(root, options).new handler
|
81
|
+
|
82
|
+
threads, port = options.fetch(:threads, 25), options.fetch(:port, 9090)
|
83
|
+
|
84
|
+
transport = Thrift::ServerSocket.new port
|
59
85
|
transport_factory = Thrift::FramedTransportFactory.new
|
60
86
|
|
61
|
-
|
87
|
+
ThreadPoolServer.new(stack, transport, transport_factory, nil, threads).tap do |server|
|
88
|
+
# Assign bookkeeping data that is spread across multiple objects
|
89
|
+
server.port = port
|
90
|
+
|
91
|
+
yield server if block_given?
|
92
|
+
end
|
62
93
|
end
|
63
94
|
|
64
|
-
def
|
65
|
-
|
95
|
+
def threaded(root, handler, options = { })
|
96
|
+
stack = wrap(root, options).new handler
|
97
|
+
|
98
|
+
port = options.fetch :port, 9090
|
99
|
+
|
100
|
+
transport = Thrift::ServerSocket.new port
|
101
|
+
transport_factory = Thrift::FramedTransportFactory.new
|
102
|
+
|
103
|
+
ThreadedServer.new(stack, transport, transport_factory).tap do |server|
|
104
|
+
# Assign bookkeeping data that is spread across multiple objects
|
105
|
+
server.port = port
|
66
106
|
|
67
|
-
|
68
|
-
fail ArgumentError, ':logger required'
|
107
|
+
yield server if block_given?
|
69
108
|
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def wrap(root, options = { })
|
112
|
+
processor = root < ::Thrift::Processor ? root : root.const_get(:Processor)
|
113
|
+
|
114
|
+
processors = processor.ancestors.select do |ancestor|
|
115
|
+
ancestor < ::Thrift::Processor
|
116
|
+
end
|
117
|
+
|
118
|
+
processor_rpcs = processors.each_with_object({ }) do |ancestor, bucket|
|
119
|
+
rpc_methods = ancestor.
|
120
|
+
instance_methods(include_superclass = false).
|
121
|
+
select { |m| m =~ /^process_(.+)$/ }
|
70
122
|
|
71
|
-
|
72
|
-
|
123
|
+
rpc_names = rpc_methods.map do |rpc_method|
|
124
|
+
rpc_method.to_s.match(/^process_(.+)$/)[1]
|
125
|
+
end
|
126
|
+
|
127
|
+
bucket[ancestor] = rpc_names.map(&:to_sym)
|
73
128
|
end
|
74
129
|
|
75
|
-
|
76
|
-
|
130
|
+
rpc_names = processor_rpcs.flat_map do |_, values|
|
131
|
+
values
|
77
132
|
end
|
78
133
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
134
|
+
rpc_protocol_exceptions = processor_rpcs.each_with_object({ }) do |(processor_klass, rpcs), bucket|
|
135
|
+
rpcs.each do |rpc|
|
136
|
+
result_class = rpc.to_s
|
137
|
+
result_class[0] = result_class[0].upcase
|
138
|
+
result_class_name = "#{result_class}_result"
|
139
|
+
|
140
|
+
service_namespace = processor_klass.name.match(/^(.+)::Processor$/)[1]
|
141
|
+
|
142
|
+
fields = Object.const_get "#{service_namespace}::#{result_class_name}::FIELDS"
|
83
143
|
|
84
|
-
|
144
|
+
exception_fields = fields.values.select do |meta|
|
145
|
+
meta.key?(:class) && meta.fetch(:class) < ::Thrift::Exception
|
146
|
+
end
|
147
|
+
|
148
|
+
bucket[rpc] = exception_fields.each_with_object({ }) do |meta, exceptions|
|
149
|
+
exceptions[meta.fetch(:name).to_sym] = meta.fetch(:class)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
publisher = Publisher.new
|
155
|
+
|
156
|
+
stack = MiddlewareStack.new
|
157
|
+
stack.use InstrumentationMiddleware, publisher
|
158
|
+
stack.use ValidationMiddleware
|
85
159
|
|
86
160
|
wrapped = Class.new processor do
|
87
161
|
extend Forwardable
|
88
162
|
|
89
|
-
|
163
|
+
def_delegators :@handler, :stack
|
164
|
+
def_delegators :@handler, :publisher
|
165
|
+
|
166
|
+
def_delegators :stack, :use
|
167
|
+
def_delegators :publisher, :publish, :subscribe
|
90
168
|
|
91
169
|
define_method :initialize do |handler|
|
92
|
-
stack_delegator = Class.new
|
170
|
+
stack_delegator = Class.new HandlerWrapper
|
93
171
|
stack_delegator.module_eval do
|
94
|
-
|
95
|
-
rpc_name
|
96
|
-
|
97
|
-
define_method rpc_name.to_sym do |*args|
|
98
|
-
call RPC.new(rpc_name, args)
|
172
|
+
rpc_names.each do |rpc_name|
|
173
|
+
define_method rpc_name do |*args|
|
174
|
+
call RPC.new(rpc_name, args, rpc_protocol_exceptions.fetch(rpc_name, [ ]))
|
99
175
|
end
|
100
176
|
end
|
101
177
|
end
|
102
178
|
|
103
|
-
super stack_delegator.new(stack, handler)
|
179
|
+
super stack_delegator.new(stack, publisher, handler)
|
104
180
|
end
|
105
181
|
end
|
106
182
|
|