search
LoginSignup
0

More than 5 years have passed since last update.

posted at

zbxapi(Ruby)でZabbixから取得した結果をキャッシュする

zbxapi(Ruby)でZabbixから取得した結果をキャッシュする

はじめに

BounscaleというオートスケールするHeroku Addonを作っています。

Bounscaleはオートスケーリングの実施をするバックエンド部分にオープンソースの監視ソフトZabbixを利用しています。1サーバ当たりの集約率を上げるために、Zabbix自体への接続をできるだけ回避する方法を考案したので、下記に書いておきます。

背景

ZabbixはZabbixAPIというWebAPIを備えています。Bounscaleでは、フロントエンドのRubyからZabbixAPIへの接続ライブラリにはzbxapiというgemを利用しています。
Zabbix上のデータが多数溜まってくると、このZabbixAPIからのレスポンスが悪くなってきて、フロントエンドがもっさりしてきます。

もちろんZabbix自体のチューニングを実施する事も重要ですが、ほとんど更新のないようなデータを、Rubyが繰り返しZabbixへリクエスト部分があったので、キャッシュ化することで不要なやりとりを回避しようと考えました。

アイデア

ZabbixAPIはHTTPのリクエスト/レスポンスをJSON形式でやり取りします。
そこで、リクエストのJSONデータをキー、レスポンスをバリューとしたキーバリューストアを準備すれば、キャッシングに使えそうです。
キーは一意であれば全データである必要はありませんので、SHAなどでハッシュ化した文字列を持たせておけば十分でしょう。

バージョン

Zabbix 2.0.3
zbxapi 0.2.415

実装方式

zbxapiライブラリのメソッドをRubyのオープンクラスとaliasでチェーン化して、間にキャッシュの機構をはさみます。キーバリューストアは今回のサンプルでは単にグローバルなハッシュテーブルとします。

ソース

下記のような感じで実装してみました。

一応Thread.currentでスイッチのON/OFFを切り替えていてcache_enableブロックで囲まれている箇所でのみ有効となるようにしています。(Thread.currentはノンブロッキングだと崩壊しそうですが、今回はサンプルなので多めに見てください)

また、リクエストのJSONの中に全体の問い合わせの通番のような感じでカウントアップしていく値が含まれているので、そこは塗りつぶし(gsub)しています。

require 'rubygems'
require 'zbxapi'
require 'openssl'

# 簡易なキャッシュ用のクラス
class ZabbixCache
  class << self
    @@cache = {}

    def set(key, value)
      @@cache[key] = value
    end

    def get(key)
      @@cache[key]
    end

    def cache_enable
      begin
        Thread.current[:zabbix_cache_enable] = true
        yield
      ensure
        Thread.current[:zabbix_cache_enable] = nil
      end
    end
  end
end

# zbxapiから提供されるZabbixAPIクラスを再オープン
class ZabbixAPI
  alias :do_request_orig :do_request
  private
  def do_request(json_obj,truncate_length=5000)
    if defined? ZabbixCache
      json_obj_key = OpenSSL::Digest::SHA1.hexdigest(json_obj.gsub(/"id":\d*/, ''))
      cache = ZabbixCache.get(json_obj_key)
      result = nil
      enable = Thread.current[:zabbix_cache_enable]
      if cache && enable
        puts 'CACHE HIT'
        result = cache
      else
        result = do_request_orig(json_obj, truncate_length)
        ZabbixCache.set(json_obj_key, result)
      end
      return result
    else
      return do_request_orig(json_obj, truncate_length)
    end
  end
end

# 実行

puts "####### login ######"
zabbix = ZabbixAPI.new('http://localhost/zabbix', :debug => 4)
zabbix.login('admin', 'zabbix')

puts "####### request without cache ######"
result = zabbix.user.get(:filter => {:alias => ['admin']}).first
p result

puts "####### request with cache ######"
ZabbixCache.cache_enable do
  cached_result = zabbix.user.get(:filter => {:alias => ['admin']}).first
  p cached_result
end

結果

結果は下記です。

####### login ######
D4 ..././zbxapi.rb:do_request_orig:352 Sending: {"method":"user.login","auth":null,"params":{"password":"zabbix","user":"admin"},"id":0,"jsonrpc":"2.0"}
D4 ..././zbxapi.rb:do_request_orig:354 Response Code: 200
D4 ..././zbxapi.rb:do_request_orig:355 Response Body: {"jsonrpc":"2.0","result":"efbabb46f84d3c9f409392b575261156","id":0}
D4 ..././zbxapi.rb:do_request_orig:352 Sending: {"method":"APIInfo.version","auth":"efbabb46f84d3c9f409392b575261156","params":{},"id":1,"jsonrpc":"2.0"}
D4 ..././zbxapi.rb:do_request_orig:354 Response Code: 200
D4 ..././zbxapi.rb:do_request_orig:355 Response Body: {"jsonrpc":"2.0","result":"1.4","id":1}
####### request without cache ######
D4 ..././zbxapi.rb:do_request_orig:352 Sending: {"method":"user.get","auth":"efbabb46f84d3c9f409392b575261156","params":{"filter":{"alias":["admin"]}},"id":2,"jsonrpc":"2.0"}
D4 ..././zbxapi.rb:do_request_orig:354 Response Code: 200
D4 ..././zbxapi.rb:do_request_orig:355 Response Body: {"jsonrpc":"2.0","result":[{"userid":"1"}],"id":2}
{"userid"=>"1"}
####### request with cache ######
CACHE HIT
{"userid"=>"1"}

下記の部分がZabbixAPIへのリクエスト処理です。

D4 ..././zbxapi.rb:do_request_orig:352 Sending: {"method":"user.get","auth":"efbabb46f84d3c9f409392b575261156","params":{"filter":{"alias":["admin"]}},"id":2,"jsonrpc":"2.0"}
D4 ..././zbxapi.rb:do_request_orig:354 Response Code: 200
D4 ..././zbxapi.rb:do_request_orig:355 Response Body: {"jsonrpc":"2.0","result":[{"userid":"1"}],"id":2}

キャッシュを効かせた状態では表示されていないので、見事にキャッシュされています。

終わりに

キャッシュの機構の骨子は上記で作ることができました。

次はどのタイミングでリフレッシュして最新のデータに同期するのかが問題となってきますが、これについてはアプリケーション毎にタイミングが異なるので、慎重に検討する必要があるでしょう。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0