Недавно мы представили реализацию функции получения среднего времени ответа сервера с помощью команды ping. После публикации статьи нам поступило определенное количество справедливой критики по поводу ненадежности команды ping. По этой причине было решено опубликовать этот рецепт с реализацией того же функционала без использования ping.
Команда ping имеет ряд существенных минусов, из-за которых наша предыдущая реализация может плохо или вообще не работать:
- Зависимость от операционной системы и прав на исполнение команды ping;
- Зависимость от сторонней утилиты (ping);
- На сервере, который пингуем, могут быть заблокированы ICMP-пакеты.
Самым надежным и простым, на наш взгляд, способом получения времени ответа сервера является получение времени открытия TCP-соединения. Открываем соединение, дожидаемся момента, когда оно будет открыто, считаем разницу времени между началом открытия и получением подтверждения об открытии, закрываем соединение. Для надежности повторяем X раз и считаем среднее время ответа. Этот способ не имеет указанных выше недостатков команды ping, кроме того, можно указать нужный нам порт (это одновременно и минус, так как ICMP вообще не использует это понятие).
Реализацию мы описали в 2 функции: однократное обращение к серверу и многократное многопоточное обращение с использованием первой функции:
import std.stdio; void main() { averageResponseTime("lhs-blog.info", 443).writeln; } long pingTime(string host, ushort port, ushort timeout = 400) { import std.socket; import std.datetime; auto socket = new TcpSocket(); // таймауты отправки и приема данных socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"msecs"(timeout)); socket.setOption(SocketOptionLevel.SOCKET, SocketOption.SNDTIMEO, dur!"msecs"(timeout)); socket.setOption(SocketOptionLevel.SOCKET, SocketOption.TCP_NODELAY, 1); InternetAddress addr = new InternetAddress(host, port); // получить текущее системное время immutable auto startTime = MonoTime.currTime; try { // открываем соединение socket.connect(addr); } catch (Exception e) { // если что-то пошло не так, то возвращаем -1 return -1; } // код ниже выполнится только если и после того, как socket.connect // вернет успех, то есть соединение будет установлено immutable auto stopTime = MonoTime.currTime; try { // socket.close; } catch (Exception e) { return -1; } return (stopTime - startTime).total!"msecs"; } long averageResponseTime(string host, ushort port = 80, ushort attempts = 3, ushort timeout = 400) { import std.parallelism; import std.algorithm; auto pings = new long[attempts]; // параллельно заполняем массив foreach (i, ref num; pings.parallel) { num = pingTime(host, port, timeout); } return cast(long) pings.sum / pings.length; }
Эта реализация успешно используется в некоторых наших проектах. Если вам есть что сказать по этой теме (например, предложить свой вариант), ждем в наших социальных сетях (ссылки есть вверху сайтбара).