VaadinのWebSocket通信(Push)が定期的に切断されるので調べた

AWS

Vaadinフレームワークで構築したアプリケーションをAWS Elastic Beanstalkで稼働させているのですが、ロードバランサーをCLB(Classic Load Balancer)からALB(Application Load Balancer)へ移行したら、1分おきに「Server connection lost, trying to reconnect…」と表示されて数秒で復旧を繰り返すようになってしまいました。

症状

Google Chromeのコンソールを確認すると、WebSocket通信(VaadinのPUSH処理)が切断されていることが分かります。

Websocket closed, reason: Connection was closed abnormally (that is, with no close frame being sent). - wasClean: false

com.vaadin.client.communication.DefaultConnectionStateHandler
WARNING: Reconnecting because of PUSH failure

URLの末尾に「?debug」を付けてVaadinのデバッグメッセージを見ても同じような内容です。

Push connection closed
Reopening push connection
Reconnecting because of PUSH failure
Reconnect attempt 1 for PUSH
Trying to re-establish server connection…
Push connection re-established using websocket
Re-established connection to server

おそらくVaadinのServer Pushのデフォルトの通信方式がWebSocket通信なのですが、移行前のCLBはWebSocket通信に対応してなかったのでLong Polling通信が行われていて、移行後のALBはWebSocket通信に対応しているので、WebSocket通信が行われるようになり、なぜか1分毎に切断されてしまう状況ではないかと考えました。

Application Load Balancerの設定変更

ALBのアイドルタイムアウト(IdleTimeout)が通信断に影響しているのではと考え、デフォルトの60秒から3600秒(1時間)に変更してみましたが相変わらず切断されてしまいました。

Nginxの設定変更で解決

ALBを通さずにEC2のパブリックIPに直接アクセスしても同じように切断されることが分かったので、Nginxを疑ってみたらデフォルトのタイムアウト値が60秒であることが分かりました。
amazon web services – AWS Elastic Beanstalk 504 Gateway Timeout – Stack Overflow

AWS Elastic Beanstalkの設定ファイル(.ebextensions/01_nginx.config)を作成し、デフォルトのタイムアウト値が60秒から3600秒(1時間)になるように設定すると切断されなくなりました。

設定ファイルについてはこちらが参考になります。
設定ファイル (.ebextensions) による高度な環境のカスタマイズ – AWS Elastic Beanstalk

files:
  "/opt/elasticbeanstalk/private/nginx/nginx.conf.erb":
    owner: root
    group: root
    mode: "000644"
    content: |
      # Elastic Beanstalk Nginx Configuration File
      
      user                    nginx;
      error_log               /var/log/nginx/error.log warn;
      pid                     /var/run/nginx.pid;
      worker_processes        auto;
      worker_rlimit_nofile    <%= user_file_limit %>;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       /etc/nginx/mime.types;
          default_type  application/octet-stream;
      
          log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                            '$status $body_bytes_sent "$http_referer" '
                            '"$http_user_agent" "$http_x_forwarded_for"';
      
          include       conf.d/*.conf;
      
          map $http_upgrade $connection_upgrade {
              default     "upgrade";
          }
      
          server {
              listen        <%= instance_port %> default_server;
              access_log    /var/log/nginx/access.log main;
      
              client_header_timeout 3600;
              client_body_timeout   3600;
              keepalive_timeout     3600;
              send_timeout          3600;
              proxy_connect_timeout 3600;
              proxy_read_timeout    3600;
              proxy_send_timeout    3600;
              gzip                  <%= gzip_toggle %>;
              gzip_comp_level       4;
              gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
      
              # Include the Elastic Beanstalk generated locations
              include conf.d/elasticbeanstalk/*.conf;
          }
      }

Long Pollingではだめ?

WebSocket通信でなくてよければ@Pushアノテーションの転送モードをLong Pollingにしたら回避できるような気がします。

@Push(transport = Transport.LONG_POLLING)
public class PushyUI extends UI {
タイトルとURLをコピーしました