利用AppMetrics对Web进行监控教程

一、基础准备

1. 安装依赖

这里可以通过nuget或使用命令行进行安装,具体需要安装的类库如下(注意版本):

Install-Package App.Metrics.AspNetCore.Mvc -Version 2.0.0

由于我们需要兼容Prometheus进行监控,所以我们还需要安装对应的格式化库,具体如下:

Install-Package App.Metrics.Formatters.Prometheus -Version 2.0.0

以上就是需要的类库,接下来我们开始进行其他初始化部分。

2. 初始配置

为了保证其能够正常工作,我们需要根据不同的环境设定对应的appsettings.json文件从而让度量指标可以根据不同的环境进行输出,这里考虑到实际情况尚未存在不同的配置可能性故统一配置即可,打开appsettings.json输入下配置项:

{
"MetricsOptions": {
"DefaultContextLabel": "MetricsApplication",
"Enabled": true
},
"MetricsWebTrackingOptions": {
"ApdexTrackingEnabled": true,
"ApdexTSeconds": 0.3,
"IgnoredHttpStatusCodes": [ 404 ],
"IgnoreRoutesRegexPatterns": [],
"OAuth2TrackingEnabled": false
},
"MetricEndpointsOptions": {
"MetricsEndpointEnabled": true,
"MetricsTextEndpointEnabled": true,
"EnvironmentInfoEndpointEnabled": true
}
}

参数DefaultContextLabel可以设定为我们期望其他名称,这里建议采用项目的简写名称,保证项目之间不存在冲突即可。参数ApdexTSeconds用于设定应用的响应能力标准,其采用了当前流行的Apdex标准,这里使用者可以根据自身应用的实际情况调整对应的参数,其他相关参数建议默认即可。

3. 启用度量指标

因为我们的数据需要符合Promethues格式,所以后续教程我们会替换默认的格式采用符合的格式。首先我们需要Program.cs里输入以下内容:

        public static IWebHost BuildWebHost(string[] args)
{
Metrics = AppMetrics.CreateDefaultBuilder()
.OutputMetrics.AsPrometheusPlainText()
.OutputMetrics.AsPrometheusProtobuf()
.Build(); return WebHost.CreateDefaultBuilder(args)
.ConfigureMetrics(Metrics)
.UseMetrics(options =>
{
options.EndpointOptions = endpointsOptions =>
{
endpointsOptions.MetricsTextEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusTextOutputFormatter>().First();
endpointsOptions.MetricsEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusProtobufOutputFormatter>().First();
};
})
.UseStartup<Startup>()
.Build();
}

其中为了能够支持其他格式,我们需要手动实例化Metrics对象完成相关初始化然后将其注入到asp.net core中,其中相关格式的代码主要是由以下这几部分组成:

OutputMetrics.AsPrometheusPlainText()
OutputMetrics.AsPrometheusProtobuf() endpointsOptions.MetricsTextEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusTextOutputFormatter>().First();
endpointsOptions.MetricsEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusProtobufOutputFormatter>().First();

完成以上操作后,我们最后还需要进行其他配置,打开Startup.cs文件增加如下内容:

services.AddMvc().AddMetrics();

至此我们就完成了基本的初始化了,通过启动程序并访问localhost:5000/metrics-text即可查看最终的输出内容。

二、自定义指标

由于其内部已经默认提供了若干的指标,但是并不能符合实际业务的需求故以下将对常用的度量指标类型以及用法进行介绍,这里这里大家通过注入IMetrics接口对象即可访问,所以下面这部分代码不在阐述。

1. 仪表盘(Gauge)

最常见的类型,主要是用于直接反应当前的指标情况,比如我们常见的CPU和内存基本都是使用这种方式进行显示的,可以直观的看到当前的实际的状态情况。对于所有的指标我们都需要定义对应的Options,当然这可以完成携程静态变量供应用程序全局使用。

比如下面我们定义一个表示当前发生错误次数的指标:

GaugeOptions Errors = new GaugeOptions()
{
Name = "Errors"
};

完成指标的定义后,我们就可以在需要使用的地方进行指标数据的修改,比如下面我们将错误数量设置为10:

metrics.Measure.Gauge.SetValue(MyMetricsRegistry.Errors, 10);

