Browse Source
This change adds collection of stats in key infrastructure components of the WebSocket message broker config setup and exposes the gathered information for logging and viewing (e.g. via JMX). WebSocketMessageBrokerStats is a single class that assembles all gathered information and by default logs it once every 15 minutes. Application can also easily expose to JMX through an MBeanExporter. A new section in the reference documentation provides a summary of the available information. Issue: SPR-11739pull/585/merge
10 changed files with 523 additions and 16 deletions
@ -0,0 +1,196 @@
@@ -0,0 +1,196 @@
|
||||
/* |
||||
* Copyright 2002-2014 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.socket.config; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler; |
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
||||
import org.springframework.web.socket.messaging.StompSubProtocolHandler; |
||||
import org.springframework.web.socket.messaging.SubProtocolHandler; |
||||
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler; |
||||
|
||||
import java.util.concurrent.Executor; |
||||
import java.util.concurrent.ScheduledFuture; |
||||
import java.util.concurrent.ScheduledThreadPoolExecutor; |
||||
import java.util.concurrent.ThreadPoolExecutor; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
|
||||
/** |
||||
* A central class for aggregating information about internal state and counters |
||||
* from key infrastructure components of the setup that comes with |
||||
* {@code @EnableWebSocketMessageBroker} for Java config and |
||||
* {@code <websocket:message-broker>} for XML. |
||||
* |
||||
* <p>By default aggregated information is logged every 15 minutes at INFO level. |
||||
* The frequency of logging can be changed via {@link #setLoggingPeriod(long)}. |
||||
* |
||||
* <p>This class is declared as a Spring bean by the above configuration with the |
||||
* name "webSocketMessageBrokerStats" and can be easily exported to JMX, e.g. with |
||||
* the {@link org.springframework.jmx.export.MBeanExporter MBeanExporter}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 4.1 |
||||
*/ |
||||
public class WebSocketMessageBrokerStats { |
||||
|
||||
private static Log logger = LogFactory.getLog(WebSocketMessageBrokerStats.class); |
||||
|
||||
|
||||
private SubProtocolWebSocketHandler webSocketHandler; |
||||
|
||||
private StompSubProtocolHandler stompSubProtocolHandler; |
||||
|
||||
private StompBrokerRelayMessageHandler stompBrokerRelay; |
||||
|
||||
private ThreadPoolExecutor inboundChannelExecutor; |
||||
|
||||
private ThreadPoolExecutor outboundChannelExecutor; |
||||
|
||||
private ScheduledThreadPoolExecutor sockJsTaskScheduler; |
||||
|
||||
private ScheduledFuture<?> loggingTask; |
||||
|
||||
private long loggingPeriod = 15 * 60 * 1000; |
||||
|
||||
|
||||
public void setSubProtocolWebSocketHandler(SubProtocolWebSocketHandler webSocketHandler) { |
||||
this.webSocketHandler = webSocketHandler; |
||||
this.stompSubProtocolHandler = initStompSubProtocolHandler(); |
||||
} |
||||
|
||||
private StompSubProtocolHandler initStompSubProtocolHandler() { |
||||
for (SubProtocolHandler handler : this.webSocketHandler.getProtocolHandlers()) { |
||||
if (handler instanceof StompSubProtocolHandler) { |
||||
return (StompSubProtocolHandler) handler; |
||||
} |
||||
} |
||||
SubProtocolHandler defaultHandler = this.webSocketHandler.getDefaultProtocolHandler(); |
||||
if (defaultHandler != null && defaultHandler instanceof StompSubProtocolHandler) { |
||||
return (StompSubProtocolHandler) defaultHandler; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public void setStompBrokerRelay(StompBrokerRelayMessageHandler stompBrokerRelay) { |
||||
this.stompBrokerRelay = stompBrokerRelay; |
||||
} |
||||
|
||||
public void setInboundChannelExecutor(ThreadPoolTaskExecutor inboundChannelExecutor) { |
||||
this.inboundChannelExecutor = inboundChannelExecutor.getThreadPoolExecutor(); |
||||
} |
||||
|
||||
public void setOutboundChannelExecutor(ThreadPoolTaskExecutor outboundChannelExecutor) { |
||||
this.outboundChannelExecutor = outboundChannelExecutor.getThreadPoolExecutor(); |
||||
} |
||||
|
||||
public void setSockJsTaskScheduler(ThreadPoolTaskScheduler sockJsTaskScheduler) { |
||||
this.sockJsTaskScheduler = sockJsTaskScheduler.getScheduledThreadPoolExecutor(); |
||||
this.loggingTask = initLoggingTask(3 * 60 * 1000); |
||||
} |
||||
|
||||
private ScheduledFuture<?> initLoggingTask(long initialDelay) { |
||||
if (logger.isInfoEnabled() && this.loggingPeriod > 0) { |
||||
return this.sockJsTaskScheduler.scheduleAtFixedRate(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
logger.info(WebSocketMessageBrokerStats.this.toString()); |
||||
} |
||||
}, initialDelay, this.loggingPeriod, TimeUnit.MILLISECONDS); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Set the frequency for logging information at INFO level in milliseconds. |
||||
* If set 0 or less than 0, the logging task is cancelled. |
||||
* <p>By default this property is set to 30 minutes (30 * 60 * 1000). |
||||
*/ |
||||
public void setLoggingPeriod(long period) { |
||||
if (this.loggingTask != null) { |
||||
this.loggingTask.cancel(true); |
||||
} |
||||
this.loggingPeriod = period; |
||||
this.loggingTask = initLoggingTask(0); |
||||
} |
||||
|
||||
/** |
||||
* Return the configured logging period frequency in milliseconds. |
||||
*/ |
||||
public long getLoggingPeriod() { |
||||
return this.loggingPeriod; |
||||
} |
||||
|
||||
/** |
||||
* Get stats about WebSocket sessions. |
||||
*/ |
||||
public String getWebSocketSessionStatsInfo() { |
||||
return (this.webSocketHandler != null ? this.webSocketHandler.getStatsInfo() : "null"); |
||||
} |
||||
|
||||
/** |
||||
* Get stats about STOMP-related WebSocket message processing. |
||||
*/ |
||||
public String getStompSubProtocolStatsInfo() { |
||||
return (this.stompSubProtocolHandler != null ? this.stompSubProtocolHandler.getStatsInfo() : "null"); |
||||
} |
||||
|
||||
/** |
||||
* Get stats about STOMP broker relay (when using a full-featured STOMP broker). |
||||
*/ |
||||
public String getStompBrokerRelayStatsInfo() { |
||||
return (this.stompBrokerRelay != null ? this.stompBrokerRelay.getStatsInfo() : "null"); |
||||
} |
||||
|
||||
/** |
||||
* Get stats about the executor processing incoming messages from WebSocket clients. |
||||
*/ |
||||
public String getClientInboundExecutorStatsInfo() { |
||||
return (this.inboundChannelExecutor != null ? getExecutorStatsInfo(this.inboundChannelExecutor) : "null"); |
||||
} |
||||
|
||||
/** |
||||
* Get stats about the executor processing outgoing messages to WebSocket clients. |
||||
*/ |
||||
public String getClientOutboundExecutorStatsInfo() { |
||||
return (this.outboundChannelExecutor != null ? getExecutorStatsInfo(this.outboundChannelExecutor) : "null"); |
||||
} |
||||
|
||||
/** |
||||
* Get stats about the SockJS task scheduler. |
||||
*/ |
||||
public String getSockJsTaskSchedulerStatsInfo() { |
||||
return (this.sockJsTaskScheduler != null ? getExecutorStatsInfo(this.sockJsTaskScheduler) : "null"); |
||||
} |
||||
|
||||
private String getExecutorStatsInfo(Executor executor) { |
||||
String s = executor.toString(); |
||||
return s.substring(s.indexOf("pool"), s.length() - 1); |
||||
} |
||||
|
||||
public String toString() { |
||||
return "WebSocketSession[" + getWebSocketSessionStatsInfo() + "]" + |
||||
", stompSubProtocol[" + getStompSubProtocolStatsInfo() + "]" + |
||||
", stompBrokerRelay[" + getStompBrokerRelayStatsInfo() + "]" + |
||||
", inboundChannel[" + getClientInboundExecutorStatsInfo() + "]" + |
||||
", outboundChannel" + getClientOutboundExecutorStatsInfo() + "]" + |
||||
", sockJsScheduler[" + getSockJsTaskSchedulerStatsInfo() + "]"; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue