如何保护应用程序免受虚拟机时间跟踪丢失问题的影响?

How to protect an application against virtual machine time tracking loss issue?

场景

我们有一个用 Java 编写的应用程序。以前在物理机上 运行 很流畅。 已经做出迁移到虚拟机的决定。现在,该应用程序在日志时间戳中经常出现错误。

插图

虚拟化之前

Timestamp    | Caller | Message
00:00:01.735 |  foo   | Downloading something
00:00:05.123 |  foo   | Downloaded something
00:00:05.123 |  bar   | Analyzing something
...
00:00:08.990 |  foo   | END

如您所见,时间戳值在物理机上不断增长。

虚拟化后

Timestamp    | Caller | Message
00:00:01.735 |  foo   | Downloading something
00:00:05.123 |  bar   | Downloaded something
00:00:05.123 |  baz   | Analyzing something
...
00:00:04.485 |  foo   | END

现在,日志显示进程在下载过程中结束。

采取的解决方案

我们已将 VM 与 NTP 服务器同步。这个问题消失了几天。现在回来了。

问题

主机 OS: RHEL 6.5
来宾 OS: RHEL 5.4
虚拟化平台:RHEV 3.4

您必须在不同的选项之间做出选择

  • 时间同步更频繁,时间更精确,但时间戳顺序更不一致...
  • 通过降低时间同步的频率来降低​​时间的精确度,甚至完全禁用它,但时间戳顺序更加一致...

据我所知,不可能具有与硬件计算机相同的准确度。

currentTimeMillis() 是开箱即用的线程安全的,但是因为它基于 OS 的时钟时间,所以如果系统的时钟时间发生变化,它可能会不准确,并且是容易出现 时间漂移 (不准确性的累积增加)。

nanoTime() 开箱即用 不是 线程安全的,而是使用系统的 "atomic clock" (在可能的情况下*),免疫系统时钟的变化,适当如果你objective 只是打印独立于它的事件或事件的时间戳,并且很多 不太容易 在循环中用于 依赖于*,尽管在很长一段时间内,时间漂移会变得很明显。

  • * - 一些特定的 OS 根本不提供或支持 "atomic clocks",在这些特定情况下,Java 没办法只好用普通时钟了。如果您怀疑您的系统可能是其中之一,请 google 关于它。
  • * - 我在这里所说的 "dependent" 操作的意思是当方法或动作被触发或具有值时这取决于自上次调用相同或不同方法以来经过的时间,例如在 游戏循环 中。
    • 发生这种情况是因为时间戳只是一个时间戳,它没有考虑执行操作本身所花费的时间。

这似乎不是你的情况,但如果你遇到这样一种情况,你正在实施 依赖 时间戳的操作,如上一节所述注意上面的部分,你想要做的是实现一个 delta-time,它使用 nanoTime() 时间戳来获取准确计算两次测量之间经过的时间,但这也允许使用增量。

增量时间是执行 动画 的正确方法。

最后,这是 RHEL 5.4 的一个错误。将 Guest OS 升级到 RHEL 5.11 可解决问题。