这样我们就完成了指标的设定,但是有时候我们还想却分具体的Error是那个层面发起的,这个时候我们需要使用到Tag了,下面我们在设定值的同时设定指标,当然也可以在新建指标的时候通过Tags变量,并且通用于其他所有指标:

var tags = new MetricTags("fromt", "db");
metrics.Measure.Gauge.SetValue(MyMetricsRegistry.Errors, tags, 10);

至此我们就完成了一个基本的指标,下面我们继续其他类型指标。

2. 计数值(Counter)

对于HTTP类型的网站来说,存在非常多的数量需要统计记录,所以计数值此时就特别适合这类情况,比如我们需要统计请求数量就可以利用这类指标类型,下面我们就以请求数来定义这个指标:

var requestCounter = new CounterOptions()
{
Name = "httpRequest",
MeasurementUnit = Unit.Calls
};

以上我们定义了一个计数指标,其中我们可以看到我们这里使用了一个新变量MeasurementUnit主要是用于定义指标单位的,当然这个只是辅助信息会一同输出到结果,下面我们需要进行增加和减少,考虑到大多数情况都是减1和增1的情况:

metrics.Measure.Counter.Increment(requestCounter);

实际情况可能我们都是统计请求但是期望还能单独统计特定接口的请求,这个时候我们在原本调用方式基础上增加额外的参数:

metrics.Measure.Counter.Increment(requestCounter, "api");

如果嫌每次增长1比较慢,我们通过其函数的重载形式填写我们希望增长的具体值。

3. 计量值(Meter)

有点类似于计数值但是相比来说,它可以提供更加丰富的信息,比如每1、5、15分钟的增长率等,所以对于一些需要通过增长率观察的数据特别时候,这里我们以请求的反应状态码进行记录来体现其用途:

var httpStatusMeter = new MeterOptions()
{
Name = "Http Status",
MeasurementUnit = Unit.Calls
};

以上我们完成了一个指标的定义,下面我们开始使用其并且定义不同的状态的码的发生情况,具体如下:

metrics.Measure.Meter.Mark(httpStatusMeter, "200");
metrics.Measure.Meter.Mark(httpStatusMeter, "500");
metrics.Measure.Meter.Mark(httpStatusMeter, "401");

当然如果希望增加的数量自定控制也可以使用其提供的重载形式进行。

4. 柱状图(Histogram)

顾名思义,主要反应数据的分布情况,所以这里不在重复阐述,大家对于这种数据表现形式还是比较了解的,所以下面就直接以实际代码的实列进行介绍,便于大家的理解:

var postAndPutRequestSize = new HistogramOptions()
{
Name = "Web Request Post & Put Size",
MeasurementUnit = Unit.Bytes
};

以上我们定义一个体现Post和Put请求的数据尺寸的指标,下面我们利用随机数来进行数据的模拟对其进行数据填充,便于显示数据:

var rnd = new Random();

foreach (var i in Enumerable.Range(0, 50))
{
var t = rnd.Next(0, 10); metrics.Measure.Histogram.Update(postAndPutRequestSize, t);
}

5. 时间线(Timer)

对应指标的监控闭然少不了对于时间的记录,特别对于HTTP来说,直接影响到用户的体验就是响应时间,素以我们也需要时刻关于这类指标的变化情况及时做出反应,下面我们就以数据库的响应时间的情况作为指标进行监控:

TimerOptions DatabaseTimer = new TimerOptions()
{
Name = "Database Timer",
MeasurementUnit = Unit.Items,
DurationUnit = TimeUnit.Milliseconds,
RateUnit = TimeUnit.Milliseconds
};

上面我们通过特别的属性指定了改指标记录时间的单位,下面我们使用其指标进行数据的记录:

using(metrics.Measure.Timer.Time(DatabaseTimer))
{
//to do sonmething
}

我们可以看到为了方便的记录请求的时间,我们使用using进行涵括,并将需要记录耗时的请求操作放入其中,在请求完成操作后就可以正确的记录其需要的时间。

6. apdex

采用了一种标准的性能指标计算方式,用法类似与上述,这里仅仅列举用法:

ApdexOptions SampleApdex = new ApdexOptions
{
Name = "Sample Apdex"
}; using(metrics.Measure.Apdex.Track(SampleApdex))
{
Thread.Sleep(100);
}

