ActiveMQ Plug-in Development Example-Task Log

Keywords: Apache Java xml



The project encountered a scenario where task logs need to be recorded on ActiveMQ. Actually AMQ itself has a Logging Plugin, but it is not always suitable for the actual scenario of the project. Think about it, just a while ago for other projects to do a MQTT protocol authentication plug-in, the technical basis has been, or write a customized version of the plug-in for the project.

My previous article ActiveMQ Plug-in Development It introduces how to develop an AMQ plug-in. In fact, the function of this time is based on the content of the previous code to modify. The main function is to use HTTP POST method to send a message to a server every time AMQ receives a task message. Represents that tasks flow through MQ.

Let's look at the entry class first. Compared with the previous code, we add two parameters, which can be manually configured in the configuration file activemq.xml.

package com.cn.amqs;
import org.apache.activemq.broker.Broker;   
import org.apache.activemq.broker.BrokerPlugin;   
import org.apache.activemq.plugin.StatisticsBrokerPlugin;
import org.apache.commons.logging.Log;   
import org.apache.commons.logging.LogFactory;  

public class MessageLogPlugin implements BrokerPlugin {   

  private Log log = LogFactory.getLog(StatisticsBrokerPlugin.class);  
  private String seviceUrl;
  private String sign;
  public Broker installPlugin(Broker broker) throws Exception {   
    log.info("install MessageLogPlugin");   
    return new MessageLog(broker,serviceUrl,sign);   
  }   
  public void setServiceUrl(String serviceUrl) {
    this.serviceUrl=serviceUrl;
  }
  ......
}  

The main functions are implemented in the MesageLog class.

  1. Whenever a task message comes in, it determines whether the message has ever been there, and if it is received for the first time, it sends a post message to the server.
  2. Record the number of messages for a task
  3. In order to prevent the number of tasks from increasing indefinitely, a timing cleanup mechanism is set up (but since Timer is set for each task, the applicable scenario should be scenarios with fewer tasks or with shorter cleanup time, otherwise it is also a consumption of resources).
  4. Distinguishing between Up and Down Tasks
package com.cn.amqs;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.activemq.broker.Broker;   
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ProducerBrokerExchange;
import org.apache.activemq.command.Message;
import org.apache.commons.logging.Log;   
import org.apache.commons.logging.LogFactory;

/**
 * Realize sending a message to an address automatically when each task arrives at MQ
 * @author MiSterRabbit
 */
public class MessageLog extends BrokerFilter{
private Log log;  
/**Downlink task HashMap*/
private ConcurrentHashMap<Object, Integer> downWards;
/**Uplink task HashMap*/
private ConcurrentHashMap<Object, Integer> upWards;
private String seviceUrl;
private String sign;

public MessageLog(Broker next,String seviceUrl,String sign) {   
super(next);   
downWards = new ConcurrentHashMap<Object, Integer>();
upWards = new ConcurrentHashMap<Object, Integer>();
this.seviceUrl=seviceUrl;
this.sign=sign.isEmpty()? "Branch":"headquarters";
log = LogFactory.getLog(com.cn.amqs.MessageLog.class);
log.info("initialize Message Log plugin");
}

/**
* Timer Class, which implements periodic cleaning of log HashMap to prevent unlimited growth of Map
*/
class missionTimer extends TimerTask {
private String missionID;
private Log log;
private ConcurrentHashMap<Object, Integer> map;
public missionTimer(String missionID, Log log, ConcurrentHashMap<Object, Integer> map) {
this.missionID=missionID;
this.log=log;
this.map=map;
}
@Override
public void run() {
    this.map.remove(missionID);
this.log.info("[FLOW_LOG] Remove expired mission: "+missionID);
}
}

/**
* Determine whether the log is in the map, send a message if it is not, and add a counter if it exists
* @param missionID  task number
* @param map  Task sending and task uploading use different map s
*/
 public synchronized void insertIntoMap(String missionID, ConcurrentHashMap<Object,Integer> map,String direction) {
 if(map.containsKey(missionID)) {
 int count = map.get(missionID)+1;
 map.put(missionID,count);
           this.log.debug("[FLOW_LOG] "+map);
       } else{
       map.put(missionID,1);
           this.log.info("[FLOW_LOG] Receive a new "+direction+" mission: "+missionID);
           // Open a thread to send a task data. The MissionSend class here is actually opening a thread to send an http post message.
           if (direction.equalsIgnoreCase("DOWNWARD")){
           MissionSend tmqs = new MissionSend(missionID, super.getBrokerName().toString().substring(3), "ActiveMQ", "/opt/activemq/apache-activemq-5.13.4/data/mission.log", this.sign+"MQ Receiving downlink tasks", "ok", this.seviceUrl);
       new Thread(tmqs,"mission_send").start();
           } else {
           MissionSend tmqs = new MissionSend(missionID, super.getBrokerName().toString().substring(3), "ActiveMQ", "/opt/activemq/apache-activemq-5.13.4/data/mission.log", this.sign+"MQ Receiving Upstream Tasks", "ok", this.seviceUrl);
       new Thread(tmqs,"mission_send").start();
           }
           // Use Timer to clean up regularly, and clean up the task in 1800 seconds
           Timer timer =new Timer();
           TimerTask task = new missionTimer(missionID,this.log,downWards);
           timer.schedule(task,1800000);
       }
 }
 
 /**
  * The judgment is executed whenever MQ receives a message from a producer.
  */
 public void send(ProducerBrokerExchange producerExchange, Message messageSend) throws Exception {
// If the task ID is not empty and messages are not sent through the cluster
 if ((messageSend.getProperty("misid") != null) && 
(!producerExchange.getProducerState().getInfo().getProducerId().toString().contains("MQ_"))) {
   // If the destination does not contain an UPLAOD field, it is judged to be a downlink of the message or an upstream of the message. Record a log and then call insertIntoMap to determine whether an http post message needs to be sent
 if (!messageSend.getDestination().toString().toLowerCase().contains("upload")) {
 this.log.info("[FLOW_LOG] Down Mission: " + messageSend.getProperty("misid") + ".  Destination: " + messageSend.getDestination() + ".  Producer: "+producerExchange.getConnectionContext().getConnection().getRemoteAddress());
 insertIntoMap(messageSend.getProperty("misid").toString(),downWards,"DOWNWARD");
 } else {
this.log.info("[FLOW_LOG] Up Mission: " + messageSend.getProperty("misid") + ".  Destination: " + messageSend.getDestination()+".  Producer: "+producerExchange.getConnectionContext().getConnection().getRemoteAddress());
insertIntoMap(messageSend.getProperty("misid").toString(),upWards,"UPWARD");
 }
 }
   super.send(producerExchange, messageSend);
 }
}  

MissionSend's classes can be freely extended. I won't dwell on that.

The plug-in function brings a strong expansibility to AMQ. Users can realize the secondary development of the function without modifying the existing function. When I am free, I will compile a list of functions that plug-ins can implement. In fact, if you have time, look at the BrokerFilter class, you can understand the plug-in can achieve the function.

Posted by abitshort on Sat, 25 May 2019 14:29:43 -0700