设计模式——生产者消费者模式

@SuppressWarnings({ "rawtypes", "unchecked" })
@ManagedResource(objectName = QueuesHolder.QUEUEHOLDER_MBEAN_NAME, description = "Queues Holder Bean")
public class QueuesHolder {
	/**
	 * QueueManager注册的名称.
	 */
	public static final String QUEUEHOLDER_MBEAN_NAME = "Core:type=QueueManagement,name=queueHolder";
	// private static Log logger = LogFactory.getLog(QueuesHolder.class);//初始化需要,勿删
	private static ConcurrentMap<String, BlockingQueue> queueMap = new MapMaker().concurrencyLevel(32).makeMap();// 消息队列

	/**
	 * 根据提供的queueName 入队列
	 * 
	 * @param queueName
	 * @param object
	 * @return
	 */
	public static void add(String queueName, int queueSize, Object object) {
		BlockingQueue bq = getQueue(queueName, queueSize);
		try {
			bq.add(object); // bq.offer(object);(这里也可以使用offer,offer不会抛出异常)
			LogUtil.debug("put Log to queue【{}】,msg={}", queueName, object);
		} catch (IllegalStateException e) {
			LogUtil.error("加入队列失败:队列【{}】size【{}】满了!object=", queueName, queueSize, object, e);
		} catch (Exception e) {
			LogUtil.error("加入队列失败:队列【{}】size【{}】逻辑异常!object={}", queueName, queueSize, object, e);
		}
	}

	/**
	 * 根据queueName获得消息队列的静态函数. 如消息队列还不存在, 会自动进行创建.
	 */
	public static <T> BlockingQueue<T> getQueue(String queueName, int queueSize) {
		BlockingQueue queue = queueMap.get(queueName);
		if (queue == null) {
			queue = new LinkedBlockingQueue(queueSize);
			queueMap.put(queueName, queue);
		}
		return queue;
	}

	/**
	 * 根据queueName获得消息队列中未处理消息的数量,支持基于JMX查询.
	 */
	@ManagedOperation(description = "Get message count in queue")
	@ManagedOperationParameters({ @ManagedOperationParameter(name = "queueName", description = "Queue name") })
	public static int getQueueLength(String queueName, int queueSize) {
		return getQueue(queueName, queueSize).size();
	}

	/**
	 * add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element
	 * 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 offer 添加一个元素并返回true 如果队列已满,则返回false poll 移除并返问队列头部的元素
	 * 如果队列为空,则返回null peek 返回队列头部的元素 如果队列为空,则返回null put 添加一个元素 如果队列满,则阻塞 take 移除并返回队列头部的元素 如果队列为空,则阻塞
	 */

}

消费:

@SuppressWarnings("rawtypes")
public abstract class QueueConsumer implements Runnable {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    protected String queueName = "log";//任务所消费的队列名称
    protected int queueSize = Integer.MAX_VALUE;//每个队列的最大长度, 默认为Integer最大值, 设置时不改变已创建队列的最大长度.
    protected int batchSize = 10;//批量读取事件数量, 默认为10.
    
    protected int shutdownTimeout = Integer.MAX_VALUE;
	protected BlockingQueue queue;
    protected ListeningExecutorService executor;
    
    /**
     * 启动并执行队列
     */
    public void start() {
    	logger.debug("启动批量入库任务并执行队列 queueName[{}] queueSize[{}]",queueName,queueSize);
		try {
			queue = QueuesHolder.getQueue(queueName, queueSize);
	        executor = MoreExecutors.listeningDecorator(new ThreadPoolExecutor(2,4,10,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(10),new CustomizableThreadFactory("Queue Consumer-" + queueName)));
	        executor.execute(this);
	        logger.debug("启动批量入库任务成功");
		} catch (Exception e) {
			logger.error("批量入库任务队列消费初始化失败!",e);
		}
    }                                                                                       

    /**
     * 停止任务
     */
    @PreDestroy
    public void stop() throws IOException {
        try {
            ThreadUtils.normalShutdown(executor, shutdownTimeout, TimeUnit.MILLISECONDS);
        } finally {
            // TODO
        }

    }
    
