Rubyでリトライとか、SSH経由でコマンドとか
実家にいるとどうも時間持て余してしまうので、昔書いたスクリプトを備忘録的に残しておく。一年以上前に書いたやつだな。。。
リトライ
たとえば、特定のURLに対して並列でGETしたい、ただステータスコードがおかしかったりしたら、キャッチしておきたいようなとき。CDNを暖気運転させるときに使いました。
require 'net/http' require 'parallel' require 'timeout' BASE_URL = 'example.org' REQUEST_PATH = '/' NUM_THREADS = 18 # zcat /var/log/hogehoge/access_log.20141228.gz | perl -anle 'print $F[6]' | head -n 100 > request_path.list とかで取得しとく PATH_LIST = './request_path.list' class HttpClient def get_url(base_url, request_path) begin failed ||= 0 timeout(10) do response_status = Net::HTTP.get_response(base_url, request_path) validate_status_code(response_status.code, request_path) end rescue Timeout::Error puts "Timeout Occur! URL: #{base_url}#{request_path}" rescue RuntimeError failed += 1 puts "status code error: retry is started" retry if failed < 3 end end def validate_status_code(code, request_path) if code.to_i == 200 || code.to_i == 404 || code.to_i == 400 #puts "#{request_path}, #{code}" else raise "status code is NOT 200, but #{code}, #{request_path}" end end end puts "#{Parallel.processor_count} procesor(s)" request_paths = [] File.read(PATH_LIST).each_line do |line| request_paths << line.chop end client = HttpClient.new() Parallel.map(request_paths, :in_threads => NUM_THREADS) {|path| client.get_url(BASE_URL, path) }
コツは以下ですね。
begin failed ||= 0 <<中略>> rescue Timeout::Error puts "Timeout Occur! URL: #{base_url}#{request_path}" rescue RuntimeError failed += 1 retry if failed < 3 end
failedを初期化して、3未満だったらretryする。以上。
SSH経由でコマンド
Rubyの場合はSSH経由だとnet/sshが主流なのだろうか。
#!/usr/bin/env ruby require 'systemu' require "open3" require 'logger' require 'net/ssh' @log = Logger.new(STDOUT) @log.level = Logger::INFO def get_master_binlog(port="3306") @log.info "target port: #{port}" o, e, s = Open3.capture3("mysql -uhogehoge -S /var/run/hogehoge/mysql#{port}.sock -e 'show master logs' | cut -f 1") new_o = o.split("\n") target_binlogs = new_o.select {|v| v if /mysql/ =~ v} end def execute_ssh_command(host="localhost", user="mysql", command="hostname") begin Net::SSH.start(host, user) do |ssh| stdout = '' stderr = '' ssh.exec!(command) do |channel, stream, data| stdout << data if stream == :stdout stderr << data if stream == :stderr end stdout end rescue => e p e.message end end def is_existance_remote_file(remote_host, file_path) is_existed = false existance = execute_ssh_command(remote_host, 'mysql', "ls -l #{file_path}") if existance.empty? @log.info "not found: #{file_path}" else @log.info "Found: #{file_path}" is_existed = true end is_existed end target_port = [3306, 3307] target_port.each do |port| get_master_binlog(port).each do |binlog| if is_existance_remote_file('XXX.XXX.XXX.XXX', "/backup/binlog/#{binlog}*") puts "existed" end end end
複数ポートに対応したbinlogがリモートホストにあるか調べたかったらしい。
以下の部分すな。
begin Net::SSH.start(host, user) do |ssh| stdout = '' stderr = '' ssh.exec!(command) do |channel, stream, data| stdout << data if stream == :stdout stderr << data if stream == :stderr end stdout end rescue => e p e.message end
まぁ、またいつか参考にするかもしれないし、備忘録ということで。