Далее приводится обсуждение NTCP, которое состоялось в марте 2007 года. Оно не было обновлено с учетом текущей реализации. Актуальную спецификацию NTCP см. на странице NTCP2 .
Обсуждение NTCP против SSU, март 2007
Вопросы по NTCP
(адаптировано из IRC-обсуждения между zzz и cervantes)
Почему NTCP предпочтительнее SSU, разве у NTCP не больше накладных расходов и задержка? У него лучшая надежность.
Разве streaming lib поверх NTCP не страдает от классических проблем TCP-over-TCP? Что если у нас был бы действительно простой UDP транспорт для трафика, исходящего от streaming-lib? Я думаю, SSU должен был быть так называемым действительно простым UDP транспортом - но он просто оказался слишком ненадежным.
Анализ “NTCP Considered Harmful” от zzz
Опубликовано в новом Syndie, 2007-03-25. Это было опубликовано для стимулирования обсуждения, не принимайте это слишком серьезно.
Резюме: NTCP имеет более высокую задержку и накладные расходы по сравнению с SSU, и более склонен к сбоям при использовании с библиотекой потоковой передачи. Однако трафик маршрутизируется с предпочтением NTCP над SSU, и это в настоящее время жестко задано в коде.
Обсуждение
В настоящее время у нас есть два транспорта, NTCP и SSU. В текущей реализации NTCP имеет более низкие “ставки”, чем SSU, поэтому он предпочтителен, за исключением случаев, когда для узла существует установленное SSU соединение, но нет установленного NTCP соединения.
SSU похож на NTCP в том, что он реализует подтверждения, тайм-ауты и повторные передачи. Однако SSU — это код I2P со строгими ограничениями на тайм-ауты и доступной статистикой по времени круговой задержки, повторным передачам и т.д. NTCP основан на Java NIO TCP, который представляет собой черный ящик и предположительно реализует стандарты RFC, включая очень длинные максимальные тайм-ауты.
Большинство трафика в I2P происходит из streaming-lib (HTTP, IRC, Bittorrent), который является нашей реализацией TCP. Поскольку транспорт нижнего уровня обычно использует NTCP из-за более низких ставок, система подвержена хорошо известной и страшной проблеме TCP-over-TCP http://sites.inka.de/~W1011/devel/tcp-tcp.html , где как верхние, так и нижние уровни TCP одновременно выполняют повторные передачи, что приводит к коллапсу.
В отличие от сценария PPP over SSH, описанного в ссылке выше, у нас есть несколько переходов для нижнего уровня, каждый из которых покрывается NTCP-соединением. Поэтому каждая задержка NTCP обычно намного меньше, чем задержка библиотеки потокового вещания верхнего уровня. Это снижает вероятность коллапса.
Кроме того, вероятность коллапса снижается, когда TCP нижнего уровня жестко ограничен низкими таймаутами и малым количеством повторных передач по сравнению с верхним уровнем.
Релиз .28 увеличил максимальный таймаут библиотеки потоковой передачи с 10 сек до 45 сек, что значительно улучшило ситуацию. Максимальный таймаут SSU составляет 3 сек. Максимальный таймаут NTCP предположительно составляет не менее 60 сек, что является рекомендацией RFC. Нет способа изменить параметры NTCP или мониторить производительность. Коллапс слоя NTCP [редактор: текст потерян]. Возможно, внешний инструмент типа tcpdump помог бы.
Однако, при работе на .28, upstream в i2psnark обычно не держится на высоком уровне. Он часто падает до 3-4 КБ/с, прежде чем снова подняться. Это сигнал о том, что обрывы все еще происходят.
SSU также более эффективен. NTCP имеет более высокие накладные расходы и, вероятно, более высокое время обратного пути. При использовании NTCP соотношение (выходные данные туннеля) / (выходные данные i2psnark) составляет как минимум 3.5 : 1. При проведении эксперимента, где код был модифицирован для предпочтения SSU (параметр конфигурации i2np.udp.alwaysPreferred не действует в текущем коде), соотношение снизилось до примерно 3 : 1, что указывает на лучшую эффективность.
По данным статистики библиотеки потоковой передачи, показатели значительно улучшились - размер окна за время жизни увеличился с 6.3 до 7.5, RTT снизился с 11.5с до 10с, отправок на подтверждение уменьшилось с 1.11 до 1.07.
То, что это оказалось весьма эффективным, было удивительно, учитывая, что мы изменяли транспорт только для первого из 3-5 общих hops, которые проходят исходящие сообщения.
Влияние на скорость исходящего трафика i2psnark было неясным из-за обычных колебаний. Также для эксперимента входящий NTCP был отключен. Влияние на скорость входящего трафика в i2psnark было неясным.
Предложения
1A) Это просто - Мы должны изменить приоритеты заявок так, чтобы SSU был предпочтительным для всего трафика, если мы можем сделать это без возникновения всевозможных других проблем. Это исправит опцию конфигурации i2np.udp.alwaysPreferred, чтобы она работала (либо как true, либо как false).
1B) Альтернатива 1A), не так просто - Если мы можем пометить трафик, не нанося ущерба нашим целям анонимности, мы должны идентифицировать трафик, генерируемый streaming-lib, и заставить SSU генерировать низкую ставку для этого трафика. Этот тег должен будет сопровождать сообщение через каждый hop так, чтобы пересылающие router’ы также учитывали предпочтение SSU.
2) Дальнейшее ограничение SSU (снижение максимального количества повторных передач с текущих 10) вероятно разумно для уменьшения вероятности коллапса.
3) Нам нужно дополнительное исследование преимуществ и недостатков полунадежного протокола под библиотекой потоковой передачи. Приносят ли повторные передачи через один переход пользу и являются ли большим выигрышем, или они хуже чем бесполезны? Мы могли бы сделать новый SUU (secure unreliable UDP), но вероятно это не стоит того. Мы могли бы возможно добавить тип сообщения без требования подтверждения в SSU, если мы вообще не хотим никаких повторных передач трафика streaming-lib. Желательны ли строго ограниченные повторные передачи?
4) Код приоритетной отправки в .28 работает только для NTCP. Пока что мои тесты не показали большой пользы от приоритетов SSU, поскольку сообщения не стоят в очереди достаточно долго, чтобы приоритеты принесли какую-либо пользу. Но нужно больше тестирования.
5) Новый максимальный таймаут streaming lib в 45 секунд вероятно всё ещё слишком мал. RFC TCP указывает 60 секунд. Он вероятно не должен быть короче максимального таймаута базового NTCP (предположительно 60 секунд).
Ответ от jrandom
Опубликовано в новом Syndie, 2007-03-27
В целом, я открыт для экспериментов с этим, но помните, зачем изначально появился NTCP - SSU провалился при коллапсе перегрузки. NTCP “просто работает”, и хотя частота повторных передач 2-10% может обрабатываться в обычных одноузловых сетях, это дает нам частоту повторных передач 40% с 2-узловыми tunnel. Если учесть некоторые измеренные частоты повторных передач SSU, которые мы наблюдали до внедрения NTCP (10-30+%), это дает нам частоту повторных передач 83%. Возможно, эти показатели были вызваны низким таймаутом в 10 секунд, но такое значительное увеличение навредит нам (помните, умножьте на 5 и получите половину пути).
В отличие от TCP, у нас нет обратной связи от tunnel, чтобы знать, дошло ли сообщение - нет подтверждений на уровне tunnel. У нас есть сквозные ACK, но только для небольшого количества сообщений (всякий раз, когда мы распространяем новые session tags) - из 1,553,591 клиентских сообщений, отправленных моим router, мы пытались подтвердить получение только 145,207 из них. Остальные могли незаметно не дойти или успешно доставиться.
Меня не убеждает аргумент TCP-поверх-TCP для нас, особенно при разделении по различным путям передачи данных. Измерения в I2P, конечно, могут убедить меня в обратном.
Максимальный таймаут NTCP предположительно составляет не менее 60 секунд, что является рекомендацией RFC. Не существует способа изменить параметры NTCP или отслеживать производительность.
Верно, но сетевые соединения достигают такого уровня только когда происходит что-то действительно плохое - тайм-аут повторной передачи в TCP часто составляет порядка десятков или сотен миллисекунд. Как отмечает foofighter, у них есть 20+ лет опыта и исправления ошибок в их TCP стеках, плюс индустрия стоимостью в миллиарды долларов, оптимизирующая аппаратное и программное обеспечение для хорошей работы в соответствии с тем, что они делают.
NTCP имеет более высокие издержки и, вероятно, большее время кругового обхода. При использовании NTCP соотношение (выход туннеля) / (выход данных i2psnark) составляет как минимум 3,5 : 1. В ходе эксперимента, где код был изменен для предпочтения SSU (параметр конфигурации i2np.udp.alwaysPreferred не действует в текущем коде), соотношение сократилось примерно до 3 : 1, что указывает на лучшую эффективность.
Это очень интересные данные, хотя больше с точки зрения перегрузки router’а, чем эффективности пропускной способности - вам нужно было бы сравнить 3.5*$n*$NTCPRetransmissionPct ./. 3.0*$n*$SSURetransmissionPct. Эта точка данных предполагает, что в router’е есть что-то, что приводит к избыточному локальному накоплению в очереди сообщений, которые уже передаются.
размер окна времени жизни увеличен с 6.3 до 7.5, RTT снижен с 11.5с до 10с, отправок на ACK снижено с 1.11 до 1.07.
Помните, что отправки-на-ACK — это только выборка, а не полный подсчет (поскольку мы не пытаемся подтверждать каждую отправку ACK). Это также не случайная выборка, а выборка, которая более интенсивно охватывает периоды бездействия или начало всплеска активности — устойчивая нагрузка не потребует много ACK.
Размеры окон в этом диапазоне по-прежнему катастрофически малы для получения реальной выгоды от AIMD и все еще слишком малы для передачи одного 32КБ BT-чанка (увеличение нижнего предела до 10 или 12 покрыло бы это).
Тем не менее, статистика wsize выглядит многообещающе - как долго она поддерживалась?
Собственно, для целей тестирования вы можете захотеть взглянуть на StreamSinkClient/StreamSinkServer или даже TestSwarm в apps/ministreaming/java/src/net/i2p/client/streaming/ - StreamSinkClient это CLI приложение, которое отправляет выбранный файл в выбранное назначение, а StreamSinkServer создает назначение и записывает любые отправленные ему данные (отображая размер и время передачи). TestSwarm объединяет оба - заливает случайные данные тому, к кому подключается. Это должно дать вам инструменты для измерения устойчивой пропускной способности через библиотеку streaming, в отличие от BT choke/send.
1A) Это просто - > Мы должны изменить приоритеты заявок так, чтобы SSU предпочиталось для всего трафика, если > мы можем сделать это, не вызывая множество других проблем. Это исправит > параметр конфигурации i2np.udp.alwaysPreferred, чтобы он работал (либо как true > либо как false).
Учет настройки i2np.udp.alwaysPreferred — это в любом случае хорошая идея, пожалуйста, не стесняйтесь зафиксировать это изменение. Но давайте соберем немного больше данных, прежде чем переключать предпочтения, поскольку NTCP был добавлен для решения проблемы коллапса перегрузки, вызванного SSU.
1B) Альтернатива к 1A), не так просто - > Если мы можем маркировать трафик без негативного влияния на наши цели анонимности, мы > должны идентифицировать трафик, сгенерированный streaming-lib > и заставить SSU генерировать низкую ставку для этого трафика. Этот тег должен будет идти с > сообщением через каждый hop > так, чтобы маршрутизирующие router’ы также учитывали предпочтение SSU.
На практике существует три типа трафика - построение/тестирование туннелей, запросы/ответы netDb и трафик библиотеки потоковой передачи. Сеть была спроектирована таким образом, чтобы различать эти три типа было очень сложно.
2) Дальнейшее ограничение SSU (уменьшение максимального количества повторных передач с текущих > 10) вероятно разумно для снижения вероятности коллапса.
При 10 повторных передачах мы уже в глубокой заднице, согласен. Одна, может быть две повторные передачи разумны с точки зрения транспортного уровня, но если другая сторона слишком перегружена, чтобы отправить ACK вовремя (даже с реализованной возможностью SACK/NACK), мы мало что можем сделать.
На мой взгляд, чтобы действительно решить основную проблему, нам нужно выяснить, почему router так перегружается, что не успевает отправить ACK вовремя (что, по моим наблюдениям, происходит из-за конкуренции за CPU). Возможно, мы можем перестроить некоторые процессы в обработке router’а, чтобы передача данных по уже существующему tunnel’у имела более высокий приоритет CPU, чем расшифровка нового запроса на создание tunnel’а? Хотя нужно быть осторожными, чтобы избежать голодания процессов.
3) Нам нужно дальнейшее изучение преимуществ против вреда полунадежного протокола > под библиотекой потоковой передачи. Являются ли повторные передачи через один hop полезными > и большим выигрышем, или они хуже чем бесполезны? > Мы могли бы создать новый SUU (secure unreliable UDP), но вероятно это того не стоит. Мы > могли бы возможно добавить тип сообщения не-требующий-ACK в SSU, если мы вообще не хотим > повторных передач трафика streaming-lib. Желательны ли строго ограниченные > повторные передачи?
Стоит изучить - что если мы просто отключим повторные передачи SSU? Это, вероятно, приведет к значительно более высоким показателям повторной отправки в потоковой библиотеке, но возможно и нет.
4) Код приоритетной отправки в .28 предназначен только для NTCP. Пока что мои тесты не показали особой пользы от приоритетов SSU, поскольку сообщения не накапливаются в очереди достаточно долго, чтобы приоритеты могли принести пользу. Но необходимо больше тестирования.
Есть UDPTransport.PRIORITY_LIMITS и UDPTransport.PRIORITY_WEIGHT (учитываются TimedWeightedPriorityMessageQueue), но в настоящее время веса почти все одинаковые, поэтому эффекта нет. Это можно настроить, конечно (но как вы упоминаете, если нет очереди, это не имеет значения).
5) Новый максимальный тайм-аут библиотеки потоковой передачи в 45 секунд, вероятно, все еще слишком мал. RFC TCP указывает 60 секунд. Он, вероятно, не должен быть короче максимального тайм-аута базового NTCP (предположительно 60 секунд).
Эти 45 секунд — это максимальный тайм-аут повторной передачи библиотеки потокового вещания, а не тайм-аут потока. В TCP на практике тайм-ауты повторной передачи на порядки меньше, хотя да, могут достигать 60 секунд на каналах через открытые провода или спутниковые передачи ;) Если мы увеличим тайм-аут повторной передачи библиотеки потокового вещания, например, до 75 секунд, мы сможем сходить за пивом, прежде чем загрузится веб-страница (особенно при транспорте с надёжностью менее 98%). Это одна из причин, по которой мы предпочитаем NTCP.
Ответ от zzz
Опубликовано в новом Syndie, 2007-03-31
При 10 повторных передачах мы уже в глубокой заднице, согласен. Одна, может две > повторные передачи разумны с точки зрения транспортного уровня, но если другая сторона > слишком перегружена, чтобы отправить ACK вовремя (даже с реализованной возможностью SACK/NACK), > мы мало что можем сделать. > > С моей точки зрения, чтобы действительно решить основную проблему, нам нужно разобраться, почему > router становится настолько перегруженным, что не может отправить ACK вовремя (что, по моим наблюдениям, происходит из-за > конкуренции за CPU). Возможно, мы можем перестроить некоторые вещи в обработке router’а, чтобы > сделать передачу по уже существующему tunnel’у более высоким приоритетом CPU, чем > расшифровка нового запроса tunnel’а? Хотя мы должны быть осторожны, чтобы избежать > голодания.
Один из моих основных методов сбора статистики — включить net.i2p.client.streaming.ConnectionPacketHandler=DEBUG и наблюдать за временем RTT и размерами окон по мере их изменения. Если обобщить, то обычно можно увидеть 3 типа соединений: ~4с RTT, ~10с RTT и ~30с RTT. Цель — снизить количество соединений с RTT в 30 секунд. Если причиной является конкуренция за CPU, то возможно поможет некоторое перераспределение нагрузки.
Уменьшение максимального количества ретрансмиссий SSU с 10 — это просто попытка наугад, поскольку у нас нет достоверных данных о том, происходит ли коллапс, есть ли проблемы с TCP-over-TCP или что-то еще, поэтому необходимо больше данных.
Стоит изучить - что если мы просто отключим ретрансляции SSU? Это > вероятно приведет к гораздо более высокой частоте повторной отправки в streaming lib, но возможно и нет.
Что я не понимаю, если вы могли бы пояснить, так это преимущества повторных передач SSU для трафика, не использующего streaming-lib. Нужно ли сообщениям туннелей (например) использовать полунадежный транспорт, или они могут использовать ненадежный или условно надежный транспорт (максимум 1 или 2 повторные передачи, например)? Другими словами, зачем полунадежность?
(но как вы упоминаете, если нет очереди, это не имеет значения).
Я реализовал приоритетную отправку для UDP, но она срабатывала примерно в 100 000 раз реже, чем код на стороне NTCP. Возможно, это подсказка для дальнейшего исследования или намёк — я не понимаю, почему на NTCP это происходило бы намного чаще, но может быть, это намёк на то, почему NTCP работает хуже.
Вопрос, отвеченный jrandom
Опубликовано в новом Syndie, 2007-03-31
измеренные показатели ретрансмиссии SSU, которые мы наблюдали до реализации NTCP > (10-30+%) > > Может ли сам router измерить это? Если да, можно ли выбирать транспорт на основе > измеренной производительности? (т.е. если SSU соединение с узлом теряет > неприемлемое количество сообщений, предпочитать NTCP при отправке этому узлу)
Да, в настоящее время это использует эту статистику как простейший способ определения MTU (если частота повторных передач высокая, используется малый размер пакета, но если низкая, то используется большой размер пакета). Мы попробовали несколько подходов при первом внедрении NTCP (и при первом переходе от оригинального TCP транспорта), которые предпочитали бы SSU, но легко отказывались от этого транспорта для пира, заставляя его возвращаться к NTCP. Однако, безусловно, в этом отношении можно было бы сделать больше, хотя это быстро усложняется (как/когда корректировать/сбрасывать заявки, делиться ли этими предпочтениями между несколькими пирами или нет, делиться ли этим между несколькими сессиями с одним и тем же пиром (и как долго), и т.д.).
Ответ от foofighter
Опубликовано в новом Syndie, 2007-03-26
Если я правильно понял, основная причина в пользу TCP (в целом, как старой, так и новой разновидности) заключалась в том, что не нужно беспокоиться о написании хорошего TCP стека. Что не является невозможно сложной задачей… просто существующие TCP стеки имеют 20-летнее преимущество.
Насколько мне известно, не было много глубокой теории относительно предпочтения TCP перед UDP, за исключением следующих соображений:
- Сеть только с TCP очень зависит от доступных узлов (тех, которые могут перенаправлять входящие соединения через свой NAT)
- Тем не менее, даже если доступные узлы редки, их высокая пропускная способность несколько смягчает проблемы топологической нехватки
- UDP позволяет “пробивать NAT”, что дает людям возможность быть “в некотором роде псевдо-доступными” (с помощью introducers), которые иначе могли бы только исходящие соединения
- “Старая” реализация TCP транспорта требовала много потоков, что убивало производительность, в то время как “новый” TCP транспорт хорошо работает с малым количеством потоков
- Router’ы группы A падают при насыщении UDP. Router’ы группы B падают при насыщении TCP.
- “Ощущается” (то есть есть некоторые признаки, но нет научных данных или качественной статистики), что A развернута шире, чем B
- Некоторые сети передают не-DNS UDP датаграммы с откровенно дерьмовым качеством, в то время как все еще как-то заботятся о передаче TCP потоков.
На этом фоне небольшое разнообразие транспортов (столько, сколько нужно, но не больше) кажется разумным в любом случае. Какой должен быть основным транспортом, зависит от их производительности. Я видел неприятные вещи на своей линии, когда пытался использовать её полную пропускную способность с UDP. Потери пакетов на уровне 35%.
Мы определенно можем попробовать поиграть с приоритетами UDP против TCP, но я настоятельно рекомендую быть осторожным в этом вопросе. Я настоятельно советую не изменять их слишком радикально все сразу, иначе это может что-то сломать.
Ответ от zzz (для foofighter)
Опубликовано в новом Syndie, 2007-03-27
Насколько мне известно, не было много глубокой теории относительно предпочтения TCP перед UDP, за исключением следующих соображений:
Это все обоснованные вопросы. Однако вы рассматриваете два протокола изолированно, а не думаете о том, какой транспортный протокол лучше всего подходит для конкретного протокола более высокого уровня (например, streaming lib или нет).
Я имею в виду, что вы должны принять во внимание библиотеку потоковой передачи.
Поэтому либо измените настройки для всех, либо обрабатывайте трафик потоковых библиотек по-другому.
Именно об этом говорит мое предложение 1B) - иметь разные приоритеты для трафика streaming-lib по сравнению с трафиком не streaming-lib (например, сообщения построения туннелей).
На этом фоне небольшое разнообразие транспортов (столько, сколько нужно, но > не больше) представляется разумным в любом случае. Какой должен быть основным транспортом, > зависит от их производительности. Я видел неприятные вещи на своей линии, когда > пытался использовать её полную пропускную способность с UDP. Потери пакетов на уровне 35%.
Согласен. Новая версия .28, возможно, улучшила ситуацию с потерей пакетов через UDP, а может и нет.
Один важный момент - код транспорта действительно запоминает сбои транспорта. Поэтому если UDP является предпочтительным транспортом, он попробует его первым, но если он не сработает для определенного пункта назначения, при следующей попытке для этого пункта назначения он попробует NTCP, а не будет снова пытаться использовать UDP.
Мы определенно можем попробовать поиграть с приоритетами UDP против TCP, но я призываю к осторожности в этом вопросе. Я настоятельно рекомендую не изменять их слишком радикально сразу, иначе это может всё сломать.
У нас есть четыре настроечных параметра - четыре значения ставок (SSU и NTCP, для уже подключенных и еще не подключенных). Мы могли бы сделать так, чтобы SSU предпочитался перед NTCP только в том случае, если оба подключены, например, но пробовать NTCP первым, если ни один из транспортов не подключен.
Другой способ сделать это постепенно — перенести только трафик streaming lib (предложение 1B), однако это может быть сложно и иметь последствия для анонимности, я не знаю. Или, возможно, перенести трафик только для первого исходящего хопа (т.е. не передавать флаг следующему router), что дает только частичную выгоду, но может быть более анонимно и проще.
Результаты обсуждения
… и другие связанные изменения в том же временном периоде (2007):
- Значительная настройка параметров библиотеки streaming, существенно повышающая производительность исходящих соединений, была реализована в версии 0.6.1.28
- Приоритетная отправка для NTCP была реализована в версии 0.6.1.28
- Приоритетная отправка для SSU была реализована zzz, но так и не была зафиксирована
- Расширенное управление ставками транспорта i2np.udp.preferred было реализовано в версии 0.6.1.29.
- Pushback для NTCP был реализован в версии 0.6.1.30, отключён в версии 0.6.1.31 из-за проблем с анонимностью, и снова включён с улучшениями для решения этих проблем в версии 0.6.1.32.
- Ни одно из предложений zzz 1-5 не было реализовано.