すべてのプロダクト
Search
ドキュメントセンター

Server Load Balancer:CLB と WebSocket を使用したリアルタイムメッセージング

最終更新日:Jul 25, 2026

WebSocket は、単一の TCP 接続上で全二重通信チャネルを提供するネットワークプロトコルです。クライアントとサーバー間の持続的な接続を可能にし、双方が能動的にデータを送受信できるようにします。この設計により、頻繁に新しい接続を確立する際のオーバーヘッドとレイテンシーが削減され、従来の HTTP リクエスト/レスポンスモデルよりも効率的になります。WebSocket は主に、リアルタイム通信を必要とするアプリケーションで使用されます。クラシックロードバランサー (CLB) は、デフォルトで WebSocket プロトコルをサポートしています。

WebSocket の概要

WebSocket を使用する理由

Web 技術の進化に伴い、ライブチャットルームやリアルタイムコメントなどの機能を実現するために、サーバーがリアルタイムでデータをプッシュする必要性が高まっています。クライアントのブラウザがサーバーに繰り返し HTTP リクエストを送信して最新のデータを取得する従来のポーリング方式には、大きな欠点があります。大きな HTTP ヘッダーと小さなデータペイロードを持つリクエストが頻繁に発生すると、サーバーの負荷が増大し、帯域幅が無駄になります。

これらの問題に対処するため、HTML5 では WebSocket プロトコルが導入されました。これは、クライアントとサーバー間の通信に対してより効率的なソリューションを提供します。WebSocket は全二重通信をサポートしており、サーバーとクライアントが同時にデータを送受信できることを意味します。これにより、サーバーはポーリングリクエストを待つことなく、新しいデータを能動的にクライアントにプッシュできます。この双方向のリアルタイム通信メカニズムは、データ転送効率を向上させ、不要なネットワークリクエストを削減し、サーバーリソースと帯域幅を節約することで、よりスムーズで応答性の高いユーザーエクスペリエンスを提供します。

WebSocket の主な特徴

通信は、標準的な TCP 3ウェイハンドシェイクから始まります。次に、クライアントはプロトコルアップグレードハンドシェイクとして知られる特別な HTTP リクエストを送信します。このハンドシェイクが成功すると、接続は HTTP から WebSocket に切り替わります。それ以降のクライアントとサーバー間のすべての通信は WebSocket プロトコルを使用し、同じ接続上で双方向のデータ交換が可能になります。

WebSocket 接続が確立されると、その接続はアクティブなまま維持されます。この持続的で低レイテンシーの接続により、継続的な双方向のデータ転送が可能になり、データ交換の効率が向上します。

WebSocket はデータフレームを使用して通信します。データフレームには独自のフレームプロトコル形式があり、ヘッダーは簡潔です。データはテキストまたはバイナリとして送信できます。この方法により、持続的接続におけるプロトコルのオーバーヘッドが削減され、ネットワークの相互作用がより効率的になります。これにより、サーバーリソースと帯域幅が節約され、よりスムーズなリアルタイムのインタラクティブな体験が提供されます。

WebSocket プロトコルの詳細については、公式ドキュメント The WebSocket Protocol をご参照ください。

WebSocket のユースケース

WebSocket は、AI アプリケーション、オンラインチャットルーム、リアルタイム通知システム、多人数参加型オンラインゲーム、リアルタイム市場データフィードなど、高速でリアルタイムな双方向通信を必要とするアプリケーションに最適です。

利用シーンの例

ある企業が、Web ベースのオンラインチャットアプリケーションを Alibaba Cloud 上にデプロイしたいと考えています。ユーザーはドメイン名を通じてバックエンドサービスにアクセスし、リアルタイム通信を行います。インスタントメッセージングアプリケーションとして、低レイテンシーで効率的なリアルタイムの双方向通信が求められます。

この企業の Web サイトサービスは、高い同時実行性と持続的接続の管理という課題に直面しています。ユーザー数が増加するにつれて、各インタラクションで新しい接続が必要となる従来の HTTP モデルでは、多数の同時接続ユーザーによるリアルタイム通信をサポートできなくなります。これにより、サーバー負荷が急増し、パフォーマンスが低下します。

