Flume监控流程

           首先在flume-ng-node中org.apache.flume.node.Application的main方法中,有两个方法分别是startAllComponents()和startAllComponents(conf)方法。其中startAllComponents(conf)方法有一个this.loadMonitoring();来启动监控方法loadMonitoring()loadMonitoring()方法

private void loadMonitoring() {
  Properties systemProps = System.getProperties();
  Set<String> keys = systemProps.stringPropertyNames();
  try {
    if (keys.contains(CONF_MONITOR_CLASS)) {
      String monitorType = systemProps.getProperty(CONF_MONITOR_CLASS);
      Class<? extends MonitorService> klass;
      try {
            klass = MonitoringType.valueOf(
            monitorType.toUpperCase(Locale.ENGLISH)).getMonitorClass();
      } catch (Exception e) {
              klass = (Class<? extends MonitorService>) Class.forName(monitorType);
      }
      this.monitorServer = klass.newInstance();
      Context context = new Context();
      for (String key : keys) {
        if (key.startsWith(CONF_MONITOR_PREFIX)) {
          context.put(key.substring(CONF_MONITOR_PREFIX.length()),
              systemProps.getProperty(key));
        }
      }
          monitorServer.configure(context);
            monitorServer.start();
    }
  } catch (Exception e) {
    logger.warn("Error starting monitoring. "
        + "Monitoring might not be available.", e);
  }
}

其中monitorServer.configure(context);来加载监控服务的配置信息,monitorServer.start();启动监控服务。
这里的monitorServer就会有两种:GangliaServer和HTTPMetricsServer,他们都实现了MonitorService这个接口。这里我们只追踪HTTPMetricsServer。

其中会初始化一个jettyServer来提供监控数据的访问服务,里面的核心方法还是handle方法,定义了监控数据访问的url,这里的url就是获取监控json格

式数据的http地址。通过源码我们可以看到 metricsMap = JMXPollUtil.getAllMBeans();具体的数据都是从这条语句得来的,再仔细看可以得知,这些监

控数据是同JMX的方式得到的。除了以上的源码,我们需要关注以外,我们还需要关注具体监控组件的源码,这些源码都是在flume-ng-core中的org.apache.flume.instrumentation包下面,所有的监控组件都会继承MonitoredCounterGroup实现xxxCounterMBean接口,MonitoredCounterGroup中定义了一些基本公有的监控属性,xxxCounterMBean定义了获取监控元素的方法接口,具体实现还是在监控组件中实现。

http监控

           Flume可以通过HTTP以JSON形式报告metrics,启用HTTP监控,内部会启动一个jetty服务,Flume需要配置一个端口,flume默认的端口是41414,这个在http监控组件的源码中可以看到

public class HTTPMetricsServer implements MonitorService {

  private Server jettyServer;
  private int port;
  private static Logger LOG = LoggerFactory.getLogger(HTTPMetricsServer.class);
  public static int DEFAULT_PORT = 41414;
  public static String CONFIG_PORT = "port";

使用方式

           如果仅仅想查看flume运行时的相关的数据量,则使用http这种监控方式,只需要在启动flume的时候在启动参数上面加上监控配置,例如这样:

#!/bin/sh      
nohup /data/server/flume-1.8.0/bin/flume-ng agent -c /data/server/flume-1.8.0/conf  -f /data/server/flume-1.8.0/conf/kafka_channel.conf -n a0 -Dflume.monitoring.type=http -Dflume.monitoring.port=5653  -Dflume.root.logger=INFO,console> /data/server/flume-1.8.0/conf/flume-client.log &

注意1:41414是flume监控的默认端口,在配置启动参数时可以更改这个参数,按照自己的需求更改这个端口。其中-Dflume.monitoring.type=http表示使用http方式来监控,后面的-Dflume.monitoring.port=1234表示我们需要启动的监控服务的端口号为5653,这个端口号可以自己随意配置。然后启动flume之后,通过http://ip:5653/metrics就可以得到flume的一个json格式的监控数据.

json数据网页显示

不同的浏览器显示的方式和结果也不一样,比如360浏览器会让你下载的metrics文件中有json格式的数据,无法刷新,体验感极差。Google Chrome浏览器会将该json数据直接显示到网页上,字体非常小,也没有一定的显示格式。而Firefox浏览器会将json处理后再显示到网页上,很形象和友好,看相关监控指标数据很直观,目前是感觉显示效果最好的浏览器。

下面展示不同浏览器展示json数据的区别:

Google Chrome浏览器

 

火狐浏览器:

flume文件监听无反应 flume监控文件机制_flume监控

对比效果一目了然,推荐使用火狐浏览器查看flume监控指标

flume重启监控指标变化

