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 {