このシナリオでは、CLB と WebSocket プロトコルを使用することで、高い同時実行性下での持続的接続を効果的に管理できます。vServer グループ内の複数のバックエンドサーバーに WebSocket アプリケーションをデプロイし、メッセージ同期に Redis を使用することで、サービスは高可用性を実現します。これにより、オンラインチャットアプリケーションに信頼性が高く効率的なリアルタイムメッセージングソリューションが提供されます。

注意事項

CLB の HTTP リスナーは、デフォルトで WebSocket プロトコルをサポートしています。CLB はホットアップデートをサポートしており、これは設定変更が既存の持続的接続に影響を与えないことを意味します。

以下の点にご注意ください:

  • CLB とバックエンドサーバー間の接続で特定の HTTP バージョン (HTTP/1.1 など) を使用する場合、バックエンドサーバー上でも同じ HTTP バージョンをサポートする Web サーバーを使用する必要があります。

  • HTTP リスナーのデフォルトの接続リクエストのタイムアウトは 60 秒です。CLB とバックエンドサーバー間で 60 秒以上メッセージの交換がない場合、CLB は能動的に接続を閉じます。

    • デフォルトの 60 秒のタイムアウトでは不十分な場合、リスナーの 接続リクエストタイムアウト を変更できます。

    • 接続を開いたままにするには、少なくとも 60 秒に 1 回はパケットを交換するためのキープアライブメカニズムを実装する必要があります。

  • CLB の HTTP リスナーはデフォルトで WebSocket プロトコルをサポートし、HTTPS リスナーはデフォルトで WebSocket Secure (WSS) プロトコルをサポートします。追加の設定は不要です。

  • TCP リスナーを使用する場合、CLB は組み込みの WebSocket サポートを提供しません。バックエンドサーバーで WebSocket を設定する必要があります。TCP リスナーの設定項目 (スケジューリングアルゴリズム、セッション維持、帯域幅調整、接続タイムアウト) には、WebSocket 関連のオプションは含まれていません。

  • リッスンポートを設定する際は、ポート 25、135、139、444、445、5800、5900 などの脆弱性のあるポートを避けてください。この制限は CLB ではなく、ISP ネットワークによって課せられています。CLB コンソールではこれらのポートの設定はブロックされませんが、一部の ISP ネットワークではこれらのポート上のトラフィックがブロックされます。

前提条件

  • クラシックロードバランサー (CLB) インスタンスを作成済みであること。

  • 3 台の Elastic Compute Service (ECS) インスタンスが必要です:ECS01、ECS02、ECS03。

    • ECS01 と ECS02 は WebSocket アプリケーションのデプロイに使用し、ECS03 は Redis のデプロイに使用します。

    • このチュートリアルでは、すべてのサーバーで CentOS 7.9 を実行します。

    • ECS01、ECS02、ECS03 は同じセキュリティグループに配置することを推奨します。異なるセキュリティグループにある場合は、サーバー間で必要な通信ポートのトラフィックを許可するルールを設定してください。

  • ICP 登録が完了した登録済みドメイン名が必要です。詳細については、「Alibaba Cloud ドメイン名の登録」および「ICP 登録」をご参照ください。

操作手順

ステップ 1:サービスのデプロイ

ECS03 インスタンスに Redis を、ECS01 および ECS02 インスタンスに WebSocket アプリケーションをデプロイする必要があります。

このトピックでは、デモンストレーション目的で CentOS 7.9 上のシンプルな Python ベースのオンラインチャットルームを使用します。この例は参考用です。本番環境では、独自のアプリケーションを使用してください。

