Permalink
Browse files

adding dash

  • Loading branch information...
1 parent f70cc81 commit 9eada04ae74dee4f0e8d4f878271336a3662ea9d @jondot committed Sep 8, 2012
View
@@ -0,0 +1 @@
+guard 'coffeescript', :input => 'lib/frenzy_bunnies/web/public/js', :output => 'lib/frenzy_bunnies/web/public/js', :all_on_start => true
View
@@ -1,8 +1,78 @@
# FrenzyBunnies
-TODO: Write a gem description
+A lightweight JRuby based library backed by RabbitMQ and the very efficient `hot_bunnies` RabbitMQ driver for very fast and
+efficient processing of RabbitMQ background jobs and messages.
+
+Unlike other background job processing libraries, a Frenzy Bunnies worker is offering its work to a native JVM-based thread pool, where threads are allocated and cached.
+
+This firstly means the processing model isn't process-per-worker (saving memory) and it also isnt fixed-thread-per-worker based (saving memory even further).
+
+
+## Quick Start
+
+You basically just need to define a worker in its own class, and then
+decide if you want to use the Frenzy Bunnies runner
+`frenzy_bunnies` to run it, or do it programmatically via the
+`FrenzyBunnies::Context` API.
+
+```ruby
+class FeedWorker
+ include FrenzyBunnies::Worker
+ from_queue 'new.feeds', :prefetch => 20, :threads => 13, :durable => true
+
+ def work(msg)
+ puts msg
+ ack!
+ end
+end
+```
+
+You indicate that a class is a worker by `include
+FrenzyBunnies::Worker`. Set up a queue with `from_queue` and implement a
+`work(msg)` method.
+
+You should indicate successful processing with
+`ack!`, otherwise it will be rejected and lost (per RabbitMQ semantics,
+in future versions, they'll add a feature where rejected messages goes
+to an error queue).
+
+### Running with CLI
+
+Running a worker with the command-line binary is easy
+
+ $ frenzy_bunnies start_workers worker_file.rb
+
+Where `worker_file.rb` is a file containing all of your worker(s)
+definition. FrenzyBunnies will require the file and immediately start
+handing work to your workers.
+
+### Running Programatically
+
+Assuming that workers are already `require`d in your code, their classes
+should be visible by the moment you write this code:
+
+```ruby
+f = FrenzyBunnies::Context.new
+f.run FeedWorker,FeedDownloader
+```
+
+Which will run your workers immediately.
+
+
+## Web Dashboard
+
+context definitions
+
+## In Detail
+
+### Tweaking Workers
+
+worker definitions
+
+running context definitions
+
+### AMQP Queue Wireup
-## Installation
Add this line to your application's Gemfile:
View
@@ -17,4 +17,8 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency 'hot_bunnies', '>= 1.4.0.pre3'
gem.add_runtime_dependency 'thor'
+ gem.add_runtime_dependency 'sinatra'
+ gem.add_runtime_dependency 'atomic'
+
+ gem.add_development_dependency 'guard-coffeescript'
end
View
@@ -6,7 +6,9 @@ module FrenzyBunnies
require "frenzy_bunnies/version"
+require 'frenzy_bunnies/health'
require 'frenzy_bunnies/queue_factory'
require 'frenzy_bunnies/context'
require 'frenzy_bunnies/worker'
+require 'frenzy_bunnies/web'
@@ -1,22 +1,33 @@
require 'logger'
+require 'frenzy_bunnies/web'
class FrenzyBunnies::Context
- attr_reader :queue_factory, :logger
+ attr_reader :queue_factory, :logger, :env
def initialize(opts={})
@opts = opts
@opts[:host] ||= 'localhost'
@opts[:exchange] ||= 'frenzy_bunnies'
+ @opts[:heartbeat] ||= 5
+ @opts[:web_host] ||= 'localhost'
+ @opts[:web_port] ||= 11333
+ @opts[:web_threadfilter] ||= /^pool-.*/
+ @opts[:env] ||= ''
+
+ @env = @opts[:env]
@logger = @opts[:logger] || Logger.new(STDOUT)
- @connection = HotBunnies.connect(:host => @opts[:host])
- @connection.add_shutdown_listener(lambda { |cause| puts cause; stop; sleep(10); start;})
+ @connection = HotBunnies.connect(:host => @opts[:host], :heartbeat_interval => @opts[:heartbeat])
+ @connection.add_shutdown_listener(lambda { |cause| @logger.error("Disconnected: #{cause}"); stop;})
@queue_factory = FrenzyBunnies::QueueFactory.new(@connection, @opts[:exchange])
- @klasses = []
end
def run(*klasses)
+ @klasses = []
klasses.each{|klass| klass.start(self); @klasses << klass}
+ Thread.new do
+ FrenzyBunnies::Web.run_with(@klasses, :host => @opts[:web_host], :port => @opts[:web_port], :threadfilter => @opts[:web_threadfilter], :logger => @logger)
+ end
end
def stop
@@ -0,0 +1,10 @@
+module FrenzyBunnies
+ module Health
+ module Providers
+
+ end
+ end
+end
+
+require 'frenzy_bunnies/health/collector'
+
@@ -0,0 +1,21 @@
+class FrenzyBunnies::Health::Collector
+ def initialize(opts={})
+ @providers = []
+ Dir["#{File.dirname(__FILE__)}/providers/*.rb"].each do |f|
+ require f
+ name = File.basename(f, '.*')
+ provider_klass = FrenzyBunnies::Health::Providers.const_get(camelize name)
+ @providers << provider_klass.new(opts[name.to_sym])
+ end
+ end
+
+ def collect
+ @providers.map{|p| p.report }.inject(:merge)
+ end
+
+ # real basic camelizer, beware!. meant to avoid including active-support here.
+ def camelize(str)
+ str.split('_').map {|s| s.capitalize}.join
+ end
+end
+
@@ -0,0 +1,42 @@
+require 'java'
+
+java_import 'java.lang.management.ManagementFactory'
+
+class FrenzyBunnies::Health::Providers::Jvm
+ def initialize(opts)
+ # init beans
+ @opts = opts
+ @memorymx = ManagementFactory.memory_mx_bean
+ @threadmx = ManagementFactory.thread_mx_bean
+ @threadmx_thread_info = @threadmx.java_method :getThreadInfo, [Java::long, Java::int]
+ @runtimemx = ManagementFactory.runtime_mx_bean
+ end
+
+ def report
+ h = {}
+ heap = @memorymx.heap_memory_usage
+ h[:heap_usage_used_bytes] = heap.used
+ h[:heap_usage_max_bytes] = heap.max
+ h[:heap_usage_committed_bytes] = heap.committed
+ h[:heap_usage_human] = heap.to_s
+
+ h[:jvm_uptime_ms] = @runtimemx.uptime
+
+ ids = @threadmx.all_thread_ids
+ h[:threads] = ids.map do |id|
+ info = @threadmx_thread_info.call(id, 10)
+ if info && info.thread_name =~ @opts[:threadfilter]
+ {
+ :name => info.thread_name,
+ :stack_trace => info.stack_trace.to_a.inject([]){|a,s| a<<s.to_s }
+ }
+ else
+ nil
+ end
+ end.compact
+
+ ids.map{|i| @threadmx_thread_info.call(i, 10) }.compact.map {|inf| inf.stack_trace.to_a.inject([]){|a,s| a<<s.to_s }}
+
+ h
+ end
+end
@@ -10,7 +10,7 @@ def build_queue(name, prefetch, durable)
exchange = channel.exchange(@exchange, :type => :direct, :durable => durable)
- queue = channel.queue(name)
+ queue = channel.queue(name, :durable => durable)
queue.bind(exchange, :routing_key => name)
queue
end
@@ -1,3 +1,3 @@
module FrenzyBunnies
- VERSION = "0.0.1"
+ VERSION = "0.0.5"
end
View
@@ -0,0 +1,51 @@
+require 'sinatra/base'
+
+
+class FrenzyBunnies::Web < Sinatra::Base
+ configure do
+ # disable logging
+ set :public_folder, File.expand_path('web/public', File.dirname(__FILE__))
+ end
+
+ before do
+ content_type 'application/json'
+ end
+
+ not_found do
+ 'Cant find that, sorry.'
+ end
+
+ error do
+ 'Oops. There was an error - ' + env['sinatra.error'].name
+ end
+
+ get '/ping' do
+ 'ok'
+ end
+
+ get '/health' do
+ settings.health_collector.collect.to_json
+ end
+
+ get '/stats' do
+ jobs.map do |klass|
+ { :name => klass.name,
+ :stats => klass.jobs_stats }
+ end.to_json
+ end
+
+ get '/' do
+ redirect '/index.html'
+ end
+
+ def self.run_with(jobs, opts={})
+ set :jobs, jobs
+ set :health_collector, FrenzyBunnies::Health::Collector.new({:jvm => {:threadfilter => opts[:threadfilter]}})
+ @logger = opts[:logger]
+ @logger.info "* running web dashboard bound to #{opts[:host]} on port #{opts[:port]}."
+ Rack::Handler::WEBrick.run self, :Host => opts[:host], :Port => opts[:port], :Logger => WEBrick::Log.new("/dev/null"), :AccessLog => [nil, nil]
+ end
+ def jobs
+ settings.jobs
+ end
+end

Large diffs are not rendered by default.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit 9eada04

Please sign in to comment.