thrift_rack 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91940d7237d325873fa1aa7b9a6db6d93549d76c60ce045695652664aa1ac375
4
- data.tar.gz: 3853a703f5aa6b3aae575d18963c16c2b366c6da43faa186ea8dd2893037b2c0
3
+ metadata.gz: 3f79e2c9d1ce63045d0fb7042f7109911a632fec51c7e09a9c8edde7958447b5
4
+ data.tar.gz: 6f421077a9a66de076519bcfe42beb9f0316e928b36402bad86dea39ba71afee
5
5
  SHA512:
6
- metadata.gz: 7271e71e2410c02aa94ef4e81b0e5be8e0f310dda5338cde11f7d4458c8ee7cf84197a895b8e2fd7cf1905b1d849ee02d90951bfc17cd939908f617b07e77221
7
- data.tar.gz: fa40c932b3bbb545eb7c93da1be7c43483e80f68450b4c07c1ff1520bfa3119aaf148348c398ab24ead02b2dade1f6b06e220c5c40e02036c7fe46c1e28fc2d5
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.5)
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.0)
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.2)
14
+ connection_pool (2.2.5)
15
15
  method_source (0.9.0)
16
- net-http-persistent (3.0.1)
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.0.7)
22
- rake (10.5.0)
23
- redis (4.1.2)
24
- thrift (0.10.0.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 (~> 10.0)
32
+ rake (~> 13.0)
33
33
  thrift_rack!
34
34
 
35
35
  BUNDLED WITH
36
- 1.16.6
36
+ 1.17.3
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # ThriftRack
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/thrift_rack`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Write usage instructions here
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
 
@@ -0,0 +1,13 @@
1
+ class ThriftRack
2
+ class ActiveRecord
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ ::ActiveRecord::Base.connection_pool.with_connection do
9
+ @app.call(env)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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
- if ThriftRack::Atom.redis.set("thrift_request:#{rpc_id}", true, nx: true, ex: 600)
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
- attr_accessor :redis
24
-
25
- def redis
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
@@ -1,8 +1,14 @@
1
1
  require 'securerandom'
2
2
  class ThriftRack
3
3
  class Client
4
- def initialize(url, client_klass, request_id = nil)
5
- @request_id = request_id || "no-request"
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
- @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")
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
- ThriftRack::Client.logger.info(
33
- JSON.dump(
34
- request_at: request_at.iso8601(6),
35
- request_id: @request_id,
36
- rpc_id: rpc_id,
37
- duration: ((end_time - request_at) * 1000).round(4),
38
- path: URI(@url).path,
39
- func: method,
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
- attr_accessor :app_name
50
- attr_reader :pool_size
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 pool_size=(p)
61
- http = Net::HTTP::Persistent.new(name: self.app_name, pool_size: p)
62
- http.retry_change_requests = true
63
- http.max_requests = 100
64
- http.verify_mode = 0
65
- HttpClientTransport.default = http
66
- @pool_size = p
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 Rack::Response.new(["Not Valid Thrift Request"], 400, {'Content-Type' => 'text/plain'}) unless req.post? && req.env["CONTENT_TYPE"] == THRIFT_HEADER
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 < StandardError; end
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
- if resp.code.to_i != 200
31
- if resp.code.to_i == 409
32
- raise ProcessedRequest.new(@url)
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.new("#{resp.code} on #{@url} with body #{data}")
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
- if resp.code.to_i != 503
48
- return resp
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
- return @default if @default
61
- @default = Net::HTTP::Persistent.new
62
- @default.retry_change_requests = true
63
- @default.max_requests = 100
64
- @default.verify_mode = 0
65
- @default
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
@@ -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
- end_time = Time.now
16
- ThriftRack::Logger.logger.info(
17
- JSON.dump(
18
- request_at: request_at.iso8601(6),
19
- request_id: req.env["HTTP_X_REQUEST_ID"],
20
- rpc_id: req.env["HTTP_X_RPC_ID"],
21
- duration: ((end_time - request_at) * 1000).round(4),
22
- path: req.path,
23
- func: req.env["HTTP_X_RPC_FUNC"],
24
- from: req.env["HTTP_X_FROM"],
25
- tag: Logger.tag,
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
@@ -6,7 +6,7 @@ class ThriftRack
6
6
 
7
7
  def call(env)
8
8
  req = Rack::Request.new(env)
9
- return Rack::Response.new(["PONG"], 200, {'Content-Type' => 'text/plain'}) if req.path == "/ping"
9
+ return 200, {'Content-Type' => 'text/plain'}, ["PONG"] if req.path == "/ping"
10
10
  @app.call(env)
11
11
  end
12
12
  end
@@ -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.class}"
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
@@ -1,3 +1,3 @@
1
1
  class ThriftRack
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6"
3
3
  end
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 Rack::Response.new(["No Thrift Server For #{req.path}"], 404, {'Content-Type' => 'text/plain'}) unless server_class
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
- def self.app(servers = nil)
44
- Rack::Builder.new(ThriftRack.new(servers)) do
45
- use ThriftRack::LaunchTimestamp
46
- use ThriftRack::Ping
47
- use ThriftRack::FormatCheck
48
- use ThriftRack::Atom
49
- use ThriftRack::Logger
50
- use ThriftRack::Sentry if defined? Raven
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.0'
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", "~> 10.0"
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.5
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: 2019-07-11 00:00:00.000000000 Z
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.0
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.0
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: '10.0'
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: '10.0'
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
- rubyforge_project:
159
- rubygems_version: 2.7.9
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: []