Заставить UDP терять меньше сообщений в .NET / Mono - PullRequest
1 голос
/ 30 октября 2010

В настоящее время мы выполняем некоторые тесты для академического проекта с открытым исходным кодом, Logbus-ng . Он в основном реализует протокол Syslog по UDP (RFC 5426) и TLS (RFC 5425). Мы знаем, что преимуществом TLS является надежность (т. Е. Мы не потеряем сообщения), но с недостатком производительности.

У нас есть клиент для тестирования, а также специальная поддельная установка Apache, которая отправляет сообщения с высокой скоростью. Наша цель - снизить потери пакетов UDP до минимума. Apache 1.3.41 был инструментирован для отправки специальных сообщений журнала через UDP (не в формате Syslog, а в специальном коротком синтаксисе, который мы анализируем на стороне сервера), и такой инструментарий заставляет отправлять более 2000 сообщений при запуске httpd, и мы хотим, чтобы это произошло :)

Более того, я могу вам сказать, что на этапе развертывания Apache это небольшое количество сообщений (по сравнению с другими рабочими нагрузками, которые мы отправляли на сервер журналов) отправляется с чрезвычайно высокой скорость , возможно затопление UDP.

Теперь сервер журналов расположен на другом компьютере, нежели HTTP-сервер, и оба имеют едва приличное оборудование (даже не двухъядерный процессор, а Pentium 4 с HyperThread). Код сервера журнала находится в C #. Следующий метод запускается четырьмя потоками с приоритетом AboveNormal

    UdpClient _client;
    IQueue<T>[] _byteQueues; //not really IQueue, but a special FIFO queue class that reduces overhead to the minimum

    private void ListenerLoop()
    {
        IPEndPoint remoteEndpoint = new IPEndPoint(IPAddress.Any, 0);
        while (_listen)
        {
            try
            {
                byte[] payload = _client.Receive(ref remoteEndpoint);

                _byteQueues[
                    (((Interlocked.Increment(ref _currentQueue))%WORKER_THREADS) + WORKER_THREADS)%WORKER_THREADS].
                    Enqueue(payload);
            }
            catch (SocketException)
            {
            }
            catch (Exception)
            {
            } //Really do nothing? Shouldn't we stop the service?
        }
    }

Чтобы сократить время, которое поток проводит за пределами метода Receive, мы не анализируем полученное сообщение, а храним его в одной из 4 специальных очередей, которые будут прочитаны другими рабочими потоками. Насколько я знаю, .NET планировщик жадный, поэтому независимо от того, как долго ожидают потоки, потоки с более высоким приоритетом будут запланированы раньше и могут вызвать голодание, поэтому мы в настоящее время не заботимся об увеличении количества потоков в приложение (их около 20).

Мы не только увеличиваем приоритет потока, но и пытаемся увеличить размер буфера UDP до 1 МБ. Вот фрагмент кода инициализации

try
{
    Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
                            {
#if !MONO
                                //Related to Mono bug 643475
                                ExclusiveAddressUse = true,
#endif
                            };

    if (ReceiveBufferSize >= 0) clientSock.ReceiveBufferSize = ReceiveBufferSize;
    clientSock.Bind(localEp);
    _client = new UdpClient {Client = clientSock};
}
catch (SocketException ex)
{
    throw new LogbusException("Cannot start UDP listener", ex);
}

ReceiveBufferSize настраивается во время выполнения ...

Каждое сообщение журнала, отправленное Apache, очень короткое, я думаю, не более 50 байтов. В нашей лаборатории мы используем гигабитный Ethernet.

