在開發中,我有一個 javascript websocket 直接連接到 TomEE,并且 websocket 保持連接沒有問題。
在 httpd 代理后面使用 TomEE 的生產中,連接在大約 30 秒后超時。
這是虛擬主機配置的相關部分
ProxyPass / ajp://127.0.0.1:8009/ secret=xxxxxxxxxxxx
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://127.0.0.1:8080/$1" [P,L]
我曾嘗試使用 reconnecting-websocket npm 庫,但它似乎一直在生成 websocket,直到 chrome 記憶體不足。原始 websockets 保持狀態 101 而不是更改為完成。
我確實讀到防火墻會導致它斷開連接,但我搜索了 firewalld 和 websocket 并找不到任何東西
uj5u.com熱心網友回復:
看起來答案是實作“乒乓”。這可以防止防火墻或代理終止連接。
如果你 ping 一個 websocket(客戶端或服務器),那么規范說它必須回應(pong)。但是 Javascript websocket 取決于瀏覽器的實作,因此最好在服務器上對所有客戶端實作 30 秒的 ping。例如
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(value = "/websockets/admin/autoreply")
public class MyWebSocket {
private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
private static final Set<String> alive = Collections.synchronizedSet(new HashSet<String>());
@OnOpen
public void onOpen(Session session) throws IOException {
sessions.add(session);
alive.add(session.getId());
}
@OnMessage
public void onMessage(Session session, String string) throws IOException {
// broadcast(string);
}
@OnMessage
public void onPong(Session session, PongMessage pongMessage) throws IOException {
// System.out.println("pong");
alive.add(session.getId());
}
@OnClose
public void onClose(Session session) throws IOException {
sessions.remove(session);
}
@OnError
public void onError(Session session, Throwable throwable) {
// Do error handling here
}
public void broadcast(String string) {
synchronized (sessions) {
for (Session session : sessions) {
broadcast(session, string);
}
}
}
private void broadcast(Session session, String string) {
try {
session.getBasicRemote().sendText(string);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void ping() {
synchronized (sessions) {
for (Session session : sessions) {
ping(session);
}
}
}
private void ping(Session session) {
try {
synchronized (alive) {
if (alive.contains(session.getId())) {
String data = "Ping";
ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
session.getBasicRemote().sendPing(payload);
alive.remove(session.getId());
} else {
session.close();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
和計時器服務看起來像這樣
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.ScheduleExpression;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import org.apache.tomcat.websocket.server.DefaultServerEndpointConfigurator;
import tld.domain.api.websockets.MyWebSocket;
@Singleton
@Lock(LockType.READ)
@Startup
public class HeartbeatTimer {
@Resource
private TimerService timerService;
@PostConstruct
private void construct() {
final TimerConfig heartbeat = new TimerConfig("heartbeat", false);
timerService.createCalendarTimer(new ScheduleExpression().second("*/30").minute("*").hour("*"), heartbeat);
}
@Timeout
public void timeout(Timer timer) {
if ("heartbeat".equals(timer.getInfo())) {
// System.out.println("Pinging...");
try {
DefaultServerEndpointConfigurator dsec = new DefaultServerEndpointConfigurator();
MyWebSocket ws = dsec.getEndpointInstance(MyWebSocket.class);
ws.ping();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/364293.html
標籤:阿帕奇 网络套接字 暂停 apache-tomee
