🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsHeader and Footer not showing in Excel(14.09.2026 um 22:43 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsKB5129194 Windows 11 26H1 Out of Band Update - Deskmodder.de(14.09.2026 um 19:25 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsHeader and Footer not showing in Excel(14.09.2026 um 22:43 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsKB5129194 Windows 11 26H1 Out of Band Update - Deskmodder.de(14.09.2026 um 19:25 Uhr)

🔧 Programmierung 🕛 vor 2 Jahren 8 Min Lesezeit
0

Simple Go Chat Application in under 100 lines of code - Part 2

↗ Quelle (dev.to)
🗣️ Stimme:

This blog is the first part of a multi part series.




  • . It also creates a volume that maps a file nginx.conf to the file /etc/nginx/nginx.conf inside the nginx container.



    Let’s create the nginx.conf file now.




    CODE
    events {
    }

    http {
    upstream go_chat {
    server app:8080;
    }

    server {
    listen 80;

    location / {
    proxy_pass http://go_chat;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade websocket;
    proxy_set_header Connection Upgrade;
    }
    }
    }






    This configuration makes sure that any requests reaching localhost:80 are proxied to our web instances. It also sets some headers that are required for websockets to work as expected.



    Also, we need to update the url used to create the websocket connection in index.html from localhost:8080/ws to localhost:80/ws since all requests will go through the nginx server.



    Time for some experimentation!! Let’s run the docker containers.




    CODE
    docker-compose build
    docker-compose up -d






    Now, let’s test our chat app by opening localhost in two browser windows side by side.





    From the logs, we can see that both the browsers' websocket connections are handled by different server instances and so messages broadcasted by go_chat-app-1 instance do not reach the clients connected to go_chat-app-2 instance, as each server only broadcasts to the connections handled by them.



    Now, to solve this problem, we will be using the Pub/Sub feature of redis. Other than being a database, redis can also act like a message broker and we will be leveraging that in our web application. Lets install the redis client library written in go using go get




    CODE
    go get github.com/redis/go-redis/v9






    Let’s create a redis client in the main function and pass it to the serveWs function.




    CODE
    func main() {
    redisHost := os.Getenv("REDIS_HOST")
    redisPort := os.Getenv("REDIS_PORT")
    rdb := redis.NewClient(&redis.Options{
    Addr: fmt.Sprintf("%s:%s", redisHost, redisPort),
    })

    // Rest of the main function
    }






    Let’s rewrite the serveWs function like this




    CODE
    func serveWs(rdb *redis.Client) func(c *gin.Context) {
    return func(c *gin.Context) {
    upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
    log.Printf("Error in upgrading web socket. Error: %v", err)
    return
    }

    go handleClient(conn, rdb)
    }
    }






    Let’s see what has changed





    • serveWs is now a function that accepts a redis client and returns a gin handler.

    • The redis client is passed to the handleClient function.



    Now let's modify the handleClient function




    CODE
    const channel = "chat"

    func handleClient(c *websocket.Conn, rdb *redis.Client) {
    defer func() {
    delete(clients, c)
    log.Println("Closing Websocket")
    c.Close()
    }()
    clients[c] = struct{}{}

    for {
    var msg Message
    err := c.ReadJSON(&msg)
    if err != nil {
    log.Printf("Error in reading json message. Error : %v", err)
    return
    }

    msgBytes, err := json.Marshal(msg)
    if err != nil {
    fmt.Println("Err marshaling", err.Error())
    return
    }

    err = rdb.Publish(context.Background(), channel, string(msgBytes)).Err()
    if err != nil {
    fmt.Println("Error publishing:", err.Error())
    }
    }
    }






    The handleClient no longer calls broadcast to send messages to other connections. Instead every message received from a connection is published to a redis channel called chat using the rdb.Publish method.



    But how will we consume these messages from redis? To do that, let’s run a go routine that is called inside the main function.




    CODE
    func main() {
    redisHost := os.Getenv("REDIS_HOST")
    redisPort := os.Getenv("REDIS_PORT")
    rdb := redis.NewClient(&redis.Options{
    Addr: fmt.Sprintf("%s:%s", redisHost, redisPort),
    })

    go func() {
    ctx := context.Background()
    sub := rdb.Subscribe(ctx, channel)
    for {
    message, err := sub.ReceiveMessage(ctx)
    if err != nil {
    fmt.Println("Error receiving message", err)
    }
    if message != nil {
    broadcast([]byte(message.Payload))
    }
    }
    }()

    router := gin.Default()
    // Rest of the main function
    }






    The go routine subscribes to the same redis channel chat and reads all messages that are published to that channel using an infinitely running for loop. The message is then broadcasted to all websocket clients using the broadcast function.



    This is how the final main.go file will look like,




    CODE
    package main

    import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"

    "github.com/gin-gonic/gin"
    "github.com/gorilla/websocket"
    "github.com/redis/go-redis/v9"
    )

    const channel = "chat"

    func main() {
    redisHost := os.Getenv("REDIS_HOST")
    redisPort := os.Getenv("REDIS_PORT")
    rdb := redis.NewClient(&redis.Options{
    Addr: fmt.Sprintf("%s:%s", redisHost, redisPort),
    })

    go func() {
    ctx := context.Background()
    sub := rdb.Subscribe(ctx, channel)
    for {
    message, err := sub.ReceiveMessage(ctx)
    if err != nil {
    fmt.Println("Error receiving message", err)
    }
    if message != nil {
    broadcast([]byte(message.Payload))
    }
    }
    }()

    router := gin.Default()
    router.StaticFile("/", "./static/index.html")
    router.GET("/ws", serveWs(rdb))
    err := router.Run()
    if err != nil {
    log.Fatalf("Unable to start server. Error %v", err)
    }
    log.Println("Server started successfully.")
    }

    func serveWs(rdb *redis.Client) func(c *gin.Context) {
    return func(c *gin.Context) {
    upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
    conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
    if err != nil {
    log.Printf("Error in upgrading web socket. Error: %v", err)
    return
    }

    go handleClient(conn, rdb)
    }
    }

    var clients = make(map[*websocket.Conn]struct{})

    type Message struct {
    From string `json:"from"`
    Message string `json:"message"`
    }

    func broadcast(msgBytes []byte) {
    for conn := range clients {
    conn.WriteMessage(websocket.TextMessage, msgBytes)
    }
    }

    func handleClient(c *websocket.Conn, rdb *redis.Client) {
    defer func() {
    delete(clients, c)
    log.Println("Closing Websocket")
    c.Close()
    }()
    clients[c] = struct{}{}

    for {
    var msg Message
    err := c.ReadJSON(&msg)
    if err != nil {
    log.Printf("Error in reading json message. Error : %v", err)
    return
    }

    msgBytes, err := json.Marshal(msg)
    if err != nil {
    fmt.Println("Err marshaling", err.Error())
    return
    }

    err = rdb.Publish(context.Background(), channel, string(msgBytes)).Err()
    if err != nil {
    fmt.Println("Error publishing:", err.Error())
    }
    }
    }






    Now, let’s understand how this solution works.





    In conclusion, we've successfully addressed the scalability limitations of our chat application by integrating Redis pub-sub. By leveraging Docker for containerization and nginx for load balancing, we've achieved a robust and scalable architecture. With Redis acting as a message broker, our application now seamlessly distributes messages across multiple instances, ensuring real-time communication regardless of server distribution. This implementation not only enhances scalability but also lays the foundation for further optimizations and feature enhancements. Cheers to building resilient, real-time applications with Golang!

    Vollständiger Original-Bericht
    Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
    ↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
The Gemini desktop app is now available for Windows
1 Quelle
Header and Footer not showing in Excel
1 Quelle
Burn Out, Or Fade Away
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Simple Go Chat Application in under 100 lines of code - Part 2

Thematisch verwandte Begriffe: Simple, Chat, Application, under · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...