单channel监听RabbitMQ消息队列,多线程处理消息任务的实现

项目上碰到一个java后台实现问题,底层依赖的以前开发团队的rabbitMQ的实现,该底层open了一个channel,并一直保持着对该channel的监听,在这个基础上我需要实现的就是一直监听该channel,并将接受到的消息交给其他线程去处理,保证监听主线程不被阻塞。需求大致是这样的。

下面看下我的具体实现:

一、主线程监听消息队列,并将接受的消息交给其他线程去处理,并且处理消息的其他线程数量有限制。这里考虑用concurrent包里的semaphore,信号量可以认为是一定数量num的许可证,主线程将消息和一张许可证交给一个子线程去处理,子线程处理完了(不管成功或失败)都要将许可证上交给主线程,供下一个子线程使用,这样就可能出现两种情况:1)许可证富裕,主线程继续将消息交给子线程去消费;2)许可证已被多个子线程占用,还未交回主线程手里。这样主线程就阻塞了。这个玩意刚好满足我的业务需求。看下代码:

private int threadNum = 10;
private Semaphore semaphore = new Semaphore(threadNum);
private Executor executor = Executors.newCachedThreadPool();
private int allowedChildThreads = 10;

public void doJob() {
		Logger.info("begin consuming!", new Object[0]);
		MessageClientFactory.getClient().consume("xxxQueue", true, new MessageConsumer() {
			@Override
			public void consume(String message) {
				Logger.info(message, new Object[0]);
				
				try {
					JSONObject messageObject = JSONObject.parseObject(message);
					
					String jobType = messageObject.getString("job_type"); //任务类型
					String jobID = messageObject.getString("job_id"); //任务ID
					Date jobSubmitTime = messageObject.getDate("job_submit_time"); //任务提交时间
					String params = messageObject.getString("params"); //任务指标参数
					
					//构建线程间信息传递对象
					Map threadDelivery = new HashMap<>();
					
					threadDelivery.put("job_type", jobType);
					threadDelivery.put("job_id", jobID);
					threadDelivery.put("params", params);
					
					semaphore.acquire(); //许可证减少一张
					executor.execute(new MessageSplitToMany(threadDelivery, semaphore, executor, allowedChildThreads)); //将消费信息和许可证一并交给子线程MessageSplitToMany,他处理完释放许可证
				} catch (Exception e) {
					Logger.error(e, "消费主线程发生错误", new Object[0]);
				}
			}
		});
		
	}

二、MessageSplitToMany子线程进一步将消息拆分并交给一定数量的孙线程处理(用semaphore限制并发数),在这些孙线程未全部处理完成前,MessageSplitToMany子线程阻塞。这里CountDownLatch正好满足需求。

/**
 * 一条消息分解成多个指标线程并行计算
 * @author fujian
 *
 */
public class MessageSplitToMany implements Runnable {
	
	private Map threadDelivery;
	private Semaphore semaphore;
	private Executor executor;
	private int allowedChildThreads;
	
	public MessageSplitToMany(Map threadDelivery, Semaphore semaphore, Executor executor, int allowedChildThreads) {
		this.threadDelivery = threadDelivery;
		this.semaphore = semaphore;
		this.executor = executor;
		this.allowedChildThreads = allowedChildThreads;
	}
	@Override
	public void run() {
		try {
			String params = threadDelivery.get("params");
			Logger.info(params, new Object[0]);
			JSONObject multiIdxParams = JSONObject.parseObject(params);
			//对并发处理单个指标计算的工作线程数量有一定的约束,所允许的工作线程数量不能超过设定值
			if(allowedChildThreads > multiIdxParams.size()) {
				allowedChildThreads = multiIdxParams.size();
							}
			
			Semaphore innerSemaphore = new Semaphore(allowedChildThreads);
			CountDownLatch countDownLatch = new CountDownLatch(multiIdxParams.size());
			for(String idxName : multiIdxParams.keySet()) {
				String idxParams = multiIdxParams.getString(idxName);
				
				Map childThreadDelivery = new LinkedHashMap<>();
				childThreadDelivery.put("job_id", threadDelivery.get("job_id"));
				childThreadDelivery.put("index_id", DigestUtils.md5Hex(idxName + idxParams));
				childThreadDelivery.put("job_type", threadDelivery.get("job_type"));
				childThreadDelivery.put("idx_name", idxName);
				childThreadDelivery.put("params", idxParams);
				
				innerSemaphore.acquire(); //孙信号量控制子线程中的并发数
				executor.execute(new IdxCalAndSaveThread(countDownLatch, innerSemaphore, childThreadDelivery));//将countDownLatch及孙信号量传给IdxCalAndSaveThread孙线程,由它countdown及释放孙信号量
			}
			
			countDownLatch.await(); //在message中所有指标计算完成前主线程阻塞,直至countdown减为0
			
			Logger.info("-----" + Thread.currentThread().getName() + "consumer thread finished the all idxCals Cal-----");
			
		} catch (Exception e) {
			Logger.error(e, "并发处理消息过程发生错误", new Object[0]);
		} finally {
			semaphore.release(); //释放子信号量,这个信号量是从主线程传递进来的
		}
	}

}

三、孙线程是最底层的处理线程,其最终要释放孙信号量及对countDownLatch减一,

/**
 * 指标计算及存储
 * @author fujian
 *
 */
public class IdxCalAndSaveThread implements Runnable {
	
	private Semaphore innerSemaphore;
	private CountDownLatch countDownLatch;
	private Map threadDelivery;
	
	public IdxCalAndSaveThread(CountDownLatch countDownLatch, Semaphore innerSemaphore, Map threadDelivery) {
		this.countDownLatch = countDownLatch;
		this.innerSemaphore = innerSemaphore;
		this.threadDelivery = threadDelivery;
	}
	
	
	@Override
	public void run() {
		try {
				String idxName = threadDelivery.get("idx_name").toString();
				
				String params = threadDelivery.get("params").toString();
				threadDelivery.remove("params");
				
				threadDelivery.put("start_time", System.currentTimeMillis());
				
				//调用indexCal 层指标计算api,计算指标
				//考虑idxName为idxCal_01
				Object idx;
				if(idxName.contains("_")) {
					String idxCalName = idxName.substring(0, idxName.lastIndexOf("_"));
					idx = IdxCalHandler.handle(idxCalName, params);
				} else {
					idx = IdxCalHandler.handle(idxName, params);
				}

				threadDelivery.put("end_time", System.currentTimeMillis());
				threadDelivery.put("idx_result", idx);
				Logger.info(idxName + "cal is finished!", new Object[0]);
				
				//save to mongo
				saveToDB(threadDelivery);
				Logger.info(idxName + "cal result had saved into mongo", new Object[0]);
			
		} catch (Exception e) {
			Logger.error(e, "单个指标计算过程发生错误", new Object[0]);
		} finally { //最终要释放孙信号量及countdown减一
			innerSemaphore.release();
			countDownLatch.countDown();
		}
	}
}

你可能感兴趣的:(多线程)