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运行采集数据,相关监控指标就会一直做加1的操作。这个在源码也能看出来。如果flume服务挂了,重启后所有的指标就又从0开始计数。
监控指标的各个含义
监控程序的书写
通过上面的介绍,我们已经知道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;
}
}
}