乐趣区

关于mqtt:粘性会话负载均衡-MQTT-Broker-集群详解二

在上一篇文章《MQTT Broker 集群详解(一):负载平衡》中,咱们简略介绍了 MQTT 负载平衡:负载平衡既能够利用于传输层,也能够用于应用层。在本文中,咱们将具体介绍应用层负载平衡,其中最乏味的局部:粘性会话(sticky-session)。

本文由两局部组成,第一局部将介绍 MQTT 会话,以及在分布式 MQTT Broker 集群中解决会话面临的挑战;第二局部是通过在 EMQ X 4.3 集群后面配置 HAProxy 2.4 负载均衡器,带读者亲自体验如何充分利用粘性会话实现负载平衡。

MQTT 会话

为了继续接管音讯,MQTT 客户端通常会连贯至 MQTT Broker 进行订阅并放弃长期连贯。因为网络问题或客户端软件维护等起因,连贯可能会中断一段时间,这并不常见,但客户端通常心愿在从新连贯胜利后依然能接管到中断期间漏收的音讯。

因而,为客户端提供服务的 MQTT Broker 应该为客户端放弃会话(依据客户端的申请,将「Clean-Session」标记设置为 false)。此时,即便客户端断开连接,订阅者以后订阅的主题以及传递给这些主题的音讯(QoS1 和 2)等也会由音讯服务器(broker)保留。

当具备长久会话的客户端从新连贯时,它不须要从新订阅主题,音讯服务器应该将所有未发送的音讯发送给该客户端。

咱们之前写过一篇对于 MQTT 会话的文章,如果您对 MQTT 会话的技术细节感兴趣,能够通过浏览这篇文章做进一步理解。

会话接管

当 MQTT Brokers 造成集群时,事件会变得更加简单。从客户端的角度来看,要连贯的服务器不止一个,很难晓得哪个服务器最适宜连贯。咱们须要网络中的另一个要害组件:负载均衡器。负载均衡器成为整个集群的接入点,并将客户端的连贯路由到集群中的某一个服务器。

如果客户端通过负载均衡器连贯到服务器(例如,node1),而后断开连接并稍后从新连贯,则新连贯可能会路由到集群中的不同服务器(例如,node3)。在这种状况下,node3 应该在客户端断开连接时开始向客户端发送未发送的音讯。

实现集群范畴的长久会话有很多不同的策略。例如,整个集群能够共享一个全局存储来保留客户端的会话。

然而,更具可扩展性的解决方案通常以分布式形式解决这个问题,即数据从一个节点迁徙到另一个节点。这种迁徙称为会话接管。会话接管应该对客户端齐全通明,但它是有代价的,尤其是当有很多音讯须要解决时。

粘性会话解决方案

这里的「粘性」一词指的是负载均衡器可能在从新连贯时将客户端路由到之前服务器的能力,这能够防止会话接管。当有许多客户端在同一时间从新连贯时,或者在一个有问题的客户端重复断开连接并再次连贯的状况下,这是一个特地有用的性能。

为了让负载均衡器以「粘性」形式分派连贯,服务器须要晓得连贯申请中的客户端标识符(有时是用户名)——这须要负载均衡器查看 MQTT 数据包以查找此类信息。

一旦取得客户端标识符(或用户名),对于动态集群,服务器能够将客户端标识符(或用户名)散列到服务器 ID。或者为了更好的灵活性,负载均衡器能够抉择保护一个从客户端标识符(或用户名)到指标节点 ID 的映射表。

在下一节中,咱们将演示 HAProxy 2.4 中的粘性表策略。

应用 HAProxy 2.4 实现粘性会话

为了尽量减少先决条件,在这个演示集群中,咱们将在 docker 容器中启动两个 EMQ X 节点和一个 HAProxy 2.4。

创立 docker 网络

为了使容器彼此连贯,咱们为它们创立了一个 docker 网络。

docker network create test.net

启动两个 EMQ X 4.3 节点

为了使节点彼此连贯,应该在网络名称空间(test.net)中调配容器名称和 EMQX 节点名称。

启动 node1

docker run -d \
  --name n1.test.net \
  --net test.net \
  -e EMQX_NODE_NAME=emqx@n1.test.net \
  -e EMQX_LISTENER__TCP__EXTERNAL__PROXY_PROTOCOL=on \
  emqx/emqx:4.3.7

启动 node2

docker run -d \
  --name n2.test.net \
  --net test.net \
  -e EMQX_NODE_NAME=emqx@n2.test.net \
  -e EMQX_LISTENER__TCP__EXTERNAL__PROXY_PROTOCOL=on \
  emqx/emqx:4.3.7

留神环境变量

EMQX_LISTENER__TCP__EXTERNAL__PROXY_PROTOCOL. 该变量是为 TCP 监听器启用二进制代理协定,以便服务器能够取得客户端的实在 IP 地址信息,而不是负载均衡器的 IP 地址。

使 EMQ X 节点退出集群

docker exec -it n2.test.net emqx_ctl cluster join emqx@n1.test.net

如果所有按预期进行,应该打印输出这样的日志:

[EMQ X] emqx shutdown for join
Join the cluster successfully.
Cluster status: #{running_nodes => ['emqx@n1.test.net','emqx@n2.test.net'], stopped_nodes => []} 

启动 HAProxy 2.4

创立文件 /tmp/haproxy.config,内容如下:

global
  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:172.100.239.4:9999
  stats socket :9999 level admin expose-fd listeners

defaults
  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 n1.test.net:1883 check-send-proxy send-proxy-v2
  server emqx2 n2.test.net:1883 check-send-proxy send-proxy-v2

在测试 docker 网络中启动 haproxy:

docker run -d \
  --net test.net \
  --name proxy.test.net \
  -p 9999:9999 \
  -v /tmp/haproxy.cfg:/haproxy.cfg \
  haproxy:2.4 haproxy -f /haproxy.cfg

测试

当初咱们应用风行的 mosquitto MQTT 客户端(也在 docker 中)对其进行测试。

咱们启动一个订阅者(名为 subscriber1)订阅 t/# 主题

docker run --rm -it --net test.net eclipse-mosquitto \
    mosquitto_sub -h proxy.test.net -t 't/#' -I subscriber1

而后从另一个客户端向 t/xyz 公布一条 hello 音讯

docker run --rm -it --net test.net eclipse-mosquitto \
    mosquitto_pub -h proxy.test.net -t 't/xyz' -m 'hello'

如果所有都按预期进行,订阅者应该打印出 hello 音讯。

查看 HAProxy 中的粘性表

咱们还能够应用如下命令查看在 HAProxy 中创立的粘性表。这须要 socat 命令,所以咱们从 docker 主机运行它。

show table emqx_tcp_back" | sudo socat stdio tcp4-connect:127.0.0.1:9999

该命令应该打印以后连贯,如下所示:

# table: emqx_external_tcp_listners, type: string, size:102400, used:1
0x7f930c033d90: key=subscriber1 use=0 exp=1793903 server_id=2 server_key=emqx2

在这个例子中,客户端 subscriber1 被固定连贯到服务器 emqx2

结语

至此,咱们能够理解到从客户端的角度看,EMQ X 集群是如何通过负载均衡器对外部提供服务的。

在本系列文章的后续内容中,咱们将跟踪一个 MQTT 音讯从发布者到订阅者的全过程,以便大家理解 EMQ X 如何将它在集群中复制和转发。敬请期待。

本系列中的其它文章

  • MQTT Broker 集群详解(一):负载平衡

版权申明:本文为 EMQ 原创,转载请注明出处。

原文链接:https://www.emqx.com/zh/blog/mqtt-broker-clustering-part-2-sticky-session-load-balancing

技术支持:如对本文或 EMQ 相干产品有疑难,可拜访 EMQ 问答社区 https://askemq.com 发问,咱们将会及时回复反对。

更多技术干货,欢送关注咱们公众号【EMQ 中文社区】。

退出移动版