![](https://s3.amazonaws.com/media-p.slid.es/uploads/arturt/images/209076/faye-logo.gif)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/arturt/images/209118/Rack-logo.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/arturt/images/209175/faye-cluster.png)
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,399