ECS03 への Redis のデプロイ

  1. ECS03 インスタンスにログインします。

  2. 以下のコマンドをコピーして実行し、Redis をインストールおよび設定します。

    # EPEL (Extra Packages for Enterprise Linux) をインストール
    sudo yum install epel-release -y
    # Redis をインストール
    sudo yum install redis -y
    # Redis サービスを開始し、有効化
    sudo systemctl start redis
    sudo systemctl enable redis
    # Redis 設定ファイルを編集してリモート接続を許可
    sudo sed -i 's/^bind 127.0.0.1$/bind 0.0.0.0/' /etc/redis.conf
    sudo sed -i 's/^protected-mode yes/protected-mode no/' /etc/redis.conf
    # 変更を有効にするために Redis サービスを再起動
    sudo systemctl restart redis
    # Redis のステータスを確認
    sudo systemctl status redis
    
  3. コマンドがエラーなく実行され、出力に Redis サービスが active (running) 状態であることが示された場合、デプロイと設定は成功です。

    ● redis.service - Redis persistent key-value database
       Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
       Active: active (running) since ...
     Main PID: 12345 (redis-server)
       CGroup: /system.slice/redis.service
               └─12345 /usr/bin/redis-server 0.0.0.0:6379
    

ECS01 への WebSocket のデプロイ

  1. ECS01 インスタンスにログインします。

  2. sudo pip3 install flask flask-socketio flask-cors redis を実行して、依存関係をインストールします。

  3. vi ECS01_ws.py を実行し、i キーを押して編集モードに入ります。

  4. 以下のコードをコピーして貼り付けます:

    テストサービスのサンプルコード

    説明

    13 行目の redis_url の IP アドレスを、ご利用の Redis サーバー (ECS03) の IP アドレスに置き換えてください。

    import os
    import redis
    from flask import Flask, render_template, request
    from flask_cors import CORS
    from flask_socketio import SocketIO, emit, disconnect
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'secret!'
    # クロスオリジンリソース共有 (CORS) を有効化
    CORS(app)
    # Redis をメッセージキューおよび状態ストアとして設定
    redis_url = "redis://192.168.*.*:6379/0"  # ご利用の Redis サーバーの IP アドレスに置き換えてください
    redis_client = redis.StrictRedis.from_url(redis_url)
    # デバッグを容易にするためにログレベルを DEBUG に設定
    socketio = SocketIO(app, message_queue=redis_url, manage_session=True, logger=True, engineio_logger=True, cors_allowed_origins="*")
    SESSION_PREFIX = "session:"
    def set_session_data(session_id, key, value):
        redis_client.hset(f"{SESSION_PREFIX}{session_id}", key, value)
    def get_session_data(session_id, key):
        return redis_client.hget(f"{SESSION_PREFIX}{session_id}", key)
    def delete_session_data(session_id):
        redis_client.delete(f"{SESSION_PREFIX}{session_id}")
    @app.route('/')
    def index():
        return render_template('index.html')
    @socketio.on('connect')
    def handle_connect():
        try:
            session_id = request.sid  # クライアントのセッション ID を取得
            print(f"Session {session_id} connected.")
            welcome_message = "チャットルームに入室しました!"
            emit('message', welcome_message)
            set_session_data(session_id, "username", '')  # ユーザー名を空で初期化
        except Exception as e:
            print(f"Error during connection: {str(e)}")
    @socketio.on('disconnect')
    def handle_disconnect():
        try:
            session_id = request.sid
            username = get_session_data(session_id, "username")
            if username:
                username = username.decode()
                leave_message = f"{username} がチャットルームから退出しました。"
                emit('message', leave_message, broadcast=True)
                print(leave_message)
            delete_session_data(session_id)
            print(f"Session {session_id} disconnected.")
        except Exception as e:
            print(f"Error during disconnection: {str(e)}")
    @socketio.on('set_username')
    def handle_set_username(username):
        session_id = request.sid
        set_session_data(session_id, "username", username)
        print(f"Username for client {session_id} set to {username}")
        emit('message', f"ユーザー名が {username} に設定されました。")
    @socketio.on('message')
    def handle_message(msg):
        session_id = request.sid
        username = get_session_data(session_id, "username")
        if username:
            username = username.decode()
            formatted_message = f"{username}: {msg}"
            emit('message', formatted_message, broadcast=True)
            print(formatted_message)
        else:
            warning_message = "メッセージの送信に失敗しました:最初にユーザー名を設定してください。"
            emit('message', warning_message)
            print(warning_message)
    if __name__ == '__main__':
        # templates ディレクトリが存在することを確認
        if not os.path.exists('templates'):
            os.makedirs('templates')
        # Flask テンプレート (index.html) を使用
        html_code = '''<!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>チャットルーム</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                display: flex;
                flex-direction: column;
                align-items: center;
                margin: 0;
                padding: 0;
                background-color: #f0f0f0;
            }
            h1 {
                color: #333;
            }
            .chat-container {
                width: 90%;
                max-width: 600px;
                background: white;
                padding: 20px;
                border-radius: 8px;
                box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            }
            .user-container, .message-container {
                display: flex;
                margin-bottom: 10px;
            }
            .user-container input, .message-container input {
                flex: 1;
                padding: 10px;
                margin-right: 10px;
                border: 1px solid #ccc;
                border-radius: 4px;
            }
            .message-container {
                margin-top: 10px;
            }
            button {
                padding: 10px;
                background-color: #0056b3;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
            }
            button:hover {
                background-color: #004099;
            }
            #messages {
                border: 1px solid #ccc;
                padding: 10px;
                height: 300px;
                overflow-y: scroll;
                margin-bottom: 10px;
                border-radius: 4px;
                background-color: #f9f9f9;
            }
        </style>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
        <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    </head>
    <body>
        <h1>オンラインチャットルーム</h1>
        <div class="chat-container">
            <div class="user-container">
                <input type="text" id="username" autocomplete="off" placeholder="ユーザー名を入力">
                <button onclick="setUsername()">ユーザー名を設定</button>
            </div>
            <div id="messages"></div>
            <div class="message-container">
                <input type="text" id="myMessage" autocomplete="off" placeholder="メッセージを入力...">
                <button onclick="sendMessage()">送信</button>
            </div>
        </div>
        <script>
            var socket = io({ transports: ['websocket', 'polling', 'flashsocket'] });
            var usernameSet = false;
            socket.on('connect', function() {
                console.log("サーバーに接続しました!");
                socket.on('message', function(msg){
                    $('#messages').append($('<div>').text(msg));
                    $('#messages').scrollTop($('#messages')[0].scrollHeight);
                });
            });
            function setUsername() {
                var username = $('#username').val();
                if (username) {
                    socket.emit('set_username', username);
                    usernameSet = true;  // ユーザー名が設定されたことをマーク
                } else {
                    alert("ユーザー名は空にできません!");
                }
            }
            function sendMessage() {
                if (usernameSet) {
                    var message = $('#myMessage').val();
                    if (message) {
                        socket.send(message);
                        $('#myMessage').val('');
                    } else {
                        alert("メッセージは空にできません!");
                    }
                } else {
                    alert("最初にユーザー名を設定してください!");
                }
            }
        </script>
    </body>
    </html>
    '''
        # テンプレートをファイルに保存
        with open('templates/index.html', 'w') as file:
            file.write(html_code)
        socketio.run(app, host='0.0.0.0', port=5000)
    
  5. Esc キーを押し、:wq と入力して変更を保存します。

  6. sudo python3 ECS01_ws.py コマンドを実行してスクリプトを実行します。

  7. 以下の出力が表示された場合、WebSocket アプリケーションはポート 5000 で起動しています。

    Server initialized for threading.
     * Serving Flask app 'ECS01_ws' (lazy loading)
     * Environment: production
       WARNING: This is a development server. Do not use it in a production deployment.
       Use a production WSGI server instead.
     * Debug mode: off
     * Running on all addresses.
       WARNING: This is a development server. Do not use it in a production deployment.
     * Running on http://192.168.*.*:5000/ (Press CTRL+C to quit)
    

    アプリケーションの起動に失敗した場合は、ポートがすでに使用されていないか、コマンドやコードのコピーが間違っていないかを確認してください。

