Я наконец-то решил провести некоторые эксперименты самостоятельно.
Несколько фактов, касающихся протокола эксперимента:
- Вместо того чтобы искать смещение опорных часов , я просто проверил разности часов между виртуальными машинами Azure и хранилищем Azure .
- Время часов хранилища Azure было получено с помощью HTTP-хака, вставленного ниже.
- Измерения были проведены в центре обработки данных Azure в Северной Европе с 250 небольшими виртуальными машинами.
- Задержка между хранилищем и виртуальными машинами, измеренная с помощью
Stopwatch
, всегда была меньше 1 мс для минималистических неаутентифицированных запросов (в основном HTTP-запросы возвращались с 400 ошибками, но все еще с Date:
доступным в заголовках HTTP).
Результаты:
- Около 50% виртуальных машин имеют смещение тактовой частоты хранения больше 1 с.
- Около 5% виртуальных машин имеют смещение тактовой частоты хранения более 2 с.
- Наблюдения смещений часов менее 1% близки к 3 с.
- Ручные выбросы, близкие к 4 с.
- Смещение тактовой частоты между одной виртуальной машиной и хранилищем обычно варьируется в пределах + 1 / -1 с от одного запроса к следующему.
Технически, мы не слишком далеки от цели допуска 2 с, хотя для синхронизации внутри центра обработки данных вам не нужно далеко продвигаться в эксперименте, чтобы наблюдать близко к смещению 4 с . Если мы предположим, что для смещений тактовых импульсов предполагается нормальное (то есть гауссовское) распределение, то я бы сказал, что использование любого тактового порога ниже 6 с неизбежно приведет к проблемам планирования.
/// <summary>
/// Substitute for proper NTP (Network Time Protocol)
/// when UDP is not available, as on Windows Azure.
/// </summary>
public class HttpTimeChecker
{
public static DateTime GetUtcNetworkTime(string server)
{
// HACK: we can't use WebClient here, because we get a faulty HTTP response
// We don't care about HTTP error, the only thing that matter is the presence
// of the 'Date:' HTTP header
var tc = new TcpClient();
tc.Connect(server, 80);
string response;
using (var ns = tc.GetStream())
{
var sw = new StreamWriter(ns);
var sr = new StreamReader(ns);
string req = "";
req += "GET / HTTP/1.0\n";
req += "Host: " + server + "\n";
req += "\n";
sw.Write(req);
sw.Flush();
response = sr.ReadToEnd();
}
foreach(var line in response.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
if(line.StartsWith("Date: "))
{
return DateTime.Parse(line.Substring(6)).ToUniversalTime();
}
}
throw new ArgumentException("No date to be retrieved among HTTP headers.", "server");
}
}