本文主要介绍自定义扩展dubbo的监控服务,监控中心的统计数据通过Metrics输出到graphite,
然后用grafana可视化。
Metrics介绍
Metrics,谷歌翻译就是度量的意思。当我们需要为某个系统某个服务做监控、做统计,就需要用到Metrics。
举个栗子,一个图片压缩服务:
每秒钟的请求数是多少(TPS)?
平均每个请求处理的时间(mst)?
请求处理的最长耗时(maxElapsed)?
等待处理的请求队列长度?
又或者一个缓存服务:
缓存的命中率?
平均查询缓存的时间?
基本上每一个服务、应用都需要做一个监控系统,这需要尽量以少量的代码,实现统计某类数据的功能。
以 Java 为例,目前最为流行的 metrics 库是来自 Coda Hale 的 dropwizard/metrics,该库被广泛地应用于各个知名的开源项目中。例如 Hadoop,Kafka,Spark,JStorm 中。
具体可以参考 Metrics
dubbo monitor扩展
- 实现MonitorService接口
- DubboMonitor默认1分钟发送一次统计数据到注册中心
// 定时任务执行器
private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3, new NamedThreadFactory("DubboMonitorSendTimer", true));
// 统计信息收集定时器
private final ScheduledFuture<?> sendFuture;
private final Invoker<MonitorService> monitorInvoker;
private final MonitorService monitorService;
private final long monitorInterval;
private final ConcurrentMap<Statistics, AtomicReference<long[]>> statisticsMap = new ConcurrentHashMap<Statistics, AtomicReference<long[]>>();
public DubboMonitor(Invoker<MonitorService> monitorInvoker, MonitorService monitorService) {
this.monitorInvoker = monitorInvoker;
this.monitorService = monitorService;
this.monitorInterval = monitorInvoker.getUrl().getPositiveParameter("interval", 60000);
// 启动统计信息收集定时器
sendFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
public void run() {
// 收集统计信息
try {
send();
} catch (Throwable t) { // 防御性容错
logger.error("Unexpected error occur at send statistic, cause: " + t.getMessage(), t);
}
}
}, monitorInterval, monitorInterval, TimeUnit.MILLISECONDS);
}
- 我们可以自定义一个实现MonitorService接口的实现类
public class DubboMonitorService implements MonitorService
- 定义一个阻塞队列,实现统计数据的收集和获取
private final BlockingQueue<URL> queue;
//获取
public DubboMonitorService() { // dubbo 那边是每分钟向监控中心发送一次调用统计数据
queue = new LinkedBlockingQueue<URL>(100000);
scheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(()->{
while (running) {
try {
if(queue.isEmpty()){
break;
}
saveInvoke(); // 记录统计日志 解析url数据 并转换成metrics
} catch (Throwable t) { // 防御性容错
logger.error("Unexpected error occur at write stat log, cause: " + t.getMessage(), t);
try {
Thread.sleep(5000); // 失败延迟
} catch (Throwable t2) {
logger.error("sleep then still Throwable");
t2.printStackTrace();
}
}
}
}, 10, 10, TimeUnit.SECONDS);
/*registry = new MetricRegistry();
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
reporter.start(10, TimeUnit.SECONDS);*/
}
//收集
@Override
public void collect(URL statistics) {
queue.offer(statistics);
}
- 具体的dubbo monitor 可以参考官方的 simple-monitor
监听MetricRegistry 并通过GraphiteReporter输出数据到Graphite
@Configuration
class MonitoringConfiguration {
@Bean
// graphite.host 就是下面安装的graphite的服务IP,默认端口 2003
public Graphite graphite(@Value("${graphite.host}") String graphiteHost,
@Value("${graphite.port}") int graphitePort) {
return new Graphite(new InetSocketAddress(graphiteHost, graphitePort));
}
@Bean
public GraphiteReporter graphiteReporter(Graphite graphite,
MetricRegistry registry) {
GraphiteReporter reporter =
GraphiteReporter.forRegistry(registry)
.prefixedWith("monitor.stats")
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.filter(MetricFilter.ALL)
.build(graphite);
reporter.start(1, TimeUnit.MINUTES);
return reporter;
}
@Bean
public MemoryUsageGaugeSet memoryUsageGaugeSet(MetricRegistry registry) {
MemoryUsageGaugeSet memoryUsageGaugeSet = new MemoryUsageGaugeSet();
registry.register("memory", memoryUsageGaugeSet);
return memoryUsageGaugeSet;
}
@Bean
public ThreadStatesGaugeSet threadStatesGaugeSet(MetricRegistry registry) {
ThreadStatesGaugeSet threadStatesGaugeSet = new ThreadStatesGaugeSet();
registry.register("threads", threadStatesGaugeSet);
return threadStatesGaugeSet;
}
}
Graphite安装
- linux 参考[Graphite监控上手指南](http://www.infoq.com/cn/articles/graphite-intro)
- mac 比较简单
- whisper carbon graphite-web 都要装 直接brew就行了,默认安装路径:/opt/graphite
- 当然要先装python pip 这个自己研究
pip install whisper
brew carbon
brew graphite-web
注意:
carbon 安装完可以直接启动的 必须先启动
graphite-web 是django工程,需要配置初始数据库,比较麻烦
1.database config:
sudo PYTHONPATH=/opt/graphite/webapp django-admin.py migrate --settings=graphite.settings --run-syncdb
http://graphite.readthedocs.io/en/latest/config-local-settings.html
2.抽取静态文件:
PYTHONPATH=/opt/graphite/webapp django-admin.py collectstatic --noinput --settings=graphite.settings
http://graphite.readthedocs.io/en/latest/config-local-settings.html STATIC_ROOT
3.django 不能用1.6的版本 pip install django
初始化数据库:PYTHONPATH=/opt/graphite/webapp django-admin migrate --settings=graphite.settings --run-syncdb
4.启动应用:
sudo PYTHONPATH=`pwd`/storage/whisper ./bin/run-graphite-devel-server.py --port=8085 --libs=`pwd`/webapp /opt/graphite
安装grafana
这个比较简单,官网有详细安装说明
mac 例子:
brew update
brew install grafana
grafana里面配置好数据源,就可以自定义可视化报表了
最后
当然可以结合spring boot actuator,将可以配合Metrics一起运作
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricSet;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.metrics.Metric;
import java.util.*;
public class DbCountMetrics implements PublicMetrics,MetricSet{
private Collection<BaseDao> baseDaos;
public DbCountMetrics(Collection<BaseDao> baseDaos) {
this.baseDaos = baseDaos;
}
@Override
public Collection<Metric<?>> metrics() {
List<Metric<?>> metrics = new LinkedList<>();
for (BaseDao baseDao : baseDaos) {
String name = DbCountRunner.getRepositoryName(baseDao.getClass());
String metricName = "counter.datasource." + name;
metrics.add(new Metric(metricName, baseDao.getCount()));
}
return metrics;
}
@Override
public Map<String,com.codahale.metrics.Metric> getMetrics() {
final Map<String,com.codahale.metrics.Metric> gauges = new HashMap<>();
for (Metric springMetric : metrics()) {
gauges.put(springMetric.getName(), (Gauge<Number>) springMetric::getValue);
}
return gauges;
}
}
附最终效果图
本人github