              只要Flume不重启服务这里就会一直做增量的变更,因为所有的监控指标其实就是一个累加器,flume运行采集数据,相关监控指标就会一直做加1的操作。这个在源码也能看出来。如果flume服务挂了,重启后所有的指标就又从0开始计数。

监控指标的各个含义

flume文件监听无反应 flume监控文件机制_flume http方式监控_02

监控程序的书写

             通过上面的介绍,我们已经知道flume监控原理和流程,flume运行时会启动一个jetty服务,把相关的监控指标通过HTTP以JSON形式报告metrics,同理我们也可以通过java程序获取这个json字符串,从而得到相关的监控指标。通过逻辑判断,获知flume运行的问题,触发相关条件就以发短信或邮件的形式报警,及时解决flume出现的问题。

比如当前的报警条件是:连接不上服务,或者获取的json字符串为空,tailDirsource和kafkaChannel处理的数据量相差超过1000,则进行报警,代码如下。

public class myThread extends Thread {
    private  String URL;
    public myThread(String URL) {
        this.URL = URL;
    }
    @Override
    public void run() {
        long timeInterval = 10000;
        int i = 0;
        //获取配置文件中的ip
        String[] str = URL.split(":");
        String ip = str[0];
        Properties prop = getPropInfo.getProp();
        while (true) {
            System.out.println("循环调用 !!!  时间=" + new Date());
            try {
            String s="";
            //发送 GET 请求,获取连接
            sendGet sendGet = new sendGet();
            s = sendGet.sendGet(URL);
            //获取flume监控指标的json字符串
            JSONObject jsonObject = JSON.parseObject(s);
            //获取各个监控指标
            JSONObject channel = jsonObject.getJSONObject("CHANNEL.c1");
            JSONObject source = jsonObject.getJSONObject("SOURCE.r1");
            //成功写入channel且提交的日志总数量
            Object channelCount = channel.get("EventPutSuccessCount");
            //目前为止source已经接收到的日志总数量
            Object sourceReceivedCount = source.get("EventReceivedCount");
            //成功写出到channel的日志总数量
            Object sourceAcceptedCount = source.get("EventAcceptedCount");

            int intChannelCount = Integer.parseInt(channelCount.toString());
            int intSourceCount = Integer.parseInt(sourceAcceptedCount.toString());
            int data1 = intSourceCount - intChannelCount;
            if (s != null && s != ""  &&(data1<1000)) {
                System.out.println("成功写入channel且提交的日志总数量: " + channelCount);
                System.out.println("目前为止source已经接收到的日志总数量: " + sourceReceivedCount);
                System.out.println("成功写出到channel的日志总数量: " + sourceAcceptedCount);
            } else {
            long timestamps = System.currentTimeMillis();
            System.out.println(timestamps+"============================================================");
            String times = new SimpleDateFormat("").format(new Date(timestamps));
            //发送报警邮件
            SendMailFunction.errorSendFunction(times, prop.getProperty("mailContent"), "/data/src/flume_data_monitor/flume.properies");
            //发送报警短信
            SendMessages sendMessages = new SendMessages();
            sendMessages.sendMessage(prop.getProperty("phone1"),"服务器:"+"【"+ip+"】"+prop.getProperty("messageContent") );
            sendMessages.sendMessage(prop.getProperty("phone2"),"服务器:"+"【"+ip+"】"+prop.getProperty("messageContent"));
            sendMessages.sendMessage(prop.getProperty("phone3"),"服务器:"+"【"+ip+"】"+prop.getProperty("messageContent"));
            System.out.println(times+"============================================================");
            //出现异常时的各个指标的数据量
            System.out.println("成功写入channel且提交的日志总数量: " + channelCount);
            System.out.println("目前为止source已经接收到的日志总数量: " + sourceReceivedCount);
            System.out.println("成功写出到channel的日志总数量: " + sourceAcceptedCount);
            //如果出现异常,等待五分钟处理时间,五分钟处理不好继续发送警告
            Thread.sleep(1000*60*5);
            i++;
            }
            Thread.sleep(timeInterval);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //超过三次报警跳出循环结束报警
        if(i>3){
            break;
        }
        }
    }