package com.forest.ape.mq.impl; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.forest.ape.mq.CallableHandler; import com.forest.ape.mq.CallableHandler.AsynSentHandler; import com.forest.ape.mq.OutBridge; import com.forest.ape.server.persistence.Request; import com.rabbitmq.client.*; /** * * @author CHQ * 2012-3-16 */ public class SendWorker extends Thread { static Logger LOG = LoggerFactory.getLogger(SendWorker.class); String EXCHANGE = "EXCHANGE"; static List<String> asList;/* * = Arrays.asList("hare@app-20", "rabbit@app-20"/* , * "GreyBit@app-20" ); */ boolean throttle = false; List<String> learners; boolean isRunning = true; static ConnectionFactory connectionFactory; static Address[] addrArr; int retry = 0; static boolean enableHA = false; String topic = "#"; // private volatile SortedSet<Long> unconfirmedSet = Collections // .synchronizedSortedSet(new TreeSet()); private BlockingQueue<MQPacket> outstandingQueue = new LinkedBlockingDeque<MQPacket>(); OutBridge bridge; String epoch; Connection conn; Channel ch; static { SendWorker.loadConfig(); } /** * each time create a new exchange and queue for new leader and new followers * @param leaderId * @param followers * @param epoch */ public SendWorker(String leaderId, List<String> followers, String epoch) { super("SendWorker"); this.EXCHANGE = this.EXCHANGE + "_" + epoch; this.epoch = epoch; this.learners = followers; this.bridge = new DefaultOutBridge(); connectionFactory = new ConnectionFactory(); } @Override public void run() { try { // Setup LOG.info("start sendworker..."); conn = connectionFactory.newConnection(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", asList); // first durable if (!enableHA) haPolicy = null; declareQueues(ch, learners, haPolicy); // sec durable ch.exchangeDeclare(EXCHANGE, "topic", true); // ch.addConfirmListener(new ConfirmListenerHandler()); ch.confirmSelect(); queueBinds(ch, learners, EXCHANGE, topic); // third durable // ch.basicPublish(EXCHANGE, "anomoy", // MessageProperties.PERSISTENT_BASIC, message.getBytes()); while(isRunning) { MQPacket packet = outstandingQueue.take(); bridge.enmq(EXCHANGE, ch, packet); if (packet.handler != null) { packet.handler.handleAck(ch.waitForConfirms()); } } // ch.waitForConfirmsOrDie(); } catch (InterruptedException e) { LOG.warn("", e); } catch (Throwable e) { LOG.info("foobar", e); } } public boolean enQueue(MQPacket packet) { if (!throttle) { return outstandingQueue.add(packet); } return false; } private void queueBinds(Channel ch, List<String> learners2, String exchange2, String topic2) throws IOException { for (String learner : learners2) { ch.queueBind(learner + "_" + epoch, exchange2, topic2); } } private void declareQueues(Channel ch, List<String> learners, Map<String, Object> haPolicy) throws IOException { for (String string : learners) { ch.queueDeclare(string + "_" + epoch, true, false, false, haPolicy); } } // class ConfirmListenerHandler implements ConfirmListener { // // // public void handleAck(long seqNo, boolean multiple) { // if (multiple) { // unconfirmedSet.headSet(seqNo + 1).clear(); // } else { // unconfirmedSet.remove(seqNo); // } // } // // public void handleNack(long seqNo, boolean multiple) { // // handle the lost messages somehow // } // } public void shutdown() throws InterruptedException, IOException { LOG.warn("#shutdown#:mq sender"); throttle = true; while(outstandingQueue.size() > 0) { TimeUnit.SECONDS.sleep(1); } isRunning = false; this.interrupt(); ch.close(); conn.close(); } public static 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"); } } catch (Throwable e) { LOG.error("config parse error!", e); } } }