本文章以代码为主,如果诸位对代码中有任何不理解的地方,非常欢迎您提出问题,非常乐意能帮助到你
整体项目选型
基础框架选用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'