三、高级指标

1. 平均响应

很多时候我们仅仅依靠一个指标很难完成一个实际的需求,是所以我们就需要将多个指标进行组合进行,比如我们期望得到请求次数,同时还有请求的总时间和平均响应时间等,为此我们可以特殊的指标将多个指标进行组合,具体操作如下:

var cacheHitRatioGauge = new GaugeOptions
{
Name = "Cache Gauge",
MeasurementUnit = Unit.Calls
}; var cacheHitsMeter = new MeterOptions
{
Name = "Cache Hits Meter",
MeasurementUnit = Unit.Calls
}; var databaseQueryTimer = new TimerOptions
{
Name = "Database Query Timer",
MeasurementUnit = Unit.Calls,
DurationUnit = TimeUnit.Milliseconds,
RateUnit = TimeUnit.Milliseconds
}; var cacheHits = metrics.Provider.Meter.Instance(cacheHitsMeter);
var calls = metrics.Provider.Timer.Instance(databaseQueryTimer); var cacheHit = new Random().Next(0, 2) == 0; using(calls.NewContext())
{
if (cacheHit)
{
cacheHits.Mark(5);
} Thread.Sleep(cacheHit ? 10 : 100);
} metrics.Measure.Gauge.SetValue(cacheHitRatioGauge, () => new HitRatioGauge(cacheHits, calls, m => m.OneMinuteRate));

四、利用Promethues和Grafana进行监控

1. 环境准备

这里需要使用到PrometheusGrafana,为了避免版本导致的区别这里提供了对应百度云的下载地址,大家可以自行进行下载。

Prometheus对应提取码为2b1r

Grafana对应提取码为mjym

完成以上下载后需要解压到对应文件夹下即可。

2. 配置服务

首先我们需要针对Prometheus进行配置,我们打开prometheus.yml文件新增基于AppMetrics的监控指标。

  - job_name: 'appweb'
scrape_interval: 5s
metrics_path: '/metrics-text'
static_configs:
- targets: ['localhost:5000']

完成之后我们可以先打开采集让其在后台持续采集,后面我们需要针对AppMetrics暴露的数据进行调整。

3. 应用指标输出

通过实际的测试发现基于2.0.0版本的Prometheus存在问题,因为指标类型被大写了,导致Prometheus无法正确读取,所以我们需要将源码复制出来进行操作,这里直接给出了对应的源码文件,

主要的工作就是将AsciiFormatter.cs中的HELPTYPE进行了小写而已,对应文件如下。

PS:考虑到很多基于2.0的所以这里保留了基于HTTP的文本实现方式发布了一个对应的版本库:

