Let’s implement a real-time package tracking app with RabbitMQ and Web socket using Go 🚀
What is this app?
This application provides real-time package location information using vehicle information because vehicles carry packages. So that it answers the questions like where is my package now, where is it going? (Currently, I didn’t implement broadcasting on WebSocket. You can think of communication 1-1)
Application Architecture

- RabbitMQ with docker-compose
- Websocket with echo framework
- Based on this clean architecture template
Source Code
https://github.com/Abdulsametileri/package-tracking-app
Websocket
There are many materials about WebSocket, but I strongly recommend that you read WebSocket’s RFC document.
This protocol has two parts: a handshake and data transfer.

Once the client and server have both sent their handshakes, and if the handshake was successful, then the data transfer part starts. This is a two-way communication channel where each side can, independently from the other, send data at will.
RabbitMQ
I think RabbitMQ documentation is excellent. I recommend that you learn basic concepts like (queue, exchange, etc.) by following their tutorial and reading the AMQP protocol specification.

Websocket Handler
upgrader.Upgrade
upgrades the HTTP server connection to the WebSocket protocol. It checks the handshake process; for example, it checks whether request headers are correct, such as upgrade=Websocket
and connection=Upgrade
equal or not.

To handle the WebSocket closing handshake, I use wsConn.ReadMessage()
When the client navigates another page or something like ReadMessage()
returns a non-nil error. When the error is not nil, I call context’s cancel function. In doing so, the context’s done channel is closed so we can close the underlying TCP connection and return it from our handler, as shown below in the first select case.

In the select statement default state, we can listen to our package_status
queue; when the new package status arrives, we can pass it information to the WebSocket. Note: p.PUseCase.TrackByVehicleID(ctx, vehicleID)
This method is based on <-chan amqp.Delivery
. We get package info from this channel when a new message comes in our queue.
Package Usecase
In our use case, there is no specific rule in there. So we can get bytes message from RabbitMQ client and marshal our package struct format.
RabbitMQ Client
I open a TCP connection and a channel (virtual AMQP connection) within it. Channels are full-duplex, meaning that one channel can be used to both publish and consume messages.
I configure queue with declare keyword -create if not present, otherwise continue- and register consumer chan on it.
I continuously listen within the (c *rabbitmqClient) ConsumeByVehicleID
method. I use the message_id
property to differentiate messages.
Note: I use (c *rabbitmqClient) Publish
method on the main method for only test purposes.
Put it all together
When we run the application, we see the package location every 3 seconds.

Source Code
https://github.com/Abdulsametileri/package-tracking-app
References
https://datatracker.ietf.org/doc/html/rfc6455
https://www.rabbitmq.com/protocol.html
https://www.sohamkamani.com/golang/context-cancellation-and-values/
https://www.amazon.com/RabbitMQ-Essentials-distributed-scalable-applications/dp/1789131669