一. 概述
1. 版本:2.7.8
2. 说明
Monitor 监控层
用来统计RPC 调用次数和调用耗时时间,扩展接口为MonitorFactory,对应的实现类为DubboMonitorFactroy。
用户可以实现该层的MonitorFactory扩展接口,实现自定义监控统计策略。
二. MonitorFactory工厂实现
1. 结构图
2. MonitorFactory接口
Monitor工厂接口
默认实现为DubboMonitorFactory
@SPI("dubbo")
public interface MonitorFactory {
@Adaptive("protocol")
Monitor getMonitor (URL url) ;
}
3. AbstractMonitorFactory抽象类
通过加锁的方式创建监控对象(DubboMonitor)
每个URL作为一个监控单元
getMonitor()方法获取Monitor对象:如存在,则直接返回
getMonitor()方法获取Monitor对象:如不存在,则加锁初始化并缓存在静态变量中
public abstract class AbstractMonitorFactory implements MonitorFactory {
private static final Logger logger = LoggerFactory.getLogger(AbstractMonitorFactory.class);
private static final ReentrantLock LOCK = new ReentrantLock ();
private static final Map<String, Monitor> MONITORS = new ConcurrentHashMap <>();
private static final Map<String, CompletableFuture<Monitor>> FUTURES = new ConcurrentHashMap <>();
private static final ExecutorService EXECUTOR =
new ThreadPoolExecutor (0 , 10 , 60L , TimeUnit.SECONDS, new SynchronousQueue <Runnable>(),
new NamedThreadFactory ("DubboMonitorCreator" , true ));
public static Collection<Monitor> getMonitors () {
return Collections.unmodifiableCollection(MONITORS.values());
}
@Override
public Monitor getMonitor (URL url) {
url = url.setPath(MonitorService.class.getName()).addParameter(INTERFACE_KEY, MonitorService.class.getName());
String key = url.toServiceStringWithoutResolving();
Monitor monitor = MONITORS.get(key);
Future<Monitor> future = FUTURES.get(key);
if (monitor != null || future != null ) {
return monitor;
}
LOCK.lock();
try {
monitor = MONITORS.get(key);
future = FUTURES.get(key);
if (monitor != null || future != null ) {
return monitor;
}
final URL monitorUrl = url;
final CompletableFuture<Monitor> completableFuture
= CompletableFuture.supplyAsync(
() -> AbstractMonitorFactory.this .createMonitor(monitorUrl));
FUTURES.put(key, completableFuture);
completableFuture.thenRunAsync(new MonitorListener (key), EXECUTOR);
return null ;
} finally {
LOCK.unlock();
}
}
protected abstract Monitor createMonitor (URL url) ;
class MonitorListener implements Runnable {
private String key;
public MonitorListener (String key) {
this .key = key;
}
@Override
public void run () {
try {
CompletableFuture<Monitor> completableFuture = AbstractMonitorFactory.FUTURES.get(key);
AbstractMonitorFactory.MONITORS.put(key, completableFuture.get());
AbstractMonitorFactory.FUTURES.remove(key);
} catch (InterruptedException e) {
logger.warn("Thread was interrupted unexpectedly, monitor will never be got." );
AbstractMonitorFactory.FUTURES.remove(key);
} catch (ExecutionException e) {
logger.warn("Create monitor failed, monitor data will not be collected until you fix this problem. " , e);
}
}
}
}
4. DubboMonitorFactory实现类
createMonitor方法创建DubboMonitor对象,该对象构造时创建定时器周期统计数据
public class DubboMonitorFactory extends AbstractMonitorFactory {
private Protocol protocol;
private ProxyFactory proxyFactory;
public void setProtocol (Protocol protocol) {
this .protocol = protocol;
}
public void setProxyFactory (ProxyFactory proxyFactory) {
this .proxyFactory = proxyFactory;
}
@Override
protected Monitor createMonitor (URL url) {
URLBuilder urlBuilder = URLBuilder.from(url);
urlBuilder.setProtocol(url.getParameter(PROTOCOL_KEY, DUBBO_PROTOCOL));
if (StringUtils.isEmpty(url.getPath())) {
urlBuilder.setPath(MonitorService.class.getName());
}
String filter = url.getParameter(REFERENCE_FILTER_KEY);
if (StringUtils.isEmpty(filter)) {
filter = "" ;
} else {
filter = filter + "," ;
}
urlBuilder.addParameters(CHECK_KEY, String.valueOf(false ),
REFERENCE_FILTER_KEY, filter + "-monitor" );
Invoker<MonitorService> monitorInvoker = protocol.refer(MonitorService.class, urlBuilder.build());
MonitorService monitorService = proxyFactory.getProxy(monitorInvoker);
return new DubboMonitor (monitorInvoker, monitorService);
}
}
三. Monitor实现
1. 结构图
2. MonitorService接口
collect方法:用于统计监控信息
lookup方法:查看监控数据
public interface MonitorService {
String APPLICATION = "application" ;
String INTERFACE = "interface" ;
String METHOD = "method" ;
String GROUP = "group" ;
String VERSION = "version" ;
String CONSUMER = "consumer" ;
String PROVIDER = "provider" ;
String TIMESTAMP = "timestamp" ;
String SUCCESS = "success" ;
String FAILURE = "failure" ;
String INPUT = INPUT_KEY;
String OUTPUT = OUTPUT_KEY;
String ELAPSED = "elapsed" ;
String CONCURRENT = "concurrent" ;
String MAX_INPUT = "max.input" ;
String MAX_OUTPUT = "max.output" ;
String MAX_ELAPSED = "max.elapsed" ;
String MAX_CONCURRENT = "max.concurrent" ;
void collect (URL statistics) ;
List<URL> lookup (URL query) ;
}
3. Monitor接口
public interface Monitor extends Node , MonitorService {
}
4. DubboMonitor实现类
数据从URL收集
构造函数创建定时任务(scheduledExecutorService.scheduleWithFixedDelay)
定时任务周期调用send()方法统计数据
public class DubboMonitor implements Monitor {
private static final Logger logger = LoggerFactory.getLogger(DubboMonitor.class);
private static final int LENGTH = 10 ;
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 ConcurrentMap<Statistics, AtomicReference<long []>> statisticsMap = new ConcurrentHashMap <Statistics, AtomicReference<long []>>();
public DubboMonitor (Invoker<MonitorService> monitorInvoker, MonitorService monitorService) {
this .monitorInvoker = monitorInvoker;
this .monitorService = monitorService;
final long monitorInterval = monitorInvoker.getUrl().getPositiveParameter("interval" , 60000 );
sendFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
send();
} catch (Throwable t) {
logger.error("Unexpected error occur at send statistic, cause: " + t.getMessage(), t);
}
}, monitorInterval, monitorInterval, TimeUnit.MILLISECONDS);
}
public void send () {
if (logger.isDebugEnabled()) {
logger.debug("Send statistics to monitor " + getUrl());
}
String timestamp = String.valueOf(System.currentTimeMillis());
for (Map.Entry<Statistics, AtomicReference<long []>> entry : statisticsMap.entrySet()) {
Statistics statistics = entry.getKey();
AtomicReference<long []> reference = entry.getValue();
long [] numbers = reference.get();
long success = numbers[0 ];
long failure = numbers[1 ];
long input = numbers[2 ];
long output = numbers[3 ];
long elapsed = numbers[4 ];
long concurrent = numbers[5 ];
long maxInput = numbers[6 ];
long maxOutput = numbers[7 ];
long maxElapsed = numbers[8 ];
long maxConcurrent = numbers[9 ];
String protocol = getUrl().getParameter(DEFAULT_PROTOCOL);
URL url = statistics.getUrl()
.addParameters(MonitorService.TIMESTAMP, timestamp,
MonitorService.SUCCESS, String.valueOf(success),
MonitorService.FAILURE, String.valueOf(failure),
MonitorService.INPUT, String.valueOf(input),
MonitorService.OUTPUT, String.valueOf(output),
MonitorService.ELAPSED, String.valueOf(elapsed),
MonitorService.CONCURRENT, String.valueOf(concurrent),
MonitorService.MAX_INPUT, String.valueOf(maxInput),
MonitorService.MAX_OUTPUT, String.valueOf(maxOutput),
MonitorService.MAX_ELAPSED, String.valueOf(maxElapsed),
MonitorService.MAX_CONCURRENT, String.valueOf(maxConcurrent),
DEFAULT_PROTOCOL, protocol
);
monitorService.collect(url);
long [] current;
long [] update = new long [LENGTH];
do {
current = reference.get();
if (current == null ) {
update[0 ] = 0 ;
update[1 ] = 0 ;
update[2 ] = 0 ;
update[3 ] = 0 ;
update[4 ] = 0 ;
update[5 ] = 0 ;
} else {
update[0 ] = current[0 ] - success;
update[1 ] = current[1 ] - failure;
update[2 ] = current[2 ] - input;
update[3 ] = current[3 ] - output;
update[4 ] = current[4 ] - elapsed;
update[5 ] = current[5 ] - concurrent;
}
} while (!reference.compareAndSet(current, update));
}
}
@Override
public void collect (URL url) {
int success = url.getParameter(MonitorService.SUCCESS, 0 );
int failure = url.getParameter(MonitorService.FAILURE, 0 );
int input = url.getParameter(MonitorService.INPUT, 0 );
int output = url.getParameter(MonitorService.OUTPUT, 0 );
int elapsed = url.getParameter(MonitorService.ELAPSED, 0 );
int concurrent = url.getParameter(MonitorService.CONCURRENT, 0 );
Statistics statistics = new Statistics (url);
AtomicReference<long []> reference = statisticsMap.computeIfAbsent(statistics, k -> new AtomicReference <>());
long [] current;
long [] update = new long [LENGTH];
do {
current = reference.get();
if (current == null ) {
update[0 ] = success;
update[1 ] = failure;
update[2 ] = input;
update[3 ] = output;
update[4 ] = elapsed;
update[5 ] = concurrent;
update[6 ] = input;
update[7 ] = output;
update[8 ] = elapsed;
update[9 ] = concurrent;
} else {
update[0 ] = current[0 ] + success;
update[1 ] = current[1 ] + failure;
update[2 ] = current[2 ] + input;
update[3 ] = current[3 ] + output;
update[4 ] = current[4 ] + elapsed;
update[5 ] = (current[5 ] + concurrent) / 2 ;
update[6 ] = current[6 ] > input ? current[6 ] : input;
update[7 ] = current[7 ] > output ? current[7 ] : output;
update[8 ] = current[8 ] > elapsed ? current[8 ] : elapsed;
update[9 ] = current[9 ] > concurrent ? current[9 ] : concurrent;
}
} while (!reference.compareAndSet(current, update));
}
@Override
public List<URL> lookup (URL query) {
return monitorService.lookup(query);
}
@Override
public URL getUrl () {
return monitorInvoker.getUrl();
}
@Override
public boolean isAvailable () {
return monitorInvoker.isAvailable();
}
@Override
public void destroy () {
try {
ExecutorUtil.cancelScheduledFuture(sendFuture);
} catch (Throwable t) {
logger.error("Unexpected error occur at cancel sender timer, cause: " + t.getMessage(), t);
}
monitorInvoker.destroy();
}
}
四. 过滤器统计监控数据
1. 结构图
2. Filter接口
invoke()方法 :执行过滤逻辑
onResponse()方法 :正常执行后执行相关逻辑
onError()方法 :发生异常后执行相关逻辑
@SPI
public interface Filter {
Result invoke (Invoker<?> invoker, Invocation invocation) throws RpcException;
interface Listener {
void onResponse (Result appResponse, Invoker<?> invoker, Invocation invocation) ;
void onError (Throwable t, Invoker<?> invoker, Invocation invocation) ;
}
}
3. MonitorFilter实现类
invoke()方法:并发计数
monitor.collect(statisticsURL):正常或异常执行后调用DubboMonitor.collect()收集数据
@Activate(group = {PROVIDER, CONSUMER})
public class MonitorFilter implements Filter , Filter.Listener {
private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
private static final String MONITOR_FILTER_START_TIME = "monitor_filter_start_time" ;
private final ConcurrentMap<String, AtomicInteger> concurrents = new ConcurrentHashMap <String, AtomicInteger>();
private MonitorFactory monitorFactory;
public void setMonitorFactory (MonitorFactory monitorFactory) {
this .monitorFactory = monitorFactory;
}
@Override
public Result invoke (Invoker<?> invoker, Invocation invocation) throws RpcException {
if (invoker.getUrl().hasParameter(MONITOR_KEY)) {
invocation.put(MONITOR_FILTER_START_TIME, System.currentTimeMillis());
getConcurrent(invoker, invocation).incrementAndGet();
}
return invoker.invoke(invocation);
}
private AtomicInteger getConcurrent (Invoker<?> invoker, Invocation invocation) {
String key = invoker.getInterface().getName() + "." + invocation.getMethodName();
return concurrents.computeIfAbsent(key, k -> new AtomicInteger ());
}
@Override
public void onResponse (Result result, Invoker<?> invoker, Invocation invocation) {
if (invoker.getUrl().hasParameter(MONITOR_KEY)) {
collect(invoker, invocation, result, RpcContext.getContext().getRemoteHost(), (long ) invocation.get(MONITOR_FILTER_START_TIME), false );
getConcurrent(invoker, invocation).decrementAndGet();
}
}
@Override
public void onError (Throwable t, Invoker<?> invoker, Invocation invocation) {
if (invoker.getUrl().hasParameter(MONITOR_KEY)) {
collect(invoker, invocation, null , RpcContext.getContext().getRemoteHost(), (long ) invocation.get(MONITOR_FILTER_START_TIME), true );
getConcurrent(invoker, invocation).decrementAndGet();
}
}
private void collect (Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
try {
URL monitorUrl = invoker.getUrl().getUrlParameter(MONITOR_KEY);
Monitor monitor = monitorFactory.getMonitor(monitorUrl);
if (monitor == null ) {
return ;
}
URL statisticsURL = createStatisticsUrl(invoker, invocation, result, remoteHost, start, error);
monitor.collect(statisticsURL);
} catch (Throwable t) {
logger.warn("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);
}
}
private URL createStatisticsUrl (Invoker<?> invoker, Invocation invocation, Result result, String remoteHost, long start, boolean error) {
long elapsed = System.currentTimeMillis() - start;
int concurrent = getConcurrent(invoker, invocation).get();
String application = invoker.getUrl().getParameter(APPLICATION_KEY);
String service = invoker.getInterface().getName();
String method = RpcUtils.getMethodName(invocation);
String group = invoker.getUrl().getParameter(GROUP_KEY);
String version = invoker.getUrl().getParameter(VERSION_KEY);
int localPort;
String remoteKey, remoteValue;
if (CONSUMER_SIDE.equals(invoker.getUrl().getParameter(SIDE_KEY))) {
localPort = 0 ;
remoteKey = MonitorService.PROVIDER;
remoteValue = invoker.getUrl().getAddress();
} else {
localPort = invoker.getUrl().getPort();
remoteKey = MonitorService.CONSUMER;
remoteValue = remoteHost;
}
String input = "" , output = "" ;
if (invocation.getAttachment(INPUT_KEY) != null ) {
input = invocation.getAttachment(INPUT_KEY);
}
if (result != null && result.getAttachment(OUTPUT_KEY) != null ) {
output = result.getAttachment(OUTPUT_KEY);
}
return new URL (COUNT_PROTOCOL, NetUtils.getLocalHost(), localPort, service + PATH_SEPARATOR +
method, MonitorService.APPLICATION, application, MonitorService.INTERFACE, service,
MonitorService.METHOD, method, remoteKey, remoteValue,
error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1" , MonitorService.ELAPSED,
String.valueOf(elapsed), MonitorService.CONCURRENT, String.valueOf(concurrent),
INPUT_KEY, input, OUTPUT_KEY, output, GROUP_KEY, group, VERSION_KEY, version);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2016-02-20 jstack_查看当前进程及所属线程执行情况
2016-02-20 MySql_安装及简单命令
2011-02-20 在Asp.net中动态添加css文件,js文件,控件