【新手向】完整的Grape项目(从配置到部署)

1973 查看

本文章以代码为主,如果诸位对代码中有任何不理解的地方,非常欢迎您提出问题,非常乐意能帮助到你

整体项目选型

基础框架选用Grape,该项目不需要输出任何html页面,只需要实现简单的http接口.
ORM层使用ActiveRecord,存在表间关联查询,放弃使用mongodb,转用mysql,故而选择AR.
Web服务器使用Rainbows,支持多进程.
部署使用capistrano,最好的部署工具.

项目的加载顺序

0.config/rainbows.rb
服务器设置

# rainbows config
worker_processes 4
Rainbows! do
  use :ThreadSpawn
  worker_connections 100
end

# paths and things
wd          = File.expand_path('../../', __FILE__)
tmp_path    = File.join(wd, 'tmp','pids')
log_path    = File.join(wd, 'log')

Dir.mkdir(tmp_path) unless File.exist?(tmp_path)
pid_path    = File.join(tmp_path, 'rainbows.pids')
err_path    = File.join(log_path, 'rainbows.error.log')
out_path    = File.join(log_path, 'rainbows.out.log')


# If running the master process as root and the workers as an unprivileged
# user, do this to switch euid/egid in the workers (also chowns logs):
# user "unprivileged_user", "unprivileged_group"

# tell it where to be
working_directory wd

# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
listen 38000, :tcp_nopush => false

# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30

# feel free to point this anywhere accessible on the filesystem
pid pid_path

# By default, the Unicorn logger will write to stderr.
# Additionally, ome applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
stderr_path err_path
stdout_path out_path

preload_app true

before_fork do |server, worker|
  # # This allows a new master process to incrementally
  # # phase out the old master process with SIGTTOU to avoid a
  # # thundering herd (especially in the "preload_app false" case)
  # # when doing a transparent upgrade.  The last worker spawned
  # # will then kill off the old master process with a SIGQUIT.
  old_pid = "#{server.config[:pid]}.oldbin"

  if old_pid != server.pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end
  #
  # Throttle the master from forking too quickly by sleeping.  Due
  # to the implementation of standard Unix signal handlers, this
  # helps (but does not completely) prevent identical, repeated signals
  # from being lost when the receiving process is busy.
  # sleep 1
end

1.config.ru
Rainbows还是一款Rack服务器.启动就是通过加载config.ru开始.

# This file is used by Rack-based servers to start the application.
#加载boot.rb
require ::File.expand_path('../boot',  __FILE__)
#管理AR的数据库链接,确保每次数据库链接使用完毕之后,都可正确释放掉.
use ActiveRecord::ConnectionAdapters::ConnectionManagement

logger = Logger.new("log/#{ENV["RACK_ENV"]}.log")
#设置服务器的log
use Rack::CommonLogger, logger

# 运行Api
run ::UsersApi

2.boot.rb
设定运行环境

require 'rubygems'

# Set rack environment
ENV['RACK_ENV'] ||= "development"

# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
Bundler.require(:default, ENV['RACK_ENV'])

# Set project configuration
require File.expand_path("../application", __FILE__)

3.application.rb
设定项目相关的配置.


# set database connection require 'active_record' ActiveRecord::Base.establish_connection YAML::load(File.open('config/database.yml'))[ENV["RACK_ENV"]] ActiveSupport.on_load(:active_record) do self.include_root_in_json = false self.default_timezone = :local self.time_zone_aware_attributes = false end #load all file %w{lib app}.each do |dir| Dir.glob(File.expand_path("../#{dir}", __FILE__) + '/**/*.rb').each do |file| require file end end # initialize log Dir.mkdir('log') unless File.exist?('log') case ENV["RACK_ENV"] when "production" LogSupport.logger = "log/production.log" else LogSupport.logger = "log/development.log" LogSupport.console = true end ActiveRecord::Base.logger = LogSupport.logger

加入db的rake任务

tasks/db.rake

namespace :db do

  task :environment do
    type = ENV["RAILS_ENV"] || 'development' 
    ActiveRecord::Base.establish_connection YAML::load(File.open('config/database.yml'))[type]
    ActiveRecord::Base.logger = LogSupport.logger
  end

  desc "Migrate the database through scripts in db/migrate. "
  task :migrate => :environment do
    ActiveRecord::Migrator.migrate('db/migrate', ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
  end

end

加入Console

console.rb

require ::File.expand_path('../../boot',  __FILE__)

require 'irb'
require 'irb/completion'

IRB.start(__FILE__)

添加rainbow_stopper

rainbow_stopper用来关停当前的项目的服务.

#encoding: utf-8
require 'fileutils'

current_path = File.dirname(File.dirname(File.expand_path(__FILE__)))
puts "current path: #{current_path}"

rainbows_pid = File.join(current_path, 'tmp/pids/rainbows.pids')

if File.exist?(rainbows_pid)
  pid = File.read(rainbows_pid)
  puts "Exit rainbows running on: #{pid}"
  Process.kill('QUIT', pid.to_i) rescue nil

  FileUtils.rm_rf(rainbows_pid)
  sleep 5
else
  puts "No rainbows is running due to pid: #{rainbows_pid}"
end

加入capistrano部署支持

config/deploy.rb

require "bundler/capistrano"
require "rvm/capistrano"
require "capistrano/ext/multistage"

set :stages, %w{alpha production}
set :default_stage, "alpha"

set :application, "sth"
set :repository,  "http://svn.sth.com/svn/sth/trunk/"
set :svn_username, "sennmac"
set :svn_password, "0987654321"

set :scm, :subversion
# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`

# if you want to clean up old releases on each deploy uncomment this:
# after "deploy:restart", "deploy:cleanup"

# if you're still using the script/reaper helper you will need
# these http://github.com/rails/irs_process_scripts

# If you are using Passenger mod_rails uncomment this:
namespace :deploy do
  task :start do
    run "cd #{current_path}; ruby script/rainbow_stopper.rb"
    run "cd #{current_path}; export RAILS_ENV=production;export RACK_ENV=production; bundle exec rainbows -D -N -E production -c config/rainbows.rb config.ru"
  end

  task :links, :roles => :app, :except => { :no_release => true } do
    run "ln -sf #{deploy_to}/shared/config/database.yml #{latest_release}/config/database.yml"
  end
end

set :rvm_ruby_string, '1.9.3-p286'

after 'deploy:finalize_update', 'deploy:links'
before 'deploy:restart',  'deploy:migrate'