0

24時間以内に使ったコマンドと回数をワンライナーで

zshのhistoryコマンド使うよりも~/.zsh-history読んだほうが簡単だった

tail -5000 ~/.zsh-history | nkf -u | ruby -lane 'time, cmd = $_.scan(/^: (\d+):\d;(.+)$/)[0]; puts cmd.split(/\s/)[0] if time.to_i > Time.now.to_i-60*60*24' | sort | uniq -c | sort -nr


結果
 156 ruby
66 git
42 tail
40 node
38 ls
18 curl
15 npm
12 cat
11 heroku
10 history
9 cd
8 ssh
8 less
7 echo
6 mount_ramdisk
6 history-all
6 emacs
5 scp
5 mv
5 gem
5 bundle
4 screen
4 rm
3 mkdir
3 irb
3 cp
2 wc
2 tweet
2 touch
2 sh
2 markdown
2 df
1 which
1 wget
1 tw
1 rake
1 p
1 open
1 hostname
1 head
1 gi
1 chmod
1 ../tmp
1 ..

0

Rubyでディスクの空き容量を取得

sys-filesystemというgemを使うと簡単だった。
MacとUbuntuでしか試してないけどWindowsやBSDでも同じコードでディスク状態を取得できるらしい。

参考:rubyでディスクの空き容量を取得してみる (やまかわのログ)

gem install sys-filesystem


ギガバイト単位で取得
require 'sys/filesystem'

stat = Sys::Filesystem.stat('/')

total = (stat.blocks * stat.block_size).to_f / 1024 / 1024 / 1024
available = (stat.blocks_available * stat.block_size).to_f / 1024 / 1024 / 1024
puts "#{total}GB中、#{available}GB使用可能"

total = total.to_s.scan(/^(\d+\.\d{0,3})/)[0][0].to_f
available = available.to_s.scan(/^(\d+\.\d{0,3})/)[0][0].to_f
puts "約#{total}GB中、#{available}GB使用可能"


112.19928741455078GB中、5.0075531005859375GB使用可能
約112.199GB中、5.007GB使用可能


Sys::Filesystem.stat(‘/’) はマウントポイントで、”/Volumes/Machintosh HD 2″とかを引数に渡せば別のディスクも読める。

0

HerokuのSinatraにバックグラウンドワーカーを詰め込んで節約

Webアプリと同じプロセスにworker入れてお金が節約できる。


Webアプリは “リクエスト来る→サーバーで処理→レスポンス返す” というのを繰り返すわけだが、サーバーでの処理に時間がかかる場合にそこを別のプロセスに任せて、先にレスポンスを返しておいて、あとで結果は取りに来てよ、という実装をする事がある。


時間がかかる処理は2つに大別できる。

  1. 動画をエンコードするとか。CPU負荷が高くて時間がかかるのでWebサーバーとは別の場所で動かしたい
  2. Twitter APIを10回ぐらい使った結果をまとめて返すとか。CPU負荷は低いけどIO待ちが長い
2の方について、HerokuのRuby環境で安く上げる方法をまとめる。


手法

HerokuのcedarスタックでRuby使う時はwebサーバーとしてThinが起動する。
ThinはEventMachineの中で動いてるので、EM::defer等が使える。
Herokuは1プロセス目は無料、2プロセス目を起動させると課金されるが、EventMachineでworkerをWebアプリのプロセス内に同居させればお金がかからなくなる。


非同期処理

例えば、ユーザー登録されたらメール返す処理の場合
post '/regist_user' do
mail_addr = params[:mail]
send_mail(mail_addr, 'hello!!') ## メール送信する処理
redirect '/' ## トップページに戻す
end
これだとメールが送信されるまでレスポンスを返せないので、ブラウザが固まる。
また、Sinatra+Thinを1プロセスしか起動していない場合、1リクエスト/レスポンス処理するまで次のリクエストが処理できないので、
メール送信されるまで他の人からのアクセスを全員待たせる事になる。


重い処理をEM::deferで囲むだけで、そこは別スレッドで処理される。すぐにレスポンスが返るので、みんな待たずに済む。
post '/regist_user' do
mail_addr = params[:mail]
EM::defer do
send_mail(mail_addr, 'hello!!') ## メール送信する処理
end
redirect '/' ## すぐレスポンス返す
end


たとえばTwitter APIを連続使用する時は1秒間隔を空けろとか指定があるけど、そういうのをSinatraのプロセス内で行うならEM::deferでやってしまうのが良いと思う。
お金払ってdelayed_jobを使わなくても、EM::deferで囲むだけで非同期になるので便利。
CPUに負荷がかかるタイプの処理は素直にお金払って別のプロセス起動したほうがいいと思う。


ジョブキュー

処理時間が長い仕事が大量にある時、仕事のリストを作っておいて、順番に処理していくという手法がある。
普通はDB等に仕事を保存しておいて、Webアプリとは別のプロセスが順番に処理し、結果をDBに入れておくとかするけど
これもWebアプリに内蔵させられる。

config.ru でSinatraアプリを起動した後にEM::deferを書いておくと、Webアプリとは別スレッドでずっと動き続けるループが作れる。
require 'sinatra'
require 'eventmachine'
## (略)

run Sinatra::Application