● redis.service – Redis persistent key-value database
   Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/redis.service.d
           └─limit.conf
   Active: active (running) since Thu 2xxx xxx xxx xxx CST; 6s ago
  Process: 14715 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=0/SUCCESS)
 Main PID: 14730 (redis-server)
   CGroup: /system.slice/redis.service
           └─14730 /usr/bin/redis-server 0.0.0.0:6379

ECS02 への WebSocket のデプロイ

  1. ECS02 インスタンスにログインします。

  2. sudo pip3 install flask flask-socketio flask-cors redis を実行して、依存関係をインストールします。

  3. vi ECS02_ws.py を実行し、i キーを押して編集モードに入ります。

  4. 以下のコードをコピーして貼り付けます:

    テストサービスのサンプルコード

    説明

    13 行目の redis_url の IP アドレスを、ご利用の Redis サーバー (ECS03) の IP アドレスに置き換えてください。

    import os
    import redis
    from flask import Flask, render_template, request
    from flask_cors import CORS
    from flask_socketio import SocketIO, emit, disconnect
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'secret!'
    # クロスオリジンリソース共有 (CORS) を有効化
    CORS(app)
    # Redis をメッセージキューおよび状態ストアとして設定
    redis_url = "redis://192.168.*.*:6379/0"  # ご利用の Redis サーバーの IP アドレスに置き換えてください
    redis_client = redis.StrictRedis.from_url(redis_url)
    # デバッグを容易にするためにログレベルを DEBUG に設定
    socketio = SocketIO(app, message_queue=redis_url, manage_session=True, logger=True, engineio_logger=True, cors_allowed_origins="*")
    SESSION_PREFIX = "session:"
    def set_session_data(session_id, key, value):
        redis_client.hset(f"{SESSION_PREFIX}{session_id}", key, value)
    def get_session_data(session_id, key):
        return redis_client.hget(f"{SESSION_PREFIX}{session_id}", key)
    def delete_session_data(session_id):
        redis_client.delete(f"{SESSION_PREFIX}{session_id}")
    @app.route('/')
    def index():
        return render_template('index.html')
    @socketio.on('connect')
    def handle_connect():
        try:
            session_id = request.sid  # クライアントのセッション ID を取得
            print(f"Session {session_id} connected.")
            welcome_message = "チャットルームに入室しました!"
            emit('message', welcome_message)
            set_session_data(session_id, "username", '')  # ユーザー名を空で初期化
        except Exception as e:
            print(f"Error during connection: {str(e)}")
    @socketio.on('disconnect')
    def handle_disconnect():
        try:
            session_id = request.sid
            username = get_session_data(session_id, "username")
            if username:
                username = username.decode()
                leave_message = f"{username} がチャットルームから退出しました。"
                emit('message', leave_message, broadcast=True)
                print(leave_message)
            delete_session_data(session_id)
            print(f"Session {session_id} disconnected.")
        except Exception as e:
            print(f"Error during disconnection: {str(e)}")
    @socketio.on('set_username')
    def handle_set_username(username):
        session_id = request.sid
        set_session_data(session_id, "username", username)
        print(f"Username for client {session_id} set to {username}")
        emit('message', f"ユーザー名が {username} に設定されました。")
    @socketio.on('message')
    def handle_message(msg):
        session_id = request.sid
        username = get_session_data(session_id, "username")
        if username:
            username = username.decode()
            formatted_message = f"{username}: {msg}"
            emit('message', formatted_message, broadcast=True)
            print(formatted_message)
        else:
            warning_message = "メッセージの送信に失敗しました:最初にユーザー名を設定してください。"
            emit('message', warning_message)
            print(warning_message)
    if __name__ == '__main__':
        # templates ディレクトリが存在することを確認
        if not os.path.exists('templates'):
            os.makedirs('templates')
        # Flask テンプレート (index.html) を使用
        html_code = '''<!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>チャットルーム</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                display: flex;
                flex-direction: column;
                align-items: center;
                margin: 0;
                padding: 0;
                background-color: #f0f0f0;
            }
            h1 {
                color: #333;
            }
            .chat-container {
                width: 90%;
                max-width: 600px;
                background: white;
                padding: 20px;
                border-radius: 8px;
                box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            }
            .user-container, .message-container {
                display: flex;
                margin-bottom: 10px;
            }
            .user-container input, .message-container input {
                flex: 1;
                padding: 10px;
                margin-right: 10px;
                border: 1px solid #ccc;
                border-radius: 4px;
            }
            .message-container {
                margin-top: 10px;
            }
            button {
                padding: 10px;
                background-color: #0056b3;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
            }
            button:hover {
                background-color: #004099;
            }
            #messages {
                border: 1px solid #ccc;
                padding: 10px;
                height: 300px;
                overflow-y: scroll;
                margin-bottom: 10px;
                border-radius: 4px;
                background-color: #f9f9f9;
            }
        </style>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
        <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    </head>
    <body>
        <h1>オンラインチャットルーム</h1>
        <div class="chat-container">
            <div class="user-container">
                <input type="text" id="username" autocomplete="off" placeholder="ユーザー名を入力">
                <button onclick="setUsername()">ユーザー名を設定</button>
            </div>
            <div id="messages"></div>
            <div class="message-container">
                <input type="text" id="myMessage" autocomplete="off" placeholder="メッセージを入力...">
                <button onclick="sendMessage()">送信</button>
            </div>
        </div>
        <script>
            var socket = io({ transports: ['websocket', 'polling', 'flashsocket'] });
            var usernameSet = false;
            socket.on('connect', function() {
                console.log("サーバーに接続しました!");
                socket.on('message', function(msg){
                    $('#messages').append($('<div>').text(msg));
                    $('#messages').scrollTop($('#messages')[0].scrollHeight);
                });
            });
            function setUsername() {
                var username = $('#username').val();
                if (username) {
                    socket.emit('set_username', username);
                    usernameSet = true;  // ユーザー名が設定されたことをマーク
                } else {
                    alert("ユーザー名は空にできません!");
                }
            }
            function sendMessage() {
                if (usernameSet) {
                    var message = $('#myMessage').val();
                    if (message) {
                        socket.send(message);
                        $('#myMessage').val('');
                    } else {
                        alert("メッセージは空にできません!");
                    }
                } else {
                    alert("最初にユーザー名を設定してください!");
                }
            }
        </script>
    </body>
    </html>
    '''
        # テンプレートをファイルに保存
        with open('templates/index.html', 'w') as file:
            file.write(html_code)
        socketio.run(app, host='0.0.0.0', port=5000)
                      
  5. Esc キーを押し、:wq と入力して変更を保存します。

  6. sudo python3 ECS02_ws.py コマンドを実行してスクリプトを実行します。

  7. 以下の出力が表示された場合、WebSocket アプリケーションはポート 5000 で起動しています。

    Server initialized for threading.
     * Serving Flask app 'ECS02_ws' (lazy loading)
     * Environment: production
       WARNING: This is a development server. Do not use it in a production deployment.
       Use a production WSGI server instead.
     * Debug mode: off
     * Running on all addresses.
       WARNING: This is a development server. Do not use it in a production deployment.
     * Running on http://192.168.*.*:5000/ (Press CTRL+C to quit)
    

    アプリケーションの起動に失敗した場合は、ポートがすでに使用されていないか、コマンドやコードのコピーが間違っていないかを確認してください。

