class Pokan::Server
Gossip server class, using the Pokan::Peer
class. This is the main class instantied by user code.
When a server class is created, the user should set:
-
the gossip interval
-
actual data to be stored, and possibly shared with other servers accross the network
-
The server behavior on a defined set of events, declared in an event driven syntax.
The user can also use epoll for faster I/O notification, if the server is running on a Linux system.
Sample usage is shown below:
s = Pokan::Server.new s.gossip_every(1) #=> tells the server to gossip every second s.use_epoll #=> I'm in a Linux system s.store 'k', 'v' e.on :new_key do |key, value| puts "What a great key I just got: #{key}!" end # more event related code... e.start #=> last command, blocks execution
Constants
- AFTER_EVENTS
- BEFORE_EVENTS
- ON_EVENTS
Attributes
Public Class Methods
Creates a new gossip server. You must pass the machine’s IP address in the network. This information will be used when changing information with other peers running Pokan
. This also set some defaults, namely:
-
server will bind TCP and UDP port number 5555. If you have something else running, you must set them like:
server.tcp_port = 9876 server.udp_port = 9989
-
epoll
will not be used (you might want to set it if you are in a Linux system. SeePokan::Server#use_epoll
-
The server will comunicate with other peers _every second_
Of course, all of these are customizable to your particular needs.
Pokan::Peer::new
# File lib/pokan/server.rb, line 62 def initialize(address) @event_handler = EventHandler.new @seed_address, @seed_port = '', '' @periodics = [] self.address = address self.udp_port = 5555 super() initialize_ivs end
Public Instance Methods
Same as the Pokan::Server#before
, but it also supports the :sync
event. Use this method to run some code after the server gossips with a random peer or after it syncs with a seed (if you passed one on initialization. See Pokan::Server#start
).
# File lib/pokan/server.rb, line 133 def after(event, &block) event_name = prepend('after_', event) register_event(event_name, &block) if AFTER_EVENTS.include? event end
Adds behaviour before a given event occurs. For now, the only event supported is :gossip
. Use this method if you want some code to run every time before the server gossips with a random peer.
# File lib/pokan/server.rb, line 124 def before(event, &block) event_name = prepend('before_', event) register_event(event_name, &block) if BEFORE_EVENTS.include? event end
Adds behavior to the server, to be executed periodically (every given amount of seconds).
server.every 2 do puts "Heya!" end
Every 2 seconds “Heya!” will be printed out.
# File lib/pokan/server.rb, line 147 def every(seconds, &action) @periodics << { :period => seconds, :action => action } end
Use this method to specify the time interval you wish to use for changing gossip messages among peers. Default is 1 second.
# File lib/pokan/server.rb, line 86 def gossip_every(seconds) @gossip_interval = seconds end
Defines the behaviour you want the server to have on different events. For now, you can define your behaviour for the following events:
-
:new_key
- This will be called whenever a new_key is inserted (and there was no previous value for that) -
:key_changed
- This is called when a value for a certain key is updated (a value with greater timestamp) -
:key_removed
- The name speaks for itself
For all the events, both the key and the value are passed to the block.
Example:
server = Pokan::Server.new('10.10.10.8') server.on :new_key do |key, value| puts "New key #{key} was inserted with value #{value}!" end
# File lib/pokan/server.rb, line 117 def on(event, &block) register_event(event, &block) if ON_EVENTS.include? event end
This method starts the server, which will bind specified TCP and UDP ports (or 5555 by default). Optionally, you can pass the address of another pokan instance (a seed), and the server will automatically send a message to the seed and update its data. If no seed information is passed, the server will start with no data and assume it is a seed.
Example:
server = Pokan::Server.new('10.10.10.8') server.start :gossip_with => '10.10.10.9:5555'
In the example above, the server will try to contact (via TCP) a pokan server running on 10.10.10.9
, port 5555
, and update its data accordingly.
# File lib/pokan/server.rb, line 166 def start(options = {}) seed = options[:gossip_with] if seed.nil? act_as_seed else @seed_address, @seed_port = parse_host_and_port(seed) say_hello(@seed_address, @seed_port) build_trap @event_handler.emit :before_gossip, :with => [@seed_address, options[:redis]] sync_with(@seed_address, options[:redis]) @event_handler.emit :after_sync, :with => [1] end start_event_loop end
Finishes the server
# File lib/pokan/server.rb, line 185 def stop kill EventMachine.stop end
Stores a key, value pair that will be shared among all the peers in the network.
Pokan::Peer#store
# File lib/pokan/server.rb, line 92 def store(key, value, timestamp=Time.now) super save @event_handler.emit :new_key, :with => [key, value] end
Call this method if you want to use epoll in your Linux system. Warning: this will not throw an exception nor will warn you if you use this method in a non-Linux machine. You might face errors when you try to start the server, though.
# File lib/pokan/server.rb, line 76 def use_epoll @epoll = true end
# File lib/pokan/server.rb, line 80 def using_epoll? @epoll end
Private Instance Methods
# File lib/pokan/server.rb, line 239 def build_trap trap('INT') { prepare_to_die } trap('TERM') { prepare_to_die } trap('QUIT') { prepare_to_die } end
# File lib/pokan/server.rb, line 259 def gossip EventMachine.add_periodic_timer(@gossip_interval) { query = Query.new(Peer) peer = query.where(:random => true, :role => ['seed', 'peer'], :not => [id], :status => 'alive').first unless peer.nil? @event_handler.emit :before_gossip, :with => [peer.address, peer.udp_port, digest_message.size] Network.udp(digest_message, peer.address, peer.udp_port) @event_handler.emit :after_gossip, :with => [peer.address, peer.udp_port] end } end
Sets default variables, such as:
-
TCP and UDP ports: 5555
-
do not use epoll
-
gossip every second
# File lib/pokan/server.rb, line 196 def initialize_ivs self.tcp_port = 5555 @epoll = false @gossip_interval = 1 end
# File lib/pokan/server.rb, line 206 def parse_host_and_port(address) address.split ':' end
# File lib/pokan/server.rb, line 249 def prepare_to_die say_goodbye(@seed_address, @seed_port) @event_handler.emit :shutdown exit 0 end
# File lib/pokan/server.rb, line 210 def prepend(string, event) event_str = string + event.to_s event_str.to_sym end
# File lib/pokan/server.rb, line 202 def register_event(event, &block) @event_handler.register(event, &block) end
# File lib/pokan/server.rb, line 231 def register_periodics @periodics.each do |callback| EventMachine.add_periodic_timer(callback[:period]) { callback[:action].call } end end
# File lib/pokan/server.rb, line 255 def say_goodbye(address, port) Network.tcp(goodbye_message, address, port) end
# File lib/pokan/server.rb, line 245 def say_hello(address, port) Network.tcp(hello_message, address, port) end
# File lib/pokan/server.rb, line 215 def start_event_loop EventMachine.run { gossip register_periodics RequestHandler.address = address RequestHandler.port = udp_port @event_handler.emit :start, :with => [address, tcp_port, udp_port, @epoll, @gossip_interval, @seed_address, @seed_port] EventMachine.open_datagram_socket address, udp_port, RequestHandler EventMachine.start_server address, tcp_port, RequestHandler } end