    /**
     * 初始化
     */
    protected abstract void init();
    
    /**
     * 设置任务所消费的队列名称.默认为log
     */
    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }
    
    /**
     * 设置每个队列的最大长度. 默认为Integer最大值, 设置时不改变已创建队列的最大长度.
     */
    public void setQueueSize(int queueSize) {
        this.queueSize = queueSize;
    }
    
    /**
     * 批量读取事件数量. 默认为10.
     */
    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    /**
     * 停止任务时最多等待的时间, 单位为毫秒.
     */
    public void setShutdownTimeout(int shutdownTimeout) {
        this.shutdownTimeout = shutdownTimeout;
    }
}

 

 

public abstract class BlockingConsumer extends QueueConsumer {

    /**
     * 线程执行函数,阻塞获取消息并调用processMessage()进行处理.
     */
    public void run() {
    	Object message = null;
        // 循环阻塞获取消息直到线程被中断.
        try {
        	logger.debug("BlockingConsumer threadName:{},status={}", Thread.currentThread().getName(), Thread.currentThread().isInterrupted());
            while (!Thread.currentThread().isInterrupted()) {
                message = queue.take();
                processMessage(message);
            }
        } catch (InterruptedException e) {
            logger.error("消费线程阻塞被中断");
        } finally {
            // 退出线程前调用清理函数.
            clean();
        }
    }

    /**
     * 消息处理
     */
    protected abstract void processMessage(Object message);

    /**
     * 退出清理
     */
    protected abstract void clean();
}

具体消费消费类:日志批量入库(日志添加进队列另外写一个就行)

@Component("accessLogWriter")
public class AccessLogWriter extends BlockingConsumer {
    protected List<AccessLogDTO> eventsBuffer;

    @Autowired
    private SSCommonDAO ssCommonDAO;
    
    @Value("${queue.queueName.accessLog}")
	private String queueNameP; // 队列名 
    @Value("${queue.queueSize.accessLog}")
	private int queueSizeP; // 队列大小 100
    @Value("${queue.batchSize.accessLog}")
	private int batchSizeP;// 批量读取事件数量 10

    @Override
    @PostConstruct
    public void init(){
    	setQueueName(queueNameP);
    	setQueueSize(queueSizeP);
    	setBatchSize(batchSizeP);
    	eventsBuffer = Collections.synchronizedList(new ArrayList<AccessLogDTO>(batchSizeP));
    	start();
    }
    
    /**
     * 消息处理函数,将消息放入buffer,当buffer达到batchSize时执行批量更新函数.
     */
    @Override
    protected void processMessage(Object message) {
		AccessLogDTO event = (AccessLogDTO) message;
		eventsBuffer.add(event);
		LogUtil.debug("has event[size:{}]: {}", eventsBuffer.size(), event);
		// 已到达BufferSize则执行批量插入操作
		if (eventsBuffer.size() >= batchSize) {
			insertBatch();
		}
		if (queue.size()==0&&eventsBuffer.size()>0){
            insertBatch();
        }
    }

    /**
     * 将Buffer中的事件列表批量插入数据库.
     */
    private void insertBatch() {
    	LogUtil.debug("达到上限{},执行批量入库操作", eventsBuffer.size());
        try {
            ssCommonDAO.insert(LoginInterceptorUtil.getAccessLogBatchSql(), eventsBuffer);
        } catch (Exception e) {
        	LogUtil.error("批量提交任务时发生错误.", e);
        } finally {
        	// 清除已完成的Buffer
            eventsBuffer.clear();
        }
    }

    /**
     * 退出清理函数,完成buffer中未完成的消息.
     */
    @Override
    protected void clean() {
        if (!eventsBuffer.isEmpty()) {
        	insertBatch();
        }
        LogUtil.debug("cleaned task {}", this);
    }

    public List<AccessLogDTO> getEventsBuffer() {
        return eventsBuffer;
    }

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值