ThriftRack
ThriftRack implements thrift Compact Protocol + HTTP Transport
with rack, and makes it easy to write a server with convention.
Installation
Add this line to your application's Gemfile:
gem 'thrift_rack'
And then execute:
$ bundle
Or install it yourself as:
$ gem install thrift_rack
Usage
Server
Assume you use rails only with activemodel
、activerecord
、activesupport
. Because ThriftRack act as controller and view, you do not need actionview
and actionpack
You cloud new a rails app use this command
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
Suppose you has a thrift named math.thrift
to add two number:
# math.thrift
namespace rb thrift.math # namesapce should same with filename
service MathService { # Math is capitalized from Math
int add(1: int i, 2: int j)
}
then you should implement this thrift in math_server.rb
class MathServer < ThriftRack::Server
def add(i, j)
i + j
end
end
Then change the config.ru
require_relative 'config/environment'
app = ThriftRack.app(ThriftRack::Server.children)
run app
Another config under initializers
#initializers/thrift.rb
Dir["#{Rails.root}/lib/thrift/**/*.rb"].each { |file| require file } # support generate thrift files under lib/thrift
Dir["#{Rails.root}/app/servers/*.rb"].each { |file| require file } # support servers under app/servers
ThriftRack.redis = Redis.new
ThriftRack::Logger.tag = { key: value} # tag to logs
at_exit do
ThriftRack::Logger.logger.close
end
Then run this bash
rackup
what happend?
- We implement the math.thrift with
POST
method at/math
path - A Server seems like a controller, except that you do not need to write routes.
- 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.
- Client will auto retry with network jitter, Atom ensure each request at most processed once.
-
/ping
should be use to health check, all request to/ping
will not be log -
ThriftRack::Logger
log each rpc request with json format. You could use ELK to analyze each request
Client
client cloud write like this
class Math
attr_accessor :client
def initialize(request_id = "no-request-id") # web should has one request_id
@client = ThriftRack::Client.new("http://127.0.0.1:300/math", ThriftClientClass, request_id)
end
def add(i, j)
self.client.add(i, j)
end
end
Another config under initializers
#initializers/thrift.rb
Dir["#{Rails.root}/lib/thrift/**/*.rb"].each { |file| require file }
ThriftRack::Client.config Rails.application.class.parent.name.underscore
what happend?
- Client will auto generate a
rpc_id
to tracer rpc request - Client will add app_name to header
- Client use HTTP 1.1, wich persistent tcp connection with multi http requests
- Client has a connection pool, network error will auto retry
- Client will log each rpc process time
Furthermore
you chould write a supperclass like under, other client inherit superclass
-
you could write a around_action to set request
class ApplicationController < ActionController::API around_action :set_thread_local_request private def set_thread_local_request Thread.current["request"] = request yield ensure Thread.current["request"] = nil end end
-
write a superclass
class ClientBase def initialize(req = nil) if req @request_id = req.request_id end end def request_id @request_id ||= Thread.current["request"] ? Thread.current["request"].request_id : "no-request-id" end def client ThriftRack::Client.new("http:127.0.0.1:3000/#{_namespace.underscore}", _client_class, request_id) end def respond_to_missing?(method, include_private = false) self.client.respond_to?(method) end def method_missing(method, *args) return super unless self.client.respond_to?(method) self.class_eval do define_method method.to_sym do |*params| self.client.public_send(method, *params) end end self.public_send(method, *args) end private def _namespace @namespace ||= self.class.name end def _client_class "Thrift::#{_namespace}::#{_namespace}Service::Client".constantize end class << self def default self.new end def respond_to_missing?(method, include_private = false) self.default.respond_to?(method) end def method_missing(method, *params) return super unless self.default.respond_to?(method) define_singleton_method method.to_sym do |*args| self.default.public_send(method, *args) end self.public_send(method, *params) end end end
-
inherit super class
class Math < ClientBase def add(i, j) self.client.add(i, j) end end
-
use
Math.add(1, 2) # will auto assign request_id is with request
Development
After checking out the repo, run bin/setup
to install dependencies. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/thrift_rack. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the ThriftRack project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.