Sticky session load balancing — MQTT broker clustering part 2

MQTT session

In order to continuously receive messages, MQTT clients usually subscribe to MQTT broker with a long-living connection. It is not unusual that the connection might be broken for a while due to network issues or client software maintenance reasons, but the clients often wish to receive messages published during the time when the connection was broken.

Session takeover

Things get a bit more complicated when MQTT brokers form a cluster. From the client’s perspective, there are more than one brokers to connect to, and it’s hard to know which broker is the best to connect to. We need another critical component in the network: the load balancer. The load balancer becomes the access point for the entire cluster, and routes the connections from clients to one of the brokers in the cluster.

Sticky session to the rescue

The word ‘sticky’ here is referring to the ability of the load balancer being able to route the client to the old broker at reconnect, which can avoid session take over. This is an especially useful feature when there are many clients reconnecting around the same time, or in case of a problematic client repeatedly disconnecting and connecting again.

Sticky session with HAProxy 2.4

To minimise the prerequisites, in this demo cluster, we’ll start two EMQ X nodes and an HAProxy 2.4 in docker containers.

Create a docker network

In order for the containers to connect to each other, we create a docker network for them.

docker network create

Start two EMQ X 4.3 nodes

In order for the nodes to connect to each other, the container name and the EMQX node name should be assigned within the network namespace (

docker run -d \
--name \
--net \
-e \
docker run -d \
--name \
--net \
-e \

Make EMQ X nodes join a cluster

docker exec -it emqx_ctl cluster join
[EMQ X] emqx shutdown for join
Join the cluster successfully.
Cluster status: #{running_nodes => ['',''], stopped_nodes => []}

Start HAProxy 2.4

Create a file /tmp/haproxy.configwith below content

log stdout format raw daemon debug
nbproc 1
nbthread 2
cpu-map auto:1/1-2 0-1
# Enable the HAProxy Runtime API
# e.g. echo "show table emqx_tcp_back" | sudo socat stdio tcp4-connect:
stats socket :9999 level admin expose-fd listeners

log global
mode tcp
option tcplog
maxconn 1024000
timeout connect 30000
timeout client 600s
timeout server 600s

frontend emqx_tcp
mode tcp
option tcplog
bind *:1883
default_backend emqx_tcp_back

backend emqx_tcp_back
mode tcp

# Create a stick table for session persistence
stick-table type string len 32 size 100k expire 30m

# Use ClientID / client_identifier as persistence key
stick on req.payload(0,0),mqtt_field_value(connect,client_identifier)

# send proxy-protocol v2 headers
server emqx1 check-send-proxy send-proxy-v2
server emqx2 check-send-proxy send-proxy-v2
docker run -d \
--net \
--name \
-p 9999:9999 \
-v /tmp/haproxy.cfg:/haproxy.cfg \
haproxy:2.4 haproxy -f /haproxy.cfg

Test it out

Now we use the popular mosquitto MQTT client (also in docker) to test it out.

docker run --rm -it --net eclipse-mosquitto \
mosquitto_sub -h -t 't/#' -I subscriber1
docker run --rm -it --net eclipse-mosquitto \
mosquitto_pub -h -t 't/xyz' -m 'hello'

Inspect the sticky table in HAProxy

We can also inspect the sticky table created in HAProxy with this command. It requires socat command, so I am running it from the docker host.

show table emqx_tcp_back" | sudo socat stdio tcp4-connect:
# table: emqx_external_tcp_listners, type: string, size:102400, used:1
0x7f930c033d90: key=subscriber1 use=0 exp=1793903 server_id=2 server_key=emqx2

Other articles in this series



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
EMQ Technologies

EMQ Technologies

EMQ is an open-source IoT data infrastructure software provider, delivering the world’s leading open-source MQTT message broker and stream processing database.