主要有两个类,一个队列类和一个job的抽象类。
保证队列类中的key的唯一性,就可以用spring配置多个实例。水平有限,欢迎吐槽。
上代码:
1、队列类
import net.spy.memcached.MemcachedClient; import net.spy.memcached.internal.OperationFuture; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import com.izx.services.common.Constant; /** * * @ClassName: MemCacheQueue * @Description: 基于memcache的消息队列的实现 * @author hai.zhu * @date 2016-3-31 下午3:29:00 * */ public class MemCacheQueue implements InitializingBean, DisposableBean,ApplicationContextAware { private static final Log log = LogFactory.getLog(MemCacheQueue.class); /** * 队列名 */ private String key; /** * 队列锁失效分钟 */ private Integer lockExpireMinite = 3; private MemcachedClient memcachedClient; private ApplicationContext applicationContext; ListenerThread listenerThread = new ListenerThread(); public void setKey(String key) { this.key = key; } public void setMemcachedClient(MemcachedClient memcachedClient) { this.memcachedClient = memcachedClient; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void destroy() throws Exception { try { this.sign = false; listenerThread.interrupt(); } catch (Exception e) { log.error(e); } } @Override public void afterPropertiesSet() throws Exception { //初始化队列,用add防止重启覆盖 memcachedClient.add(Constant.MEMCACHE_GLOBAL_QUEUE_STARTKEY + key, 0, "0"); memcachedClient.add(Constant.MEMCACHE_GLOBAL_QUEUE_ENDKEY + key, 0, "0"); //设置任务线程 listenerThread.setDaemon(true); listenerThread.start(); } /** * * @Title: push * @Description: 唯一对外方法,放入要执行的任务 * @param @param value * @param @throws Exception 设定文件 * @return void 返回类型 * @throws */ public synchronized void push(MemCacheQueueJobAdaptor value) throws Exception { //分布加锁 queuelock(); //放入队列 memcachedClient.incr(Constant.MEMCACHE_GLOBAL_QUEUE_ENDKEY + key, 1); Object keyorder = memcachedClient.get(Constant.MEMCACHE_GLOBAL_QUEUE_ENDKEY + key); memcachedClient.set(Constant.MEMCACHE_GLOBAL_QUEUE_VARIABLE + key + "_" + keyorder, 0, value); //分布解锁 queueUnLock(); } /** * * @Title: pop * @Description: 取出要执行的任务 * @param @return * @param @throws Exception 设定文件 * @return MemCacheQueueJobAdaptor 返回类型 * @throws */ private synchronized MemCacheQueueJobAdaptor pop() throws Exception { Object keyorderstart = memcachedClient.get(Constant.MEMCACHE_GLOBAL_QUEUE_STARTKEY + key); Object keyorderend = memcachedClient.get(Constant.MEMCACHE_GLOBAL_QUEUE_ENDKEY + key); if(keyorderstart.equals(keyorderend)){ return null; } MemCacheQueueJobAdaptor adaptor = (MemCacheQueueJobAdaptor)memcachedClient.get(Constant.MEMCACHE_GLOBAL_QUEUE_VARIABLE + key + "_" + keyorderstart); memcachedClient.incr(Constant.MEMCACHE_GLOBAL_QUEUE_STARTKEY + key, 1); memcachedClient.delete(Constant.MEMCACHE_GLOBAL_QUEUE_VARIABLE + key + "_" + keyorderstart); return adaptor; } /** * * @Title: queuelock * @Description: 加锁 * @param @throws InterruptedException 设定文件 * @return void 返回类型 * @throws */ private void queuelock() throws Exception { do { OperationFuture<Boolean> sign = memcachedClient.add(Constant.MEMCACHE_GLOBAL_QUEUE_LOCK + key, lockExpireMinite * 60, key); if(sign.get()){ return; } else { log.debug("key: " + key + " locked by another business"); } Thread.sleep(300); } while (true); } /** * * @Title: queueUnLock * @Description: 解锁 * @param 设定文件 * @return void 返回类型 * @throws */ private void queueUnLock() { memcachedClient.delete(Constant.MEMCACHE_GLOBAL_QUEUE_LOCK + key); } private boolean sign = true; private long THREAD_SLEEP = 10; class ListenerThread extends Thread { @Override public void run(){ log.error("队列["+key+"]开始执行"); while(sign){ try { Thread.sleep(THREAD_SLEEP); dojob(); } catch (Exception e) { log.error(e); } } } private void dojob(){ try{ queuelock(); MemCacheQueueJobAdaptor adaptor = pop(); //逐个执行 if(adaptor != null){ THREAD_SLEEP = 10; try { adaptor.setApplicationContext(applicationContext); adaptor.onMessage(); } catch (Exception e) { log.error(e); } }else{ THREAD_SLEEP = 5000; } }catch(Exception e){ log.error(e); }finally{ queueUnLock(); } } } }
2、job抽象类
import org.springframework.context.ApplicationContext; import java.io.Serializable; /** * * @ClassName: MemCacheQueueJobAdaptor * @Description: 基于memcache队列的任务适配器 * @author hai.zhu * @date 2015-12-11 上午11:48:26 * @param <T> */ public abstract class MemCacheQueueJobAdaptor implements Serializable{ private static final long serialVersionUID = -5071415952097756327L; private ApplicationContext applicationContext; public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } /** * * @Title: onMessage * @Description: 异步执行任务接口 * @author hai.zhu * @param @param value 设定文件 * @return void 返回类型 * @throws */ public abstract void onMessage(); }
3、部分放在constant的常量
/** * 基于memcache的队列存放前缀 */ public static String MEMCACHE_GLOBAL_QUEUE_VARIABLE = "MEMCACHE_GLOBAL_QUEUE_VARIABLE_"; /** * 基于memcache的队列锁的前缀 */ public static String MEMCACHE_GLOBAL_QUEUE_LOCK = "MEMCACHE_GLOBAL_QUEUE_LOCK_"; /** * 基于memcache的队列锁的开始元素 */ public static String MEMCACHE_GLOBAL_QUEUE_STARTKEY = "MEMCACHE_GLOBAL_QUEUE_STARTKEY_"; /** * 基于memcache的队列锁的结束元素 */ public static String MEMCACHE_GLOBAL_QUEUE_ENDKEY = "MEMCACHE_GLOBAL_QUEUE_ENDKEY_";
4、spring配置,保证队列名的唯一性就可以配置多个队列
<!-- 枚举类型需要转换 --> <bean id="KETAMA_HASH" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"> <property name="staticField" value="net.spy.memcached.DefaultHashAlgorithm.KETAMA_HASH" /> </bean> <!-- memcache客户端 --> <bean id="memcachedClient" class="net.spy.memcached.spring.MemcachedClientFactoryBean"> <property name="servers" value="192.168.75.154:11277,192.168.75.154:11277,192.168.75.154:11277"/> <property name="protocol" value="BINARY"/> <property name="transcoder"> <bean class="net.spy.memcached.transcoders.SerializingTranscoder"> <property name="compressionThreshold" value="1024"/> </bean> </property> <property name="opTimeout" value="1000"/> <property name="timeoutExceptionThreshold" value="1998"/> <property name="hashAlg" ref="KETAMA_HASH"/> <property name="locatorType" value="CONSISTENT"/> <property name="failureMode" value="Redistribute"/> <property name="useNagleAlgorithm" value="false"/> </bean> <!-- 队列配置 --> <bean id="onequeue" class="com.izx.services.queque.MemCacheQueue"> <property name="memcachedClient" ref="memcachedClient"/> <property name="key" value="onequeue"/> </bean>