Skip to content

costarastrology/websockets-prototype

Repository files navigation

websockets-prototype

websockets-prototype Build Status

A simple experiment / prototype exploring how to scale webscokets across multiple servers, using Redis for inter-server communication.

Build

You can build the project with stack:

stack build

For development, you can enable fast builds with file-watching, documentation-building, & test-running:

stack test --haddock --fast --file-watch --pedantic

To build & open the documentation, run:

stack haddock --open websockets-prototype

Usage

Interserver Communication

# Start a server on port 9000
$ PORT=9000 stack run
# Start another server on port 9001
$ PORT=9001 stack run
# Check out the subscriber counts for Redis channels
$ redis-cli
127.0.0.1:6379> PUBSUB NUMSUB ping1 ping2
1) "ping1"
2) (integer) 2
3) "ping2"
4) (integer) 2
127.0.0.1:6379> exit
# Hit the servers, observe both servers printing even though the request was
# only to one.
curl localhost:9000/ping1
curl localhost:9000/ping2
curl localhost:9001/ping1
curl localhost:9001/ping2

Websockets

You'll need a WebSockets client. Postman has one built in & is what we use.

# Start a server on port 9000
$ PORT=9000 stack run
# Start another server on port 9001
$ PORT=9001 stack run

Open a websocket connection to each server in 2 clients via the /websockets route(ws://localhost:9000/websockets & ws://localhost:9001/websockets). You should see each server print out the new connection & the list of connected clients.

In a client, send this message:

{
    "channel": "chat",
    "message": {
        "type": "SubmitMessage",
        "contents": "Hello World"
    }
}

The receiving server should print out the parsed message & all clients should receive this reply back, no matter what server they are connected to:

{
    "channel": "chat",
    "message": {
        "contents": {
            "content": "Hello World",
            "postedAt": "2022-09-09T18:12:45.90481586Z"
        },
        "type": "NewMessageReceived"
    }
}

Hit either of the ping API routes with curl:

curl http://localhost:9001/ping2

You should see every websockets client receive this message:

{
    "channel": "ping",
    "message": {
        "contents": 2,
        "type": "Ping"
    }
}

Websocket Subscriptions

Again, start two servers & connect a client to each. In one client, subscribe to a new chat room:

{
    "channel": "chat-room",
    "message": {
        "type": "JoinRoom",
        "contents": {
            "room": "#haskell"
        }
    }
}

You will receive back a members list(accurate list not implemented):

{
    "channel": "chat-room",
    "message": {
        "contents": {
            "room": "#haskell",
            "users": []
        },
        "type": "MemberList"
    }
}

As well as an acknowledgement of joining:

{
    "channel": "chat-room",
    "message": {
        "contents": {
            "room": "#haskell",
            "user": "b5eda4bd-a8f2-4d6f-81c5-3da4c4333679"
        },
        "type": "JoinedRoom"
    }
}

Send a message to the chat room:

{
    "channel": "chat-room",
    "message": {
        "type": "SendMessage",
        "contents": {
            "room": "#haskell",
            "message": "Monad monoid category whatever"
        }
    }
}

You will receive the message back, the other client will not get this since it is not subscribed:

{
    "channel": "chat-room",
    "message": {
        "contents": {
            "message": "Monad monoid category whatever",
            "room": "#haskell",
            "user": "b5eda4bd-a8f2-4d6f-81c5-3da4c4333679"
        },
        "type": "NewMessage"
    }
}

Now, join the room in the other client:

{
    "channel": "chat-room",
    "message": {
        "type": "JoinRoom",
        "contents": {
            "room": "#haskell"
        }
    }
}

The first client will be notified of the new user joining:

{
    "channel": "chat-room",
    "message": {
        "contents": {
            "room": "#haskell",
            "user": "7407f221-f839-4ebb-9184-000879870af1"
        },
        "type": "JoinedRoom"
    }
}

Now whenever a client sends a message, both clients will receive it back.

A client can leave a room to stop receiving those messages:

{
    "channel": "chat-room",
    "message": {
        "type": "LeaveRoom",
        "contents": {
            "room": "#haskell"
        }
    }
}

All subscribed clients will receive notice of their departure:

{
    "channel": "chat-room",
    "message": {
        "contents": {
            "room": "#haskell",
            "user": "b5eda4bd-a8f2-4d6f-81c5-3da4c4333679"
        },
        "type": "LeftRoom"
    }
}

LICENSE

BSD-3

About

A Haskell prototype for exploring how to scale websocket subscriptions across multiple servers using redis.

Topics

Resources

License

Stars

Watchers

Forks