Во время последнего эксперимента с такой конфигурацией сервер журналов получил только 700+ из более чем 2900 сгенерированных. Wireshark сообщил о более чем 2900 сообщений на сокете UDP, но трассировка журнала Logbus (которая хранит все полученные сообщения в файл) сообщает только об этих 700/800. Выполните команду cat /proc/net/udp и обманите lsof, чтобы найти правильные отчеты о строках много отброшенных пакетов. Журналы были определенно отправлены с очень высокой скоростью. Если мы изменим ядро ​​Apache на короткое время (чуть меньше миллисекунды) после каждого вызова журнала, мы снизим потери до нуля, но производительность также снизится до почти-ноль . Мы проведем такой тест, но мы должны доказать эффективность Logbus-ng в реальных сценариях: (

Мои прямые вопросы

  1. Помогает ли UdpClient.ReceiveBufferSize предотвращать потерю пакетов? Что еще я могу сделать в C #?
  2. Очевидно, предполагается также работать в Mono, но знаете ли вы о возможных ошибках с этим свойством? Я имею в виду, кто-нибудь когда-нибудь сообщал об ошибке? (Моно 2,8)
  3. Знаете ли вы, может ли отправка пакетов на локальный хост сначала уменьшить потерю пакетов? (Я бы запустил специальный экземпляр сервера журналов на компьютере с веб-сервером, а затем перенаправил журналы через TLS, который не теряет, на настоящий сервер журналов)
  4. Что бы вы посоветовали мне уменьшить коэффициент потерь

В настоящее время мы должны выполнить специальный тест с Apache, и мы можем только использовать UDP для доставки сообщений. Мы не можем выбрать TLS, потому что для этого у нас есть только C # API.

Заранее спасибо за любую помощь. Я надеюсь, что было ясно. Вы можете найти исходный код приемника UDP на SVN , если это поможет

Ответы [ 3 ]

1 голос
/ 30 октября 2010

ReceiveBufferSize определенно влияет на сокеты UDP (т. Е. UdpClient), если потеря пакета происходит из-за переполнения буфера, тогда да, увеличение ReceiveBufferSize поможет.

Имейте в виду, что если скорость передачи данных настолько высока, что вы просто не можете читать из буфера достаточно быстро достаточно долго, то неизбежно, что вы переполните даже самый большой из буферов.

Я эффективно использовал UdpClient.Client.ReceiveBufferSize в Mono 2.6.7, работающем в Ubuntu, поэтому я считаю, что реализация Mono в порядке, конечно, я еще не использовал это в Mono 2.8.

По моему опыту, при отправке пакетов UDP на локальный хост с очень высокой скоростью возможна некоторая потеря пакетов, хотя я никогда не сталкивался с такой потерей пакетов в реальном приложении. Таким образом, вы могли бы иметь некоторый успех с этим подходом.

Вам также нужно посмотреть, где происходит потеря пакетов, возможно, потеря пакетов связана с сетевой инфраструктурой, коллизиями пакетов, коммутатор может отбрасывать пакеты из-за некоторого ограничения на коммутатор.

Проще говоря, вы должны быть готовы к обработке и ожидать потери пакетов при использовании UDP.

0 голосов
/ 08 июля 2015

Очевидно, что он также должен работать в Mono, но знаете ли вы о возможных ошибках с этим свойством?Я имею в виду, кто-нибудь когда-нибудь сообщал об ошибке?(Mono 2.8)

Очевидно, что установка размера буфера приема сокета была сильно нарушена в моно до версии 3.2.7: вместо указанного размера он получал бы случайные значения, поэтому пыталсяувеличение размера буфера может фактически ухудшить производительность: - (

https://github.com/mono/mono/commit/bbd4ee4181787189fbb1f8ba6364afdd982ae706

0 голосов
/ 09 января 2014

Если вы используете udp, вы должны ожидать потерю пакета.Есть много причин для потери пакетов.Для вас, скорее всего, из-за переполнения пакетов в любом месте канала.может быть вашим переключателем или может быть вашим приемником.Причина переполнения заключается в том, что UDP не имеет какого-либо управления перегрузкой.Tcp имеет управление перегрузкой (медленный запуск), поэтому при tcp его переполнение никогда (теоретически в идеальной среде) не происходит.

Многообещающим способом предотвращения переполнения для передачи udp является использование стратегии управления перегрузкой медленного старта tcp вручную.

Чтобы ответить на ваши прямые вопросы 1. Нет. См. Алгоритм медленного запуска tcp.2. Скорее всего, это не ошибка.UDP разработан таким образом.Есть причина для этого и есть необходимость в этом.3. Нет. Прокси не помогает.4. Простейшей реализацией для борьбы с потерей пакетов из-за переполнения является ожидание подтверждения от получателя (с учетом того, что получатель успешно принял пакет) перед отправкой большего количества пакетов.Конечно, это не помогает в борьбе с потерей пакетов по другой причине.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...