内存异步及JMS异步框架实现

      一般来说,对于那些处理时间比较长,需要马上响应并且做成异步后不会影响其它流程的做成异步。比如用户注册成功后发邮件给用户,这个可以在用户注册成功后马上返回,而不需要等到邮件发送成功才返回。异步有二种方式:内存异步和JMS异步。

     内存异步即把要处理的请求放到内存队列中,然后由多个线程去消费。这种方式性能比较高,但是会存在请求丢失和内存溢出的风险。比如服务器突然down机,那么队列中未处理完的请求就会丢失;如果请求处理时间太长,并且请求一直在增加,即生产者速度要大于消费者速度时,就存在内存溢出的风险。这种适合对数据丢失不敏感,并且生产者速度要小于消费者速度的场合。

    JMS异步即把要处理的请求先持久化到数据库,然后多个线程去消费。这种方式性能相对来说要慢一些,但是不会出现请求丢失和内存溢出的情况。这种适合不允许数据丢失,请求处理时间比较长的场合。

 

异步框架,使用元数据和AOP的方式,将内存异步和JMS异步统一起来。

package com.konceptusa.infinet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 方法异步
 * @author Jwin
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target( {ElementType.METHOD })
@Documented
public @interface Async
{
	//异步队列名称,默认为类名.方法名
	String queueName() default "";
	//消费者线程数
	int threadCount() default 10;
	//队列报警值,仅对内存异步有效
	int warningQueueSize() default 100;
	//是否使用jms异步,默认为内存异步
	boolean jmsAsync() default false;
}

 

 

 

package com.konceptusa.infinet.imsupport.aop;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.jms.ConnectionFactory;
import javax.jms.Queue;

import org.apache.activemq.command.ActiveMQQueue;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.DefaultMessageListenerContainer;

import com.konceptusa.framework.asyn.AsynCallInfo;
import com.konceptusa.framework.asyn.jms.AsynExecuteMessageListener;
import com.konceptusa.framework.core.jmsservice.ObjectMessageSenderImpl;
import com.konceptusa.framework.core.jmsservice.SendMessageFailedException;
import com.konceptusa.infinet.annotation.Async;

/**
 * 异步AOP
 * @author Jwin
 *
 */
public class AsyncInterceptor implements DisposableBean
{
	private final static Log LOG = LogFactory.getLog(AsyncInterceptor.class);
	private Map<String, ThreadPoolExecutor> executorMap = new HashMap<String, ThreadPoolExecutor>();
	private Map<String, ObjectMessageSenderImpl> messageSenderMap = new HashMap<String, ObjectMessageSenderImpl>();
	private Map<String, DefaultMessageListenerContainer> listerContainerMap = new HashMap<String, DefaultMessageListenerContainer>();
	private ConnectionFactory connectionFactory;
	public Object async(final ProceedingJoinPoint pjp, Async async) throws Throwable
	{
		if(!async.jmsAsync())
		{
			memAsync(pjp, async);			
		}
		else
		{
			jmsAsync(pjp,async);
		}
		return null;
	}
	
	private void jmsAsync(ProceedingJoinPoint pjp, Async async)
	{
		ObjectMessageSenderImpl objectMessageSender = null;
		String queueName = getQueueName(pjp, async);
		synchronized (messageSenderMap)
		{
			objectMessageSender = messageSenderMap.get(queueName);
			if(objectMessageSender == null)
			{
				LOG.info("init jms queueName " + queueName + " thread count " + async.threadCount() );
				objectMessageSender = new ObjectMessageSenderImpl();
				Queue queue = new ActiveMQQueue(queueName);
				JmsTemplate jmsTemplate = new JmsTemplate();
				jmsTemplate.setConnectionFactory(connectionFactory);
				jmsTemplate.setDefaultDestination(queue);
				objectMessageSender.setJmsTemplate(jmsTemplate);
				messageSenderMap.put(queueName, objectMessageSender);
				DefaultMessageListenerContainer defaultMessageListenerContainer = new DefaultMessageListenerContainer();
				defaultMessageListenerContainer.setConcurrentConsumers(async.threadCount());
				defaultMessageListenerContainer.setConnectionFactory(connectionFactory);
				defaultMessageListenerContainer.setDestination(queue);
				defaultMessageListenerContainer.setCacheLevelName("CACHE_CONSUMER");
				defaultMessageListenerContainer.setMaxMessagesPerTask(20);
				AsynExecuteMessageListener messageListener = new AsynExecuteMessageListener();
				messageListener.setTarget(pjp.getTarget());
				defaultMessageListenerContainer.setMessageListener(messageListener);
				defaultMessageListenerContainer.afterPropertiesSet();
				listerContainerMap.put(queueName, defaultMessageListenerContainer);
			}			
		}
		AsynCallInfo asynCallInfo = new AsynCallInfo(pjp.getSignature().getName(),pjp.getArgs());
		objectMessageSender.asynSendMessage(asynCallInfo);
	}