Install-Package Sino.Metrics.Formatters.Prometheus -Version 0.1.2
  • AsciiFormatter.cs
    internal static class AsciiFormatter
{
public static void Format(Stream destination, IEnumerable<MetricFamily> metrics)
{
var metricFamilys = metrics.ToArray();
using (var streamWriter = new StreamWriter(destination, Encoding.UTF8))
{
streamWriter.NewLine = "\n";
foreach (var metricFamily in metricFamilys)
{
WriteFamily(streamWriter, metricFamily);
}
}
} internal static string Format(IEnumerable<MetricFamily> metrics, NewLineFormat newLine)
{
var newLineChar = GetNewLineChar(newLine);
var metricFamilys = metrics.ToArray();
var s = new StringBuilder();
foreach (var metricFamily in metricFamilys)
{
s.Append(WriteFamily(metricFamily, newLineChar));
} return s.ToString();
} private static void WriteFamily(StreamWriter streamWriter, MetricFamily metricFamily)
{
streamWriter.WriteLine("# HELP {0} {1}", metricFamily.name, metricFamily.help.ToLower());
streamWriter.WriteLine("# TYPE {0} {1}", metricFamily.name, metricFamily.type.ToString().ToLower());
foreach (var metric in metricFamily.metric)
{
WriteMetric(streamWriter, metricFamily, metric);
}
} private static string WriteFamily(MetricFamily metricFamily, string newLine)
{
var s = new StringBuilder();
s.Append(string.Format("# HELP {0} {1}", metricFamily.name, metricFamily.help.ToLower()), newLine);
s.Append(string.Format("# TYPE {0} {1}", metricFamily.name, metricFamily.type.ToString().ToLower()), newLine);
foreach (var metric in metricFamily.metric)
{
s.Append(WriteMetric(metricFamily, metric, newLine), newLine);
} return s.ToString();
} private static void WriteMetric(StreamWriter streamWriter, MetricFamily family, Metric metric)
{
var familyName = family.name; if (metric.gauge != null)
{
streamWriter.WriteLine(SimpleValue(familyName, metric.gauge.value, metric.label));
}
else if (metric.counter != null)
{
streamWriter.WriteLine(SimpleValue(familyName, metric.counter.value, metric.label));
}
else if (metric.summary != null)
{
streamWriter.WriteLine(SimpleValue(familyName, metric.summary.sample_sum, metric.label, "_sum"));
streamWriter.WriteLine(SimpleValue(familyName, metric.summary.sample_count, metric.label, "_count")); foreach (var quantileValuePair in metric.summary.quantile)
{
var quantile = double.IsPositiveInfinity(quantileValuePair.quantile)
? "+Inf"
: quantileValuePair.quantile.ToString(CultureInfo.InvariantCulture);
streamWriter.WriteLine(
SimpleValue(
familyName,
quantileValuePair.value,
metric.label.Concat(new[] { new LabelPair { name = "quantile", value = quantile } })));
}
}
else if (metric.histogram != null)
{
streamWriter.WriteLine(SimpleValue(familyName, metric.histogram.sample_sum, metric.label, "_sum"));
streamWriter.WriteLine(SimpleValue(familyName, metric.histogram.sample_count, metric.label, "_count"));
foreach (var bucket in metric.histogram.bucket)
{
var value = double.IsPositiveInfinity(bucket.upper_bound) ? "+Inf" : bucket.upper_bound.ToString(CultureInfo.InvariantCulture);
streamWriter.WriteLine(
SimpleValue(
familyName,
bucket.cumulative_count,
metric.label.Concat(new[] { new LabelPair { name = "le", value = value } }),
"_bucket"));
}
}
else
{
// not supported
}
} private static string WriteMetric(MetricFamily family, Metric metric, string newLine)
{
var s = new StringBuilder();
var familyName = family.name; if (metric.gauge != null)
{
s.Append(SimpleValue(familyName, metric.gauge.value, metric.label), newLine);
}
else if (metric.counter != null)
{
s.Append(SimpleValue(familyName, metric.counter.value, metric.label), newLine);
}
else if (metric.summary != null)
{
s.Append(SimpleValue(familyName, metric.summary.sample_sum, metric.label, "_sum"), newLine);
s.Append(SimpleValue(familyName, metric.summary.sample_count, metric.label, "_count"), newLine); foreach (var quantileValuePair in metric.summary.quantile)
{
var quantile = double.IsPositiveInfinity(quantileValuePair.quantile)
? "+Inf"
: quantileValuePair.quantile.ToString(CultureInfo.InvariantCulture);
s.Append(
SimpleValue(
familyName,
quantileValuePair.value,
metric.label.Concat(new[] { new LabelPair { name = "quantile", value = quantile } })), newLine);
}
}
else if (metric.histogram != null)
{
s.Append(SimpleValue(familyName, metric.histogram.sample_sum, metric.label, "_sum"), newLine);
s.Append(SimpleValue(familyName, metric.histogram.sample_count, metric.label, "_count"), newLine);
foreach (var bucket in metric.histogram.bucket)
{
var value = double.IsPositiveInfinity(bucket.upper_bound) ? "+Inf" : bucket.upper_bound.ToString(CultureInfo.InvariantCulture);
s.Append(
SimpleValue(
familyName,
bucket.cumulative_count,
metric.label.Concat(new[] { new LabelPair { name = "le", value = value } }),
"_bucket"), newLine);
}
}
else
{
// not supported
} return s.ToString();
} private static string WithLabels(string familyName, IEnumerable<LabelPair> labels)
{
var labelPairs = labels as LabelPair[] ?? labels.ToArray(); if (labelPairs.Length == 0)
{
return familyName;
} return string.Format("{0}{{{1}}}", familyName, string.Join(",", labelPairs.Select(l => string.Format("{0}=\"{1}\"", l.name, l.value))));
} private static string SimpleValue(string family, double value, IEnumerable<LabelPair> labels, string namePostfix = null)
{
return string.Format("{0} {1}", WithLabels(family + (namePostfix ?? string.Empty), labels), value.ToString(CultureInfo.InvariantCulture));
} private static string GetNewLineChar(NewLineFormat newLine)
{
switch (newLine)
{
case NewLineFormat.Auto:
return Environment.NewLine;
case NewLineFormat.Windows:
return "\r\n";
case NewLineFormat.Unix:
case NewLineFormat.Default:
return "\n";
default:
throw new ArgumentOutOfRangeException(nameof(newLine), newLine, null);
}
} private static void Append(this StringBuilder sb, string line, string newLineChar)
{
sb.Append(line + newLineChar);
}
}
  • MetricsPrometheusTextOutputFormatter.cs
    public class MetricsPrometheusTextOutputFormatter : IMetricsOutputFormatter
{
private readonly MetricsPrometheusOptions _options; public MetricsPrometheusTextOutputFormatter()
{
_options = new MetricsPrometheusOptions();
} public MetricsPrometheusTextOutputFormatter(MetricsPrometheusOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); } /// <inheritdoc/>
public MetricsMediaTypeValue MediaType => new MetricsMediaTypeValue("text", "vnd.appmetrics.metrics.prometheus", "v1", "plain"); /// <inheritdoc/>
public async Task WriteAsync(
Stream output,
MetricsDataValueSource metricsData,
CancellationToken cancellationToken = default(CancellationToken))
{
if (output == null)
{
throw new ArgumentNullException(nameof(output));
} using (var streamWriter = new StreamWriter(output))
{
await streamWriter.WriteAsync(AsciiFormatter.Format(metricsData.GetPrometheusMetricsSnapshot(_options.MetricNameFormatter), _options.NewLineFormat));
}
}
}

