1, the principle of
The dubbo architecture is as follows:
By reading the Dubbo source code, all RPC method calls are intercepted by MonitorFilter,
MonitorFilter.invoke()
public Result invoke(Invoker<? > invoker, Invocation invocation) throws RpcException {if (invoker.getUrl().hasParameter("monitor")) {
RpcContext context = RpcContext.getContext();
long start = System.currentTimeMillis();
this.getConcurrent(invoker, invocation).incrementAndGet();
Result var7;
try {
Result result = invoker.invoke(invocation);
this.collect(invoker, invocation, result, context, start, false);
var7 = result;
} catch (RpcException var11) {
this.collect(invoker, invocation, (Result)null, context, start, true);
throw var11;
} finally {
this.getConcurrent(invoker, invocation).decrementAndGet();
}
return var7;
} else {
returninvoker.invoke(invocation); }}Copy the code
For services configured with monitoring, basic statistics for some methods are collected.
MonitorFilter.collect()
private void collect(Invoker<? > invoker, Invocation invocation, Result result, RpcContext context, long start, boolean error) { try { long elapsed = System.currentTimeMillis() - start; int concurrent = this.getConcurrent(invoker, invocation).get(); String application = invoker.getUrl().getParameter("application");
String service = invoker.getInterface().getName();
String method = RpcUtils.getMethodName(invocation);
URL url = invoker.getUrl().getUrlParameter("monitor");
Monitor monitor = this.monitorFactory.getMonitor(url);
int localPort;
String remoteKey;
String remoteValue;
if ("consumer".equals(invoker.getUrl().getParameter("side"))) {
context = RpcContext.getContext();
localPort = 0;
remoteKey = "provider";
remoteValue = invoker.getUrl().getAddress();
} else {
localPort = invoker.getUrl().getPort();
remoteKey = "consumer";
remoteValue = context.getRemoteHost();
}
String input = "";
String output = "";
if (invocation.getAttachment("input") != null) {
input = invocation.getAttachment("input");
}
if(result ! = null && result.getAttachment("output") != null) {
output = result.getAttachment("output");
}
monitor.collect(new URL("count", NetUtils.getLocalHost(), localPort, service + "/" + method, new String[]{"application", application, "interface", service, "method", method, remoteKey, remoteValue, error ? "failure" : "success"."1"."elapsed", String.valueOf(elapsed), "concurrent", String.valueOf(concurrent), "input", input, "output", output}));
} catch (Throwable var21) {
logger.error("Failed to monitor count service " + invoker.getUrl() + ", cause: "+ var21.getMessage(), var21); }}Copy the code
DubboMonitor collects simple statistics on the collected data, such as the number of successes, failures, and call time. After collecting statistics, the data is stored locally.
DubboMonitor.collect()
public void collect(URL url) {
int success = url.getParameter("success", 0);
int failure = url.getParameter("failure", 0);
int input = url.getParameter("input", 0);
int output = url.getParameter("output", 0);
int elapsed = url.getParameter("elapsed", 0);
int concurrent = url.getParameter("concurrent", 0);
Statistics statistics = new Statistics(url);
AtomicReference<long[]> reference = (AtomicReference)this.statisticsMap.get(statistics);
if (reference == null) {
this.statisticsMap.putIfAbsent(statistics, new AtomicReference());
reference = (AtomicReference)this.statisticsMap.get(statistics);
}
long[] update = new long[10];
long[] current;
do {
current = (long[])reference.get();
if (current == null) {
update[0] = (long)success;
update[1] = (long)failure;
update[2] = (long)input;
update[3] = (long)output;
update[4] = (long)elapsed;
update[5] = (long)concurrent;
update[6] = (long)input;
update[7] = (long)output;
update[8] = (long)elapsed;
update[9] = (long)concurrent;
} else{ update[0] = current[0] + (long)success; update[1] = current[1] + (long)failure; update[2] = current[2] + (long)input; update[3] = current[3] + (long)output; update[4] = current[4] + (long)elapsed; update[5] = (current[5] + (long)concurrent) / 2L; update[6] = current[6] > (long)input ? current[6] : (long)input; update[7] = current[7] > (long)output ? current[7] : (long)output; update[8] = current[8] > (long)elapsed ? current[8] : (long)elapsed; update[9] = current[9] > (long)concurrent ? current[9] : (long)concurrent; }}while(! reference.compareAndSet(current, update)); }Copy the code
DubboMonitor has asynchronous threads that periodically (every minute by default) send collected data to the remote monitoring service.
public DubboMonitor(Invoker<MonitorService> monitorInvoker, MonitorService monitorService) {
this.monitorInvoker = monitorInvoker;
this.monitorService = monitorService;
this.monitorInterval = (long)monitorInvoker.getUrl().getPositiveParameter("interval", 60000);
this.sendFuture = this.scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
public void run() {
try {
DubboMonitor.this.send();
} catch (Throwable var2) {
DubboMonitor.logger.error("Unexpected error occur at send statistic, cause: " + var2.getMessage(), var2);
}
}
}, this.monitorInterval, this.monitorInterval, TimeUnit.MILLISECONDS);
}
Copy the code
Call the remote MonitorService.collect method and set the locally cached data to zero.
DubboMonitor.send()
public void send() {
if (logger.isInfoEnabled()) {
logger.info("Send statistics to monitor " + this.getUrl());
}
String timestamp = String.valueOf(System.currentTimeMillis());
Iterator i$ = this.statisticsMap.entrySet().iterator();
while(i$.hasNext()) {
Entry<Statistics, AtomicReference<long[]>> entry = (Entry)i$.next();
Statistics statistics = (Statistics)entry.getKey();
AtomicReference<long[]> reference = (AtomicReference)entry.getValue();
long[] numbers = (long[])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];
URL url = statistics.getUrl().addParameters(new String[]{"timestamp", timestamp, "success", String.valueOf(success), "failure", String.valueOf(failure), "input", String.valueOf(input), "output", String.valueOf(output), "elapsed", String.valueOf(elapsed), "concurrent", String.valueOf(concurrent), "max.input", String.valueOf(maxInput), "max.output", String.valueOf(maxOutput), "max.elapsed", String.valueOf(maxElapsed), "max.concurrent", String.valueOf(maxConcurrent)});
this.monitorService.collect(url);
long[] update = new long[10];
while(true) {
long[] current = (long[])reference.get();
if (current == null) {
update[0] = 0L;
update[1] = 0L;
update[2] = 0L;
update[3] = 0L;
update[4] = 0L;
update[5] = 0L;
} 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;
}
if (reference.compareAndSet(current, update)) {
break; }}}}Copy the code
Dubbo monitors the main open source projects, all implement MonitorService interface for monitoring, the differences are data storage, report statistical logic, the basic principles are similar.
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";
String OUTPUT = "output";
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 var1);
List<URL> lookup(URL var1);
}
Copy the code
2. Monitor the selection
Mainstream DuBBo monitoring mainly includes:
- dubbo-monitor
- dubbo-d-monitor
- dubbokeeper
- dubbo-monitor-simple
Here’s a simple comparison:
plan | Support version | Basis function | Open source authors | Community activity | Data is stored | Maintenance costs |
---|---|---|---|---|---|---|
dubbo-monitor | Based on DubBox, dubbo is also theoretically supported | Generally,QPS, RT, service status, etc., lack of report function | Korea is the clothes | 513 stars, submitted two years ago | Mysql, directing | Non-intrusive, need to periodically clean historical data |
dubbo-d-monitor | dubbo | Generally, just some basic data | personal | 189 stars. Filed a year ago | Mysql, redis(no further maintenance) | Non-intrusive, need to periodically clean historical data |
dubbokeeper | dubbo | Rich, in addition to basic indicator data, top200 data report, but also provides similar dubo-admin functions (traffic limiting, timeout setting, consumer client setting, fault tolerance, etc.), while supporting ZK node visualization | Personal organization | 989 stars. Submission within a month | Mysql, mongodb, Lucene | Non-intrusive, need to periodically clean up the history |
dubbo-monitor-simple | dubbo | humble | Dubbo official | 330 stars, due within a month | File storage | Non-invasive, but current online use found large amounts of data often hang |
Dubbokeeper >dubbo-monitor> dubbod-monitor. Therefore, DubboKeeper is selected as the dubbo service monitoring solution.
3, deployment,
We use mongodb storage solution and single-node deployment.
Environment: JDK1.8 or higher (low version not tested), install Tomcat, install ZooKeeper and start, install mongodb
1, get the source github.com/dubboclub/d…
Dubbokeeper-master = dubbokeeper-master = dubbokeeper-master = dubbo-mongodb.properties = dubbokeeper-master = dubbokeeper-master = dubbokeeper-master = dubbo-mongodb.properties = dubbokeeper-master = dubbokeeper-master = dubbokeeper-master = dubbo-mongodb.properties
Run the \dubbokeeper-master\install-mongodb. Sh command to generate a target directory, which contains the following three folders and a compressed package
archive-tmp
mongodb-dubbokeeper-server
mongodb-dubbokeeper-ui
mongodb-dubbokeeper-server.tar.gz
Copy the code
3. Run the mongodb-dubbokeeper-server/bin/start-mongodb.
4. Copy the war package in mongodb- dubbokeeper-UI to the Tomcat webapps directory and start Tomcat.
5, and finally, open a browser, type http://localhost:8080/dubbokeeper-ui-1.0.1.
In the business code, you only need to configure the Dubbo monitoring connection to the registry to complete the monitoring data collection.
<dubbo:monitor protocol="registry"/>
Copy the code
Main configuration information:
dubbo.application.name=mongodb-monitor
dubbo.application.owner=bieber
dubbo.registry.address=zookeeper://*.*.*.*:2181?backup=*.*.*.*:2181,*.*.*.*:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20884
dubbo.protocol.dubbo.payload=20971520
Dubbo Data collection period in milliseconds
monitor.collect.interval=60000
#use netty4
dubbo.provider.transporter=netty4
Dubbokeeper writes mongodb cycles in seconds
monitor.write.interval=60
# mongdb configuration
dubbo.monitor.mongodb.url=localhost
dubbo.monitor.mongodb.port=27017
dubbo.monitor.mongodb.dbname=dubbokeeper
dubbo.monitor.mongodb.username=
dubbo.monitor.mongodb.password=
dubbo.monitor.mongodb.storage.timeout=60000
Copy the code
4. Main functions
On the home page, you can see the overall application information (distinguishing application providers and consumers), the number of services, node deployment information and dependency diagram, etc.
Admin provides most of the functionality of all native Dubo-Admin.
ZooPeeper displays zooKeeper node information
Monitor displays dubbo monitoring information
Application overview information can be filtered by time:
Detailed application information, including interface time, concurrency, failure, and success:
Method level Overview and details:
5, encountered pit
The default value of monitor.write. Interval is set to 6000. The unit of source code discovery is seconds.
2. Dubbokeeper does not index Collections by default. It will be very slow to open collections if there is a large amount of data.
import pymongo
from pymongo import MongoClient
import time
import datetime
import sys
import os
client = MongoClient('127.0.0.1', 27017)
db = client['dubbokeeper']
collectionlist = db.collection_names()
for collection in collectionlist:
ifcollection! ='application':
db[collection].ensure_index([("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("serviceInterface",pymongo.DESCENDING)])
db[collection].ensure_index([("method",pymongo.DESCENDING)])
db[collection].ensure_index([("serviceInterface",pymongo.DESCENDING),("method",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("serviceInterface",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("concurrent",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("elapsed",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("failureCount",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("successCount",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("serviceInterface",pymongo.DESCENDING),("elapsed",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("serviceInterface",pymongo.DESCENDING),("concurrent",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("serviceInterface",pymongo.DESCENDING),("failureCount",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
db[collection].ensure_index([("serviceInterface",pymongo.DESCENDING),("successCount",pymongo.DESCENDING),("timestamp",pymongo.DESCENDING)])
print 'success'
Copy the code
3. Generally, historical data need not be saved for a long time. At present, we keep online data for 2 weeks and provide the following script to delete data regularly.
import pymongo
from pymongo import MongoClient
import time
import datetime
import sys
import os
day=int(sys.argv[1])
print day
timestamp = time.time()*1000-1000*24*3600*day
print timestamp
client = MongoClient('127.0.0.1', 27017)
db = client['dubbokeeper']
collectionlist = db.collection_names()
for collection in collectionlist:
ifcollection! ='application':
db[collection].remove({"timestamp": {"$lt": timestamp}})
print 'clean mongodb data success'
Copy the code
Clear 15 days’ data periodically every day
0 3 * * * python /home/monitor/shell/clean-mongodb.py 15
Copy the code
4, mongodb cache eats memory, it is best to configure more than 8GB server, or a large amount of cluster deployment can be considered
5. Dubbokeeper-ui native interaction is a bit clunky, with some pages iterating through all application data, which is inefficient. If there are too many applications, they may not be opened due to timeout. The server team has simply optimized the interaction and can only view one application or one interface at a time. If you have any requirements, you can leave a message, and we will open source it later.