	private String getQueueName(ProceedingJoinPoint pjp, Async async)
	{
		String queueName = async.queueName();
		//如果没有指定队列名,则以 类名.方法名 为队列名
		if(StringUtils.isBlank(queueName))
		{
			queueName = pjp.getTarget().getClass().getSimpleName() + "." + pjp.getSignature().getName();
		}
		return queueName;
	}
	private void memAsync(final ProceedingJoinPoint pjp, Async async)
	{
		ThreadPoolExecutor executor = null;
		final String queueName = getQueueName(pjp, async);
		synchronized (executorMap)
		{
			executor = executorMap.get(queueName);
			if(executor == null)
			{
				LOG.info("init mem queueName " + queueName + " thread count " + async.threadCount() + " warning queue size " + async.warningQueueSize());
				executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(async.threadCount());
				executorMap.put(queueName, executor);
			}
		}
		Thread thread = new Thread(){
			@Override
			public void run()
			{
				try
				{
					pjp.proceed();
				} catch (Throwable e)
				{
					LOG.error("async call fail", e);
				}
			}
		};
		executor.execute(thread);
		int size = executor.getQueue().size();
		if( size >= async.warningQueueSize())
		{
			LOG.warn("queueName " + queueName + " exceeds warning queue size " + async.warningQueueSize() + " current size " + size);
		}
	}
	public void destroy() throws Exception
	{
		for(String key : executorMap.keySet())
		{			
			LOG.info("destroy mem queue " + key);
			ThreadPoolExecutor executor = executorMap.get(key);
			executor.shutdown();
			executor.awaitTermination(2, TimeUnit.SECONDS);
		}
		for(String key : listerContainerMap.keySet())
		{			
			LOG.info("destroy jms queue " + key);
			DefaultMessageListenerContainer container = listerContainerMap.get(key);
			container.destroy();
		}
		executorMap.clear();
		listerContainerMap.clear();
	}
	public void setConnectionFactory(ConnectionFactory jmsConnectionFactory)
	{
		this.connectionFactory = jmsConnectionFactory;
	}
}

 

 

对需要异步的方法加入@Aysnc 即可实现异步

 

内存异步

	@Async
	public String memAsyncFind(Long userid)
	{
		System.out.println("mem asyncFind userid " + userid);
		return userid.toString();
	} 

 

JMS异步

	@Async(jmsAsync=true)
	public String jmsAsyncFind(Long userid)
	{
		System.out.println("jms asyncFind userid " + userid);
		return userid.toString();
	} 

 

AOP配置

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
   http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
   "
	default-autowire="byName">
 	<bean id="asyncInterceptor"
		class="com.konceptusa.infinet.imsupport.aop.AsyncInterceptor">
	</bean>
	
	<aop:config>
		<aop:pointcut id="asyncPointcut"
				expression="execution(* com.konceptusa.infinet.test.service..*.*(..)) and @annotation(async)" />
		<aop:aspect id="asyncAspect" ref="asyncInterceptor">
			<aop:around method="async" pointcut-ref="asyncPointcut" />
		</aop:aspect>
	</aop:config>	
	
</beans>

Activemq配置 

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:amq="http://activemq.org/config/1.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
  http://activemq.org/config/1.0 http://activemq.apache.org/schema/activemq-core.xsd
  http://activemq.apache.org/camel/schema/spring http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">

	<broker xmlns="http://activemq.org/config/1.0" brokerName="test"
		dataDirectory="${activemq.data}/data/test">
		<persistenceAdapter>
			<journaledJDBC journalLogFiles="10"
				dataDirectory="${activemq.data}/persistence"
				dataSource="#activemq-ds" createTablesOnStartup="true"
				useDatabaseLock="false" />
		</persistenceAdapter>

		<systemUsage>
			<systemUsage>
				<memoryUsage>
					<memoryUsage limit="100 mb"
						percentUsageMinDelta="20" />
				</memoryUsage>
				<storeUsage>
					<storeUsage limit="1 gb" />
				</storeUsage>
			</systemUsage>
		</systemUsage>
	</broker>

	<bean id="activemq-ds"
		class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close">
		<property name="driverClass" value="${database.driverName.mq}" />
		<property name="jdbcUrl" value="${database.url.mq}" />
		<property name="user" value="${database.user.mq}" />
		<property name="password" value="${database.password.mq}" />
		<property name="initialPoolSize" value="${database.initialSize}" />
		<property name="minPoolSize" value="${database.initialSize}" />
		<property name="maxPoolSize" value="${database.maxActive}" />
		<property name="maxIdleTime" value="${database.maxIdleTime}" />
		<property name="acquireIncrement" value="${database.acquireIncrement}" />
		<property name="numHelperThreads" value="${database.numHelperThreads}" />
		<property name="automaticTestTable"
			value="${database.automaticTestTable}" />
		<property name="maxStatements" value="${database.maxStatements}" />
		<property name="maxStatementsPerConnection"
			value="${database.maxStatementsPerConnection}" />
		<property name="idleConnectionTestPeriod"
			value="${database.idleConnectionTestPeriod}" />
	</bean>

	<connectionFactory xmlns="http://activemq.org/config/1.0"
		id="jmsConnectionFactory" brokerURL="vm://test" />

	<bean id="connectionFactory"
		class="org.springframework.jms.connection.SingleConnectionFactory">
		<property name="targetConnectionFactory"
			ref="jmsConnectionFactory" />
	</bean>
</beans>
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值