thrift_rack 0.2.5 → 0.2.6
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/Gemfile.lock +10 -10
- data/README.md +202 -4
- data/lib/thrift_rack/active_record.rb +13 -0
- data/lib/thrift_rack/atom.rb +7 -6
- data/lib/thrift_rack/client.rb +54 -22
- data/lib/thrift_rack/downgrade.rb +46 -0
- data/lib/thrift_rack/format_check.rb +1 -1
- data/lib/thrift_rack/http_client_transport.rb +24 -15
- data/lib/thrift_rack/logger.rb +25 -13
- data/lib/thrift_rack/ping.rb +1 -1
- data/lib/thrift_rack/server.rb +5 -1
- data/lib/thrift_rack/server_metric.rb +28 -0
- data/lib/thrift_rack/version.rb +1 -1
- data/lib/thrift_rack.rb +29 -13
- data/thrift_rack.gemspec +2 -2
- metadata +13 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f79e2c9d1ce63045d0fb7042f7109911a632fec51c7e09a9c8edde7958447b5
|
4
|
+
data.tar.gz: 6f421077a9a66de076519bcfe42beb9f0316e928b36402bad86dea39ba71afee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27020172c648b060af7a24b08f23d9ab8a3a1cf6a1b8c24a8b5f9547b2bd3ccbb2be5fdecb2b8caa5b7696800b160e82268f0bfb30ed340a7fa80713b198e638
|
7
|
+
data.tar.gz: 49cf0c76cf8f38f4d68ac06584fbe4e9b2e0a778c73926ac345f37885452482fbae10d81724392befe19de170f1021d85e87c0fd54b63d2f9968389b8be379e7
|
data/Gemfile.lock
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
thrift_rack (0.2.
|
4
|
+
thrift_rack (0.2.6)
|
5
5
|
net-http-persistent (>= 3.0)
|
6
6
|
rack (>= 2.0.6)
|
7
7
|
redis (>= 3.0)
|
8
|
-
thrift (~> 0.10
|
8
|
+
thrift (~> 0.10)
|
9
9
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
13
|
coderay (1.1.2)
|
14
|
-
connection_pool (2.2.
|
14
|
+
connection_pool (2.2.5)
|
15
15
|
method_source (0.9.0)
|
16
|
-
net-http-persistent (
|
16
|
+
net-http-persistent (4.0.1)
|
17
17
|
connection_pool (~> 2.2)
|
18
18
|
pry (0.11.3)
|
19
19
|
coderay (~> 1.1.0)
|
20
20
|
method_source (~> 0.9.0)
|
21
|
-
rack (2.
|
22
|
-
rake (
|
23
|
-
redis (4.
|
24
|
-
thrift (0.
|
21
|
+
rack (2.2.3)
|
22
|
+
rake (13.0.1)
|
23
|
+
redis (4.4.0)
|
24
|
+
thrift (0.14.2)
|
25
25
|
|
26
26
|
PLATFORMS
|
27
27
|
ruby
|
@@ -29,8 +29,8 @@ PLATFORMS
|
|
29
29
|
DEPENDENCIES
|
30
30
|
bundler (~> 1.16)
|
31
31
|
pry (~> 0)
|
32
|
-
rake (~>
|
32
|
+
rake (~> 13.0)
|
33
33
|
thrift_rack!
|
34
34
|
|
35
35
|
BUNDLED WITH
|
36
|
-
1.
|
36
|
+
1.17.3
|
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# ThriftRack
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
ThriftRack implements [thrift]([https://thrift.apache.org](https://thrift.apache.org/)) `Compact Protocol + HTTP Transport ` with rack, and makes it easy to write a server with convention.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -22,7 +20,207 @@ Or install it yourself as:
|
|
22
20
|
|
23
21
|
## Usage
|
24
22
|
|
25
|
-
|
23
|
+
### Server
|
24
|
+
|
25
|
+
Assume you use rails only with `activemodel`、`activerecord`、`activesupport`. Because ThriftRack act as controller and view, you do not need `actionview` and `actionpack`
|
26
|
+
|
27
|
+
You cloud new a rails app use this command
|
28
|
+
|
29
|
+
```bash
|
30
|
+
rails new demo --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-active-record --skip-active-storage --skip-action-cable --skip-sprockets --skip-javascript --skip-turbolinks --skip-test --skip-system-test --skip-bootsnap --api --skip-webpack-install
|
31
|
+
```
|
32
|
+
|
33
|
+
Suppose you has a thrift named `math.thrift` to add two number:
|
34
|
+
|
35
|
+
```thrift
|
36
|
+
# math.thrift
|
37
|
+
namespace rb thrift.math # namesapce should same with filename
|
38
|
+
|
39
|
+
service MathService { # Math is capitalized from Math
|
40
|
+
int add(1: int i, 2: int j)
|
41
|
+
}
|
42
|
+
```
|
43
|
+
|
44
|
+
then you should implement this thrift in `math_server.rb`
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class MathServer < ThriftRack::Server
|
48
|
+
def add(i, j)
|
49
|
+
i + j
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Then change the `config.ru`
|
55
|
+
|
56
|
+
```
|
57
|
+
require_relative 'config/environment'
|
58
|
+
app = ThriftRack.app(ThriftRack::Server.children)
|
59
|
+
run app
|
60
|
+
```
|
61
|
+
|
62
|
+
Another config under initializers
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
#initializers/thrift.rb
|
66
|
+
Dir["#{Rails.root}/lib/thrift/**/*.rb"].each { |file| require file } # support generate thrift files under lib/thrift
|
67
|
+
Dir["#{Rails.root}/app/servers/*.rb"].each { |file| require file } # support servers under app/servers
|
68
|
+
|
69
|
+
ThriftRack.redis = Redis.new
|
70
|
+
ThriftRack::Logger.tag = { key: value} # tag to logs
|
71
|
+
|
72
|
+
at_exit do
|
73
|
+
ThriftRack::Logger.logger.close
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
Then run this bash
|
78
|
+
|
79
|
+
```
|
80
|
+
rackup
|
81
|
+
```
|
82
|
+
|
83
|
+
what happend?
|
84
|
+
|
85
|
+
* We implement the math.thrift with `POST` method at `/math` path
|
86
|
+
* A Server seems like a controller, except that you do not need to write routes.
|
87
|
+
* We replace the default rails rack entrance, but it still a rack server. **You could use puma to web server, and enjoy puma's advantage**.
|
88
|
+
* Client will auto retry with network jitter, Atom ensure each request at most processed once.
|
89
|
+
* `/ping` should be use to health check, all request to `/ping` will not be log
|
90
|
+
* `ThriftRack::Logger` log each rpc request with json format. You could use ELK to analyze each request
|
91
|
+
|
92
|
+
### Client
|
93
|
+
|
94
|
+
client cloud write like this
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
class Math
|
98
|
+
attr_accessor :client
|
99
|
+
def initialize(request_id = "no-request-id") # web should has one request_id
|
100
|
+
@client = ThriftRack::Client.new("http://127.0.0.1:300/math", ThriftClientClass, request_id)
|
101
|
+
end
|
102
|
+
|
103
|
+
def add(i, j)
|
104
|
+
self.client.add(i, j)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
Another config under initializers
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
#initializers/thrift.rb
|
113
|
+
Dir["#{Rails.root}/lib/thrift/**/*.rb"].each { |file| require file }
|
114
|
+
|
115
|
+
ThriftRack::Client.config Rails.application.class.parent.name.underscore
|
116
|
+
```
|
117
|
+
|
118
|
+
what happend?
|
119
|
+
|
120
|
+
* Client will auto generate a `rpc_id` to tracer rpc request
|
121
|
+
* Client will add app_name to header
|
122
|
+
* Client use HTTP 1.1, wich persistent tcp connection with multi http requests
|
123
|
+
* Client has a connection pool, network error will auto retry
|
124
|
+
* Client will log each rpc process time
|
125
|
+
|
126
|
+
#### Furthermore
|
127
|
+
|
128
|
+
you chould write a supperclass like under, other client inherit superclass
|
129
|
+
|
130
|
+
1. you could write a around_action to set request
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class ApplicationController < ActionController::API
|
134
|
+
around_action :set_thread_local_request
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def set_thread_local_request
|
139
|
+
Thread.current["request"] = request
|
140
|
+
yield
|
141
|
+
ensure
|
142
|
+
Thread.current["request"] = nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
2. write a superclass
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class ClientBase
|
151
|
+
def initialize(req = nil)
|
152
|
+
if req
|
153
|
+
@request_id = req.request_id
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def request_id
|
158
|
+
@request_id ||= Thread.current["request"] ? Thread.current["request"].request_id : "no-request-id"
|
159
|
+
end
|
160
|
+
|
161
|
+
def client
|
162
|
+
ThriftRack::Client.new("http:127.0.0.1:3000/#{_namespace.underscore}", _client_class, request_id)
|
163
|
+
end
|
164
|
+
|
165
|
+
def respond_to_missing?(method, include_private = false)
|
166
|
+
self.client.respond_to?(method)
|
167
|
+
end
|
168
|
+
|
169
|
+
def method_missing(method, *args)
|
170
|
+
return super unless self.client.respond_to?(method)
|
171
|
+
self.class_eval do
|
172
|
+
define_method method.to_sym do |*params|
|
173
|
+
self.client.public_send(method, *params)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
self.public_send(method, *args)
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def _namespace
|
182
|
+
@namespace ||= self.class.name
|
183
|
+
end
|
184
|
+
|
185
|
+
def _client_class
|
186
|
+
"Thrift::#{_namespace}::#{_namespace}Service::Client".constantize
|
187
|
+
end
|
188
|
+
|
189
|
+
class << self
|
190
|
+
def default
|
191
|
+
self.new
|
192
|
+
end
|
193
|
+
|
194
|
+
def respond_to_missing?(method, include_private = false)
|
195
|
+
self.default.respond_to?(method)
|
196
|
+
end
|
197
|
+
|
198
|
+
def method_missing(method, *params)
|
199
|
+
return super unless self.default.respond_to?(method)
|
200
|
+
define_singleton_method method.to_sym do |*args|
|
201
|
+
self.default.public_send(method, *args)
|
202
|
+
end
|
203
|
+
self.public_send(method, *params)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
3. inherit super class
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
class Math < ClientBase
|
213
|
+
def add(i, j)
|
214
|
+
self.client.add(i, j)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
```
|
218
|
+
|
219
|
+
4. use
|
220
|
+
|
221
|
+
```
|
222
|
+
Math.add(1, 2) # will auto assign request_id is with request
|
223
|
+
```
|
26
224
|
|
27
225
|
## Development
|
28
226
|
|
data/lib/thrift_rack/atom.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'redis'
|
2
1
|
class ThriftRack
|
3
2
|
class Atom
|
4
3
|
def initialize(app)
|
@@ -9,7 +8,10 @@ class ThriftRack
|
|
9
8
|
req = Rack::Request.new(env)
|
10
9
|
rpc_id = req.env["HTTP_X_RPC_ID"]
|
11
10
|
if rpc_id
|
12
|
-
|
11
|
+
start_time = Time.now
|
12
|
+
valid = ThriftRack.redis.set("thrift_rack:atom:#{rpc_id}", true, nx: true, ex: 180)
|
13
|
+
if valid
|
14
|
+
env["ATOM_DURATION"] = ((Time.now - start_time) * 1000).round(4)
|
13
15
|
@app.call(env)
|
14
16
|
else
|
15
17
|
[409, {}, ["RPC Request Processed"]]
|
@@ -20,10 +22,9 @@ class ThriftRack
|
|
20
22
|
end
|
21
23
|
|
22
24
|
class << self
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@redis ||= Redis.new
|
25
|
+
# compatibility with old version
|
26
|
+
def redis=(r)
|
27
|
+
ThriftRack.redis = r
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
data/lib/thrift_rack/client.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
require 'securerandom'
|
2
2
|
class ThriftRack
|
3
3
|
class Client
|
4
|
-
|
5
|
-
|
4
|
+
DEFAULT_REQUEST_ID = "no-request".freeze
|
5
|
+
def initialize(url, client_klass, request = nil)
|
6
|
+
if request.is_a?(ActionDispatch::Request)
|
7
|
+
@request = request
|
8
|
+
@request_id = request.request_id
|
9
|
+
else
|
10
|
+
@request_id = request || DEFAULT_REQUEST_ID
|
11
|
+
end
|
6
12
|
@url = url
|
7
13
|
@transport = ThriftRack::HttpClientTransport.new(url)
|
8
14
|
protocol = protocol_factory.get_protocol(@transport)
|
@@ -25,20 +31,38 @@ class ThriftRack
|
|
25
31
|
begin
|
26
32
|
rpc_id = SecureRandom.uuid
|
27
33
|
request_at = Time.now
|
28
|
-
|
34
|
+
if Thread.current["RPC_FULL_TRACE"].to_s == "true"
|
35
|
+
full_trace = true
|
36
|
+
else
|
37
|
+
full_trace = @request_id == DEFAULT_REQUEST_ID || @request_id.hash % 8 == 0
|
38
|
+
end
|
39
|
+
@transport.add_headers("X-Request-ID" => @request_id, "X-Rpc-ID" => rpc_id, "X-Rpc-Func" => method.to_s, "X-From" => ThriftRack::Client.app_name || "unknown", "X-Full-Trace" => full_trace.to_s)
|
29
40
|
@client.send(method, *args)
|
30
41
|
ensure
|
31
42
|
end_time = Time.now
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
43
|
+
duration = (end_time - request_at) * 1000
|
44
|
+
process_duration = @transport.response_headers["x-server-process-duration"]&.to_f
|
45
|
+
if full_trace || duration >= 100
|
46
|
+
ThriftRack::Client.logger.info(
|
47
|
+
JSON.dump(
|
48
|
+
request_at: request_at.iso8601(6),
|
49
|
+
request_id: @request_id,
|
50
|
+
rpc_id: rpc_id,
|
51
|
+
duration: duration.round(4),
|
52
|
+
path: URI(@url).path,
|
53
|
+
func: method,
|
54
|
+
tag: ThriftRack::Client.logger_tag,
|
55
|
+
full_trace: full_trace,
|
56
|
+
extra_context: @request ? { context: "action_controller", controller: @request.params["controller"], action: @request.params["action"] } : {},
|
57
|
+
server: {
|
58
|
+
id: @transport.response_headers["x-server-id"],
|
59
|
+
private_ip: @transport.response_headers["x-server-private-ip"],
|
60
|
+
process_duration: process_duration ? process_duration.round(4) : nil,
|
61
|
+
network_duration: process_duration ? (duration - process_duration).round(4) : nil,
|
62
|
+
},
|
63
|
+
),
|
64
|
+
)
|
65
|
+
end
|
42
66
|
end
|
43
67
|
end
|
44
68
|
end
|
@@ -46,8 +70,16 @@ class ThriftRack
|
|
46
70
|
end
|
47
71
|
|
48
72
|
class << self
|
49
|
-
|
50
|
-
|
73
|
+
attr_writer :app_name, :logger_tag
|
74
|
+
|
75
|
+
def app_name
|
76
|
+
@app_name ||= Rails.application.class.parent.name.underscore if defined? Rails
|
77
|
+
@app_name
|
78
|
+
end
|
79
|
+
|
80
|
+
def logger_tag
|
81
|
+
@logger_tag || {}
|
82
|
+
end
|
51
83
|
|
52
84
|
def logger
|
53
85
|
@logger ||= if defined? Rails
|
@@ -57,13 +89,13 @@ class ThriftRack
|
|
57
89
|
end
|
58
90
|
end
|
59
91
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
92
|
+
def config(app_name, max_requests: 100, logger_tag: {})
|
93
|
+
self.app_name = app_name
|
94
|
+
self.logger_tag = logger_tag
|
95
|
+
HttpClientTransport.default = HttpClientTransport.new_http(app_name, max_requests: max_requests)
|
96
|
+
at_exit do
|
97
|
+
ThriftRack::Client.logger.close
|
98
|
+
end
|
67
99
|
end
|
68
100
|
end
|
69
101
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class ThriftRack
|
2
|
+
class Downgrade
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
@drop_percentage = 0
|
6
|
+
@last_sync_drop_percentage_at = Time.at(0)
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
if rand(100) < drop_percentage
|
11
|
+
[509, {}, ["Downgrading"]]
|
12
|
+
else
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def drop_percentage
|
20
|
+
if Time.now - @last_sync_drop_percentage_at > 3
|
21
|
+
@last_sync_drop_percentage_at = Time.now
|
22
|
+
@drop_percentage = self.class.current_drop_percentage
|
23
|
+
else
|
24
|
+
@drop_percentage
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def change_downgrade(drop_percentage)
|
30
|
+
ThriftRack.redis.set(downgrade_redis_key, drop_percentage)
|
31
|
+
end
|
32
|
+
|
33
|
+
def current_drop_percentage
|
34
|
+
ThriftRack.redis.get(downgrade_redis_key).to_f
|
35
|
+
end
|
36
|
+
|
37
|
+
def close_downgrade
|
38
|
+
ThriftRack.redis.del(downgrade_redis_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
def downgrade_redis_key
|
42
|
+
"thrift_rack:downgrade"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -6,7 +6,7 @@ class ThriftRack
|
|
6
6
|
|
7
7
|
def call(env)
|
8
8
|
req = Rack::Request.new(env)
|
9
|
-
return
|
9
|
+
return 400, {'Content-Type' => 'text/plain'}, ["Not Valid Thrift Request"] unless req.post? && req.env["CONTENT_TYPE"] == THRIFT_HEADER
|
10
10
|
@app.call(env)
|
11
11
|
end
|
12
12
|
end
|
@@ -4,10 +4,14 @@ require 'net/http/persistent'
|
|
4
4
|
class ThriftRack
|
5
5
|
class HttpClientTransport < Thrift::BaseTransport
|
6
6
|
class RespCodeError < StandardError; end
|
7
|
-
class ProcessedRequest <
|
7
|
+
class ProcessedRequest < RespCodeError; end
|
8
|
+
class ServerDowngradingError < RespCodeError; end
|
9
|
+
|
10
|
+
attr_accessor :response_headers
|
8
11
|
def initialize(url, opts = {})
|
9
12
|
@headers = {'Content-Type' => 'application/x-thrift'}
|
10
13
|
@outbuf = Thrift::Bytes.empty_byte_buffer
|
14
|
+
@response_headers = {}
|
11
15
|
@url = url
|
12
16
|
end
|
13
17
|
|
@@ -19,19 +23,23 @@ class ThriftRack
|
|
19
23
|
@headers = @headers.merge(headers)
|
20
24
|
end
|
21
25
|
|
22
|
-
|
23
26
|
def flush
|
27
|
+
self.response_headers = {}
|
24
28
|
uri = URI(@url)
|
25
29
|
post = Net::HTTP::Post.new uri.path
|
26
30
|
post.body = @outbuf
|
27
31
|
post.initialize_http_header(@headers)
|
28
32
|
resp = retry_request_with_503{ThriftRack::HttpClientTransport.default.request(uri, post)}
|
29
33
|
data = resp.body
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
self.response_headers = resp.header
|
35
|
+
resp_code = resp.code.to_i
|
36
|
+
if resp_code != 200
|
37
|
+
if resp_code == 409
|
38
|
+
raise ProcessedRequest, @url
|
39
|
+
elsif resp_code == 509
|
40
|
+
raise ServerDowngradingError, @url
|
33
41
|
else
|
34
|
-
raise RespCodeError
|
42
|
+
raise RespCodeError, "#{resp.code} on #{@url} with body #{data}"
|
35
43
|
end
|
36
44
|
end
|
37
45
|
data = Thrift::Bytes.force_binary_encoding(data)
|
@@ -44,9 +52,8 @@ class ThriftRack
|
|
44
52
|
resp = nil
|
45
53
|
3.times do |i|
|
46
54
|
resp = yield
|
47
|
-
|
48
|
-
|
49
|
-
end
|
55
|
+
return resp unless resp.code.to_i == 503
|
56
|
+
|
50
57
|
sleep(0.1 * i)
|
51
58
|
ThriftRack::HttpClientTransport.default.reconnect
|
52
59
|
end
|
@@ -57,12 +64,14 @@ class ThriftRack
|
|
57
64
|
attr_accessor :default
|
58
65
|
|
59
66
|
def default
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
67
|
+
@default ||= new_http(ThriftRack::Client.app_name || "default")
|
68
|
+
end
|
69
|
+
|
70
|
+
def new_http(name, max_requests: 100)
|
71
|
+
http = Net::HTTP::Persistent.new(name: name)
|
72
|
+
http.max_requests = max_requests
|
73
|
+
http.verify_mode = 0
|
74
|
+
http
|
66
75
|
end
|
67
76
|
end
|
68
77
|
end
|
data/lib/thrift_rack/logger.rb
CHANGED
@@ -8,23 +8,35 @@ class ThriftRack
|
|
8
8
|
|
9
9
|
def call(env)
|
10
10
|
request_at = env['LAUNCH_AT'] || Time.now
|
11
|
+
income_middleware_duration = Time.now - request_at
|
11
12
|
req = Rack::Request.new(env)
|
12
13
|
resp = @app.call(env)
|
13
14
|
resp
|
14
15
|
ensure
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
duration = ((Time.now - request_at) * 1000).round(4)
|
17
|
+
request_id = req.env["HTTP_X_REQUEST_ID"]
|
18
|
+
if req.env["HTTP_X_FULL_TRACE"]
|
19
|
+
full_trace = req.env["HTTP_X_FULL_TRACE"] == "true"
|
20
|
+
else
|
21
|
+
full_trace = request_id.hash % 8 == 0
|
22
|
+
end
|
23
|
+
if full_trace || duration >= 100
|
24
|
+
ThriftRack::Logger.logger.info(
|
25
|
+
JSON.dump(
|
26
|
+
request_at: request_at.iso8601(6),
|
27
|
+
request_id: request_id,
|
28
|
+
rpc_id: req.env["HTTP_X_RPC_ID"],
|
29
|
+
duration: duration,
|
30
|
+
income_middleware_duration: (income_middleware_duration * 1000).round(2),
|
31
|
+
atom_duration: env["ATOM_DURATION"],
|
32
|
+
path: req.path,
|
33
|
+
func: req.env["HTTP_X_RPC_FUNC"],
|
34
|
+
from: req.env["HTTP_X_FROM"],
|
35
|
+
full_trace: full_trace,
|
36
|
+
tag: Logger.tag,
|
37
|
+
),
|
38
|
+
)
|
39
|
+
end
|
28
40
|
end
|
29
41
|
|
30
42
|
class << self
|
data/lib/thrift_rack/ping.rb
CHANGED
@@ -6,7 +6,7 @@ class ThriftRack
|
|
6
6
|
|
7
7
|
def call(env)
|
8
8
|
req = Rack::Request.new(env)
|
9
|
-
return
|
9
|
+
return 200, {'Content-Type' => 'text/plain'}, ["PONG"] if req.path == "/ping"
|
10
10
|
@app.call(env)
|
11
11
|
end
|
12
12
|
end
|
data/lib/thrift_rack/server.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
class ThriftRack
|
2
2
|
class Server
|
3
|
+
def initialize(request = nil)
|
4
|
+
@_request = request
|
5
|
+
end
|
6
|
+
|
3
7
|
class << self
|
4
8
|
def inherited(subclass)
|
5
9
|
warn "Your class should end with Server not it is #{subclass}" unless subclass.name.end_with?("Server")
|
@@ -22,7 +26,7 @@ class ThriftRack
|
|
22
26
|
if Kernel.const_defined?(promissory_class_name)
|
23
27
|
Kernel.const_get(promissory_class_name)
|
24
28
|
else
|
25
|
-
raise "You should overwrite processor_class for #{self
|
29
|
+
raise "You should overwrite processor_class for #{self}"
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class ThriftRack
|
2
|
+
class ServerMetric
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
request_at = env['LAUNCH_AT'] || Time.now
|
9
|
+
status, headers, body = @app.call(env)
|
10
|
+
headers["x-server-process-duration"] = ((Time.now - request_at) * 1000).to_s
|
11
|
+
headers["x-server-id"] = self.class.server_id
|
12
|
+
headers["x-server-private-ip"] = self.class.server_private_ip
|
13
|
+
[status, headers, body]
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
attr_writer :server_id, :server_private_ip
|
18
|
+
|
19
|
+
def server_id
|
20
|
+
@server_id || "unkonwn"
|
21
|
+
end
|
22
|
+
|
23
|
+
def server_private_ip
|
24
|
+
@server_private_ip || "0.0.0.0"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/thrift_rack/version.rb
CHANGED
data/lib/thrift_rack.rb
CHANGED
@@ -7,13 +7,17 @@ require 'thrift_rack/launch_timestamp'
|
|
7
7
|
require 'thrift_rack/ping'
|
8
8
|
require 'thrift_rack/atom'
|
9
9
|
require 'thrift_rack/format_check'
|
10
|
+
require 'thrift_rack/server_metric'
|
10
11
|
require 'thrift_rack/http_client_transport'
|
12
|
+
require 'thrift_rack/downgrade'
|
13
|
+
require 'thrift_rack/active_record'
|
11
14
|
|
12
15
|
require 'rack'
|
13
16
|
require 'thrift'
|
17
|
+
require 'redis'
|
14
18
|
|
15
19
|
class ThriftRack
|
16
|
-
THRIFT_HEADER = "application/x-thrift"
|
20
|
+
THRIFT_HEADER = "application/x-thrift".freeze
|
17
21
|
|
18
22
|
def initialize(servers = nil)
|
19
23
|
servers ||= ThriftRack::Server.children
|
@@ -27,27 +31,39 @@ class ThriftRack
|
|
27
31
|
req = Rack::Request.new(env)
|
28
32
|
Thread.current["request"] = req
|
29
33
|
server_class = @maps[req.path]
|
30
|
-
return
|
34
|
+
return 400, { 'Content-Type' => 'text/plain' }, ["No Thrift Server For #{req.path}"] unless server_class
|
31
35
|
|
32
|
-
resp = Rack::Response.new([], 200, {'Content-Type' => THRIFT_HEADER})
|
36
|
+
resp = Rack::Response.new([], 200, { 'Content-Type' => THRIFT_HEADER })
|
33
37
|
|
34
38
|
transport = Thrift::IOStreamTransport.new req.body, resp
|
35
39
|
protocol = server_class.protocol_factory.get_protocol transport
|
36
|
-
server_class.processor_class.new(server_class.new).process(protocol, protocol)
|
40
|
+
server_class.processor_class.new(server_class.new(req)).process(protocol, protocol)
|
37
41
|
|
38
|
-
resp
|
42
|
+
resp_a = resp.to_a
|
43
|
+
[resp_a[0], resp_a[1], [resp_a[2].join]]
|
39
44
|
ensure
|
40
45
|
Thread.current["request"] = nil
|
41
46
|
end
|
47
|
+
class << self
|
48
|
+
attr_writer :redis
|
42
49
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
50
|
+
def app(servers = nil)
|
51
|
+
Rack::Builder.new(ThriftRack.new(servers)) do
|
52
|
+
use ThriftRack::LaunchTimestamp
|
53
|
+
use ThriftRack::ActiveRecord if defined? ::ActiveRecord::Base
|
54
|
+
# use(ActionDispatch::Executor, ::Rails.application.executor) if defined? ::Rails
|
55
|
+
use ThriftRack::Downgrade
|
56
|
+
use ThriftRack::Ping
|
57
|
+
use ThriftRack::FormatCheck
|
58
|
+
use ThriftRack::Atom
|
59
|
+
use ThriftRack::Logger
|
60
|
+
use ThriftRack::Sentry if defined? Raven
|
61
|
+
use ThriftRack::ServerMetric
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def redis
|
66
|
+
@redis ||= Redis.new
|
51
67
|
end
|
52
68
|
end
|
53
69
|
end
|
data/thrift_rack.gemspec
CHANGED
@@ -22,11 +22,11 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_dependency "rack", ">= 2.0.6"
|
25
|
-
spec.add_dependency "thrift", '~> 0.10
|
25
|
+
spec.add_dependency "thrift", '~> 0.10'
|
26
26
|
spec.add_dependency 'net-http-persistent', ">= 3.0"
|
27
27
|
spec.add_dependency 'redis', '>=3.0'
|
28
28
|
|
29
29
|
spec.add_development_dependency "bundler", "~> 1.16"
|
30
|
-
spec.add_development_dependency "rake", "~>
|
30
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
31
31
|
spec.add_development_dependency "pry", "~> 0"
|
32
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thrift_rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- xuxiangyang
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.10
|
33
|
+
version: '0.10'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.10
|
40
|
+
version: '0.10'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: net-http-persistent
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '13.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '13.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: pry
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,8 +125,10 @@ files:
|
|
125
125
|
- config.ru
|
126
126
|
- lib/config.ru
|
127
127
|
- lib/thrift_rack.rb
|
128
|
+
- lib/thrift_rack/active_record.rb
|
128
129
|
- lib/thrift_rack/atom.rb
|
129
130
|
- lib/thrift_rack/client.rb
|
131
|
+
- lib/thrift_rack/downgrade.rb
|
130
132
|
- lib/thrift_rack/format_check.rb
|
131
133
|
- lib/thrift_rack/http_client_transport.rb
|
132
134
|
- lib/thrift_rack/launch_timestamp.rb
|
@@ -134,13 +136,14 @@ files:
|
|
134
136
|
- lib/thrift_rack/ping.rb
|
135
137
|
- lib/thrift_rack/sentry.rb
|
136
138
|
- lib/thrift_rack/server.rb
|
139
|
+
- lib/thrift_rack/server_metric.rb
|
137
140
|
- lib/thrift_rack/version.rb
|
138
141
|
- thrift_rack.gemspec
|
139
142
|
homepage: https://github.com/xuxiangyang/thrift_rack
|
140
143
|
licenses:
|
141
144
|
- MIT
|
142
145
|
metadata: {}
|
143
|
-
post_install_message:
|
146
|
+
post_install_message:
|
144
147
|
rdoc_options: []
|
145
148
|
require_paths:
|
146
149
|
- lib
|
@@ -155,9 +158,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
158
|
- !ruby/object:Gem::Version
|
156
159
|
version: '0'
|
157
160
|
requirements: []
|
158
|
-
|
159
|
-
|
160
|
-
signing_key:
|
161
|
+
rubygems_version: 3.0.8
|
162
|
+
signing_key:
|
161
163
|
specification_version: 4
|
162
164
|
summary: thrift http rakc server
|
163
165
|
test_files: []
|