前端获取实时数据探索

背景介绍

最近在工作中,有个需求是前端需要获取实时展示最新的n条报警数据。使用http的请求响应,一问一答方式获取数据时,就出现可能部分数据获取不到的问题,然后在网上寻找和咨询老同事相关解决方案的时
候,发现有使用DWR的方式进行向前端推送的解决方案,

解决思路

1.ajax订阅ActiveMQ

ajax订阅activeMQ获取实时数据的流程如下
ajax订阅activeMQ数据流程图

使用ajax直接获取ActiveMQ topic中的数据,可以在activeMQ 官网中,找到Demo。需要在相应的页面中引入amq.js,amq_jquery_adapter.js以及jquery-1.4.2.min.js三个js文件,并且在后端添加相应的servlet配置。

demo中给出的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

<servlet>
<servlet-name>AjaxServlet</servlet-name>
<servlet-class>org.apache.activemq.web.AjaxServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>AjaxServlet</servlet-name>
<url-pattern>/amq/*</url-pattern>
</servlet-mapping>
<!-- activeMQ 服务地址 -->
<context-param>
<param-name>org.apache.activemq.brokerURL</param-name>
<param-value>tcp://127.0.0.1:61616</param-value>
</context-param>

同时我们需要在项目中引入对应版本的activeMQ-web.jar。来提供对订阅MQ的支持,这样在相应页面添加对应的Js订阅代码,就可以实现使用ajax订阅activeMQ topic进行消费的目的。

虽然这样就可以解决问题,但是总觉得将MQ地址直接配置到web.xml中,这种实现方式很不优雅,通过对AjaxServlet以及其父类源码阅读,发现在创建MQ连接的方式有两种,一种是通过获取获取servlet上下文中初始化时添加的全局参数org.apache.activemq.brokerURL,来创建和MQ的连接,就是上面看到的配置文件中的方法,还有一种是从serlvet上下文中获取创建连接的工厂类进行创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//从servlet上下文中获取连接工厂的属性名
public static final String CONNECTION_FACTORY_ATTRIBUTE = "org.apache.activemq.connectionFactory";

protected static synchronized void initConnectionFactory(ServletContext servletContext) {
if (factory == null) {
//获取servlet上下文中的连接工厂实例
factory = (ActiveMQConnectionFactory)servletContext.getAttribute(CONNECTION_FACTORY_ATTRIBUTE);
}
if (factory == null) { //连接工厂实例为null时,获取brokeURL等参数信息,并新建连接工厂实例
String brokerURL = getInitParameter(servletContext, BROKER_URL_INIT_PARAM);


if (brokerURL == null) {
LOG.debug("Couldn't find " + BROKER_URL_INIT_PARAM + " param, trying to find a broker embedded in a local VM");
BrokerService broker = BrokerRegistry.getInstance().findFirst();
if (broker == null) {
throw new IllegalStateException("missing brokerURL (specified via " + BROKER_URL_INIT_PARAM + " init-Param) or embedded broker");
} else {
brokerURL = "vm://" + broker.getBrokerName();
}
}

LOG.debug("Using broker URL: " + brokerURL);
String username = getInitParameter(servletContext, USERNAME_INIT_PARAM);
String password = getInitParameter(servletContext, PASSWORD_INIT_PARAM);
ActiveMQConnectionFactory amqfactory = new ActiveMQConnectionFactory(username, password, brokerURL);

// Set prefetch policy for factory
if (servletContext.getInitParameter(CONNECTION_FACTORY_PREFETCH_PARAM) != null) {
int prefetch = Integer.valueOf(getInitParameter(servletContext, CONNECTION_FACTORY_PREFETCH_PARAM)).intValue();
amqfactory.getPrefetchPolicy().setAll(prefetch);
}

// Set optimize acknowledge setting
if (servletContext.getInitParameter(CONNECTION_FACTORY_OPTIMIZE_ACK_PARAM) != null) {
boolean optimizeAck = Boolean.valueOf(getInitParameter(servletContext, CONNECTION_FACTORY_OPTIMIZE_ACK_PARAM)).booleanValue();
amqfactory.setOptimizeAcknowledge(optimizeAck);
}

factory = amqfactory;

servletContext.setAttribute(CONNECTION_FACTORY_ATTRIBUTE, factory);
}
}

从上面源码的ajaxServlet初始化方法中可以看到,首先从servlet的上下文中尝试获取ActiveMQConnectionFactory,如果没有设置则才会从上下文中找brokerURl进行初始化,所以我们可以继承AjaxServlet,然后在继承的servlet的init方法中按照需要创建ActiveMQConnectionFactory实例,然后将工厂实例添加到servlet上下文中供创建MQ连接使用。

2.使用websocket实时获取报警数据

3.websocket订阅ActiveMQ

1)基于stomp协议

2)基于mqtt协议

几种方案分析