转自:https://www.poeticoding.com/distributed-phoenix-chat-using-redis-pubsub/

In the previous article, Create a High-Availability Kubernetes Cluster on AWS with Kops, we have seen how to create a Kubernetes cluster and how to deploy the Phoenix Chat app. The single node configuration worked well, but when we tried to scale out we saw that the messages were not sent to all the browsers.

In the image below, we see a configuration where the two chat servers are isolated. When the load-balancer routes the two WebSocket connections into two different servers, there is no way for the two browsers to send messages one another.

Two Phoenix Chat containers

This problem is often solved using an external component, like Redis, which helps to broadcast the messages to all the nodes in the cluster. 
With Elixir is also possible to get rid of this component, purely relying on the communication between Elixir nodes (we will see this in the next article).

In this article we’ll see how we can scale the Phoenix Chat horizontally, running multiple servers and leveraging on Redis PubSub.

Redis PubSub

If you want to test this out on your local machine, you can find this version of the app on the poeticoding/phoenix_chat_example GitHub repo, under the pubsub_redis branch.

We are now going to use the Redis PubSub messaging implementation, so we can broadcast the messages to all the Chat nodes. But .. What is PubSub messaging? I think the AWS definition is simple and explanatory.

Publish/subscribe messaging, or pub/sub messaging, is a form of asynchronous service-to-service communication used in serverless and microservices architectures. In a pub/sub model, any message published to a topic is immediately received by all of the subscribers to the topic.

AWS – Pub/Sub Messaging

To understand better how PubSub works, let’s see it in action with Redis. The easiest way to run a Redis server on our local machine is using Docker.

$ docker run --name redis -d redis

In this way we run Redis in background, starting a container we call redis. In the Docker container we’ve just started, we now find the redis-cli which we can use to do some experiments subscribing to a channel and publishing messages on it. 
To execute the redis-cli process in the redis container, we use the docker exec command, passing the -it option to enable the interactive mode. The cli will connect to the local redis server by default.

$ docker exec -it redis redis-cli

Redis PubSub

In the image above, we run 4 different clients. Three of them subscribe to my_channel and one publishes a message to the channel. We see how Redis sends this message to all the clients subscribed to the channel.

Phoenix PubSub Redis adapter

Connecting Phoenix Chat servers to Redis

To be able to leverage on this Redis PubSub functionality in our app, we are going to use the phoenix_pubsub_redis adapter. This adapter is really easy to integrate: we just need to add it in the dependencies and configure it in our phoenix app.

#mix.exs
defp deps do
[
...
{:phoenix_pubsub_redis, "~> 2.1"}
]
end
#config/config.exs
config :chat, Chat.Endpoint,
url: [host: "localhost"],
root: Path.expand("..", __DIR__),
secret_key_base: ".......",
debug_errors: false,
pubsub: [
name: Chat.PubSub,
adapter: Phoenix.PubSub.Redis,
host: "localhost", port: 6379,
node_name: System.get_env("NODE")
]

In this case we just manually set the host and the port to localhost and 6379, but if you are running a different Redis setup you maybe need to change this setting. We also need to set a node_name for each chat server we run, so we differentiate the Elixir nodes inside the Redis PubSub channel. We’ll pass the node_name using the NODE environment variable.

That’s it, ready to roll! We start two chat servers, one on 4000 called n1 and the other one on 4001 called n2.

# Terminal 1
$ NODE=n1 PORT=4000 mix phx.server # Terminal 2
$ NODE=n2 PORT=4001 mix phx.server

Messages Broadcasted correctly using Phoenix PubSub Redis

The messages are now correctly sent to all the browsers connected to the channel, regardless of whether they are connected to different chat servers or not.

Inspecting the Phoenix PubSub messages in Redis

To understand better what’s going on, we can inspect the messages sent to Redis by the nodes. All the chat nodes publish messages on the phx:Elixir.Chat.PubSubRedis channel, but these messages are binary encoded using the :erlang.term_to_binary function, so we can’t simply use the redis-cli to properly see them.

In the repo, always under the pubsub_redis branch and in the redis_printdirectory, I’ve put a super-small Elixir app we can use to subscribe and decode the binary messages.

defmodule RedisPrint do
def subscribe(host,port,channel) do
{:ok, pubsub} = Redix.PubSub.start_link(host: host, port: port)
{:ok, ref} = Redix.PubSub.subscribe(pubsub, channel, self())
receive_messages(pubsub,ref)
end def receive_messages(pubsub,ref) do
receive do
{:redix_pubsub, ^pubsub, ^ref, :message, %{channel: _, payload: payload}} ->
:erlang.binary_to_term(payload) |> IO.inspect()
end
receive_messages(pubsub,ref)
end
end

The RedisPrint.subscribe/3function starts a PubSub process which connects to Redis and subscribes to a specific channel. It then start receiving messages, recursively calling receive_messages/2 and decoding the payload with :erlang.binary_to_term

Let’s test again the chat with two browsers and two servers, this time inspecting the messages in a separate iex session.

# Terminal 1
$ NODE=n1 PORT=4000 mix phx.server # Terminal 2
$ NODE=n2 PORT=4001 mix phx.server # redis_print
$ iex -S mix
...
%{
__struct__: Phoenix.Socket.Broadcast,
event: "new:msg",
payload: %{body: "hello from u1", user: "u1"},
topic: "rooms:lobby"
} %{
__struct__: Phoenix.Socket.Broadcast,
event: "new:msg",
payload: %{body: "hello from u2", user: "u2"},
topic: "rooms:lobby"
}

