Springboot Actuator之十一:actuator PublicMetrics
前言
接下来的几篇文章我们来分析一下spring-boot-actuator 中在org.springframework.boot.actuate.metrics中的代码,如图:
这里的代码不仅多,而且还比较复杂(类与类之间的关联关系).我们的策略是一点一点的蚕食,本文就先来分析PublicMetrics的实现,关于这部分的类图如下:
本文只分析PublicMetrics, SystemPublicMetrics, TomcatPublicMetrics, DataSourcePublicMetrics.其他的实现–>CachePublicMetrics,MetricReaderPublicMetrics,RichGaugeReaderPublicMetrics 我们后续的文章进行讲解.
解析
PublicMetrics
PublicMetrics,暴露指定的Metric通过MetricsEndpoint的接口.实现类应该小心metrics–> 它们提供了一个唯一的名字在application context中,但是它们不能在jvm,分布式环境时唯一的
该类只声明了1个方法,代码如下:
// 返回表示当前的状态的indication 通过metrics
Collection<Metric<?>> metrics();
1
2
这里有必要说明一下Metric,该类是1个持有系统测量值的不变类(1个被命名的数值和事件戳的类) 比如:测量1个服务器的活跃链接数或者是测量会议室的温度.该类是1个泛型类,其泛型参数T–>测量值的类型.其字段,构造器如下:
private final String name;
private final T value;
private final Date timestamp;
1
2
3
4
5
6
7
SystemPublicMetrics
SystemPublicMetrics–>提供各种与系统相关的度量的PublicMetrics实现,该类实现了PublicMetrics,Ordered.实现Ordered的接口的目的是在有多个PublicMetrics的集合中进行排序.,其实现如下:
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 10;
}
1
2
3
字段,构造器如下:
// 启动时间(指的是SystemPublicMetrics初始化的时间)
private long timestamp;
public SystemPublicMetrics() {
this.timestamp = System.currentTimeMillis();
}
1
2
3
4
5
6
metrics,实现如下:
public Collection<Metric<?>> metrics() {
Collection<Metric<?>> result = new LinkedHashSet<Metric<?>>();
// 1. 添加基本的统计
addBasicMetrics(result);
// 2. 添加uptime,负载等统计
addManagementMetrics(result);
return result;
}
1
2
3
4
5
6
7
8
添加基本的统计.代码如下:
protected void addBasicMetrics(Collection<Metric<?>> result) {
// NOTE: ManagementFactory must not be used here since it fails on GAE
Runtime runtime = Runtime.getRuntime();
// 1. 添加内存使用统计,name=mem,value = 总内存+堆外内存使用量
result.add(newMemoryMetric("mem",
runtime.totalMemory() + getTotalNonHeapMemoryIfPossible()));
// 2. 添加可用统计,name=mem.free,value = 可用内存
result.add(newMemoryMetric("mem.free", runtime.freeMemory()));
// 3. 添加处理器核心数统计,name=processors,value = 处理器核心数
result.add(new Metric<Integer>("processors", runtime.availableProcessors()));
// 4. 添加SystemPublicMetrics 运行时间统计,name=instance.uptime,value = 当前时间-启动时间
result.add(new Metric<Long>("instance.uptime",
System.currentTimeMillis() - this.timestamp));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
添加内存使用统计,name=mem,value = 总内存+堆外内存使用量,其中堆外内存使用量代码如下:
private long getTotalNonHeapMemoryIfPossible() {
try {
return ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed();
}
catch (Throwable ex) {
return 0;
}
}
1
2
3
4
5
6
7
8
添加可用统计,name=mem.free,value = 可用内存
添加处理器核心数统计,name=processors,value = 处理器核心数
添加SystemPublicMetrics 运行时间统计,name=instance.uptime,value = 当前时间-启动时间
添加uptime,负载等统计.代码如下:
private void addManagementMetrics(Collection<Metric<?>> result) {
try {
// Add JVM up time in ms
// 1. 添加jvm启动时间,name=uptime,value = 启动时间,单位ms
result.add(new Metric<Long>("uptime",
ManagementFactory.getRuntimeMXBean().getUptime()));
// 2. 添加系统负载,name=systemload.average,value = 启动负载
result.add(new Metric<Double>("systemload.average",
ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage()));
// 3. 添加jvm的监控统计
addHeapMetrics(result);
// 4. 添加堆外内存的统计
addNonHeapMetrics(result);
// 5. 添加线程的统计
addThreadMetrics(result);
// 6. 添加类加载相关的统计
addClassLoadingMetrics(result);
// 7. 添加垃圾回收的统计
addGarbageCollectionMetrics(result);
}
catch (NoClassDefFoundError ex) {
// Expected on Google App Engine
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
添加jvm启动时间,name=uptime,value = 启动时间,单位ms
添加系统负载,name=systemload.average,value = 启动负载
添加jvm的监控统计,代码如下:
protected void addHeapMetrics(Collection<Metric<?>> result) {
MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
.getHeapMemoryUsage();
// 1. 获得所提交的字节内存量-->这个内存量是保证java虚拟机使用的
result.add(newMemoryMetric("heap.committed", memoryUsage.getCommitted()));
// 2. 获得jvm的初始化内存数,单位:字节.如果初始内存大小未定义,则此方法返回-1
result.add(newMemoryMetric("heap.init", memoryUsage.getInit()));
// 3. 获得内存的使用量
result.add(newMemoryMetric("heap.used", memoryUsage.getUsed()));
// 4. 获得内存的最大值,返回-1,如果为指定
result.add(newMemoryMetric("heap", memoryUsage.getMax()));
}
1
2
3
4
5
6
7
8
9
10
11
12
获得所提交的字节内存量–>这个内存量是保证java虚拟机使用的
获得jvm的初始化内存数,单位:字节.如果初始内存大小未定义,则此方法返回-1
获得内存的使用量
获得内存的最大值,返回-1,如果未指定
添加堆外内存的统计,代码如下:
private void addNonHeapMetrics(Collection<Metric<?>> result) {
MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
.getNonHeapMemoryUsage();
result.add(newMemoryMetric("nonheap.committed", memoryUsage.getCommitted()));
result.add(newMemoryMetric("nonheap.init", memoryUsage.getInit()));
result.add(newMemoryMetric("nonheap.used", memoryUsage.getUsed()));
result.add(newMemoryMetric("nonheap", memoryUsage.getMax()));
}
1
2
3
4
5
6
7
8
获得所提交的字节内存量–>这个内存量是保证java虚拟机使用的
获得jvm的初始化内存数,单位:字节.如果初始内存大小未定义,则此方法返回-1
获得内存的使用量
获得内存的最大值,返回-1,如果未指定
添加线程的统计,代码如下:
protected void addThreadMetrics(Collection<Metric<?>> result) {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
// 1. 获得jvm启动以来或者统计重置以来的最大值
result.add(new Metric<Long>("threads.peak",
(long) threadMxBean.getPeakThreadCount()));
// 2. 获得daemon线程的数量
result.add(new Metric<Long>("threads.daemon",
(long) threadMxBean.getDaemonThreadCount()));
// 3. 获得jvm启动以来被创建并且启动的线程数
result.add(new Metric<Long>("threads.totalStarted",
threadMxBean.getTotalStartedThreadCount()));
// 4. 获得当前存活的线程数包括daemon,非daemon的
result.add(new Metric<Long>("threads", (long) threadMxBean.getThreadCount()));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
获得jvm启动以来或者统计重置以来的最大值
获得daemon线程的数量
获得jvm启动以来被创建并且启动的线程数
获得当前存活的线程数包括daemon,非daemon的
添加类加载相关的统计,代码如下:
protected void addClassLoadingMetrics(Collection<Metric<?>> result) {
ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean();
// 1. 获得jvm目前加载的class数量
result.add(new Metric<Long>("classes",
(long) classLoadingMxBean.getLoadedClassCount()));
// 2.获得jvm启动以来加载class的所有数量
result.add(new Metric<Long>("classes.loaded",
classLoadingMxBean.getTotalLoadedClassCount()));
// 3. 获得jvm卸载class的数量
result.add(new Metric<Long>("classes.unloaded",
classLoadingMxBean.getUnloadedClassCount()));
}
1
2
3
4
5
6
7
8
9
10
11
12
获得jvm目前加载的class数量
获得jvm启动以来加载class的所有数量
获得jvm卸载class的数量
添加垃圾回收的统计,代码如下:
protected void addGarbageCollectionMetrics(Collection<Metric<?>> result) {
// 1. 获得GarbageCollectorMXBean
List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory
.getGarbageCollectorMXBeans();
// 2.遍历之:
for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) {
String name = beautifyGcName(garbageCollectorMXBean.getName());
// 2.1. 获得gc的次数
result.add(new Metric<Long>("gc." + name + ".count",
garbageCollectorMXBean.getCollectionCount()));
// 2.2. 获得gc的时间
result.add(new Metric<Long>("gc." + name + ".time",
garbageCollectorMXBean.getCollectionTime()));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
获得GarbageCollectorMXBean
遍历之:
获得gc的次数
获得gc的时间
自动装配:
在PublicMetricsAutoConfiguration中进行自动装配.代码如下:
@Bean
public SystemPublicMetrics systemPublicMetrics() {
return new SystemPublicMetrics();
}
1
2
3
4
@Bean–> 注册1个id为systemPublicMetrics,类型为SystemPublicMetrics的bean
TomcatPublicMetrics
TomcatPublicMetrics–>提供tomcat的数据统计的PublicMetrics的实现.该类实现了PublicMetrics, ApplicationContextAware接口.
metrics 代码如下:
public Collection<Metric<?>> metrics() {
// 1. 如果applicationContext 是EmbeddedWebApplicationContext的实例,则进行后续处理,否则,返回空集合
if (this.applicationContext instanceof EmbeddedWebApplicationContext) {
// 2. 获得Manager
Manager manager = getManager(
(EmbeddedWebApplicationContext) this.applicationContext);
if (manager != null) {
// 3. 如果Manager 不等于null,则调用metrics 进行收集统计数据
return metrics(manager);
}
}
return Collections.emptySet();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
如果applicationContext 是EmbeddedWebApplicationContext的实例,则进行后续处理,否则,返回空集合
获得Manager,代码如下:
private Manager getManager(EmbeddedWebApplicationContext applicationContext) {
// 1. 获得内嵌tomcat的实例,如果不是TomcatEmbeddedServletContainer的实例,则返回null
EmbeddedServletContainer embeddedServletContainer = applicationContext
.getEmbeddedServletContainer();
if (embeddedServletContainer instanceof TomcatEmbeddedServletContainer) {
// 2. 否则,获得tomcat中Context所对应的Manager
return getManager((TomcatEmbeddedServletContainer) embeddedServletContainer);
}
return null;
}
1
2
3
4
5
6
7
8
9
10
获得内嵌tomcat的实例,如果不是TomcatEmbeddedServletContainer的实例,则返回null
否则,获得tomcat中Context所对应的Manager,代码如下:
private Manager getManager(TomcatEmbeddedServletContainer servletContainer) {
for (Container container : servletContainer.getTomcat().getHost()
.findChildren()) {
if (container instanceof Context) {
return ((Context) container).getManager();
}
}
return null;
}
1
2
3
4
5
6
7
8
9
通过遍历host中的Container,如果其是Context的子类,则直接获得其对应的Manager,否则,返回null.
如果Manager 不等于null,则调用metrics 进行收集统计数据.代码如下:
private Collection<Metric<?>> metrics(Manager manager) {
List<Metric<?>> metrics = new ArrayList<Metric<?>>(2);
// 1. 如果Manager 是ManagerBase的实例,则添加 tomcat的session最大数量,-1 -->没有限制
if (manager instanceof ManagerBase) {
addMetric(metrics, "httpsessions.max",
((ManagerBase) manager).getMaxActiveSessions());
}
// 2. 添加当前激活的session数量
addMetric(metrics, "httpsessions.active", manager.getActiveSessions());
return metrics;
}
1
2
3
4
5
6
7
8
9
10
11
12
如果Manager 是ManagerBase的实例,则添加 tomcat的session最大数量,-1 –>没有限制
添加当前激活的session数量
addMetric 实现如下:
private void addMetric(List<Metric<?>> metrics, String name, Integer value) {
metrics.add(new Metric<Integer>(name, value));
}
1
2
3
自动装配:
声明在TomcatMetricsConfiguration中,代码如下:
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnWebApplication
static class TomcatMetricsConfiguration {
@Bean
@ConditionalOnMissingBean
public TomcatPublicMetrics tomcatPublicMetrics() {
return new TomcatPublicMetrics();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
由于TomcatMetricsConfiguration上声明了如下注解:
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnWebApplication
1
2
3
因此,满足如下条件时该配置生效
@ConditionalOnClass({ Servlet.class, Tomcat.class })–> 在当前类路径下存在Servlet.class, Tomcat.class时生效
@ConditionalOnWebApplication–> 在web环境下生效
由于tomcatPublicMetrics方法声明了 @ConditionalOnMissingBean,因此当beanFactory中不存在TomcatPublicMetrics类型的bean时生效.
DataSourcePublicMetrics
DataSourcePublicMetrics–>提供数据源使用方面的数据统计的PublicMetrics的实现.
该类的字段,如下:
private static final String DATASOURCE_SUFFIX = "dataSource";
@Autowired
private ApplicationContext applicationContext;
@Autowired
private Collection<DataSourcePoolMetadataProvider> providers;
// key---> 对DataSourcePoolMetadataProvider的id生成的前缀,value-->对应的DataSourcePoolMetadata
private final Map<String, DataSourcePoolMetadata> metadataByPrefix = new HashMap<String, DataSourcePoolMetadata>();
1
2
3
4
5
6
7
8
9
10
由于该类的initialize方法注解有@PostConstruct,因此会在初始化后执行.代码如下:
@PostConstruct
public void initialize() {
// 1. 尝试获取主数据源 返回null,意味着主数据源不存在
DataSource primaryDataSource = getPrimaryDataSource();
// 2. 实例化DataSourcePoolMetadataProvider
DataSourcePoolMetadataProvider provider = new DataSourcePoolMetadataProviders(
this.providers);
// 3. 获得BeanFactory中DataSource类型的bean,遍历之
for (Map.Entry<String, DataSource> entry : this.applicationContext
.getBeansOfType(DataSource.class).entrySet()) {
String beanName = entry.getKey();
DataSource bean = entry.getValue();
// 3.1 生成前缀
String prefix = createPrefix(beanName, bean, bean.equals(primaryDataSource));
// 3.2 获得DataSource 所对应的DataSourcePoolMetadata,放入metadataByPrefix 中
DataSourcePoolMetadata poolMetadata = provider
.getDataSourcePoolMetadata(bean);
if (poolMetadata != null) {
this.metadataByPrefix.put(prefix, poolMetadata);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
尝试获取主数据源 返回null,意味着主数据源不存在.代码如下:
private DataSource getPrimaryDataSource() {
try {
return this.applicationContext.getBean(DataSource.class);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
1
2
3
4
5
6
7
8
实例化DataSourcePoolMetadataProvider
获得BeanFactory中DataSource类型的bean,遍历之
生成前缀.代码如下:
protected String createPrefix(String name, DataSource dataSource, boolean primary) {
// 1. 如果是主数据源,返回datasource.primary
if (primary) {
return "datasource.primary";
}
// 2. 如果DataSource对应的id 长度大于dataSource的长度,并且是dataSource结尾的,则截取之前的作为id,如:demoDataSource--> demo
if (name.length() > DATASOURCE_SUFFIX.length()
&& name.toLowerCase().endsWith(DATASOURCE_SUFFIX.toLowerCase())) {
name = name.substring(0, name.length() - DATASOURCE_SUFFIX.length());
}
// 3. 否则,以datasource.作为前缀进行拼接,如demo-->datasource.demo
return "datasource." + name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
如果是主数据源,返回datasource.primary
如果DataSource对应的id 长度大于dataSource的长度,并且是dataSource结尾的,则截取之前的作为id,如:demoDataSource–> demo
否则,以datasource.作为前缀进行拼接,如demo–>datasource.demo
获得DataSource 所对应的DataSourcePoolMetadata,放入metadataByPrefix 中.代码如下:
public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (DataSourcePoolMetadataProvider provider : this.providers) {
DataSourcePoolMetadata metadata = provider
.getDataSourcePoolMetadata(dataSource);
if (metadata != null) {
return metadata;
}
}
return null;
}
1
2
3
4
5
6
7
8
9
10
依次遍历持有的providers,如果能根据给定的DataSource获得DataSourcePoolMetadata,则直接返回,否则返回null.
metrics,实现如下:
public Collection<Metric<?>> metrics() {
Set<Metric<?>> metrics = new LinkedHashSet<Metric<?>>();
// 1. 遍历metadataByPrefix
for (Map.Entry<String, DataSourcePoolMetadata> entry : this.metadataByPrefix
.entrySet()) {
String prefix = entry.getKey();
// 1.1 获得前缀,如果前缀不是.结尾的,则加上.
prefix = (prefix.endsWith(".") ? prefix : prefix + ".");
DataSourcePoolMetadata metadata = entry.getValue();
// 1.2 添加Metric,name=prefix.active value = 已经在使用中的(激活)链接或者返回null,如果该信息不可用的话
addMetric(metrics, prefix + "active", metadata.getActive());
// 1.3 添加Metric,name=prefix.usage value = 当前数据库连接池的使用量,返回值在0至1之间(或者是-1,如果当前数据库连接池没有限制的话)
addMetric(metrics, prefix + "usage", metadata.getUsage());
}
return metrics;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
遍历metadataByPrefix
获得前缀,如果前缀不是.结尾的,则加上.
添加Metric,name=prefix.active value = 已经在使用中的(激活)链接或者返回null,如果该信息不可用的话
添加Metric,name=prefix.usage value = 当前数据库连接池的使用量,返回值在0至1之间(或者是-1,如果当前数据库连接池没有限制的话)
addMetric代码如下:
private <T extends Number> void addMetric(Set<Metric<?>> metrics, String name,
T value) {
if (value != null) {
metrics.add(new Metric<T>(name, value));
}
}
1
2
3
4
5
6
自动装配:
声明在DataSourceMetricsConfiguration中.代码如下:
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnBean(DataSource.class)
static class DataSourceMetricsConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(DataSourcePoolMetadataProvider.class)
public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
由于在DataSourceMetricsConfiguration上声明了如下注解:
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnBean(DataSource.class)
1
2
3
因此在满足如下条件时该配置生效:
@ConditionalOnClass(DataSource.class)–> 在类路径下存在DataSource.class时生效
@ConditionalOnBean(DataSource.class) –> 在beanFactory中存在DataSource类型的bean时生效
由于在dataSourcePublicMetrics声明了 @Conditional 注解,因此满足如下条件时生效:
@ConditionalOnMissingBean–> 在beanFactory中不存在DataSourcePublicMetrics类型的bean时生效
@ConditionalOnBean(DataSourcePoolMetadataProvider.class)–> 当在beanFactory中存在DataSourcePoolMetadataProvider类型的bean时生效
---------------------
版权声明:本文为CSDN博主「一个努力的码农」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_26000415/article/details/79134941
Springboot Actuator之十一:actuator PublicMetrics的更多相关文章
- Springboot Actuator之八:actuator的执行原理
本文接着<Springboot Actuator之七:actuator 中原生endpoint源码解析1>,前面主要分析了原生endpoint的作用. 现在着重了解actuator的执行原 ...
- springboot(十九)使用actuator监控应用【转】【补】
springboot(十九)使用actuator监控应用 微服务的特点决定了功能模块的部署是分布式的,大部分功能模块都是运行在不同的机器上,彼此通过服务调用进行交互,前后台的业务流会经过很多个微服务的 ...
- Springboot Actuator之十一:actuator transaction
前言spring boot 的自动化配置其实就是在spring 的基础上做的封装,在我们之前对mvc,aop的自动化配置中可以发现–> 只是在spring 的基础上添加了一些特性,可以认为只是一 ...
- Springboot监控之一:SpringBoot四大神器之Actuator之3-springBoot的监控和管理--指标说明
Spring Boot包含很多其他的特性,它们可以帮你监控和管理发布到生产环境的应用.你可以选择使用HTTP端点,JMX或远程shell(SSH或Telnet)来管理和监控应用.审计(Auditing ...
- SpringBoot 监控管理模块actuator没有权限的问题
SpringBoot 1.5.9 版本加入actuator依赖后, 访问/beans 等敏感的信息时候报错,如下 Tue Mar 07 21:18:57 GMT+08:00 2017 There wa ...
- SpringBoot整合Swagger和Actuator
前言 本篇文章主要介绍的是SpringBoot整合Swagger(API文档生成框架)和SpringBoot整合Actuator(项目监控)使用教程. SpringBoot整合Swagger 说明:如 ...
- SpringBoot要点之使用Actuator监控
Actuator是Springboot提供的用来对应用系统进行自省和监控的功能模块,借助于Actuator开发者可以很方便地对应用系统某些监控指标进行查看.统计等. 在pom文件中加入spring-b ...
- Spring Boot]SpringBoot四大神器之Actuator
论文转载自博客: https://blog.csdn.net/Dreamhai/article/details/81077903 https://bigjar.github.io/2018/08/19 ...
- SpringBoot四大神器之Actuator
介绍 Spring Boot有四大神器,分别是auto-configuration.starters.cli.actuator,本文主要讲actuator.actuator是spring boot提供 ...
随机推荐
- 中国工业的下一个十年在哪里?APS系统或将引领智能化转型
为什么众多的ERP软件公司没有推出相关产品,当然可以肯定的是并非客户没有此观念,如果一定要说,也只能说目前的需求还不是非常强烈,从ERP厂商非常急切的与APS公司合作,甚至有高价购买APS公司代码的情 ...
- 一、Hadoop入门概述
一.Hadoop是什么 Hadoop是一个由Apche基金会所开发的分布式系统基础架构. 主要解决海量数据的存储和海量数据的分析计算问题. 广义上来说,Hadoop通常是指一个更广泛的概念—Hadoo ...
- 转:Oracle中SQL语句执行过程中
Oracle中SQL语句执行过程中,Oracle内部解析原理如下: 1.当一用户第一次提交一个SQL表达式时,Oracle会将这SQL进行Hard parse,这过程有点像程序编译,检查语法.表名.字 ...
- Ambari 管理hadoop、hbase、zookeeper节点
简介: Apache Ambari是一种基于Web的工具,支持Apache Hadoop集群的供应.管理和监控.Ambari已支持大多数Hadoop组件,包括HDFS.MapReduce.Hive.P ...
- 6.redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?
作者:中华石杉 面试题 redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的? 面试官心理分析 redis 如果仅仅只是将数据缓存在内存里面,如果 redi ...
- Kotlin开发springboot项目(二)
Kotlin开发springboot项目(二) 中文学习网站: https://www.kotlincn.net/ 研究一下kotlin的demo: https://github.com/JetBra ...
- Elasticsearch+x-pack认证license过期处理
介绍: x-pack是Elasticsearch中的一个plugin,主要功能是实现认证功能 报错: 类似下面这样的报错,是因为licese过期导致的 解决: 1.首先去Elasticsearch的官 ...
- Python3和HTMLTestRunner生成html测试报告
1.测试环境: Python3.5+unittest+HTMLTestRunner 2.下载HTMLTestRunner.py文件 下载地址 http://tungwaiyip.info/softwa ...
- xadmin引入django-stdimage在列表页预览图片
一.安装 pip install django-stdimage 安装django-stdimage库 https://github.com/codingjoe/django-stdimage Git ...
- OpenSSL 下载和私钥证书、CERTIFICATE证书生成
openssl 是生成私钥和公钥证书的重要工具. Windows如何安装OpenSSL: 官网:https://www.openssl.org/community/mailinglists.html ...