ステップ 2:vServer グループの設定

  1. クラシックロードバランサー (CLB) コンソールにログインします。

  2. 上部のナビゲーションバーで、CLB インスタンスがデプロイされているリージョンを選択します。

  3. 左側のナビゲーションウィンドウで、インスタンス を選択します。インスタンス ページで、ターゲットインスタンスを見つけてその ID をクリックします。

  4. 仮想サーバーグループ タブで、VServer Group の作成 をクリックします。VServer Group の作成 ページで、以下のパラメーターを設定します。他のパラメーターはデフォルト値を使用するか、必要に応じて変更できます。設定が完了したら、作成 をクリックし、画面の指示に従います。

    パラメーター

    説明

    VServer グループ名

    vServer グループ名として RS1 を入力します。

  5. 仮想サーバーグループ タブで、作成した vServer グループを見つけ、操作 列の 編集 をクリックします。

  6. VServer Group の編集 ページで、追加 をクリックします。サーバー登録 ページで、バックエンドサーバー ECS01 と ECS02 を追加します。両方のサーバーのポートを、WebSocket アプリケーションが使用するポートである 5000 に設定します。

  7. VServer Group の編集 ページで、追加したサーバーを選択し、保存 をクリックします。

ステップ 3:HTTP リスナーの設定

  1. クラシックロードバランサー (CLB) コンソールにログインします。

  2. 上部のナビゲーションバーで、CLB インスタンスがデプロイされているリージョンを選択します。

  3. 左側のナビゲーションウィンドウで、インスタンス を選択します。

  4. インスタンス ページで、ターゲットインスタンスを見つけ、操作 列の リスナーの設定 をクリックします。

  5. プロトコルとリスナー ページで、以下のパラメーターを設定します。他のパラメーターはデフォルト値を使用するか、必要に応じて変更できます。設定が完了したら、次へ をクリックします。

    パラメーター

    説明

    リスナープロトコルの選択

    [HTTP] を選択します。

    リスニングポート

    この例では、ポートを 5000 に設定します。

  6. バックエンド サーバー ページで、以下のパラメーターを設定します。他のパラメーターはデフォルト値を使用するか、必要に応じて変更できます。設定が完了したら、次へ をクリックします。

    パラメーター

    説明

    サーバーグループを選択

    作成した vServer グループを選択します。

  7. [ヘルスチェック] ページでは、パラメーターにデフォルト値を使用するか、必要に応じて変更できます。その後、次へ をクリックします。

  8. 確定 ページで、設定を確認し、送信 をクリックしてリスナーを作成します。

