A simple, pint-sized MQTT broker that can be used for testing and development
Warning
This is not indended for production use and makes no attempts for large connection scaling and handling. It is also not considered feature complete, but handles most simple use cases.
Supported:
- Automatic startup or as part of your supervision tree (see Usage)
- Simple, unencrypted TCP connections
- MQTT v3.1.1
- QoS 0
- Connect, publish, subscribe, and unsubscribe
- Ping requests
- Rule forwarding
- Lifecycle events
Unsupported:
- SSL connections
- QoS 1 and 2
- MQTT v5
PintBroker
can be started in your own supervision tree or automatically
as an OTP application by including :pint_broker
in your applications
config.exs
file. It is generally recommended to start the broker in your
supervision tree for the most control and flexibility.
Important
Ensure there is no :pint_broker
key in your config.exs
as that
is used to determine starting the broker as an OTP application
Pass any of the supported configuration options as part of the child spec:
{PintBroker, broker_opts}
If you include any of the supported configuration options
in your config.exs
file, the broker will be started as a dependency of
your application and rely on the Application environment for configuration:
config :pint_broker, port: 1883, name: CoolBroker
The following configuration options are supported:
:port
- listening port for incoming connections (default:1883
):name
- Valid GenServer name (default:PintBroker
):rules
- list of message forwarding rules. See Rule forwarding:on_connect
- callback function for connect events. See Lifecycle events:on_disconnect
- callback function for disconnect events. See Lifecycle events:overrides
- additional options to pass to:gen_tcp.listen/2
. These take precedence and are unchecked. Be careful using this since it can break other options.
Many production setups will have a few topics with rules that forward
messages to a handler such as an SQS queue or central service. This allows
for scaling of many devices to communicate with a few nodes. For testing,
you can specify rules when starting the broker which will forward
Tortoise311.Package.Publish
structs to a handler function or process in
order to mimic this rule forwarding behavior. See PintBroker.add_rule/3
for
more information.
Example:
iex> s = self()
iex> handler1 = fn pub -> send(s, pub) end
iex> PintBroker.start_link(rules: [{"my/+/test/topic", handler1}])
{:ok, #PID<0.226.0>}
# You can publish from the broker or another client
iex> PintBroker.publish("my/first/test/topic", "hello world")
iex> flush()
%Tortoise311.Package.Publish{
__META__: %Tortoise311.Package.Meta{opcode: 3, flags: 0},
identifier: nil,
topic: "my/first/test/topic",
payload: "hello world",
qos: 0,
dup: false,
retain: false
}
Many broker setups have mechanism to subscribe to connect/disconnect events, such as the AWS IoT Lifecycle events which are typically configured in the infrastructure to be forwarded to a central handler or SQS queue.
PintBroker also supports this behavior by starting with :on_connect
and
:on_disconnect
options to register a callback function that receives
the :client_id
of the connection.
An example for replicating the AWS IoT Lifecyle events:
on_connect = fn client_id ->
payload = %{clientId: client_id, eventType: :connected}
Broadway.test_message(:my_broadway, Jason.encode!(payload))
end
on_disconnect = fn client_id ->
payload = %{clientId: client_id, eventType: :disconnected, disconnectReason: "CONNECTION_LOST"}
Broadway.test_message(:my_broadway, Jason.encode!(payload))
end
PintBroker.start_link(on_connect: on_connect, on_disconnect: on_disconnect)
The callback returns are ignored.
There are many full-featured MQTT brokers out there, but they require a lot of setup and configuration, maybe some dependencies, and tend to be overkill for testing of MQTT interactions between client and servers. I wanted the simplest possible broker which did not require a full ops team to implement a test environment that clients could connect to and publish/subscribe to topics for improved MQTT unit testing and local development of the whole system.