What is Faye?
-
pub/sub messaging system
-
based on the
Bayeux
protocol
-
provides message servers for Node.js and Ruby
-
client lib on server and browser side
Faye is great for...
Chat
Activity Streams
Notifications
Collaboration
Multiplayer games
Realtime data
Dashboards
2nd Screen experiences
More about Faye
Faye is just like any other Rack app
Storage layer:
-
memory
-
redis (let you scale Faye service)
Faye architecture
operations:
handshake
connect
disconnect
subscribe
unsubscribe
publish
Bayeux protocol
-
protocol for transporting asynchronous messages
-
primarily over HTTP
-
low latency between a server and web clients
Faye Server
- object-based implementation of Bayeux
-
delegates execution of Bayeux operations to the Engine
- easy to swap out the backend implementation
- does not know anything about HTTP or other network transports
Clustering
Redis
Faye
Faye Adapters
Faye adapters
XHR to another domain
modern browsers
supports many types of HTTP requests
supports old browsers
only GET requests
Ruby Sever (Faye)
require File.expand_path('../config/environment', __FILE__)
bayeux = Faye::RackAdapter.new(
:mount => '/faye',
:timeout => 25
)
# some code
run bayeux
Ruby Sever (Faye)
# ...
bayeux.add_extension(PushOnlyServer.new)
bayeux.add_extension(ServerAuth.new)
bayeux.bind(:subscribe) do |client_id, channel|
Rails.logger.info "#{client_id} subscribed #{channel}"
end
# ...
Run Ruby Server (Faye)
Run Ruby Server (Faye)
on CI
Faye Extensions
# ...
class FayeExtension
def incoming(message, callback)
# ...
callback.call(message)
end
def outgoing(message, callback)
# ...
callback.call(message)
end
end
# ...
Push Only Server Extension
class PushOnlyServer
def incoming(msg, callback)
unless msg['channel'] =~ /^\/meta\//
password = msg['ext'] &&
msg['ext']['password']
if password != AppConfig.faye.push_secret
msg['error'] = '403::Password required'
end
end
callback.call(msg)
end
# ...
end
Push Only Server Extension
class PushOnlyServer
# ...
def outgoing(message, callback)
unless message['ext'].nil?
message['ext'].delete('password')
end
callback.call(message)
end
end
Publish message on Rails server side
class LiveUpdate
def self.send_to_faye(...)
# ...
message = {
:channel => 'channel-name',
:data => {
:event => 'event-name',
:faye_socket_id => 'id'
},
:ext => {
:password => 'secret'
}
}
uri = URI.parse(internal_url)
Net::HTTP.post_form(uri,
:message => message.to_json)
# ...
end
end
Server Auth Extension
class ServerAuth
def incoming(msg, callback)
# ...
# some code on the next slide
# ...
# Call the server back now we're done
callback.call(msg)
end
end
Server Auth Extension
# ...
# Let non-subscribe messages through
unless msg['channel'] == '/meta/subscribe'
return callback.call(msg)
end
# ...
Server Auth Extension
# ...
if msg['ext'].nil?
msg['error'] = 'Missing ext key'
else
user_id = msg['ext']['user_id']
# check if user can subscribe the channel
# ...
# more code on the next slide
end
# ...
Server Auth Extension
# ...
user_id = message['ext']['user_id']
faye_token = message['ext']['faye_token']
project_id = message['ext']['project_id']
project = Project.get(project_id)
if project.present?
# ... next slide
else
message['error'] = "Project doesn't exist"
end
# ...
Server Auth Extension
# check if user has access to project etc
user = User.get(user_id)
if user && user.valid_faye_token?(faye_token)
client_id = message['fayeClientId']
# ...
$semaphore.synchronize do
$user_activities[client_id] = some_id
end
elsif user
message['error'] = "Invalid faye token"
else
message['error'] = "User doesn't exist"
end
# ...
faye.ru - Ruby Server with Mutex
# key: faye client id
# value: list of project memberships ids
$user_activities = {}
$semaphore = Mutex.new
$update_thread = Thread.new do
loop do
sleep 60
$semaphore.synchronize do
ids = $user_activities.values.uniq
# ...
Project.update_activities(ids)
end
end
end
Browser client
Subscribing to channels
Browser client
Subscribing to channels
Browser client
# CoffeeScript
class K2.FayeConnection
connectAndBindEvents: ->
# ...
url = 'http://localhost:8090/faye'
@faye_client = new Faye.Client url,
timeout: timeout
# client-side extension (next slide)
# ...
Browser client
Client-side extension
# ...
@faye_client.addExtension outgoing: (message, callback) ->
message.ext = message.ext or {}
message.ext.user_id = K2.faye.currentUserId
message.ext.faye_token = K2.faye.fayeToken
message.ext.project_id = K2.faye.projectId
callback(message)
# ...
Browser client
Bind events
class K2.FayeConnection
# ...
@faye_client.bind 'transport:up', ->
# the client is online
console.log "New socket ID: " + faye_connection.faye_client._0
# stop interval refreshing kanban board
faye_connection.polling.stop()
faye_connection.polling.fullRefresh()
Browser client
Bind events
class K2.FayeConnection
# ...
@faye_client.bind 'transport:down', ->
# the client is offline
# update data with interval
# kanbanery stuff - not related to faye
faye_connection.polling.start()
faye_connected = false
Browser client
Subscribing to channel
class K2.FayeConnection
subscribeToChannelAndBindEvents: ->
subscription = @faye_client.subscribe @fayeChannelName(), (msg) ->
console.log "GOT MESSAGE", msg
# current faye socket id
id = faye_connection.faye_client._0
# ignore message sent by client
if msg.faye_socket_id != id
faye_connection.store.refresh msg.event
Browser client
Subscription callbacks
# subscription has been set up
# and acknowledged by the server
subscription.callback ->
console.log "SUBSCRIBED"
faye_connection.polling.stop()
# error while creating subscription
subscription.errback (error) ->
console.log(error.message)
faye_connection.polling.start()
Check 2nd screen experiences
on
Thanks
Links
Alternatives for Faye
https://github.com/maccman/juggernaut (deprecated)
http://socket.io
Alternatives hosted API
http://pusher.com
http://www.pubnub.com
Other:
http://railscasts.com/episodes/260-messaging-with-faye
http://net.tutsplus.com/tutorials/ruby/how-to-use-faye-as-a-real-time-push-server-in-rails/
https://blog.jcoglan.com/2012/06/09/why-you-should-never-use-hash-functions-for-message-authentication/
Faye - pub/sub message system
By ArturT
Faye - pub/sub message system
Example of Faye implementation based on kanabanery.com
- 4,543