レッドス、WebSocketとのチャットアプリケーションを構築する方法を学びましょう


The WebSocket プロトコルは双方向(両方のサーバとクライアントがメッセージを交換することができる)を提供します、そして、全二重(サーバーまたはクライアントは同時にメッセージを送ることができます)は、それがチャットアプリケーションなどのリアルタイムシナリオに適している通信チャンネルに接続しました.WebSocket サーバと相互にメッセージを交換することができます-ピアツーピアの設定に似ています.
このブログでは、我々はどのようにシンプルなチャットアプリケーションを構築する方法を探るWebSocket and Go . 解決策は Redis 同様に(これはすぐに).

A follow-up blog post (part 2) will demonstrate how to deploy this application to Azure App Service which will communicate with Azure Redis Cache using Virtual Network integration


あなたが学びます.
  • REDISデータ構造-このアプリを使用して SET and PUBSUB
  • Redisとの相互作用 go-redis クライアント
  • The gorilla WebSocket WebSocketプロトコルの完全でテストされた実装を提供するライブラリ

  • Azure Cache for Redis クラウドで管理されたREDIS提供です
  • なぜRedis?



    チャットアプリケーションを検討しましょう.ユーザが最初に接続するとき、対応するWebSocket アプリケーション内で接続が作成されます(WebSocket そして、特定のアプリケーションインスタンスに関連付けられます.このWebSocket 接続は、ユーザー間でチャットメッセージをブロードキャストすることができます.我々は、複数のインスタンスを実行することによって我々のアプリケーション(例えば、大きなユーザーベースのアカウントを)スケールすることができます.さて、新しいユーザが入ってきた場合、新しいインスタンスに接続することができます.それで、我々は異なるユーザー(したがって、彼らのそれぞれ)でシナリオを持ちますWebSocket 接続は異なるインスタンスに関連付けられます.その結果、彼らはお互いとメッセージを交換することができなくなります-これは私たちのおもちゃのチャットアプリケーションでも受け入れられません😉
    Redis 多様性をサポートする多彩なキー値ですrich data structures 例えばList , Set , Sorted Set , Hash などの機能の一つも含まれていますPubSub 発行者がREDISチャンネルにメッセージを送ることができて、加入者がこれらのチャンネル(s)の上でメッセージを聞くことができる能力を使用する能力-両方とも完全に独立していて、互いに切り離されます.これは、我々が持っている問題を解決するために使用することができます.今、代わりにWebSocket 接続のみ、我々はredisを使用することができますchannel 各チャットアプリケーションを購読することができます.したがって、WebSocket 接続は現在、すべてのアプリケーションインスタンス(および関連するチャットユーザー)がそれらを受信することを保証するREDISチャンネルを介してパイプすることができます.
    これについては、次の節のコードに飛び込んでください.それはavailable on Github

    Please note that instead of plain WebSocket, you can also use technologies such as Azure SignalR that allows apps to push content updates to connected clients, such as a single page web or mobile application. As a result, clients are updated without the need to poll the server or submit new HTTP requests for updates


    に沿って従って、この解決策をAzureに配備するにはMicrosoft Azure アカウント.You can grab one for free 場合は、すでにそれを持っていない!

    チャットアプリケーション概要


    クイックコードウォークスルーのための時間.ここに、アプリケーション構造があります.
    .
    ├── Dockerfile
    ├── chat
    │   ├── chat-session.go
    │   └── redis.go
    ├── go.mod
    ├── go.sum
    ├── main.go
    
    インmain.go , 我々register our WebSocket handler and start the web server - 使用されるすべてはプレーンですnet/http パッケージ
        http.Handle("/chat/", http.HandlerFunc(websocketHandler))
        server := http.Server{Addr: ":" + port, Handler: nil}
        go func() {
            err := server.ListenAndServe()
            if err != nil && err != http.ErrServerClosed {
                log.Fatal("failed to start server", err)
            }
        }()
    
    The WebSocket ハンドラプロセスチャットユーザWebSocket クライアントと新しいチャットセッションを開始します.
    func websocketHandler(rw http.ResponseWriter, req *http.Request) {
        user := strings.TrimPrefix(req.URL.Path, "/chat/")
    
        peer, err := upgrader.Upgrade(rw, req, nil)
        if err != nil {
            log.Fatal("websocket conn failed", err)
        }
        chatSession := chat.NewChatSession(user, peer)
        chatSession.Start()
    }
    
    エーChatSession (一部)chat/chat-session.go ) ユーザを表し、対応するWebSocket 接続(サーバ側)
    type ChatSession struct {
        user string
        peer *websocket.Conn
    }
    
    セッションが開始されると、goroutine チャットに参加したユーザーからのメッセージを受け入れる.呼び出すことで ReadMessage() (from websocket.Conn ) インfor ループ.このゴジラexits を返します.WebSocket 接続がクローズされるか、アプリケーションがシャットダウンされるctrl+c ). 要約するために、チャット・メッセージを扱うために各々のユーザーのために産卵される別々のGoroutineがあります.
    func (s *ChatSession) Start() {
    ...
        go func() {
            for {
                _, msg, err := s.peer.ReadMessage()
                if err != nil {
                    _, ok := err.(*websocket.CloseError)
                    if ok {
                        s.disconnect()
                    }
                    return
                }
                SendToChannel(fmt.Sprintf(chat, s.user, string(msg)))
            }
        }()
    
    ユーザーからのメッセージが受信されるとすぐにWebSocket を使って他のユーザにブロードキャストしますSendToChannel 一部である機能chat/redis.go . すべてはそれをredisにメッセージを発表するpubsub チャンネル
    func SendToChannel(msg string) {
        err := client.Publish(channel, msg).Err()
        if err != nil {
            log.Println("could not publish to channel", err)
        }
    }
    
    重要な部分はsub (加入者)方程式の一部.それぞれの接続されたチャットユーザーのための専用ゴロウーンがあった場合とは対照的にsingle Goroutine(アプリケーション範囲で)Redisチャンネルを購読して、メッセージを受け取り、彼らのそれぞれのサーバー側を使用しているすべてのユーザーにそれを放送してくださいWebSocket 接続.
    func startSubscriber() {
        go func() {
            sub = client.Subscribe(channel)
            messages := sub.Channel()
            for message := range messages {
                from := strings.Split(message.Payload, ":")[0]
                for user, peer := range Peers {
                    if from != user {
                        peer.WriteMessage(websocket.TextMessage, []byte(message.Payload))
                    }
                }
            }
        }()
    }
    

    The subscription is ended when the application instance is shut down - this is turn terminates the channel for-range loop and the goroutine exits


    The startSubscriber 関数はinit() ファンクションredis.go . The init() 関数はRedisに接続し、接続が失敗した場合にアプリケーションが終了する.
    ああ!これは私たちのチャットバックエンドをフックすることができますREDISインスタンスを設定する時間です.クラウドでREDISサーバを作ろう!

    キャッシュセットアップ


    RedisのためのAzureキャッシュはAzureの中でホストされて、Azureの範囲内で、または、Azureの外でどんなアプリケーションにでもアクセス可能である安全な、専用のRedisキャッシュへのアクセスを提供します.
    このブログの目的のために、我々はAzure RedisキャッシュをBasic 開発/テストと非重要なワークロードに理想的な単一ノードキャッシュです.また、あなたから選択することに注意してくださいStandard and Premium ティアーprovide different features ranging from persistence, clustering, geo-replication, etc .
    私は使用されますAzure CLI インストール用.また、使用することができますAzure Cloud Shell ブラウザの人なら!
    Azure Redisキャッシュインスタンスをすばやく設定するには、 az redis create コマンド.
    az redis create --location westus2 --name chat-redis --resource-group chat-app-group --sku Basic --vm-size c0
    

    Checkout "Create an Azure Cache for Redis" for a step-by-step guide


    完了したら、Azure Redisキャッシュインスタンス、つまりホスト、ポート、アクセスキーに接続するために必要な情報を取得する必要があります.これはCLIを使うこともできます.
    //host and (SSL) port
    az redis show --name chat-redis --resource-group chat-app-group --query [hostName,sslPort] --output tsv
    
    //primary access key
    az redis list-keys --name chat-redis --resource-group chat-app-group --query [primaryKey] --output tsv
    

    Checkout "Get the hostname, ports, and keys for Azure Cache for Redis" for a step-by-step guide


    ……

    ……レッツチャット!


    シンプルなものを維持するために、アプリケーションはDocker image
    まず、いくつかの環境変数を設定します.
    //use port 6380 for SSL
    export REDIS_HOST=[redis cache hostname as obtained from CLI]:6380
    export REDIS_PASSWORD=[redis cache primary access key as obtained from CLI]
    export EXT_PORT=9090
    export NAME=chat1
    

    The application uses a static port 8080 internally (for the web server). We use an external port specified by EXT_PORT and map it to the port 8080 inside our container (using -p $EXT_PORT:8080)


    Dockerコンテナを起動する
    docker run --name $NAME -e REDIS_HOST=$REDIS_HOST -e REDIS_PASSWORD=$REDIS_PASSWORD -p $EXT_PORT:8080 abhirockzz/redis-chat-go
    
    その時間は、チャットに参加する!任意のWebSocketクライアントを使用できます.私は好む wscat 端末およびChrome WebSocket extension in the browser
    これを使ってデモしますwscat 私の端末から.異なるユーザをシミュレートするために2つの別々の端末を開きます.
    //terminal 1 (user "foo")
    wscat -c ws://localhost:9090/chat/foo
    
    //terminal 2 (user "bar")
    wscat -c ws://localhost:9090/chat/bar
    
    ここでチャットの例ですfoo and bar foo 最初に加わって、Aを得ましたWelcome foo! メッセージなどbar その後入社foo . 注意foo が通知されたbar 参加しました.foo and bar 前にいくつかのメッセージを交換bar レフトfoo についても、
    演習として、チャットアプリケーションの別のインスタンスを起動することができます.別のdockerコンテナを別の値でスピンEXT_PORT と名前.
    //use port 6380 for SSL
    export REDIS_HOST=[redis cache host name as obtained from CLI]:6380
    export REDIS_PASSWORD=[redis cache primary access key as obtained from CLI]
    export EXT_PORT=9091
    export NAME=chat2
    
    docker run --name $NAME -e REDIS_HOST=$REDIS_HOST -e REDIS_PASSWORD=$REDIS_PASSWORD -p $EXT_PORT:8080 abhirockzz/redis-chat-go
    
    今すぐ接続オンポート9091 (または選択したポート)別のユーザーをシミュレートする
    //user "pi"
    wscat -c ws://localhost:9091/chat/pi
    
    以来foo がアクティブであれば、通知されます.pi and foo 交換できる

    チェックレッド


    REDISデータ構造を覗いて確認しましょう.あなたは redis-cli これは.あなたがAzure Redisキャッシュを使っているならば、私は本当に便利なものを使うことを勧めますweb based Redis console これは.
    私たちはSET 名前chat-users ) アクティブユーザーを格納する
    SMEMBERS chat-users
    
    あなたは結果を見るべきですfoo and bar 現在のチャットアプリケーションに接続されていると関連するアクティブWebSocket 接続
    1) "foo"
    2) "bar"
    
    PubSubチャンネルについてはどうですか?
    PUBUSB CHANNELS
    
    単一のチャンネルがすべてのユーザに使用されるので、REDISサーバからこの結果を得るべきです:
    1) "chat"
    
    これはこのブログのポストです.パート2のために調整滞在!
    場合は、この便利な発見し、次のようにしてください🙌 私はあなたのフィードバックを得るのが大好きだ🙏🏻