How do I make two Elixir applications(server & client) communicate with Phoenix.PubSub?

I have two elixir-applications, that need to communicate with each other:

  • 1 Server that has a PubSub with 2 topics, it broadcasts to one topic and listens to the other topic.
  • 1 Client, that should subscribe to the Servers PubSub topic and broadcast to the other topic.

The scenario I want to get is that I start the Server, and then start one or multiple Clients that can subscribe to the server.

It just needs to work locally in the terminal, so it doesn’t need any fancy.

The Server uses :phoenix_pubsub, "~> 2.1" and the rest is mostly a Genserver for message-handling.

The client is basically just a Genserver that only needs to subscribe to the server and sends A message when it receives one.

Any tips on how I can make them talk?

Hello and welcome,

You could add pub_sub as a dependency in the client, then add it to the client supervision tree with the same name as the one in Phoenix.

You need to cluster both nodes. I use libcluster for this, but You have other options.

Then, it should work… :slight_smile:

For example

$ mix new demo --sup
$ cd demo
# add deps in mix.exs
# {:phoenix_pubsub, "~> 2.1"},
# start pub_sub in lib/demo/application.ex
# {Phoenix.PubSub, name: Demo.PubSub},
$ mix deps.get

Start first node and subscribe to pub_sub…

$ iex --sname demo1 --cookie cookie -S mix
iex(demo1@arakis)1> Phoenix.PubSub.subscribe(Demo.PubSub, "topic")
:ok

Start second node in another console, connect both nodes, and broadcast a message…

$ iex --sname demo2 --cookie cookie -S mix
iex(demo2@arakis)1> Node.ping :demo1@arakis
:pong
iex(demo2@arakis)2> Node.list
[:demo1@arakis]
iex(demo2@arakis)3> Phoenix.PubSub.broadcast(Demo.PubSub, "topic", %{message: "Hey there!"})
:ok

Receive message in first node…

iex(demo1@arakis)2> flush
%{message: "Hey there!"}
:ok
8 Likes

This worked lovely thank you very much! :pray:

Added pub_sub and libcuster with gossip strategy in both client and server and the talk perfectly in my localhost. :smiley:

Now I have moved on to have my Client in a docker image to start it up with docker-compose but I can’t make it connect to my localhost server anymore, my docker/docker-compose knowledge is limited so this might be very bad typing but any tips why this doesn’t work?

Dockerfile

FROM elixir:1.14.1 
# Create app directory and copy the Elixir projects into it.
RUN mkdir /app
COPY . /app
WORKDIR /app

# Install Hex package manager.
# By using `--force`, we don’t need to type “Y” to confirm the installation.
RUN mix local.hex --force

# Compile the project.
RUN mix do compile

RUN mix deps.get

docker-compose

version: '3.7'
services:
  person:
    build: .
    environment:
      - NAME=Anna
      - ERLANG_COOKIE="its_a_secret"
    ports:
      - "4000:4000"
    extra_hosts:
      - "host.docker.internal:127.0.0.1"
    volumes:
      - .:/app
    command: ["bash", "-c", "elixir --no-halt --name anna@127.0.0.1 --cookie $$ERLANG_COOKIE app.exs"]
    stdin_open: true 
    tty: true

My aim is to have multiple person-services started in my docker-compose and just be able to run docker-compose up to start and get them to connect to the server. But for now, I would be happy just to get one to start, atm everything runs but nothing happens isn’t even sure that the node is started.

Any tips?

There is a sample dockerfile here…

https://hexdocs.pm/phoenix/releases.html#containers

It’s a two stages file for building a Phoenix release. I prefer to deploy with release, than using mix.
But I am using alpine elixir image instead.

You can use environment variables, they are tricky to configure… You find them here

https://hexdocs.pm/mix/1.14/Mix.Tasks.Release.html

For example, I use these in docker compose for Phoenix service.

SECRET_KEY_BASE=...
DATABASE_URL=...
PHX_SERVER=true
RELEASE_DISTRIBUTION=name
RELEASE_NAME=...
RELEASE_COOKIE=...

and these for other non Phoenix nodes

RELEASE_DISTRIBUTION=name
RELEASE_NAME=...
RELEASE_COOKIE=...

I am new to docker, so maybe some others have better advices … but I made it works with those vars (It’s possible to use an env file as well)

Looks like a typo there: you probably wanted a 127.0.0.1 IP address.

Yes totally right, that is a typo! But it didn’t do much different, unfortunately. :sweat_smile:

I wonder if you’re missing a piece in your topology to assign your docker containers to a shared network. I’ve always had issues with separate containers talking to each other unless I explicitly set a network.

In my case, I wasn’t using docker-compose but starting docker manually.