Я бы сказал, начните с потокового соединения и адаптируйтесь оттуда, если у вас возникнут проблемы.
Если вам действительно нужно обработать миллион соединений, вам следует подумать о написании (или поиске) простого посредника запросов в C (или чего бы то ни было), который будет использовать гораздо меньше памяти на соединение, чем любая реализация Java. Брокер может получать запросы асинхронно и ставить их в очередь для рабочих, написанных на выбранном вами языке.
Таким образом, бэкэндам нужен только поток для каждого активного запроса, и вы можете просто иметь фиксированное их количество, чтобы использование памяти и базы данных было в определенной степени предопределено. Когда большое количество запросов выполняется параллельно, запросы заставляют ждать немного дольше.
Таким образом, я думаю, что вам никогда не придется прибегать к выбору каналов NIO или асинхронному вводу / выводу (NIO 2) в 64-битных системах. Модель потокового соединения работает достаточно хорошо, и вы можете выполнить масштабирование до «десятков или сотен тысяч» соединений, используя более подходящую технологию низкого уровня.
Всегда полезно избегать преждевременной оптимизации (т. Е. Писать код NIO до того, как у вас появится огромное количество подключений) и не изобретать колесо (Jetty, nginx и т. Д.), Если это возможно.