ステップ 4:DNS 名前解決の設定

説明
  • Alibaba Cloud に登録されていないドメインの場合、DNS レコードを設定する前に、まず Alibaba Cloud DNS コンソールにドメインを追加する必要があります。

  • ご利用の CLB インスタンスが内部向けインスタンスである場合、まず Elastic IP アドレス (EIP) を関連付け、その後ドメイン名を EIP にマッピングする A レコードを作成してパブリックアクセスを有効にする必要があります。

  1. 左側のナビゲーションウィンドウで、CLB > インスタンス を選択します。

  2. インスタンス ページで、ターゲットインスタンスを選択し、その IP をコピーします。

  3. 以下の手順で A レコードを追加します:

    1. Alibaba Cloud DNS コンソールにログインします。

    2. インターネットの権威ある DNS 解決 ページで、ターゲットドメイン名を見つけ、Actions 列の 解決設定 をクリックします。

    3. 解決設定 ページで、Add Record をクリックします。

    4. Add Record パネルで、以下のパラメーターを設定します。他のパラメーターはデフォルト値のままにするか、必要に応じて変更できます。その後、OK をクリックします。

      パラメーター

      説明

      Record Type

      ドロップダウンリストから [A] を選択します。

      Hostname

      ドメイン名のプレフィックスです。

      説明

      ルートドメインの場合、ホスト名を @ に設定します。

      Record Value

      コピーした CLB インスタンスの IP アドレスを入力します。

