Pixie
Faye compatible Bayeux implementation
Pixie is aPixie is inspired by Faye and was originally planned as a port, but has diverged significantly as I've learned the Erlang way of modelling these sorts of problems.
Heroku Add-on
If you're planning on running Faye on Heroku you're probably going to have a bad time. Take a look at MessageRocket as an alternative, and help support the author to maintain more great open source projects.
License
Pixie is Copyright (c) 2015 James Harton and licensed under the terms of the MIT Public License (see the LICENSE file included with this distribution for more details).
Installation
Add pixie
to your dependencies in the mix.exs
file:
def deps do
# ...
{:pixie, "~> 0.1.3"}
# ...
end
Also to the application
section of your mix.exs
file:
def application do
[
applications: [
# ...
:pixie,
# ...
]
]
end
Then use mix deps.get
to download Pixie from hex.
Status
Pixie is still pre 1.0, however it works and is compatible with the popular Faye JavaScript and Ruby clients.
Pixie is used in production at MessageRocket to handle relatively large message loads. Development is mostly guided by the needs of MessageRocket, however pull requests and issues are gratefully received.
Features
- Compatible with Faye JavaScript and Ruby clients.
- Supports both in-memory (ETS) and clustered (Redis) backends.
- Handles all Bayeux message types.
- Handles all Bayeux features except service channels.
- Handles the following connection types:
- long-polling
- cross-origin-long-polling
- callback-polling
- websocket
Usage
Once you have pixie installed in your project you can run a stand alone-server
with mix pixie.server
.
Configuration
The configuration options and their defaults are shown here:
# This is the default backend configuration, you don't need to set it.
config :pixie, :backend,
name: :ETS
# If you want to use Redis for clustering. The Redis backend defaults to
# localhost, unless you specify it here.
config :pixie, :backend,
name: :Redis,
redis_url: "redis://localhost:6379"
# When clients subscribe to channels we don't have to respond immediately, and
# can instead wait until there is a message to be sent on that channel or a
# heartbeat timeout expires, whichever happens first.
# Setting this option to true means that subscriptions are responded to
# which *may* increase time to first message for those not using websockets.
config :pixie, :subscribe_immediately, false
# Add extensions to be loaded at startup:
config :pixie, :extensions, [My.Extension.Module.Name]
# Add monitors to be loaded at startup:
config :pixie, :monitors, [
My.Monitor.Module.Name,
# ... or ...
{My.Monitor.Module.Name, [some_arg]}
]
# Explicitly configure transports available to clients:
config :pixie, :enabled_transports, ~w| long-polling cross-origin-long-polling callback-polling websocket |
Using with Phoenix
You can add Pixie as a custom dispatcher rule for Phoenix with Cowboy by adding the following to your application configuration:
config :myapp, MyApp.Endpoint,
http: [
dispatch: [
{:_, [
{"/pixie", Pixie.Adapter.Cowboy.HttpHandler, {Pixie.Adapter.Plug, []}},
{:_, Plug.Adapters.Cowboy.Handler, {MyApp.Endpoint, []}}
]}
]
]
Obviously, you can change "/pixie"
to any path you wish.
Sending messages from the server
You can publish messages from within the server using Pixie.publish
.
Pixie.publish "/my/channel", %{message: "Pixie is awesome!"}
Receiving messages from the server
You can subscribe to a channel and receive messages on that channel using
Pixie.subscribe
.
{:ok, pid} = Pixie.subscribe "/my/channel", fn (message, _pid)->
IO.puts "Received message: #{inspect message}"
end
A separate worker process is created for each subscription, and it's pid is
both returned from the subscribe
call, but also passed as the second argument
into the callback function, which means that you can do things like receive a
single message, then unsubscribe:
Pixie.subscribe "/only_one_message", fn(message, pid)->
IO.puts "Received message: #{inspect message}"
Pixie.unsubscribe pid
end
Either way, you can use Pixie.unsubscribe pid
to unsubscribe and terminate
the subscription process.
Writing extensions
Pixie supports extensions which allow you to modify messages as they come into
the server. You can write your own module and use the Pixie.Extension
behaviour. Your extension needs only implement two functions:
incoming %Pixie.Event{}
which returns a (possibly) modified event.outgoing %Pixie.Message.Publish{}
which returns a (possibly) modified message.
The Pixie.Event
struct contains the following fields:
client_id
: The ID of the Client. You can use this to findPixie.Client
andPixie.Transport
processes should you need to.message
: The incoming message from the client. Messages are represented as:%Pixie.Message.Handshake{}
: A client handshake request.%Pixie.Message.Connect{}
: A client connection request.%Pixie.Message.Subscribe{}
: A subscription request.%Pixie.Message.Publish{}
: A message to be published by the user.%Pixie.Message.Unsubscribe{}
: An unsubscription request.%Pixie.Message.Disconnect{}
: A client disconnection request.
response
: The response to be sent back to the client. You can use the functions inPixie.Protocol.Error
(automatically imported for you) or you can modify the response directly.
The details of all these structs should be available on hexdocs.pm.
You can configure Pixie to load your extensions at start-up (as per the configuration section above) or you can add and remove them at runtime.
Pixie.ExtensionRegistry.register MyExtension
# ... and ...
Pixie.ExtensionRegistry.unregister MyExtension
Writing Monitors
Pixie provides monitoring functionality which allows you to subscribe to events which are happennings in the system.
Provided events are:
- Client created.
- Client destroyed.
- Channel created.
- Channel destroyed.
- Client subscribed to channel.
- Client unsubscribed from channel.
- Message received for publication.
- Message delivered to receiving client.
All events also receive a Timex timestamp recording the time at which they were generated - as there are potentially a lot of them and they may sit in a process mailbox for some time.
You can use the Pixie.Monitor
behaviour to define your event handler:
defmodule MyMonitor do
use Pixie.Monitor
def created_channel channel_name, at do
Logger.info "Channel \#\{channel_name} created at \#\{format at}"
end
def destroyed_channel channel_name, at do
Logger.info "Channel \#\{channel_name} destroyed at \#\{format at}"
end
defp format timestamp do
timestamp
|> Date.from(:timestamp)
|> DateFormat.format!("{UNIX}")
end
end
Or you can use your own GenEvent
handler, if the monitor API doesn't work
for you.
You can find more information in the documentation.
Running the tests
Run mix espec
.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request