由于项目前期的消息机制是用redis自带的消息机制架构的,redis自带的消息机制不够专业,容易丢数据,因此项目组决定换掉redis消息机制,采用比较专业的ActiveMq,
因此需要对ActiveMq的性能和稳定性作下测试,在测试过程中自己很佩服别人的代码设计,只通过几段配置就能让系统正常用转,只通过改个数字就能让系统的吞吐量成倍提升。下面代码是我写的一个demo的配置代码
<!-- 用户注册 消息消费者-->
<bean id="userRegisterConsumer" class="com.ydm.consumer.web.UserConsumer" >
</bean>
<!-- 线程池 -->
<bean id="userRegisterMessageExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="20" />
<property name="maxPoolSize" value="100" />
<property name="daemon" value="true" />
<property name="keepAliveSeconds" value="120" />
</bean>
<!-- 目标监听队列 -->
<bean id="userRegisterDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="ydm-queue" />
</bean>
<!-- 监听器 -->
<bean id="userRegisterListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory" />
<property name="receiveTimeout" value="1000" />
<property name="destination" ref="userRegisterDestination" />
<property name="messageListener" ref="userRegisterConsumer" />
<property name="taskExecutor" ref="userRegisterMessageExecutor"></property>
<property name="concurrentConsumers" value="5"></property>
</bean>
上面注释也已经表明了,配置了一个UserConsumer,代码如下
public class UserConsumer implements MessageListener {
Logger logger=Logger.getLogger(UserConsumer.class);
@Override
public void onMessage(Message message) {
try {
ObjectMessage objectMessage=(ObjectMessage)message;
UserModel userModel=(UserModel)objectMessage.getObject();
logger.error(Thread.currentThread().getName()+"消费了"+userModel);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
配置了一个线程池,一个目标队列,一个监听器,通过上述配置项目就可以正常运转,而且消费者还是以多线程的形式在消费,这样我们可以适当提供消费者的数量就可以提高处理速度,为什么我仅仅配置上述代码,就可以完成这么复杂的功能,是谁在什么时候调用了onMessage方法?框架到底为我们做了什么?就是带着这种疑问,让我们一起来了解一下幕后吧。
首先我们要了解spring IOC容器是如何初始化的
一般我们初始化spring 容器会在web.xml中增加下面一段配置,那让我们来看看它的实现吧
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
private ContextLoader contextLoader;
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
/**
* Close the root web application context.
*/
public void contextDestroyed(ServletContextEvent event) {
if (this.contextLoader != null) {
this.contextLoader.closeWebApplicationContext(event.getServletContext());
}
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
这个类继承了ContextLoader,实现了ServletContextListener接口
ContextLoader我们主要看它的静态初始化块
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
这里主要加载了ContextLoader.properties配置文件,返回一个
Properties对象,配置文件里就一行代码
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
我们来看ServletContextListener接口,这个接口我相信大家应该都不陌生,它属于servlet api 里的内容,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。当Servlet 容器启动或终止Web 应用时,会触发ServletContextEvent 事件,该事件由ServletContextListener 来处理。在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法
public interface ServletContextListener extends EventListener {
/**
** Notification that the web application initialization
** process is starting.
** All ServletContextListeners are notified of context
** initialization before any filter or servlet in the web
** application is initialized.
*/
public void contextInitialized ( ServletContextEvent sce );
/**
** Notification that the servlet context is about to be shut down.
** All servlets and filters have been destroy()ed before any
** ServletContextListeners are notified of context
** destruction.
*/
public void contextDestroyed ( ServletContextEvent sce );
}
源代码注释上已经写的很明白了,这里就不在熬述ContextLoaderListener重写了contextInitialized方法,上面已经贴出代码了,这里有这样一句话
if (this.contextLoader == null) {
this.contextLoader = this;
}
这个this是指的什么?这个就要扯远了,这个应该跟容器有关,我用的是tomcat,它指的是Tomcat 的StandardContext,这个我也没有继续深究,怕走远了回不来了,有兴趣的可以查看tomcat的源码。
接下来让我们看看initWebApplicationContext的实现
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, ser