ステップ 5:結果の検証

異なるパブリック IP アドレスを持つ 2 台のコンピューターを準備します。各コンピューターでブラウザを使用してチャットメッセージを送受信し、CLB が WebSocket を介してリアルタイムでメッセージを配信することを確認します。

  1. ブラウザで http://<your-domain-name>:5000 にアクセスし、オンラインチャットルームアプリケーションにアクセスします。

    [オンラインチャットルーム] インターフェイスが読み込まれます。チャットメッセージエリアに「チャットルームに入室しました!」というメッセージが表示され、WebSocket 接続が確立されたことを示します。ページには、[ユーザー名を設定] ボタンのあるユーザー名設定エリアと、[送信] ボタンのあるメッセージ送信エリアが含まれています。

    ブラウザの開発者ツールを開くと、[ネットワーク] タブにブラウザが WebSocket プロトコルを使用して通信していることが表示されます。

    [ネットワーク] パネルで、フィルターを websocket に設定します。ステータスコードが 101 の WebSocket リクエストは、プロトコルのアップグレードが成功したことを示します。接続ステータスは保留中 (Pending) であり、これは持続的な WebSocket 接続が確立され、アクティブであることを意味します。

  2. チャットのユーザー名を入力し、[ユーザー名を設定] をクリックします。

  3. 各コンピューターで複数のチャットメッセージを入力し、[送信] をクリックしてアプリケーションをテストします。

    両方のブラウザがリアルタイムでメッセージを受信します。

    オンラインチャットルームアプリケーションは正常にデプロイされました。ページには、[ユーザー名] 入力ボックスと [ユーザー名を設定] ボタン、チャットメッセージ表示エリア、そして下部に [送信] ボタンのあるメッセージ入力ボックスが含まれています。User1 や User2 などの複数のユーザーが、チャットルームでリアルタイムの会話を行うことができます。

  4. これにより、CLB と WebSocket を使用することで、リアルタイムで高可用性なメッセージングが可能になることが検証されます。

