package com.forest.ape.mq.impl;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.*;
import com.rabbitmq.client.*;
import com.rabbitmq.client.AMQP.BasicProperties;
/**
*
* @author CHQ
* 2012-3-22
*/
public class ProducersGaurd {
static Logger LOG = LoggerFactory.getLogger(ProducersGaurd.class);
ConcurrentHashMap<String, MessageProducer> queues = new ConcurrentHashMap<String, MessageProducer>();
/** use for queue name for consumers */
HashMap<String, MessageProducer> namingQueues = new HashMap<String, MessageProducer>();
AtomicLong totalProducers = new AtomicLong(0);
ConnectionFactory connectionFactory;
static String EXCHANGE_NAME = "direct_data";
volatile boolean throttleAll = false;
ProducerConfig config;
String defaultQueueName = "/!/NamingQueue_";
public ProducersGaurd() throws IOException {
super();
connectionFactory = new ConnectionFactory();
config = new ProducerConfig();
config.loadConfig();
for (String consumerName : config.consumers) {
consumerName = defaultQueueName + consumerName;
MessageProducer pro = new MessageProducer(consumerName, config);
pro.start();
LOG.info("create default queue:" + consumerName);
namingQueues.put(consumerName, pro);
}
}
public void clearAll() throws IOException {
throttleAll = true;
for (MessageProducer entry : queues.values()) {
entry.shutdown();
}
queues.clear();
totalProducers.set(0);
}
public boolean appendData(String path, MQPacket packet) throws IOException {
//if append is closed, so can't do nothing
if (throttleAll) {
LOG.info("append is closed!");
return false;
}
if (path == null || packet == null)
{
LOG.warn("path can't be null or packet can't be null");
return false;
}
MessageProducer mp = queues.get(path);
if (mp == null) {
mp = new MessageProducer(path, config);
for (Map.Entry<String, MessageProducer> pro : namingQueues.entrySet()) {
String queueName = pro.getKey() + path;
LOG.info("create path(queue) :" + queueName);
pro.getValue().addPacket(new MQPacket(queueName.getBytes("UTF-8"), null));
}
queues.put(path, mp);
totalProducers.addAndGet(1);
mp.start();
}
return mp.addPacket(packet);
}
class MessageProducer extends Thread {
final Logger LOG = LoggerFactory.getLogger(MessageProducer.class);
private BlockingQueue<MQPacket> outstandingQueue = new LinkedBlockingDeque<MQPacket>();
volatile boolean isRunning = true;
Channel ch;
Connection conn;
final String bindName;
final List<String> queues = new ArrayList<String>();
int idleLimit = 60;
volatile boolean throttle = false;
/** one binding to several queues for one tree node*/
MessageProducer(String queueName, ProducerConfig config) throws IOException {
super("queue[" + queueName + "]");
idleLimit = config.queueIdleLimit;
bindName = queueName;
LOG.info("start MessageProducer...");
conn = connectionFactory.newConnection(config.addrArr);
ch = conn.createChannel();
// x-ha-policy
Map<String, Object> haPolicy = new HashMap<String, Object>();
haPolicy.put("x-ha-policy", "nodes");
haPolicy.put("x-ha-policy-params", config.asList);
// first durable
if (!config.enableHA)
haPolicy = null;
/**
* exchange - the name of the exchange
type - the exchange type direct topic fanout headers
durable - true if we are declaring a durable exchange (the exchange will survive a server restart)
*/
ch.exchangeDeclare(EXCHANGE_NAME, "direct", true);
/**
* queue - the name of the queue
durable - true if we are declaring a durable queue (the queue will survive a server restart)
exclusive - true if we are declaring an exclusive queue (restricted to this connection)
autoDelete - true if we are declaring an autodelete queue (server will delete it when no longer in use)
arguments - other properties (construction arguments) for the queue
*/
if (queueName.startsWith(defaultQueueName)) {
ch.queueDeclare(queueName, true, false, false, haPolicy);
ch.queueBind(queueName, EXCHANGE_NAME, bindName);
queues.add(queueName);
} else {
for (String each : namingQueues.keySet()) {
String rename = each + queueName;
ch.queueDeclare(rename, true, false, false, haPolicy);
/**
* queue name
* exchange name
* bind name
*/
ch.queueBind(rename, EXCHANGE_NAME, bindName);
queues.add(rename);
}
}
/**
*
exchange - the exchange to publish the message to
routingKey - the routing key
props - other properties for the message - routing headers etc
body - the message body
*/
// channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
}
@Override
public void run() {
int idle = 0;
while(isRunning && !throttle) {
try {
MQPacket p = outstandingQueue.poll(1, TimeUnit.SECONDS);
if (p == null) {
idle++;
LOG.debug("queue " + queues.toString() + " is idle[time="+idle+"]");
if (idle == idleLimit) {
throttle = true;
LOG.info("close queue " + queues.toString() + " thread!");
shutdown();
}
continue;
} else {
idle = 0;
}
// MQPacket p = outstandingQueue.take();
ch.basicPublish(EXCHANGE_NAME, bindName, config.mp, p.data);
if (p.handler != null) {
p.handler.handleAck(ch.waitForConfirms());
}
} catch (InterruptedException e) {
LOG.warn("", e);
} catch (IOException e) {
LOG.warn("", e);
}
}
}
public void shutdown() throws IOException {
LOG.info("#shutdown");
/** refuse accepting any packet */
throttle = true;
queues.remove(bindName);
if (isRunning == false)
return;
isRunning = false;
this.interrupt();
ch.close();
conn.close();
}
public boolean addPacket(MQPacket p) {
if (!throttle)
return outstandingQueue.add(p);
LOG.warn("throttle is closed!!! packet is not adding, so you should retry it!");
return false;
}
}
public void join() throws InterruptedException {
for (MessageProducer entry : queues.values()) {
entry.join();
}
}
class ProducerConfig {
Logger LOG = LoggerFactory.getLogger(ProducerConfig.class);
Address[] addrArr;
boolean enableHA = false;
List<String> asList = null;
BasicProperties mp;
int queueIdleLimit = 60;
boolean persistent = true;
List<String> consumers = null;
//List<String> consumerIDs = new ArrayList<String>();
public void loadConfig() {
try {
String config = "conf/config";
LOG.info("load Sender config: " + config);
Properties p = new Properties();
p.load(new InputStreamReader(new FileInputStream(
config), "UTF-8"));
Object value;
String addrs = (value = p.get("MQClusterAddrs")) != null ? value.toString() : null;
String[] manyAddrs = addrs.split(";");
addrArr = new Address[manyAddrs.length];
int cnt = 0;
for (String addr : manyAddrs) {
addrArr[cnt++] = new Address(addr.split(":")[0], Integer.valueOf(addr.split(":")[1]));
LOG.info("MQ node:" + addr);
}
String haPolicy = (value = p.get("MQCluster-ha-policy")) != null ? value.toString() : null;
String haPolicyDecision = haPolicy.split(";")[0];
if (haPolicyDecision.equalsIgnoreCase("true")) {
enableHA = true;
LOG.info("enable HA");
asList= Arrays.asList(haPolicy.split(";")[1].split(","));
for (String string : asList) {
LOG.info("MQ ha node:" + string);
}
} else {
enableHA = false;
LOG.info("disable HA");
}
queueIdleLimit = Integer.valueOf((value = p.get("QueueIdle")) != null ? value.toString() : null);
LOG.info("Queue Idle Limit is " + queueIdleLimit);
persistent = Boolean.valueOf((value = p.get("Persistent")) != null ? value.toString() : null);
LOG.info("Queue persistent is " + persistent);
if (persistent == true)
mp = MessageProperties.PERSISTENT_BASIC;
else {
mp = MessageProperties.BASIC;
}
value = (value = p.get("Consumers")) != null ? value.toString() : null;
if (value != null) {
consumers = Arrays.asList(value.toString().split(":"));
for (String string : consumers) {
LOG.info("consumer id:" + string);
}
}
assert(consumers != null);
} catch (Throwable e) {
LOG.error("config parse error!", e);
}
}
}
}