When the user u1, connected to n1 on port 4000 , sends the message “hello from u1”, this message is sent through the WebSocket connection and once n1receives it, it’s then published to the phx:Elixir.Chat.PubSub Redis channel.

Wrap Up

As I said at the beginning, this is just one way to make our Phoenix Chat app distributed. We’ll see in further articles how, thanks to how Elixir nodes can communicate, we connect and broadcast the messages using another Phoenix PubSub adapter.

 
 
 
 

Distributed Phoenix Chat using Redis PubSub的更多相关文章

  1. Distributed Phoenix Chat with PubSub PG2 adapter

    转自:https://www.poeticoding.com/distributed-phoenix-chat-with-pubsub-pg2-adapter/ In this article we’ ...

  2. Redis Pubsub命令用法

    一.什么是pub/sub及实现Pub/Sub功能(means Publish, Subscribe)即发布及订阅功能. Redis通过publish和subscribe命令实现订阅和发布的功能. 订阅 ...

  3. Connecting Elixir Nodes with libcluster, locally and on Kubernetes

    转自:https://www.poeticoding.com/connecting-elixir-nodes-with-libcluster-locally-and-on-kubernetes/ Tr ...

  4. redis的发布订阅模式pubsub

    前言 redis支持发布订阅模式,在这个实现中,发送者(发送信息的客户端)不是将信息直接发送给特定的接收者(接收信息的客户端),而是将信息发送给频道(channel),然后由频道将信息转发给所有对这个 ...

  5. openresty+websocket+redis simple chat

    openresty 很早就支持websocket了,但是早期的版本cosocket是单工的,处理起来比较麻烦参见邮件列表讨论 websocket chat,后来的版本cosocket是双全工的,就可以 ...

  6. Redis源代码分析(三十)--- pubsub公布订阅模式

    今天学习了Redis中比較高大上的名词,"公布订阅模式".公布订阅模式这个词在我最開始接触听说的时候是在JMS(Java Message Service)java消息服务中听说的. ...

  7. redis之PubSub

    前面我们讲了 Redis 消息队列的使用方法,但是没有提到 Redis 消息队列的不足之处,那就是它不支持消息的多播机制. 消息多播 消息多播允许生产者生产一次消息,中间件负责将消息复制到多个消息队列 ...

  8. Redis分布式缓存剖析及大厂面试精髓v6.2.6

    概述 官方说明 Redis官网 https://redis.io/ 最新版本6.2.6 Redis中文官网 http://www.redis.cn/ 不过中文官网的同步更新维护相对要滞后不少时间,但对 ...

  9. 从Redis分布式缓存实战入手到底层原理分析、面面俱到覆盖大厂面试考点

    概述 官方说明 Redis官网 https://redis.io/ 最新版本6.2.6 Redis中文官网 http://www.redis.cn/ 不过中文官网的同步更新维护相对要滞后不少时间,但对 ...

随机推荐

  1. HP Instant Information

    HP Instant Information before HP-UX 11i v3 <管理系统和工作组:HP-UX系统管理员指南> After HP-UX 11i v3 <HP-U ...

  2. 201621123001 《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集 异常 1. 常用异常 结合题集题目7-1回答 1.1 自己以前编写的代码中经常出 ...

  3. 20165326 java第七周学习笔记

    第七周学习笔记 MySQL(数据管理系统)学习 知识点总结: 不能通过关闭MySQL数据库服务器所占用的命令行窗口来关闭MySQL数据库. 如果MySQL服务器和MySQL管理工具驻留在同一台计算机上 ...

  4. L314 单音节词读音规则(二)-元音字母发音规则

    1 单个元音发音尽量拖音一下(2S),发音会饱满一些. 2开音节: 辅音(辅组)(没有)+元音+辅音+e 的单词其中:元音发字母本身音,辅音字母不为r , 字母e不发音. 相对开音节:第一个元音都发字 ...

  5. ueditor 设置高度height. ue.setHeight(400); 设置宽度 width

    1.引入的文件: <script type="text/javascript" src="../../dist/ueditor1_4_3-utf8-php/uedi ...

  6. Arduino+Avr libc制作Badusb原理及示例讲解

    一. 前言 2014年美国黑帽大会上研究人员JakobLell和Karsten Nohl展示了badusb的攻击方法后,国内与badusb相关的文章虽然有了一些,但是大部分人把相关文章都阅读后还是会有 ...

  7. socket长连接理解

    在一个tcp连接上可以连续发送多个数据包,在tcp连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持. 长连接指建立socket连接后不管是否使用都保持连接,但安全 ...

  8. 使用solr报错

    2017-11-15  20:15:18 错误介绍: 错误原因:url错误 错误解决:换成正确

  9. react-navigation实现页面框架(转载)

    初始化一个RN项目 react-native init page_framework page.json { "name": "page_framework", ...

  10. linux修改ssh端口 以及禁止root远程登录 (实验机 CentOs)

    把ssh默认远程连接端口修改为3333 1.编辑防火墙配置: vi /etc/sysconfig/iptables 防火墙新增端口3333,方法如下: -A INPUT -m state --stat ...