TCP リスナーの WebSocket 設定

TCP リスナーはレイヤー 4 (トランスポート層) の転送のみを実行します。CLB は TCP 接続を直接バックエンドサーバーに転送し、レイヤー 7 (アプリケーション層) のプロトコル処理には関与しません。CLB は TCP リスナーに対して組み込みの WebSocket プロトコルアップグレードを提供しません。バックエンドサービスは、WebSocket プロトコルアップグレードを完了するために HTTP Upgrade ハンドシェイクを実装する必要があります。

説明

これは HTTP および HTTPS リスナーとは異なります。HTTP リスナーはデフォルトで WebSocket (WS) プロトコルをサポートし、HTTPS リスナーはデフォルトで WebSocket Secure (WSS) プロトコルをサポートします。これらのリスナータイプでは追加の設定は不要です。

設定手順

TCP リスナーで WebSocket サービスを使用するには:

  1. クラシックロードバランサー (CLB) コンソールにログインします。ターゲットの CLB インスタンスを見つけ、操作 列の リスナーの設定 をクリックします。フロントエンドプロトコルを [TCP] に設定し、リッスンポートを指定します。

  2. バックエンドサーバーに WebSocket サービスをデプロイします。バックエンドサービスは、WebSocket プロトコルアップグレードを完了するために HTTP Upgrade ハンドシェイクを実装する必要があります。CLB は TCP ストリームをそのまま転送し、レイヤー 7 プロトコルを処理しません。

  3. CLB は、すべての着信 TCP 接続を直接バックエンドサーバーに転送します。バックエンドサーバーが WebSocket ハンドシェイクを処理し、持続的な WebSocket 接続を維持します。

比較:TCP リスナー vs. HTTP および HTTPS リスナー

リスナータイプ

WebSocket サポート

必要な設定

HTTP リスナー

デフォルトで WebSocket (WS) をサポート

追加設定は不要

HTTPS リスナー

デフォルトで WebSocket Secure (WSS) をサポート

追加設定は不要

TCP リスナー

組み込みの WebSocket サポートなし

バックエンドで HTTP Upgrade ハンドシェイクを実装する必要がある

ユースケース

  • HTTP または HTTPS リスナー — WebSocket プロトコルを使用する標準的な Web アプリケーションに推奨されます。CLB がプロトコルアップグレードを自動的に処理するため、設定が簡素化されます。

  • TCP リスナー — アプリケーション層でカスタムプロトコル処理が必要なシナリオや、単一のリスナーで複数のアプリケーション層プロトコルを同時にサポートする必要がある場合に適しています。

よくある質問

WebSocket Secure プロトコルの使用方法

WebSocket Secure は、WebSocket プロトコルの暗号化版です。

HTTPS リスナーは、デフォルトで WebSocket Secure プロトコルをサポートしています。WebSocket Secure プロトコルを使用するには、リスナーを設定する際に HTTPS を選択してください。

WebSocket の使用に料金はかかりますか?

WebSocket および WebSocket Secure プロトコルの使用に追加料金はかかりません。

どのリージョンが WebSocket をサポートしていますか?

CLB をサポートするすべてのリージョンで、WebSocket および WebSocket Secure もサポートされています。

参考

このチュートリアルでは、テスト目的で ECS インスタンスに Redis をデプロイする方法を説明しました。しかし、単一の Redis サーバーは単一障害点となる可能性があります。本番環境では、高可用性を確保するために Tair の使用を推奨します。詳細については、「Tair クイックスタート」をご参照ください。