EM::defer do
loop do
sleep 5
next if @@jobs.empty?
job = @@jobs.shift ## ジョブ1つ取り出す
## job処理する
end
end


起動しといたスレッドにジョブ追加する
post '/add_job' do
@@jobs.push '仕事'
end
キューにはDBとかgearmanとか使ったほうが良いと思う。


注意点

しばらくアクセスが無いと、プロセスがkillされる。クリティカルな処理は大量にキューに入れて処理しない方が良い。
メモリ使用量も注意するべき。Dyno(プロセス)ひとつあたり512MB割り当てられていて、1.5GB超えたら再起動すると書かれている。

実際はアクセスが無いと長くても2,3時間以内にはkillされてる気がするので、この方法あんまりアテにしない方が良いと思う。


あと、この「Sinatraにワーカー埋め込む」というのはEventMachine内で動くWebサーバー(ThinやWebrick)だけで使える方法なので、
自分のサーバーでUnicornやApache+Passengerで運用する場合は動くコードなのか知らない。調べてない。

0

HerokuでMongoDB+Sinatra

試しにメモ帳作った。
http://shokai-memo.herokuapp.com

ソースコード https://github.com/shokai/heroku-sinatra-mongo-memo


MongoHQMongoLabを使う。

MongoHQはHerokuが管理してるっぽくて16MBまで無料。MongoLabはObjectLabs Corporationがやってて240MBまで無料らしい。
MongoLabの方が明らかに得だけど、容量を2GBまで増やすとMongoHQの方が安くなる。


Herokuのプロジェクト作る

git init
heroku create --stack cedar


MongoDBのアドオンを有効にする

とりあえず無料プランで、どちらかをaddする
heroku addons:add mongolab:starter
heroku addons:add mongohq:free

するとMONGOLAB_URIかMONGOHQ_URLという環境変数が増えるので、確かめる
heroku config


RubyではMongoid2.4をMongoのラッパーとして使うのが良いと思う。
Herokuにデプロイしたら環境変数にMongoDBのアドレスとパスワード等が入った状態でSinatraが起動するので、読み込ませる
Mongoid.configure do |conf|
conf.from_hash {'uri' => ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL']}
end


最終的にこうなった。
mongoidは普通mongoid.ymlから設定を読む。
MongoLabやMongoHQの接続設定が環境変数にあったらそっちを使うし、無ければmongoid.ymlを読んでローカルのMongoDBに接続する。
Mongoid.logger.level =
case ENV['RACK_ENV']
when 'production'
Logger::WARN
else
Logger::DEBUG
end

Mongoid.configure do |conf|
h = {'uri' => ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL']}
unless h['uri']
yaml = YAML.load(open(File.expand_path 'mongoid.yml', File.dirname(__FILE__)).read)
h = yaml[ ENV['RACK_ENV'] || 'development' ]
end
conf.from_hash h
end



メモ帳作る

Memo modelを定義する
class Memo
include Mongoid::Document
field :created_at, :type => Time, :default => lambda{ Time.now }
field :body, :type => String, :default => ""

def self.find_by_id(id)
self.where(:_id => id).first
end

def self.latests(num=10)
self.all.desc(:created_at).limit(num)
end

def to_s
"#{body} - #{created_at}"
end
end


適当にSinatraで書いて完成。
get '/' do
mems = Memo.latests(100).map{|m|
"<p>#{Rack::Utils.escape_html m.body} - <a href='/#{m.id}'>#{m.created_at}<a><p>"
}.join('')

"<html><form method='POST' action='/'><input type='text' name='body' size=70 /><input type='submit' /></form>#{mems}</html>"
end

post '/' do
m = Memo.new :body => params[:body]
m.save!
redirect '/'
end

get '/:id' do
m = Memo.find_by_id params[:id]
halt 404, 'not found' unless m
"<html><p><a href='/'>top</a></p><p>#{Rack::Utils.escape_html m.to_s}</p></html>"
end


ローカルのRubyからHerokuのMongoDBに接続する


手元で起動したSinatraも、環境変数さえセットされていればHerokuのMongoDBに接続される
heroku config --shell | ruby -lane 'puts "export "+$_'
でてきたMONGOLAB_URIとかをターミナルにコピペすれば環境変数をセットできる。


管理画面

https://api.heroku.com/myappsからアプリを選んで、右上のaddonsからMongoLab/MongoHQの管理画面に行ける。

0

crontabでRuby動かしたら private method `require’ called for Bundler:Module

cronでbundler.require使ってるRubyスクリプトを動かした時に出たエラー。何の事かわからなくて6時間ぐらい悩んだ。


こういう構成で

.
|-- Gemfile
|-- Gemfile.lock
`-- main.rb


main.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'bundler/setup'
Bundler.require


crontabから実行
0 * * * * /usr/bin/ruby /home/shokai/src/ruby/main.rb


こんなエラーがでた
private method `require' called for Bundler:Module

Bundler.requireはカレントディレクトリかそこより上にGemfileがあったらその中身をrequireするので
0 * * * * cd /home/shokai/src/ruby && /usr/bin/ruby main.rb
こうしたら動いた。


最終的にcronこうなってる
SHELL=/usr/bin/zsh
PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/shokai/bin:$PATH

0 * * * * cd /home/shokai/src/ruby && ruby main.rb