融合spring框架使用队列自定义服务

需求

最近接到公司的这么一个需求,在不使用消息中间件的情况下,开发一个消息发送的框架来异步处理各种服务,以提高系统的吞吐量,具体有以下几点要求:
1.支持数据的可持久化,公司最看重这个
2.服务高可用,公司使用了ngnix做负载均衡,有2台服务器
3.服务队列支持暂停和恢复
4.不侵入其他业务逻辑,也就是要做到低耦合。

接到这个任务后,回去琢磨了一下,觉得应该先设计UML图和数据库。
初次使用EA软件,画了一个简单的图例。
设计图
图解:
1.CustomJob是我设计的任务类的接口,用户自定义的任务类需要继承这个接口。
2.CustomJobFactory是这个任务类的生成类,实现了ApplicationContextAware接口,方便使用spring的高级容器applicationContext.
3.QueueFactory是用来生成单例队列。
4.JobConsume是消费者,我使用单例,处理过程选择多线程,这样与spring更容易结合。
5. QueueApi是提供给其他用户的接口。

示例代码
CustomJob

package com.example.queue;

/**
 * @author 13781
 * @version 1.0
 * @created 23-9��-2018 12:42:11
 */
public interface CustomJob {

	void execute(CustomJobDetail jobDetail);

}

CustomJobFactory

package com.example.queue;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;

/**
 * @author 13781
 * @version 1.0
 * @created 22-9��-2018 21:25:07
 */
public class CustomJobFactory implements ApplicationContextAware {

	private ApplicationContext applicationContext;

	public CustomJobFactory(){

	}

	public void finalize() throws Throwable {
		super.finalize();
	}

	public void setApplicationContext(ApplicationContext applicationContext){
		this.applicationContext=applicationContext;
	}

	/**
	 * 
	 * @param bundle
	 */
	protected Object createJobInstance(CustomBundle bundle)throws InstantiationException,IllegalAccessException
	,ClassNotFoundException{
		Object obj=null;
		if(bundle.getByteCLass()!=null){
			obj=bundle.getByteCLass().newInstance();
		}else if(!StringUtils.isEmpty(bundle.getPathClass())){
			obj=Class.forName(bundle.getPathClass()).newInstance();
		}else {
			throw new InstantiationException("未找到任务类");
		}
		return obj;
	}

	/**
	 * 
	 * @param customBundle
	 */
	public CustomJob newJob(Class<? extends CustomJob> clazz,CustomBundle customBundle)throws InstantiationException,IllegalAccessException
			,ClassNotFoundException{
		customBundle.setByteCLass(clazz);
		return newJob(customBundle);
	}

	public CustomJob newJob(CustomBundle customBundle)throws InstantiationException,IllegalAccessException
			,ClassNotFoundException{
		Object obj=this.createJobInstance(customBundle);
		this.applicationContext.getAutowireCapableBeanFactory().autowireBean(obj);
		return (CustomJob)obj;
	}

}//end CustomJobFactory

QueueFactory

package com.example.queue.core;



import com.example.queue.WrapperJob;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;


/**
 * @author 13781
 * @version 1.0
 * @created 23-9��-2018 12:42:16
 */
public class QueueFactory {

	private  static BlockingQueue<WrapperJob> blockQueue=new LinkedBlockingQueue<WrapperJob>();

	public QueueFactory(){
	}

	public void finalize() throws Throwable {

	}
	public static BlockingQueue getSingleBlockQueue(){
		return blockQueue;
	}
}//end QueueFactory

JobConsume

package com.example.queue.consume;

import com.example.queue.WrapperJob;
import com.example.queue.core.QueueFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author 13781
 * @version 1.0
 * @created 23-9��-2018 12:42:14
 */
@Component
public class JobComsume extends Thread{
	private final Logger log = LoggerFactory.getLogger(this.getClass());
	private static final int cousumeSum=3;
	private volatile boolean isPause;
	private ReentrantLock lock=new ReentrantLock();
	private Condition condition=lock.newCondition();
//	private ExecutorService service=Executors.newFixedThreadPool(cousumeSum);

	public JobComsume(){

	}

	@Override
	public void run(){
		while (true){
			lock.lock();
			try{
				while(isPause){
					condition.await();
				}
			}catch (Exception e){
				e.printStackTrace();
			}finally{
				lock.unlock();
			}
			try{
				Thread.sleep(100);
				BlockingQueue<WrapperJob> blockingQueue=QueueFactory.getSingleBlockQueue();
				List<Runnable> list=new ArrayList<Runnable>();
				for (int i = 0; i <cousumeSum; i++) {
					list.add(blockingQueue.take());
				}
				ExecutorService service=Executors.newFixedThreadPool(cousumeSum);
				for (Runnable job:list ) {
					service.submit(job);
				}
				service.shutdown();
			}catch (java.lang.InterruptedException e){
				e.printStackTrace();
			}
		}

	}

