运行时性能监控对于实现和维护性能优异的系统至关重要。本文是系列文章的第 1 部分(共 3 部分),Nicholas Whitehead 将阐述如何有效地对 Java™ 性能执行低级粒度的监控。您生成的数据可以很好的反映系统操作的内在状态,并揭示影响环境稳定性及性能的约束和因素。
简介
当今的许多 Java 应用程序都依赖于一组复杂的分布式依赖关系和移动部件。很多外部因素都可能对应用程序的性能和可用性造成影响。这些影响基本上都无法完全消除或解决,且难以在预生成环境中准确模拟。Stuff happens 。但是,您可以创建并维护一个全面的系统来监控应用程序的整个生态系统,从而显著降低这些事件的严重性和持续时间。
本系列文章给出了实现此类系统的一些模式和技巧。模式,以及我将使用的一些术语,都表示泛指。通过结合示例代码和插图,它们将帮助您理解应用程序性能监控的概念。这种理解强调解决方案的必要性,并能帮助您选择商业或开源的解决方案。您可以扩展和定制一个解决方案,或者根据需要将其作为设计解决方案的蓝图。
第 1 部分:
探究应用程序性能管理(APM)系统的属性
描述系统监控的常见反面模式
列举监控 JVM 性能的方法
提供有效插装应用程序源代码的方法
第 2 部分将重点介绍插装 Java 类及资源而无需修改原始源代码的方法。第 3 部分将论述监控 JVM 外部资源的方法,包括主机及其操作系统以及数据库和消息传递系统等远程服务。它还将总结并归纳其他的 APM 问题,如数据管理、数据虚拟化、报告和报警。
回页首
APM 系统:模式和反面模式
为让大家正确入门,应当强调,虽然此处介绍的多数与 Java 相关的内容看上去与应用程序和代码性能分析的流程类似,但其实并非 如此。性能分析是一个极具价值的生产前流程,它可以确认您的 Java 代码是否可扩展、高效、快速和足够出色。但是,根据 stuff happens 公理,当您在生产中遇到无法说明的问题时,优秀的开发阶段代码性能分析可能无用武之地。
我的意思是,在生产中实现性能分析的一些方面,并从运行中的应用程序收集一些相同的实时数据及其所有外部依赖关系。该数据由一系列遍及目标的定量测量指标组成,它们为整个系统的健康状况提供细粒度和详细的表示。此外,通过保留这些指标的历史库,您可以捕获准确的基线,以帮助您确认环境仍然健康,或查明特定缺陷的根源和规模。
监控反面模式
完全没有监控资源的应用程序微乎其微,但仍然需要考虑这些反面模式,它们经常出现在运行环境中:
孤立监控的缺陷 当系统视图无法反映整体情况时,便会出现孤立 监控。最为复杂和难以诊断的问题通常涉及多个参与和相关组件。请考虑一个简单的例子,托管在 Java 应用服务器上的应用程序(所有者未知)实现了一个有故障的 JDBC 连接池类(泄漏连接)。
当应用程序所有者查看管理接口时,他们声称自己的服务器与数据库保持了 100 个连接。相反,数据库管理员(DBA)查看数据库管理控制台却能看到相同的主机实际维持了 120 个连接,并且其数量正在迅速增加。在经过整合的 APM 系统中,创建一个显示这两种指标的曲线图应该说是微不足道的。当这两个数字彼此背离时,看到该图的任何人都可以立即清楚地看到真实数字,并能准确判断出问题所在。
整合 APM 的实现并不排除监控和诊断工具,如 DBA 管理工具集、低级网络分析应用程序和数据中心管理解决方案。这些工具仍然是无价的资源,但如果它们依赖于整合视图的专有性,则难以克服孤立效果的影响。
理想 APM 系统的属性
与刚才讨论的反面模式相反,本系列文章介绍的理想 APM 系统拥有以下属性:
渗透力 :它监控所有应用程序组件和依赖关系。
粒度化 :它可以监控层次极低的函数。
整合性 :收集的所有指标将被发送到支持整合视图的同一逻辑 APM 中。
恒定 :一周 7 天,一天 24 小时不间断监控。
高效 :性能数据收集不会对监控目标造成不利影响。
实时 :可以实时显示、报告和警告监控的资源指标。
历史 :监控的资源指标将持久化存储在一个数据库中,因此可以查看、比较和报告历史数据。
在深入研究此系统的实现细节之前,了解 APM 系统的一些基本概念是有帮助的。
回页首
APM 系统概念
所有 APM 系统都能访问性能数据源 并提供数据收集 和跟踪 实用工具。注意,这些是我自己选择的用于描述一般类别的通用术语。它们并非特定于任何 APM 系统,不同 APM 系统可以使用其他术语表示相同的概念。在本文的其余部分中,我所使用的术语定义如下。
性能数据源
性能数据源(PDS)是性能或可用性数据的来源,这些数据对于反映组件的相对健康状况非常有用。例如,Java Management Extensions (JMX) 服务通常可以提供关于 JVM 健康状况的丰富数据。大多数关系数据库通过 SQL 接口发布性能数据。这两种 PDS 都是直接 源的例子,即可以直接提供性能数据。相反,推断 源测定有意和偶然操作,并且产生性能数据。例如,测试消息可以定期发送,并随后从 Java Message Service (JMS) 服务器中取回,这个往返时间将作为该服务性能的推断测量。
推断源(它的实例被称作综合事务 )有时极为有用,因为它们可以通过遍历与实际活动相同的路径来有效测定多个组件或分层调用。综合事务还在监控连续性方面发挥着重要作用,当直接源不能胜任时,它们可以确认系统在相对空闲期的健康状况。
收集和收集器
收集是从 PDS 获取性能或可用性数据的流程。对于直接 PDS,收集器 通常实现一些 API 来访问该数据。要从网络路由器读取统计数据,收集器可以使用简单网络管理协议(Simple Network Management Protocol,SNMP)或 Telnet。对于推断 PDS,收集器用于执行和测定底层操作。
跟踪和跟踪程序
跟踪是收集器向核心 APM 系统交付测量数据的流程。许多商业和开源 APM 系统都提供了一些用于此目的的 API。对于本文中的示例,我实现了一个通用的 Java 跟踪程序接口,将在下节详细讨论。
通常,大多数 APM 系统将跟踪程序提交的数据组织到某种分类的层次结构中。图 2 展示了该数据捕获的一般流程:
图 2. 收集、跟踪和 APM 系统
图 2 还展示了 APM 系统提供的常用服务:
实时显示 :近乎实时显示选定指标的图表。
报告 :生成的指标活动报告。这通常包括一系列固定报告和自定义报告,并能导出数据供用户在别处使用。
历史库 :包含原始或汇总指标的历史数据库,从而能够查看特定时间范围内的图表和报告。
报警: 将收集指标确定的具体条件通知相关个体或组的功能。典型的报警方法是电子邮件和某种类型的自定义钩子接口,可以允许运营小组将事件传播给事件处理系统。
公共跟踪 API 在 APM 的目标环境中的实现和应用提供了一些一致性。此外,自定义收集器的目的是让开发人员能够专心获取性能数据,而不必担心跟踪的问题。下一节将介绍解决此问题的 APM 跟踪接口。
回页首
ITracer:跟踪程序接口
Java 语言可以很好地充当收集器的实现语言,因为:
广泛的平台支持。Java 收集器类可以在大多数目标平台上运行,而无需修改。这使监控架构可以在本地灵活地使用 PDS 合并收集器进程,而不需要使用远程收集。
出色的性能(但是会随可用资源而变化)。
健壮的并发和同步执行支持。
支持一组丰富的通信协议。
受第三方 API 的广泛支持,比如 JDBC 实现、SNMP 和专用 Java 接口,因而能支持多种收集器库。
受活跃开源社区的支持,它提供了额外的工具和接口,使语言能访问或获取大量来源的数据。
但是,有一点需要注意,您的 Java 收集器必须能够与目标 APM 系统提供的跟踪 API 相结合。如果您的 APM 跟踪机制未提供 Java 接口,则它的一些模式将仍然适用。但是,如果目标 PDS 只基于 Java(如 JMX),而应用程序平台并不基于 Java,则需要一个桥接接口(如 IKVM)和一个 Java-to-.NET 编译器(请参阅 参考资料 )。
当缺少官方标准时,不同 APM 产品提供的跟踪 API 也全然不同。因此,我通过实现一个通用的跟踪 Java 接口(名称为 org.runtimemonitoring.tracing.ITracer )抽象了此问题。ITracer 接口是针对专用跟踪 API 的一个通用包装器。此技巧将确保源代码库不会因版本或 API 提供程序而有所不同,并且还支持实现包装 API 中不可用的额外功能。本文中的大多数其余示例都实现了 ITracer 接口和它所支持的一般底层概念。
图 3 是 org.runtimemonitoring.tracing.ITracer 接口的 UML 类图:
图 3. ITracer 接口和工厂类
跟踪类别和名称
ITracer 的基本前提是向中央 APM 系统提交一个度量和相关的名称。此活动由 trace 方法实现,该方法因提交的度量而有所不同。各 trace 方法都接受一个 String[] name 参数,其中包含复合名称的上下文组件,其结构特定于 APM 系统。复合名称向 APM 系统指示提交的名称空间和实际的指标名称;因此,复合名称中通常至少包括根类别和度量说明。底层 ITracer 实现应该知道如何通过传递的 String[] 构建复合名称。表 1 演示了复合命名约定的两个示例:
表 1. 示例复合名称
名称结构
复合名称
简单斜杠分隔
Hosts/SalesDatabaseServer/CPU Utilization/CPU3
JMX MBean ObjectName
com.myco.datacenter.apm:type=Hosts,service=SalesDatabaseServer,group=CPU Utilization,instance=CPU3
清单 1 是使用此 API 跟踪调用的简短示例:
清单 1. 跟踪 API 调用示例
ITracer simpleTracer = TracerFactory.getInstance(sprops);
ITracer jmxTracer = TracerFactory.getInstance(jprops);
.
.
simpleTracer.trace(37, "Hosts", "SalesDatabaseServer",
"CPU Utilization", "CPU3", "Current Utilization %");
jmxTracer.trace(37,
"com.myco.datacenter.apm",
"type=Hosts",
"service=SalesDatabaseServer",
"group=CPU Utilization",
"instance=CPU3", "Current Utilization %");
);
跟踪程序度量数据类型
在此接口中,度量数据可以是以下类型:
int
long
java.util.Date
String
APM 系统提供商可能支持其他数据类型的收集度量数据。
跟踪程序类型
选定了具体的度量数据类型(如 long )之后,可以根据 APM 系统支持的类型来选择解释特定值的方式。还需记住,各 APM 实现可以使用不同的术语来表示本质相同的类型,并且 ITracer 使用了一些通用的命名规则。
时间间隔 讨论跟踪程序类型需要了解时间间隔 的概念。请考虑这样一个概念:进程正在收集操作已经运行的时间并将它发送给 APM 系统。您每分钟都可以看到成百上千的调用。传输并存储每个度量的细节并不是切实有效的方法,而且也不应该在任何性能报告或图表中反应每一个度量,因为即便非常罕见的调用也可以歪曲整个信息表示。同时,由于长时期过度捕获,您将丧失粒度化的概念,因为这段时间内的合法峰值可能相当多。
解决此问题的一种模式是选取一个时间时隔来表示您希望聚合的最低粒度。1 个小时可能太长,而 200 毫秒又太短,因此您可以设定 30 秒的间隔。现在,您仍然可以对假设操作的每次调用都调用 trace,但是,保留在各时间间隔末尾的数据是:
时间间隔平均耗时
时间间隔最长耗时
时间间隔最短耗时
时间间隔内的调用次数
时间时隔开始时刻的时间戳
这是过度聚合度量和存储所有单独度量的一种有效的折衷方法。
ITracer 中表示的跟踪程序类型:
平均时间间隔 :trace(long value, String[] name) 和 trace(int value, String[] name) 方法将发出时间间隔平均值的跟踪(请参阅 时间间隔 侧栏)。这表示每个提交将被转化为当前时间间隔的聚合值。当新时间间隔开始时,聚合值计数器将重置为零。
粘附: traceSticky(value long, String[] name) 和 traceSticky(value int, String[] name) 方法发出粘附值跟踪。这表示,与时间间隔平均指标相反,聚合将它们的值保留在时间间隔中。如果现在跟踪值 5,而此后不再执行跟踪直到第二天某个时刻,则该指标将保持为 5,直到提供了新值。
增量 :增量跟踪将传递一个数值,但提供给 APM 系统(或由 APM 系统解释)的实际值是此度量与前一度量之间的增量。它们有时被称作 rate 类型,用于反映自己的性能优势。请考虑事务管理程序的提交总数度量值。该数字始终在增加,并且其绝对值几乎没有用处。该数字有用的地方是它增加的速率,因此定期收集它的绝对值并跟踪每次读取数据之间的增量可以反映事务提交的速率。增量跟踪比平均时间间隔和粘附方式的跟踪更为常用,但仍有些用例采用了平均时间间隔。增量跟踪必须能够区分只能增加的度量和同时能增减的度量。小于前值的提交度量应被忽略或造成底层增量重置。
事件 :这种类型是一种简单的非聚合指标,它表示特定事件在时间间隔内发生的次数的增量计算。由于收集器和跟踪程序都不期望知道特定时刻的运行总数,因此基本的 traceIncident(String[] name) 调用没有指定任何值,并且隐式只增加一次事件增量。当需要计算多次增量时,除了在循环中多次调用该方法之外,另一种较好的方法是通过 traceIncident(int value, String[] name) 方法根据 value 来计算合值。
智能 :智能跟踪程序是一个参数化的类型,它与跟踪程序中的某种其他类型相映射。度量值和跟踪类型将作为 String 传递,并且可将可用类型作为常量定义在接口中。当收集器不知道正在收集的数据的类型或跟踪程序类型时,这是一个非常方便的方法,但是也可以直接将收集值和配置的类型名称传递给跟踪程序。
TracerFactory 是一个普通的工厂类,用于根据传递的配置属性创建新 ITracer 实例,或者从缓存中引用已创建的 ITracer 。
收集器模式
收集通常有三种可选模式,这影响到应该使用的跟踪程序类型:
轮询: 按固定频繁调用收集器,它将检索和跟踪 PDS 中的指标或指标集的当前值。例如,可以每分钟调用一次收集器来读取主机的 CPU 利用率,或通过 JXM 接口从事务管理器读取提交事务的总数。轮询模式的前提是对目标指标的定期采样。因此,对于轮询事件,指标的值将提供给 APM 系统,但是,假定中间时期的值不变。因而,轮询收集器通常使用粘附跟踪程序类型:APM 系统在生成报告时将假定所有轮询事件之间的值不变。图 4 演示了此模式:图 4. 轮询收集模式
监听 :这种通用数据模式是 Observer 模式的一种形式。收集器将其自身注册为目标 PDS 的事件监听程序,它将在相关的事件发生时接受回调。作为回调结果发出的跟踪值取决于回调有效负荷本身的内容,但收集器至少可以跟踪每个回调的事件。图 5 演示了此模式: 图 5:监听收集模式
截取 :在此模式中,收集器将自己作为截取程序插入到目标和它的调用程序之间。对于通过该截取程序的各个活动实例,截取程序将生成一个度量并跟踪它。当截取模式是 request/response 时,收集器可以测定请求数量、响应时间、请求或响应的有效负荷。例如,HTTP 代码服务器可以充当收集器,它可以:
计算请求数,可以选择根据 HTTP 类型(GET 和 POST 等)或统一资源标识符(URI)来分类。
请求的响应时间。
测定请求和响应的大小。
由于您可以假定截取收集器能 “看到” 每一个事件,因此实现的跟踪程序通常为平均时间间隔类型。因此,如果时间间隔到期且没有活动发生,则该时间间隔的聚合值将为零,而与之前时间间隔中的活动无关。图 6 演示了此模式: 图 6. 截取收集模式
现在,我已经介绍了性能数据跟踪 API、它的底层数据类型和数据收集的模式。接下来,我将通过一些用例和示例来演示 API 的应用。
回页首
监控 JVM
从 JVM 开始实现性能监控是个明智的选择。首先,我将介绍所有 JVM 共同的性能指标,然后再介绍企业给应用程序中经常使用的一些 JVM 驻留组件。通常,Java 应用程序实例是受底层操作系统支持的进程,因此,JVM 监控的某些方面最好是从主机 OS 的视角来理解,这些内容将在第 3 部分中介绍。
在 Java Platform, Standard Edition 5 (Java SE) 发行之前,能够在运行时有效和可靠收集的内部及标准化 JVM 诊断信息非常有限。现在,java.lang.management 接口提供了一些有用的监控点,该接口是所有兼容 Java SE 5(和更新版本)的 JVM 版本的标准。这些 JVM 的某些实现提供了额外的属性指标,但是它们的访问模式却基本相同。我将重点介绍可以通过 JVM 的 MXBeans 访问的标准模式 — 部署在 VM 内部的 JMX MBeans 公开了一个管理和监控接口(请参阅 参考资料 ):
ClassLoadingMXBean :监控类加载系统。
CompilationMXBean :监控编译系统。
GarbageCollectionMXBean :监控 JVM 的垃圾收集器。
MemoryMXBean :监控 JVM 的堆和非堆内存空间。
MemoryPoolMXBean :监控 JVM 分配的内存池。
RuntimeMXBean :监控运行时系统。该 MXBean 提供的有用监控指标很少,但它确实提供了 JVM 的输入参数和启动时间及运行时间,这两者在其他派生指标中都是很有用的。
ThreadMXBean :监控线程系统。
JMX 收集器的前提是它将获取一个 MBeanServerConnection 对象,该对象可以读取部署在 JVM 中的 MBeans 的属性,读取目标属性的值,并使用 ITracer API 跟踪它们。对于这种类型的收集,决定部署收集器的位置非常关键。可行的选择包括本地部署 和远程部署 。
权限问题 本地和远程部署都可能会受到各种配置权限的限制,这些权限会阻止收集器访问 JVM 数据。这些问题大多都有相应的解决方法,但是它们的多变性使本文无法详细介绍这方面的内容。
在本地部署中,收集器和它的调用调度程序部署在目标 JVM 中。随后,JMX 收集器组件将使用 PlatformMBeanServer (可以通过 JVM 内部的 MBeanServerConnection 来连接它)访问 MXBeans。在远程部署中,收集器运行在一个单独的进程中,并使用某种形式的 JMX Remoting 来连接目标 JVM。这可能没有本地部署那么高效,但它不需要在目标系统中部署任何额外的组件。JMX Remoting 不在本文的讨论范围之内,但它的实现方法非常简单:部署一个 RMIConnectorServer 或在 JVM 中启用外部连接(请参阅 参考资料 )。
示例 JMX 收集器
本文的示例 JMX 收集器(请阅读 下载 一节,获取本文的完整源代码)包含三个单独的方法,可用于获取 MBeanServerConnection 。该收集器可以:
通过调用静态 java.lang.management.ManagementFactory.getPlatformMBeanServer() 方法,为本地 JVM 的平台 MBeanServer 获取一个 MBeanServerConnection 。
通过调用静态 javax.management.MBeanServerFactory.findMBeanServer(String agentId) 方法,为部署在本地 JVM 平台中的备用 MBeanServer 获取一个 MBeanServerConnection 。注意,一个 JVM 中可以存在多个 MBeanServer ,并且,Java Platform, Enterprise Edition (Java EE) 服务器等较为复杂的系统几乎始终拥有特定于应用服务器的 MBeanServer ,它是独立的且有别于平台 MBeanServer (请参阅 交叉注册 MBeans 侧边栏)。
使用 javax.management.remote.JMXServiceURL 通过标准 RMI Remoting 获取一个远程 MBeanServerConnection 。
清单 2 是摘录自 JMXCollector collect() 方法的代码段,它显示了 ThreadMXBean 中的收集和线程跟踪活动。点击 此处 查看完整清单:
清单 2. 示例 JMX 收集器的 collect() 方法的部分代码,它使用 ThreadMXBean
.
.
objectNameCache.put(THREAD_MXBEAN_NAME, new ObjectName(THREAD_MXBEAN_NAME));
.
.
public void collect() {
CompositeData compositeData = null;
String type = null;
try {
log("Starting JMX Collection");
long start = System.currentTimeMillis();
ObjectName n = null;
.
.
// Thread Monitoring
n = objectNameCache.get(THREAD_MXBEAN_NAME);
tracer.traceDeltaSticky((Long)jmxServer.getAttribute(on,"TotalStartedThreadCount"),
hostName, "JMX", on.getKeyProperty("type"), "StartedThreadRate");
tracer.traceSticky((Integer)jmxServer.getAttribute(on, "ThreadCount"), hostName,
"JMX", on.getKeyProperty("type"), "CurrentThreadCount");
.
.
// Done
long elapsed = System.currentTimeMillis()-start;
tracer.trace(elapsed, hostName, "JMX", "JMX Collector",
"Collection", "Last Elapsed Time");
tracer.trace(new Date(), hostName, "JMX", "JMX Collector",
"Collection", "Last Collection");
log("Completed JMX Collection in ", elapsed, " ms.");
} catch (Exception e) {
log("Failed:" + e);
tracer.traceIncident(hostName, "JMX", "JMX Collector",
"Collection", "Collection Errors");
}
}
清单 2 中的代码将跟踪 TotalThreadsStarted 和 CurrentThreadCount 的值。由于它是轮询收集器,因此两个跟踪都使用粘附选项。但是,由于 TotalThreadsStarted 是一个不断增加的数值,因此最吸引人的地方不是绝对值,而是已创建线程的速率。这样,该跟踪程序将使用 DeltaSticky 选项。
图 7 显示了此收集器创建的 APM 指标树:
图 7. JMX 收集器 APM 指标树
JMX 收集器的一些方面并未显示在清单 2 中(但是可以在 完整源代码 中看到),比如说调度注册,它将每隔 10 分钟为 collect() 方法创建一个定期回调。
在清单 2 中,不同跟踪程序类型和数据类型的实现方法将由数据源决定。例如:
TotalLoadedClasses 和 UnloadedClassCount 将作为粘附增量被跟踪,因为它们的值始终递增,而且增量在测定类加载活动方面比绝对值更加有用。
ThreadCount 变量可增加或减少,因此它将作为粘附类型被跟踪。
收集错误 将作为内部事件被跟踪,它将在收集遇到异常时递增。
为了追求效率,由于目标 MXBeans 的 JMX ObjectName 在目标 JVM 的生存期不会更改,因此收集器使用 ManagementFactory 常量名来缓存名称。
对于 MXBeans 的两种类型 — GarbageCollector 和 MemoryPool — 准确的 ObjectName 无法预先知晓,但是您可以提供一个通用的模式。在这些情况下,在初次执行收集时,您将对 MBeanServerConnection 发起一个查询,并请求与提供模式相匹配的所有 MBeans 的列表。为避免未来在目标 JVM 的生存期执行查询,返回的匹配 MBean ObjectName 将缓存在内存中。
在某些情况下,收集的目标 MBean 属性可能不是纯数值类型。MemoryMXBean 和 MemoryPoolMXBean 就是这种情况。对于这些情况,属性类型是可查询键和值的 CompositeData 对象。对于 java.lang.management JVM 管理接口,MXBean 标准采用了 JMX Open Types 模型,在该模型中,所有属性都是语言无关的类型,如 java.lang.Boolean 和 java.lang.Integer 。或者,对于 javax.management.openmbean.CompositeType 等复杂类型,这些类型可以被分解为相同简单类型的键/值对。简单类型的完整列表枚举在静态 javax.management.openmbean.OpenType.ALLOWED_CLASSNAMES 字段中。该模型支持一个类型独立层,使 JMX 客户机不用依赖于非标准的类,并且还可以支持非 Java 客户机,因为底层类型相对比较简单。有关 JMX Open Types 的更多信息,请参阅 参考资料 。
对于目标 MBean 属性是非标准复杂类型的情况,您需要确保定义该类型的类在收集器的类路径中。并且,您必须实现一些自定义代码来呈现检索到的复杂对象中的有用数据。
如果获取了单个连接并为所有收集保留了该连接,则需要通过错误检测和修复来创建一个新连接,以防止该连接出现故障。某些收集 API 提供断开监控程序,可以提示收集器关闭、消除和创建新连接。如果收集器尝试连接到由于维护而停机或由于其他原因而无法访问的 PDS,则收集器应该以合适的频率轮询并重新连接。跟踪连接的运行时间还可用于在检测到关机时减少收集的频率。这可以减少已超负荷运行了一段时间的目标 JVM 的开销。
这些示例中未实现的两个额外技巧可以改进 JMX 收集器的效率,并减少它在目标 JVM 中运行所需的开销。第一个技巧适用于从一个 MBean 中查询多个属性的情况。借助 getAttributes(ObjectName name, String[] attributes) ,您可以在一个调用中请求多个属性,而不必使用 getAttribute(ObjectName name, String attribute) 一次请求一个属性。这种差异在本地收集中可以忽略,但是在远程收集中却可以显著减少资源的使用,因为它可以减少网络调用的数量。第二个技巧是使用监控收集模式代替轮询模式,从而进一步减少 JMX 公开内存池的轮询开销。MemoryPoolMXBean 支持建立一个使用率阀值,超过该阀值时将触发向监控程序发送一个通知,而监控程序将跟踪该值。当内存使用率增加时,使用率阀值可以相应地增加。这种方法是缺陷是,如果使用率阀值没有微小的增量,则一些粒度级的数据可能会丢失,并且阀值下方的内存使用率模式将变为不可见。
最后一个未实现的技巧是测定运行时间和垃圾收集总运行时间的范围,并实现一些简单的算法来计算垃圾收集器处于活动状态的时间在已运行时间中的百分比。这是一个有用的指标,因为一些垃圾收集器(当前)是大多数应用程序必须要面对的问题。由于某些收集(分别执行了一段时间)是期望执行的,因此运行垃圾收集占用的时间可以更加清楚地反映 JVM 的内存健康状况。根据经验(因应用程序而大不相同),占用任何 15 分钟时间段内的 10% 以上则表示存在潜在问题。
收集器的外部配置
为便于演示收集流程,本文介绍的 JMX 收集器经过了适当简化,但它仅限于硬编码的收集方式。理想情况下,收集器将实现数据访问方式 ,而外部提供的配置将提供内容 。这种设计使收集器更具实用性,且易于重用。对于最高级别的重用,外部配置的收集器应该支持这些配置点:
PDS 连接工厂指令,为收集器提供用于连接到 PDS 的接口,以及在连接时使用的配置。
执行收集的频率。
尝试重新连接的频率。
收集的目标 MBean,或通配符形式的对象名称。
对于各目标,跟踪复合名称或者应该跟踪的度量片段,以及应该跟踪的数据类型。
清单 3 演示了 JMX 收集器的外部配置:
清单 3. JMX 收集器的外部配置示例
collectors.jmx.RemoteRMIMBeanServerConnectionFactory
jmx.rmi.url=service:jmx:rmi://127.0.0.1/jndi/rmi://127.0.0.1:1090/jmxconnector
AppServer3.myco.org,JMX10000
注意,TargetAttribute 元素包含一个名为 type 的属性,它表示智能类型跟踪程序的参数化变量。SINT 类型表示粘附 int ,SDINT 类型表示增量粘附 int 。
回页首
通过 JMX 监控应用程序资源
目前为止,我已经讨论了通过 JMX 监控惟一标准的 JVM 资源。但是,许多应用程序架构,如 Java EE,可以通过 JMX 公开重要的特定于应用程序的指标(这取决于供应商)。一个典型的例子是 DataSource 利用率。DataSource 是一个用于将连接池化到外部资源(通常为数据库)的服务,这限制了并发连接的数量,以保护资源不受恶意应用程序的占用。监控数据源是整个监控计划中的关键环节。由于 JMX 抽象层,该流程与之前介绍的类似。
下面是来自 JBoss 4.2 应用服务器实例的典型数据源指标:
可用连接数 :当前池中可用连接的数量。
连接数 :连接池与数据库建立的实际物理连接的数量。
最大使用连接数 :池中正在使用的连接的上限标记。
正在使用的连接数 :当前正在使用的连接数量。
已创建的连接数 :为该池创建的连接总数。
已部署的连接数 :为该池部署的连接总数。
现在,收集器将使用批属性检索,并在一个调用中获取所有属性。惟一需要注意的是,您需要查询返回的数据,以接通不同的数据和跟踪程序类型。DataSource 指标在没有活动时也是不会变化的,因此,要使数值变化,您需要生成一些负载。清单 4 显示 DataSource 收集器的 collect() 方法:
清单 4. DataSource 收集器
public void collect() {
try {
log("Starting DataSource Collection");
long start = System.currentTimeMillis();
ObjectName n = objectNameCache.get("DS_OBJ_NAME");
AttributeList attributes = jmxServer.getAttributes(on, new String[]{
"AvailableConnectionCount",
"MaxConnectionsInUseCount",
"InUseConnectionCount",
"ConnectionCount",
"ConnectionCreatedCount",
"ConnectionDestroyedCount"
});
for(Attribute attribute: (List)attributes) {
if(attribute.getName().equals("ConnectionCreatedCount")
|| attribute.getName().equals("ConnectionDestroyedCount")) {
tracer.traceDeltaSticky((Integer)attribute.getValue(), hostName,
"DataSource", on.getKeyProperty("name"), attribute.getName());
} else {
if(attribute.getValue() instanceof Long) {
tracer.traceSticky((Long)attribute.getValue(), hostName, "DataSource",
on.getKeyProperty("name"), attribute.getName());
} else {
tracer.traceSticky((Integer)attribute.getValue(), hostName,
"DataSource",on.getKeyProperty("name"), attribute.getName());
}
}
}
// Done
long elapsed = System.currentTimeMillis()-start;
tracer.trace(elapsed, hostName, "DataSource", "DataSource Collector",
"Collection", "Last Elapsed Time");
tracer.trace(new Date(), hostName, "DataSource", "DataSource Collector",
"Collection", "Last Collection");
log("Completed DataSource Collection in ", elapsed, " ms.");
} catch (Exception e) {
log("Failed:" + e);
tracer.traceIncident(hostName, "DataSource", "DataSource Collector",
"Collection", "Collection Errors");
}
}
图 8 显示了 DataSource 收集器的相应指标树:
图 8. DataSource 收集器指标树
回页首
监控 JVM 中的组件
交叉注册 MBeans 在许多情况下,可以将目标 MBeans 注册到相同 JVM 的不同 MBeanServer 中。例如,java.lang MXBean 注册在平台代理 (也称作 JVM MBeanServer )中,而 JBoss 服务器中的 DataSource MBeans 位于 jboss MBeanServer 中。在远程监控实现中,这种情况有时会增加额外的开销和配置复杂度,因为每个 MBeanServer 都需要两个远程连接。此外,为远程连接公开平台 MBeanServer 也会带来额外的开销。在这些情况下,通常采用交叉注册 MBeans 的方式,这样所有的目标 MBeans 都可以通过相同的 MBeanServer 接口来监控。请查阅本文的 源代码 ,获取名称为 map-platform-mxbeans.bsh 的示例 bean-shell 脚本。如果您将此脚本部署到 JBoss 服务器中,它将在 JBoss MBeanServer 中交叉注册平台 MBeanServer 的 MXBeans。
本节介绍的技巧可用于监控应用程序组件、服务、类和方法。相关的主要区域如下:
调用速率 :调用服务或方法的速率。
调用响应速率 :服务或方法响应的速率。
调用错误率 :服务或方法生成错误的比率。
调用运行时间 :调用在每个间隔时间内的平均、最短和最长运行时间。
调用并发性 :并发调用服务或方法时执行的线程数。
使用 Java SE 5(和更新版本)ThreadMXBean 的一些实现提供的指标,还可以收集以下指标:
系统和用户 CPU 时间 :调用某方法占用的 CPU 时间。
等待数量和总等待时间 :调用某方法或服务时,等待线程的实例数量和总占用时间。当线程进入 WAITING 或 TIMED_WAITING 等待状态并暂停另一个线程的活动时将发生等待事件。
阻塞数量和总阻塞时间 :在调用某个方法或服务时,处于 BLOCKED 状态的线程的实例数量和总占用时间。当线程等待监控锁进入或重新进入同步阻塞时会发生阻塞事件。
还可以使用备选工具集和本机接口来确定这些指标和其他指标,但这通常涉及某种级别的开销,从而造成不必要的生产运行时监控。已经说过,指标本身,甚至在收集时,是低级的。它们的作用也许仅限于分析趋势,并且很难与无法通过其他手段确定的因果效应相关联。
所有上述指标都可以通过插装类和方法的流程来收集,以便于收集和跟踪目标 APM 系统的性能数据。可以采用各种技巧来直接插装 Java 类,或者通过它们来间接计算性能指标:
源代码插装: 最基本的技巧是在源代码阶段添加插装,这样编译和部署后的类就已经在运行时包含了插装。在某些情况下,这种方法具有意义,并且一些特定的实践使它成为可行的流程和投资。
截取 :通过截取程序(执行测定和跟踪)转移调用,可以实现有效和准确的跟踪,而无需接触目标类、它们的源代码和运行时字节码。这种实践简单可取,因为存在许多 Java EE 框架和其他流行的 Java 框架:
通过配置支持抽象。
支持类注入和通过接口引用。
有某些情况下直接支持截取栈概念。执行流程经过定义了配置的对象栈,其作用是接收调用并执行一些处理,然后继续传递。
字节码插装 :该流程将字节码注入到应用程序类中。注入的字节码将添加性能数据收集插装,该插装被作为新类的一部分调用。这个流程有时极为有效,因为插装是完全经过编译的字节码,并且代码的执行路径以最细化的方式扩展,同时仍然能够收集数据。它的另一个优点是无需修改初始源代码,并且其对环境的配置更改也可能最少。此外,通用模式和字节码注入技巧允许对源代码不可用的类和库进行插装,许多第三方类属于这种情况。
类包装 :该流程使用另一个类来包装或替换目标类,前者实现了相同功能,同时也包含了插装。
在本文的第 1 部分中,我只讨论基于源代码的插装;您将在第 2 部分中了解更多关于截取、字节码插装和类包装的信息。(从拓扑学的角度来说,截取、字节码插装和类包装的本质完全相同,但它们实现结果的操作有稍微不同的含义)。
异步插装
异步插装是类插装中的基本问题。上一节讨论了轮询性能数据的概念。如果轮询完成得足够好,则它应该不会对核心应用程序性能或开销造成影响。相反,插装应用程序代码本身会直接修改和影响核心代码的执行。任何插装的目标都必须是无论如何,不产生危害 。开销损失必须尽可能接近忽略不计。事实上,消除测量本身中的极细微的损失是不可能的,但是,在获取性能数据之后,保持其余跟踪进程异步是非常重要的。可以采用若干种模式来实现异步跟踪。图 9 演示了异步跟踪的实现方法概览:
图 9. 异步跟踪
图 9 演示了一个简单的插装截取程序,它通过捕获调用的起始时间来测量它的运行时间,然后将测量数据(运行时间和指标复合名称)分发给处理队列。然后,线程池读取该队列,获取测量数据并完成跟踪流程。
回页首
源代码中的 Java 类插装
本节将讨论如何实现源代码级插装,并将提供一些最佳实践和示例代码。文章还介绍了一些新的跟踪结构,我将在源代码插装的上下文中阐明它们的操作和它们的插装模式。
虽然其他选择已经流行,但源代码插装在某些实例中是无法避免的;在某些情况下,它是惟一的解决方案。借助一些明智的预防措施,它可以实现良好的效果。需要考虑的事项包括:
如果插装代码的方案可用,并且无法实现配置更改来垂直影响插装,则使用可配置和灵活的跟踪 API 非常重要。
抽象的跟踪 API 类似于事件记录 API(如 log4j),它们的共用属性包括:
运行时冗长控制 :log4j 记录程序和附加程序的冗长等级可以在系统启动时配置,并随后在运行时修改。同样,跟踪 API 应该能够根据分级名称模式来控制哪些指标名称受跟踪支持。
输出端点配置 :log4j 通过记录程序发起记录声明,并将它分发给附加程序。经过配置,附加程序可以将记录流发送给各种输出,如文件、套接字和电子邮件。跟踪 API 不需要多样的输出方式,但抽象专有或特定于 APM 系统的库的能力将保护源代码不受外部配置的更改。
在某些情况下,通过其他方法来跟踪具体的项目不太可行。通常,我将这种情况称作上下文跟踪 。我使用该术语描述的性能数据并不是很重要,但它为主要数据添加上下文。
上下文跟踪
上下文跟踪受具体的应用程序影响极大,但是可以考虑一个经过简化的例子:含有 processPayroll(long clientId) 方法的 payroll-processing 类。当被调用时,该方法计算并存储各客户员工的薪水。您可以通过各种方法插装该方法,但是,执行中的底层模式清楚表明,调用时间的增加与员工的数量不成比例。因此,研究 processPayroll 的运行时间趋势没有上下文可供参考,除非您知道程序每次处理的员工数量。简单来讲,对于特定的时间段,processPayroll 平均耗时 x 毫秒。无法确定这个值反映的性能是好还是坏,因为您不知道它处理的员工数量是 1 还是 150,而两种情况反映的性能差别巨大。清单 5 在代码中显示了这个简化的概念:
清单 5. 上下文跟踪的例子
public void processPayroll(long clientId) {
Collection employees = null;
// Acquire the collection of employees
//...
//...
// Process each employee
for(Employee emp: employees) {
processEmployee(emp.getEmployeeId(), clientId);
}
}
此处的主要挑战是,根据大多数插装技巧,processPayroll() 方法中的任何东西都是不可触及的。因此,虽然能够插装 processPayroll 甚至 processEmployee ,但是却无法跟踪员工的数量,从而不能为方法的性能数据提供上下文。清单 6 显示了一个拙劣的硬编码示例(且有点效率不高),它将捕获上面提到的上下文数据:
清单 6. 上下文跟踪示例
public void processPayrollContextual(long clientId) {
Collection employees = null;
// Acquire the collection of employees
employees = popEmployees();
// Process each employee
int empCount = 0;
String rangeName = null;
long start = System.currentTimeMillis ();
for(Employee emp: employees) {
processEmployee(emp.getEmployeeId(), clientId);
empCount++;
}
rangeName = tracer.lookupRange("Payroll Processing", empCount);
long elapsed = System.currentTimeMillis ()-start;
tracer.trace(elapsed, "Payroll Processing", rangeName, "Elapsed Time (ms)");
tracer.traceIncident("Payroll Processing", rangeName, "Payrolls Processed");
log ("Processed Client with " + empCount + " employees.");
}
清单 6 中的关键部分是 tracer.lookupRange 调用。Ranges 是指定的收集,它由数值范围限制键控,并且拥有一个表示数值范围名称的 String 值。不再跟踪薪水处理的简单无格式运行时间,清单 6 将员工计数划分为范围,有效分隔运行时间并根据基本类似的员工计数将它们分组。图 10 显示了 APM 系统生成的指标树:
图 10:根据范围分组的薪水处理时间
图 11 演示了根据员工计数划分的薪水处理运行的时间,它揭示了员工数量和运行时间之间的相互关系:
图 11. 各范围的薪水处理运行时间
跟踪程序配置属性允许在属性文件中包括 URL,并能在其中定义范围和阀值(我将简单介绍一下阀值)。属性将在跟踪程序的构造时间被读取,并为 tracer.lookupRange 实现提供后台数据。清单 7 显示了 Payroll Processing 范围的示例配置。我选择使用 java.util.Properties 的 XML 表示,因为它更能兼容奇怪的字符。
清单 7. 范围配置示例
Payroll Process Range181+ Emps,10:1-10 Emps,50:11-50 Emps,
80:51-80 Emps,120:81-120 Emps,180:121-180 Emps
注入外部定义的范围可以使您的应用程序不必频繁更新源代码,这受益于预期的调整和服务水平协议(SLA)在业务方面的变更。当范围和阀值更改生效之后,您只需更新外部文件,而不是应用程序本身。
回页首
跟踪阀值和 SLA
外部可配置上下文跟踪的灵活性支持以更加准确和粒度化的方式来定义和测量性能阀值 。范围 定义一系列数值区间,可以在其中对测量数据进行分类,而阀值 是对范围的进一步分类,它根据测量数据的确定范围对获取的测量数据进行分类。在分析收集的性能数据时,一个常见的需求是确定和报告执行是 “成功” 还是 “失败”(因为它们未在指定时间发生)。这些数据的总和可以作为关于系统运行健康状况和性能的通用成绩单,或者作为某种形式的 SLA 遵从性评价。
使用薪水处理系统示例,考虑一个内部服务级目标,它将薪水的执行时间(在定义的员工数范围之内)定义为 Ok 、Warn 和 Critical 3 个区间。生成阀值计数的流程从概念上来说非常简单。您只需为跟踪程序提供您认为是各类别各区间的上限运行时间的值,并引导跟踪程序为分类的运行时间发起一个 tracer.traceIncident ,然后 — 为简化报告 — 提供一个总数。表 2 显示了一些经过设计的 SLA 运行时间:
表 2. 薪水处理阀值
员工数
Ok (ms)
Warn (ms)
Critical (ms)
1-10
280
400
>400
11-50
850
1200
>1200
51-80
900
1100
>1100
81-120
1100
1500
>1500
121-180
1400
2000
>2000
181+
2000
3000
>3000
ITracer API 使用与范围中相同的 XML(属性)文件中定义的值实现了阀值报告。范围和阀值定义在两个方面稍有不同。首先,阀值定义的关键值是一个正则表达式。当 ITracer 在跟踪一个数值时,它会检查阀值正则表达式是否匹配被跟踪指标的复合名称。如果匹配,则阀值会将测量数据分类为 Ok 、Warn 或 Critical ,并为跟踪附加一个额外的 tracer.traceIncident 。其次,由于阀值只定义了两个值(根据定义,Critical 值大于 warn 值),因此配置只由两个数值组成。清单 8 显示了之前介绍的薪水处理 SLA 的阀值配置:
清单 8. 薪水处理的阀值配置
你可能感兴趣的:(Java 系统运行时性能和可用性监控)
情绪觉察日记第37天
露露_e800
今天是家庭关系规划师的第二阶最后一天,慧萍老师帮我做了个案,帮我处理了埋在心底好多年的一份恐惧,并给了我深深的力量!这几天出来学习,爸妈过来婆家帮我带小孩,妈妈出于爱帮我收拾东西,并跟我先生和婆婆产生矛盾,妈妈觉得他们没有照顾好我…。今晚回家见到妈妈,我很欣赏她并赞扬她,妈妈说今晚要跟我睡我说好,当我们俩躺在床上准备睡觉的时候,我握着妈妈的手对她说:妈妈这几天辛苦你了,你看你多利害把我们的家收拾得
芦花鞋一四
许叶晗
又是在一个寒冷的夏日里,青铜和葵花决定今天一起去卖芦花鞋,奶奶亲手给他们做了一碗热乎乎的粥对他们说:“就靠你们两挣生活费了这碗粥赶紧趁热喝了吧!”于是青铜和葵花喝完了奶奶给她们做的粥,就准备去镇上卖卢花鞋,这回青铜和葵花穿着新的芦花鞋来到了镇上。青铜这回看到了很多人都在卖,用手势表达对葵花说:“这回有好多人在抢我们生意呢!我们必须得吆喝起来。”葵花点了点头。可是谁知他们也大声的叫,卖芦花喽!卖芦花
关于沟通这件事,项目经理不需要每次都面对面进行
流程大师兄
很多项目经理都会遇到这样的问题,项目中由于事情太多,根本没有足够的时间去召开会议,那在这种情况下如何去有效地管理项目中的利益相关者?当然,不建议电子邮件也不需要开会的话,建议可以采取下面几种方式来形成有效的沟通,这几种方式可以帮助你努力的通过各种办法来保持和各方面的联系。项目经理首先要问自己几个问题,项目中哪些利益相关者是必须要进行沟通的?可以列出项目中所有的利益相关者清单,同时也整理出项目中哪些
机器学习与深度学习间关系与区别
ℒℴѵℯ心·动ꦿ໊ོ꫞
人工智能 学习 深度学习 python
一、机器学习概述定义机器学习(MachineLearning,ML)是一种通过数据驱动的方法,利用统计学和计算算法来训练模型,使计算机能够从数据中学习并自动进行预测或决策。机器学习通过分析大量数据样本,识别其中的模式和规律,从而对新的数据进行判断。其核心在于通过训练过程,让模型不断优化和提升其预测准确性。主要类型1.监督学习(SupervisedLearning)监督学习是指在训练数据集中包含输入
android系统selinux中添加新属性property
辉色投像
1.定位/android/system/sepolicy/private/property_contexts声明属性开头:persist.charge声明属性类型:u:object_r:system_prop:s0图12.定位到android/system/sepolicy/public/domain.te删除neverallow{domain-init}default_prop:property
【iOS】MVC设计模式
Magnetic_h
ios mvc 设计模式 objective-c 学习 ui
MVC前言如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architecturalpattern),属于编程的方法论。MVC模式就是架构模式的一种。它是Apple官方推荐的App开发架构,也是一般开发者最先遇到、最经典的架构。MVC各层controller层Controller/ViewController/VC(控制器)负责协调Model和View,处理大部分逻辑它将数据从Mod
一百九十四章. 自相矛盾
巨木擎天
唉!就这么一夜,林子感觉就像过了很多天似的,先是回了阳间家里,遇到了那么多不可思议的事情儿。特别是小伙伴们,第二次与自己见面时,僵硬的表情和恐怖的气氛,让自己如坐针毡,打从心眼里难受!还有东子,他现在还好吗?有没有被人欺负?护城河里的小鱼小虾们,还都在吗?水不会真的干枯了吧?那对相亲相爱漂亮的太平鸟儿,还好吧!春天了,到了做窝、下蛋、喂养小鸟宝宝的时候了,希望它们都能够平安啊!虽然没有看见家人,也
UI学习——cell的复用和自定义cell
Magnetic_h
ui 学习
目录cell的复用手动(非注册)自动(注册)自定义cellcell的复用在iOS开发中,单元格复用是一种提高表格(UITableView)和集合视图(UICollectionView)滚动性能的技术。当一个UITableViewCell或UICollectionViewCell首次需要显示时,如果没有可复用的单元格,则视图会创建一个新的单元格。一旦这个单元格滚动出屏幕,它就不会被销毁。相反,它被添
element实现动态路由+面包屑
软件技术NINI
vue案例 vue.js 前端
el-breadcrumb是ElementUI组件库中的一个面包屑导航组件,它用于显示当前页面的路径,帮助用户快速理解和导航到应用的各个部分。在Vue.js项目中,如果你已经安装了ElementUI,就可以很方便地使用el-breadcrumb组件。以下是一个基本的使用示例:安装ElementUI(如果你还没有安装的话):你可以通过npm或yarn来安装ElementUI。bash复制代码npmi
地推话术,如何应对地推过程中家长的拒绝
校师学
相信校长们在做地推的时候经常遇到这种情况:市场专员反馈家长不接单,咨询师反馈难以邀约这些家长上门,校区地推疲软,招生难。为什么?仅从地推层面分析,一方面因为家长受到的信息轰炸越来越多,对信息越来越“免疫”;而另一方面地推人员的专业能力和营销话术没有提高,无法应对家长的拒绝,对有意向的家长也不知如何跟进,眼睁睁看着家长走远;对于家长的疑问,更不知道如何有技巧地回答,机会白白流失。由于回答没技巧和专业
谢谢你们,爱你们!
鹿游儿
昨天家人去泡温泉,二个孩子也带着去,出发前一晚,匆匆下班,赶回家和孩子一起收拾。饭后,我拿出笔和本子(上次去澳门时做手帐的本子)写下了1\2\3\4\5\6\7\8\9,让后让小壹去思考,带什么出发去旅游呢?她在对应的数字旁边画上了,泳衣、泳圈、肖恩、内衣内裤、tapuy、拖鞋……画完后,就让她自己对着这个本子,将要带的,一一带上,没想到这次带的书还是这本《便便工厂》(晚上姑婆发照片过来,妹妹累得
C语言如何定义宏函数?
小九格物
c语言
在C语言中,宏函数是通过预处理器定义的,它在编译之前替换代码中的宏调用。宏函数可以模拟函数的行为,但它们不是真正的函数,因为它们在编译时不会进行类型检查,也不会分配存储空间。宏函数的定义通常使用#define指令,后面跟着宏的名称和参数列表,以及宏展开后的代码。宏函数的定义方式:1.基本宏函数:这是最简单的宏函数形式,它直接定义一个表达式。#defineSQUARE(x)((x)*(x))2.带参
微服务下功能权限与数据权限的设计与实现
nbsaas-boot
微服务 java 架构
在微服务架构下,系统的功能权限和数据权限控制显得尤为重要。随着系统规模的扩大和微服务数量的增加,如何保证不同用户和服务之间的访问权限准确、细粒度地控制,成为设计安全策略的关键。本文将讨论如何在微服务体系中设计和实现功能权限与数据权限控制。1.功能权限与数据权限的定义功能权限:指用户或系统角色对特定功能的访问权限。通常是某个用户角色能否执行某个操作,比如查看订单、创建订单、修改用户资料等。数据权限:
理解Gunicorn:Python WSGI服务器的基石
范范0825
ipython linux 运维
理解Gunicorn:PythonWSGI服务器的基石介绍Gunicorn,全称GreenUnicorn,是一个为PythonWSGI(WebServerGatewayInterface)应用设计的高效、轻量级HTTP服务器。作为PythonWeb应用部署的常用工具,Gunicorn以其高性能和易用性著称。本文将介绍Gunicorn的基本概念、安装和配置,帮助初学者快速上手。1.什么是Gunico
2021年12月19日,春蕾教育集团团建活动感受——黄晓丹
黄错错加油
感受:1.从陌生到熟悉的过程。游戏环节让我们在轻松的氛围中得到了锻炼,也增长了不少知识。2.游戏过程中,我们贡献的是个人力量,展现的是团队的力量。它磨合的往往不止是工作的熟悉,更是观念上契合度的贴近。3.这和工作是一样的道理。在各自的岗位上,每个人摆正自己的位置、各司其职充分发挥才能,并团结一致劲往一处使,才能实现最大的成功。新知:1.团队精神需要不断地创新。过去,人们把创新看作是冒风险,现在人们
Cell Insight | 单细胞测序技术又一新发现,可用于HIV-1和Mtb共感染个体诊断
尐尐呅
结核病是艾滋病合并其他疾病中导致患者死亡的主要原因。其中结核病由结核分枝杆菌(Mycobacteriumtuberculosis,Mtb)感染引起,获得性免疫缺陷综合症(艾滋病)由人免疫缺陷病毒(Humanimmunodeficiencyvirustype1,HIV-1)感染引起。国家感染性疾病临床医学研究中心/深圳市第三人民医院张国良团队携手深圳华大生命科学研究院吴靓团队,共同研究得出单细胞测序
c++ 的iostream 和 c++的stdio的区别和联系
黄卷青灯77
c++ 算法 开发语言 iostream stdio
在C++中,iostream和C语言的stdio.h都是用于处理输入输出的库,但它们在设计、用法和功能上有许多不同。以下是两者的区别和联系:区别1.编程风格iostream(C++风格):C++标准库中的输入输出流类库,支持面向对象的输入输出操作。典型用法是cin(输入)和cout(输出),使用>操作符来处理数据。更加类型安全,支持用户自定义类型的输入输出。#includeintmain(){in
《投行人生》读书笔记
小蘑菇的树洞
《投行人生》----作者詹姆斯-A-朗德摩根斯坦利副主席40年的职业洞见-很短小精悍的篇幅,比较适合初入职场的新人。第一部分成功的职业生涯需要规划1.情商归为适应能力分享与协作同理心适应能力,更多的是自我意识,你有能力识别自己的情并分辨这些情绪如何影响你的思想和行为。2.对于初入职场的人的建议,细节,截止日期和数据很重要截止日期,一种有效的方法是请老板为你所有的任务进行优先级排序。和老板喝咖啡的好
Long类型前后端数据不一致
igotyback
前端
响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问
LocalDateTime 转 String
igotyback
java 开发语言
importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;publicclassMain{publicstaticvoidmain(String[]args){//获取当前时间LocalDateTimenow=LocalDateTime.now();//定义日期格式化器DateTimeFormatterformat
Linux下QT开发的动态库界面弹出操作(SDL2)
13jjyao
QT类 qt 开发语言 sdl2 linux
需求:操作系统为linux,开发框架为qt,做成需带界面的qt动态库,调用方为java等非qt程序难点:调用方为java等非qt程序,也就是说调用方肯定不带QApplication::exec(),缺少了这个,QTimer等事件和QT创建的窗口将不能弹出(包括opencv也是不能弹出);这与qt调用本身qt库是有本质的区别的思路:1.调用方缺QApplication::exec(),那么我们在接口
绘本讲师训练营【24期】8/21阅读原创《独生小孩》
1784e22615e0
24016-孟娟《独生小孩》图片发自App今天我想分享一个蛮特别的绘本,讲的是一个特殊的群体,我也是属于这个群体,80后的独生小孩。这是一本中国绘本,作者郭婧,也是一个80厚。全书一百多页,均为铅笔绘制,虽然为黑白色调,但并不显得沉闷。全书没有文字,犹如“默片”,但并不影响读者对该作品的理解,反而显得神秘,梦幻,給读者留下想象的空间。作者在前蝴蝶页这样写到:“我更希望父母和孩子一起分享这本书,使他
店群合一模式下的社区团购新发展——结合链动 2+1 模式、AI 智能名片与 S2B2C 商城小程序源码
说私域
人工智能 小程序
摘要:本文探讨了店群合一的社区团购平台在当今商业环境中的重要性和优势。通过分析店群合一模式如何将互联网社群与线下终端紧密结合,阐述了链动2+1模式、AI智能名片和S2B2C商城小程序源码在这一模式中的应用价值。这些创新元素的结合为社区团购带来了新的机遇,提升了用户信任感、拓展了营销渠道,并实现了线上线下的完美融合。一、引言随着互联网技术的不断发展,社区团购作为一种新兴的商业模式,在满足消费者日常需
消息中间件有哪些常见类型
xmh-sxh-1314
java
消息中间件根据其设计理念和用途,可以大致分为以下几种常见类型:点对点消息队列(Point-to-PointMessagingQueues):在这种模型中,消息被发送到特定的队列中,消费者从队列中取出并处理消息。队列中的消息只能被一个消费者消费,消费后即被删除。常见的实现包括IBM的MQSeries、RabbitMQ的部分使用场景等。适用于任务分发、负载均衡等场景。发布/订阅消息模型(Pub/Sub
ArcGIS栅格计算器常见公式(赋值、0和空值的转换、补充栅格空值)
研学随笔
arcgis 经验分享
我们在使用ArcGIS时通常经常用到栅格计算器,今天主要给大家介绍我日常中经常用到的几个公式,供大家参考学习。将特定值(-9999)赋值为0,例如-9999.Con("raster"==-9999,0,"raster")2.给空值赋予特定的值(如0)Con(IsNull("raster"),0,"raster")3.将特定的栅格值(如1)赋值为空值,其他保留原值SetNull("raster"==
水平垂直居中的几种方法(总结)
LJ小番茄
CSS_玄学语言 html javascript 前端 css css3
1.使用flexbox的justify-content和align-items.parent{display:flex;justify-content:center;/*水平居中*/align-items:center;/*垂直居中*/height:100vh;/*需要指定高度*/}2.使用grid的place-items:center.parent{display:grid;place-item
本周第二次约练
2cfbdfe28a51
中原焦点团队中24初26刘霞2021.12.3约练161次,分享第368天当事人虽然是带着问题来的,但是咨询过程中发现,她是经过自己不断地调整和努力才走到现在的,看到当事人的不容易,找到例外,发现资源,力量感也就随之而来。增强画面感,或者说重温,会给当事人带来更深刻的感受。
放下是一段成长的修行
小莳玥
人来到这个世界上,只有两件事:生和死。一件事已经做完了,另一件你还急什么呢?是人,都有七情六欲。是心,都有喜怒哀乐,这些再正常不过了。别总抱怨自己活得累,过得辛苦。永远记住:舒坦是留给死人的。苦,才是生活;累,才是工作;变,才是命运;忍,才是历练;容,才是智慧;静,才是修养;舍,才会得到;做,才会拥有。人生,活得太清楚,才是最大的不明白。有些事,看得很清,却说不清;有些人,了解很深,却猜不透;有些
回溯 Leetcode 332 重新安排行程
mmaerd
Leetcode刷题学习记录 leetcode 算法 职场和发展
重新安排行程Leetcode332学习记录自代码随想录给你一份航线列表tickets,其中tickets[i]=[fromi,toi]表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从JFK开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。例如,行程[“JFK”,“LGA”]与[“JFK”,“LGB
Python数据分析与可视化实战指南
William数据分析
python python 数据
在数据驱动的时代,Python因其简洁的语法、强大的库生态系统以及活跃的社区,成为了数据分析与可视化的首选语言。本文将通过一个详细的案例,带领大家学习如何使用Python进行数据分析,并通过可视化来直观呈现分析结果。一、环境准备1.1安装必要库在开始数据分析和可视化之前,我们需要安装一些常用的库。主要包括pandas、numpy、matplotlib和seaborn等。这些库分别用于数据处理、数学
java工厂模式
3213213333332132
java 抽象工厂
工厂模式有
1、工厂方法
2、抽象工厂方法。
下面我的实现是抽象工厂方法,
给所有具体的产品类定一个通用的接口。
package 工厂模式;
/**
* 航天飞行接口
*
* @Description
* @author FuJianyong
* 2015-7-14下午02:42:05
*/
public interface SpaceF
nginx频率限制+python测试
ronin47
nginx 频率 python
部分内容参考:http://www.abc3210.com/2013/web_04/82.shtml
首先说一下遇到这个问题是因为网站被攻击,阿里云报警,想到要限制一下访问频率,而不是限制ip(限制ip的方案稍后给出)。nginx连接资源被吃空返回状态码是502,添加本方案限制后返回599,与正常状态码区别开。步骤如下:
java线程和线程池的使用
dyy_gusi
ThreadPool thread Runnable timer
java线程和线程池
一、创建多线程的方式
java多线程很常见,如何使用多线程,如何创建线程,java中有两种方式,第一种是让自己的类实现Runnable接口,第二种是让自己的类继承Thread类。其实Thread类自己也是实现了Runnable接口。具体使用实例如下:
1、通过实现Runnable接口方式 1 2
Linux
171815164
linux
ubuntu kernel
http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.1.2-unstable/
安卓sdk代理
mirrors.neusoft.edu.cn 80
输入法和jdk
sudo apt-get install fcitx
su
Tomcat JDBC Connection Pool
g21121
Connection
Tomcat7 抛弃了以往的DBCP 采用了新的Tomcat Jdbc Pool 作为数据库连接组件,事实上DBCP已经被Hibernate 所抛弃,因为他存在很多问题,诸如:更新缓慢,bug较多,编译问题,代码复杂等等。
Tomcat Jdbc P
敲代码的一点想法
永夜-极光
java 随笔 感想
入门学习java编程已经半年了,一路敲代码下来,现在也才1w+行代码量,也就菜鸟水准吧,但是在整个学习过程中,我一直在想,为什么很多培训老师,网上的文章都是要我们背一些代码?比如学习Arraylist的时候,教师就让我们先参考源代码写一遍,然
jvm指令集
程序员是怎么炼成的
jvm 指令集
转自:http://blog.csdn.net/hudashi/article/details/7062675#comments
将值推送至栈顶时 const ldc push load指令
const系列
该系列命令主要负责把简单的数值类型送到栈顶。(从常量池或者局部变量push到栈顶时均使用)
0x02 &nbs
Oracle字符集的查看查询和Oracle字符集的设置修改
aijuans
oracle
本文主要讨论以下几个部分:如何查看查询oracle字符集、 修改设置字符集以及常见的oracle utf8字符集和oracle exp 字符集问题。
一、什么是Oracle字符集
Oracle字符集是一个字节数据的解释的符号集合,有大小之分,有相互的包容关系。ORACLE 支持国家语言的体系结构允许你使用本地化语言来存储,处理,检索数据。它使数据库工具,错误消息,排序次序,日期,时间,货
png在Ie6下透明度处理方法
antonyup_2006
css 浏览器 Firebug IE
由于之前到深圳现场支撑上线,当时为了解决个控件下载,我机器上的IE8老报个错,不得以把ie8卸载掉,换个Ie6,问题解决了,今天出差回来,用ie6登入另一个正在开发的系统,遇到了Png图片的问题,当然升级到ie8(ie8自带的开发人员工具调试前端页面JS之类的还是比较方便的,和FireBug一样,呵呵),这个问题就解决了,但稍微做了下这个问题的处理。
我们知道PNG是图像文件存储格式,查询资
表查询常用命令高级查询方法(二)
百合不是茶
oracle 分页查询 分组查询 联合查询
----------------------------------------------------分组查询 group by having --平均工资和最高工资 select avg(sal)平均工资,max(sal) from emp ; --每个部门的平均工资和最高工资
uploadify3.1版本参数使用详解
bijian1013
JavaScript uploadify3.1
使用:
绑定的界面元素<input id='gallery'type='file'/>$("#gallery").uploadify({设置参数,参数如下});
设置的属性:
id: jQuery(this).attr('id'),//绑定的input的ID
langFile: 'http://ww
精通Oracle10编程SQL(17)使用ORACLE系统包
bijian1013
oracle 数据库 plsql
/*
*使用ORACLE系统包
*/
--1.DBMS_OUTPUT
--ENABLE:用于激活过程PUT,PUT_LINE,NEW_LINE,GET_LINE和GET_LINES的调用
--语法:DBMS_OUTPUT.enable(buffer_size in integer default 20000);
--DISABLE:用于禁止对过程PUT,PUT_LINE,NEW
【JVM一】JVM垃圾回收日志
bit1129
垃圾回收
将JVM垃圾回收的日志记录下来,对于分析垃圾回收的运行状态,进而调整内存分配(年轻代,老年代,永久代的内存分配)等是很有意义的。JVM与垃圾回收日志相关的参数包括:
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-Xloggc
-XX:+PrintGC
通
Toast使用
白糖_
toast
Android中的Toast是一种简易的消息提示框,toast提示框不能被用户点击,toast会根据用户设置的显示时间后自动消失。
创建Toast
两个方法创建Toast
makeText(Context context, int resId, int duration)
参数:context是toast显示在
angular.identity
boyitech
AngularJS AngularJS API
angular.identiy 描述: 返回它第一参数的函数. 此函数多用于函数是编程. 使用方法: angular.identity(value); 参数详解: Param Type Details value
*
to be returned. 返回值: 传入的value 实例代码:
<!DOCTYPE HTML>
java-两整数相除,求循环节
bylijinnan
java
import java.util.ArrayList;
import java.util.List;
public class CircleDigitsInDivision {
/**
* 题目:求循环节,若整除则返回NULL,否则返回char*指向循环节。先写思路。函数原型:char*get_circle_digits(unsigned k,unsigned j)
Java 日期 周 年
Chen.H
java C++ c C#
/**
* java日期操作(月末、周末等的日期操作)
*
* @author
*
*/
public class DateUtil {
/** */
/**
* 取得某天相加(减)後的那一天
*
* @param date
* @param num
*
[高考与专业]欢迎广大高中毕业生加入自动控制与计算机应用专业
comsci
计算机
不知道现在的高校还设置这个宽口径专业没有,自动控制与计算机应用专业,我就是这个专业毕业的,这个专业的课程非常多,既要学习自动控制方面的课程,也要学习计算机专业的课程,对数学也要求比较高.....如果有这个专业,欢迎大家报考...毕业出来之后,就业的途径非常广.....
以后
分层查询(Hierarchical Queries)
daizj
oracle 递归查询 层次查询
Hierarchical Queries
If a table contains hierarchical data, then you can select rows in a hierarchical order using the hierarchical query clause:
hierarchical_query_clause::=
start with condi
数据迁移
daysinsun
数据迁移
最近公司在重构一个医疗系统,原来的系统是两个.Net系统,现需要重构到java中。数据库分别为SQL Server和Mysql,现需要将数据库统一为Hana数据库,发现了几个问题,但最后通过努力都解决了。
1、原本通过Hana的数据迁移工具把数据是可以迁移过去的,在MySQl里面的字段为TEXT类型的到Hana里面就存储不了了,最后不得不更改为clob。
2、在数据插入的时候有些字段特别长
C语言学习二进制的表示示例
dcj3sjt126com
c basic
进制的表示示例
# include <stdio.h>
int main(void)
{
int i = 0x32C;
printf("i = %d\n", i);
/*
printf的用法
%d表示以十进制输出
%x或%X表示以十六进制的输出
%o表示以八进制输出
*/
return 0;
}
NsTimer 和 UITableViewCell 之间的控制
dcj3sjt126com
ios
情况是这样的:
一个UITableView, 每个Cell的内容是我自定义的 viewA viewA上面有很多的动画, 我需要添加NSTimer来做动画, 由于TableView的复用机制, 我添加的动画会不断开启, 没有停止, 动画会执行越来越多.
解决办法:
在配置cell的时候开始动画, 然后在cell结束显示的时候停止动画
查找cell结束显示的代理
MySql中case when then 的使用
fanxiaolong
casewhenthenend
select "主键", "项目编号", "项目名称","项目创建时间", "项目状态","部门名称","创建人"
union
(select
pp.id as "主键",
pp.project_number as &
Ehcache(01)——简介、基本操作
234390216
cache ehcache 简介 CacheManager crud
Ehcache简介
目录
1 CacheManager
1.1 构造方法构建
1.2 静态方法构建
2 Cache
2.1&
最容易懂的javascript闭包学习入门
jackyrong
JavaScript
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
下面就是我的学习笔记,对于Javascript初学者应该是很有用的。
一、变量的作用域
要理解闭包,首先必须理解Javascript特殊
提升网站转化率的四步优化方案
php教程分享
数据结构 PHP 数据挖掘 Google 活动
网站开发完成后,我们在进行网站优化最关键的问题就是如何提高整体的转化率,这也是营销策略里最最重要的方面之一,并且也是网站综合运营实例的结果。文中分享了四大优化策略:调查、研究、优化、评估,这四大策略可以很好地帮助用户设计出高效的优化方案。
PHP开发的网站优化一个网站最关键和棘手的是,如何提高整体的转化率,这是任何营销策略里最重要的方面之一,而提升网站转化率是网站综合运营实力的结果。今天,我就分
web开发里什么是HTML5的WebSocket?
naruto1990
Web html5 浏览器 socket
当前火起来的HTML5语言里面,很多学者们都还没有完全了解这语言的效果情况,我最喜欢的Web开发技术就是正迅速变得流行的 WebSocket API。WebSocket 提供了一个受欢迎的技术,以替代我们过去几年一直在用的Ajax技术。这个新的API提供了一个方法,从客户端使用简单的语法有效地推动消息到服务器。让我们看一看6个HTML5教程介绍里 的 WebSocket API:它可用于客户端、服
Socket初步编程——简单实现群聊
Everyday都不同
socket 网络编程 初步认识
初次接触到socket网络编程,也参考了网络上众前辈的文章。尝试自己也写了一下,记录下过程吧:
服务端:(接收客户端消息并把它们打印出来)
public class SocketServer {
private List<Socket> socketList = new ArrayList<Socket>();
public s
面试:Hashtable与HashMap的区别(结合线程)
toknowme
昨天去了某钱公司面试,面试过程中被问道
Hashtable与HashMap的区别?当时就是回答了一点,Hashtable是线程安全的,HashMap是线程不安全的,说白了,就是Hashtable是的同步的,HashMap不是同步的,需要额外的处理一下。
今天就动手写了一个例子,直接看代码吧
package com.learn.lesson001;
import java
MVC设计模式的总结
xp9802
设计模式 mvc 框架 IOC
随着Web应用的商业逻辑包含逐渐复杂的公式分析计算、决策支持等,使客户机越
来越不堪重负,因此将系统的商业分离出来。单独形成一部分,这样三层结构产生了。
其中‘层’是逻辑上的划分。
三层体系结构是将整个系统划分为如图2.1所示的结构[3]
(1)表现层(Presentation layer):包含表示代码、用户交互GUI、数据验证。
该层用于向客户端用户提供GUI交互,它允许用户