新建好以上两个文件后我们接着需要修改Program.cs文件,具体内容如下:

        public static IWebHost BuildWebHost(string[] args)
{
Metrics = AppMetrics.CreateDefaultBuilder()
.OutputMetrics.AsPrometheusPlainText()
.Build(); return WebHost.CreateDefaultBuilder(args)
.ConfigureMetrics(Metrics)
.UseMetrics(options =>
{
options.EndpointOptions = endpointsOptions =>
{
endpointsOptions.MetricsTextEndpointOutputFormatter = new MetricsPrometheusTextOutputFormatter();
};
})
.UseStartup<Startup>()
.Build();
}

完成以上操作后我们可以启用应用,此时可以看到不断用请求到/metrics-text表示已经开始采集指标了。

4. 指标可视化

此时我们打开Grafana文件夹,通过其中的bin目录下的grafana-server.exe启动服务,然后访问localhost:3000利用初始账户密码进行登录(admin/admin)。

进入后添加Prometheus数据源。由于AppMetrics已经提供了对应的看板所以我们可以通过ID2204直接导入,并选择正确的数据源就可以看到最终的效果了。

利用AppMetrics对Web进行监控教程的更多相关文章

  1. Sentry Web 前端监控 - 最佳实践(官方教程)

    系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...

  2. zabbix利用自带的模板监控mysql数据库

    zabbix利用自带的模板监控mysql数据库 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 有些东西你不会的时候觉得它特别难,但是当你去做的时候就发现如此的简单~zabbix功能 ...

  3. RESTful Web 服务:教程

    RESTful Web 服务:教程   随着 REST 成为大多数 Web 和 Mobile 应用的默认选择,势必要对它的基本原理有所了解. 在它提出十多年后的今天,REST 已经成为最重要的 Web ...

  4. Web攻防系列教程之文件上传攻防解析(转载)

    Web攻防系列教程之文件上传攻防解析: 文件上传是WEB应用很常见的一种功能,本身是一项正常的业务需求,不存在什么问题.但如果在上传时没有对文件进行正确处理,则很可能会发生安全问题.本文将对文件上传的 ...

  5. 全面解读Python Web开发框架Django,利用Django构建web应用及其部署

    全面解读Python Web开发框架Django Django是一个开源的Web应用框架,由Python写成.采用MVC的软件设计模式,主要目标是使得开发复杂的.数据库驱动的网站变得简单.Django ...

  6. 基于Web的监控系统的开发进行分布式和现代生产(外文翻译)

    摘要 近年来,Web技术发展迅速.尤其是网络浏览器增强了其功能因为JavaScript,CSS3和HTML5的改进.因此,功能越来越丰富的基于Web的软件解决方案功能范围可用.通过使用响应式网页设计( ...

  7. 要web开发精品教程吗?免费无广告一百期连讲的那种-逐浪CMS前端开发100期入门教程全面开放

    要web开发精品教程吗?免费无广告一百期连讲的那种-逐浪CMS前端开发100期入门教程全面开放 大师主讲 经验难得 由逐浪CMS首席架构师发哥老师,亲自主理讲解. 历时一年精心打造, 汇聚了互联网诞生 ...

  8. 利用Swashbuckle生成Web API Help Pages

    利用Swashbuckle生成Web API Help Pages 这系列文章是参考了.NET Core文档和源码,可能有人要问,直接看官方的英文文档不就可以了吗,为什么还要写这些文章呢? 原因如下: ...

  9. [Python] 利用Django进行Web开发系列(一)

    1 写在前面 在没有接触互联网这个行业的时候,我就一直很好奇网站是怎么构建的.现在虽然从事互联网相关的工作,但是也一直没有接触过Web开发之类的东西,但是兴趣终归还是要有的,而且是需要自己动手去实践的 ...

随机推荐

  1. 《Java开发学习大纲文档》V8.0

    <Java开发学习大纲文档>V8.0 第八版是以实战作为核心,同时也包含前面所有版本的精华部分,第八版加入的部分有云开发(阿里云OSS存储.(github)gitlab+docker网站自 ...

  2. AcWing 247. 亚特兰蒂斯 | 扫描线

    传送门 题目描述 有几个古希腊书籍中包含了对传说中的亚特兰蒂斯岛的描述. 其中一些甚至包括岛屿部分地图. 但不幸的是,这些地图描述了亚特兰蒂斯的不同区域. 您的朋友Bill必须知道地图的总面积. 你自 ...

  3. Redhat6 RPM 软件管理常用命令汇总

    软件的安装时操作系统管理的基础,与Windows不同,Linux的软件管理有很多种方式,Redhat的最常用的是RPM方式,安装集成在光盘中的RPM包.这种方式比Windows平台的软件管理更加便捷( ...

  4. Java项目之客户信息管理软件

    模拟实现基于文本界面的客户信息管理软件,该软件能够实现对客户对象的插入. 修改和删除(用数组实现),并能够打印客户明细表. 项目采用分级菜单方式.主菜单如下: “添加客户”的界面及操作过程如下所示: ...

  5. JAVA大数类—基础操作(加减乘除、取模、四舍五入、设置保留位数)

    当基础数据类型长度无法满足需求时可以使用大数类 构造方法接受字符串为参数 BigInteger bInt = new BigInteger("123123"); BigDecima ...

  6. dp - 逆序数序列

    对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的 数列,可以很容易求出有多少个逆序对数.那么逆序对数为k的这样自然数数 ...

  7. 本地Git绑定Gitee仓库

    前言 Window的小伙伴如果还没在本地配好Git环境可以参考:https://www.cnblogs.com/poloyy/p/12185132.html 创建Gitee仓库 Gitee绑定本地Gi ...

  8. 如何使用F4的IRAM2内存

    在使用KEIL做F4的项目的时候发现RAM区有片上IRAM2选项,查了datesheet后发现这块是CCM内存区 CCM内存是在地址0x1000000映射的64KB块,只提供CPU通过数据D总线进行访 ...

  9. 关于neo4j初入门(2)

    DELETE删除 删除节点及相关节点和关系. DELETE <node-name-list> DELETE <node1-name>,<node2-name>,&l ...

  10. Java架构师线上问题排查,这些命令程序员一定用得到!

    Java架构师线上问题排查,这些命令程序员一定用得到! 线上问题排查,以下场景,你遇到过吗? 一.了解机器连接数情况 问题:1.2.3.4的sshd的监听端口是22,如何统计1.2.3.4的sshd服 ...