	public void pauseJob(){
		lock.lock();
		try{
			isPause=true;
			log.info("消费线程将暂停。。。。。。。");
		}catch (Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}

	public void resumeJob(){
		lock.lock();
		try{
			isPause=false;
			condition.signalAll();
			log.info("消费线程将恢复运行。。。。。。。");
		}catch (Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}

}//end JobComsume

QueueApi

package com.example.queue.api;

import com.example.queue.*;
import com.example.queue.core.QueueFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author 13781
 * @version 1.0
 * @created 23-9��-2018 12:42:15
 */
@Component
public class QueueApi {

	@Autowired
	private CustomWrapperJobFactory jobFactory;

	/**
	 * 
	 * @param byteClazz
	 * @param customJobDetail
	 */
	public void newJob(Class byteClazz, CustomJobDetail customJobDetail)throws InstantiationException,IllegalAccessException
			,ClassNotFoundException,InterruptedException{
		CustomBundle customBundle=new CustomBundle(customJobDetail);
		customBundle.setByteCLass(byteClazz);
		CustomJob customJob=jobFactory.newJob(customBundle);
		WrapperJob wrapperJob=new WrapperJob(customJob);
		wrapperJob.setJobDetail(customJobDetail);
		QueueFactory.getSingleBlockQueue().put(wrapperJob);
	}
}//end QueueApi

测试类

package queue;

import com.example.queue.CustomJob;
import com.example.queue.CustomJobDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JobDemo implements CustomJob {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    public void execute(CustomJobDetail jobDetail) {
        log.info(jobDetail.getJobName()+"。。。。。");
    }
}

package queue;


import com.example.queue.CustomJobDetail;
import com.example.queue.api.QueueApi;
import com.example.queue.consume.JobComsume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:springmvc.xml"})
public class QueueTest {

    @Autowired
    QueueApi api;
    @Autowired
    JobComsume consume;

    @Test
    public void test0()throws Exception{
        //创建100个任务并添加到队列
        for (int i = 0; i < 100; i++) {
            CustomJobDetail customJobDetail=new CustomJobDetail();
            customJobDetail.setJobName("任务【"+i+"】");
            api.newJob(JobDemo.class,customJobDetail);
        }
        //运行消费者
        consume.start();
        Thread.sleep(10);
        //暂停消费者
        new Thread(new Runnable() {
            public void run() {
                consume.pauseJob();
            }
        }).start();
        Thread.sleep(6000);
        //恢复消费者线程
        new Thread(new Runnable() {
            public void run() {
                consume.resumeJob();
            }
        }).start();
        Thread.sleep(10);
        //再次暂停消费者
        new Thread(new Runnable() {
            public void run() {
                consume.pauseJob();
            }
        }).start();
        Thread.sleep(100*1000L);

    }

}

测试结果

信息: Refreshing org.springframework.context.support.GenericApplicationContext@77b52d12: startup date [Mon Sep 24 12:13:45 CST 2018]; root of context hierarchy
九月 24, 2018 12:13:45 下午 org.springframework.web.servlet.handler.SimpleUrlHandlerMapping registerHandler
信息: Mapped URL path [/**] onto handler 'org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0'
九月 24, 2018 12:13:46 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache
信息: Looking for @ControllerAdvice: org.springframework.context.support.GenericApplicationContext@77b52d12: startup date [Mon Sep 24 12:13:45 CST 2018]; root of context hierarchy
九月 24, 2018 12:13:46 下午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache
信息: Looking for @ControllerAdvice: org.springframework.context.support.GenericApplicationContext@77b52d12: startup date [Mon Sep 24 12:13:45 CST 2018]; root of context hierarchy
12:13:46.144 [Thread-3] INFO com.example.queue.consume.JobComsume - 消费线程将暂停。。。。。。。
12:13:46.235 [pool-1-thread-1] INFO queue.JobDemo - 任务【0】。。。。。
12:13:46.235 [pool-1-thread-2] INFO queue.JobDemo - 任务【1】。。。。。
12:13:46.235 [pool-1-thread-3] INFO queue.JobDemo - 任务【2】。。。。。
12:13:52.144 [Thread-4] INFO com.example.queue.consume.JobComsume - 消费线程将恢复运行。。。。。。。
12:13:52.154 [Thread-5] INFO com.example.queue.consume.JobComsume - 消费线程将暂停。。。。。。。
12:13:52.248 [pool-2-thread-1] INFO queue.JobDemo - 任务【3】。。。。。
12:13:52.248 [pool-2-thread-2] INFO queue.JobDemo - 任务【4】。。。。。
12:13:52.248 [pool-2-thread-3] INFO queue.JobDemo - 任务【5】。。。。。

总结:job类中你可以注入任意多的service来处理你的逻辑,可以与spring容器结合,将代码简单的扩充后可以实现1、3和4的要求。至于第二点集群,可以取巧为加载数据的时候采用分片加载的方式,这样各个集群节点就不会重复加载数据。
源码下载地址:自定义队列服务功能源码

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值