Java през 2011 г.: гнезда с резба VS NIO: какво да избера при 64-битова ОС и най-новата версия на Java?

Прочетох няколко публикации за java.net срещу java.nio тук в StackOverflow и в някои блогове. Но все още не мога да разбера кога трябва да се предпочете NIO пред резбовани гнезда. Можете ли да разгледате заключенията ми по-долу и да ми кажете кои от тях са неправилни и кои са пропуснати?

  • Тъй като в нишковия модел трябва да отделите нишка за всяка активна връзка и всяка нишка отнема около 250 килобайта памет за своя стек, с нишка на сокет модел бързо ще изчерпите паметта си при голям брой едновременни връзки. За разлика от NIO.

  • В съвременните операционни системи и процесори голям брой активни нишки и време за превключване на контекста могат да се считат за почти незначителни за производителността

  • Пълната производителност на NIO може да бъде по-ниска, защото select() и poll(), използвани от асинхронни NIO библиотеки в среди с голямо натоварване, са по-скъпи от събуждането и поставянето в режим на заспиване на нишки.

  • NIO винаги е бил по-бавен, но ви позволява да обработвате повече едновременни връзки. По същество това е компромис време/пространство: традиционният IO е по-бърз, но има по-голям отпечатък от паметта, NIO е по-бавен, но използва по-малко ресурси.

  • Java има твърдо ограничение за едновременни нишки от 15000 / 30000 в зависимост от JVM и това ще ограничи нишката за модел на връзка до този максимален брой едновременни връзки, но JVM7 няма да има такова ограничение (не мога да потвърдя тези данни).

И така, като заключение можете да имате това:

  • Ако имате десетки хиляди едновременни връзки - NIO е по-добър избор, освен ако скоростта на обработка на заявката не е ключов фактор за вас
  • Ако имате по-малко от това - нишка на връзка е по-добър избор (като се има предвид, че можете да си позволите количество RAM, за да задържите стекове от всички едновременни нишки до максимума)
  • С Java 7 може да искате да преминете към NIO 2.0 и в двата случая.

Прав ли съм?


person Vladislav Rastrusny    schedule 25.03.2011    source източник


Отговори (5)


Това ми се струва правилно, с изключение на частта за Java, която ограничава броя на нишките – това обикновено е ограничено от операционната система, на която работи (вижте Колко нишки може да поддържа Java VM? и Не мога да преодолея 2542 Threads в Java на 4GB iMac OSX 10.6.3 Snow Leopard (32bit )).

За да достигнете толкова много нишки, вероятно ще трябва да коригирате размера на стека на JVM.

person Adam Bryzak    schedule 25.03.2011
comment
Друго нещо, което трябва да отбележите е, че ако използвате всички тези връзки, за да говорите със сървър (т.е. вие сте клиентската страна на връзката), вие сте ограничени до около 65 000 връзки поради броя на наличните локални портове. - person Adam Bryzak; 25.03.2011
comment
Не, не си. Всеки клиент се свързва към един и същ порт. Между другото, приемам тези теми, тъй като няма твърдо ограничение? Какво казва Пол Тума тук тогава: paultyma.blogspot.com/2008 /03/ - person Vladislav Rastrusny; 25.03.2011
comment
Те се свързват към един и същи порт от страна на сървъра, но клиентските връзки все още трябва да разпределят порт локално, за да получат отговор от сървъра. - person Adam Bryzak; 25.03.2011
comment
Това не може да е вярно, защото в този случай това ще са лъжите: groovy.dzone.com/ articles/512000-concurrent-websockets | coversant.net/product/soapboxserver.aspx: и двете разказват за едновременни връзки на единична машина. - person Vladislav Rastrusny; 25.03.2011
comment
Ако настроите машина с множество IP адреси (чрез виртуални интерфейси или физически мрежови карти), всеки от тях ще може да има ~65 000 клиентски връзки, съжалявам, че не го направих по-ясно в първия си коментар. Тази първа връзка, която публикувахте, споменава, че тяхната клиентска програма също отваря 64 000 връзки. - person Adam Bryzak; 25.03.2011
comment
Мисля, че това е грешно. Както знам, връзката в TCP се дефинира от четворка {host1, port1; хост2, порт2}. Ако отворите слушащ сокет, не е назначен локален порт за обработка на свързващ клиент. Но ако отворите изходяща връзка към отдалечен сървър, наистина се присвоява локален порт за обработка на новооткрита връзка. И така, те отвориха 64 000 връзки от всяка машина, за да не излизат от броя на наличните локални портове, които поддържат връзка от страна на клиента - person Vladislav Rastrusny; 26.03.2011
comment
@FractalizeR: Правилно е. Всяка изходяща връзка се нуждае от уникален номер на порт в текущите реализации на TCP. На теория е възможно TCP/IP стека да забележи кортежа, когато разпределя изходящия порт; на практика това е невъзможно поради API: по-специално факта, че bind() се случва преди connect(), независимо дали изрично или имплицитно. - person user207421; 28.03.2011
comment
Теоретично можете да използвате един и същ локален порт за много изходящи връзки, но не съм сигурен, че API е там за това. TCP използва локалния И отдалечения порт, за да реши какъв сокет се използва. Вярвам, че това, което хората правят вместо това, е да създадат множество локални IP адреси на един и същ компютър и да разпределят връзките върху всички тях (може също да изисква някои виртуални локални мрежови интерфейси). - person Dobes Vandermeer; 24.03.2012
comment
@DobesVandermeer API не е „там за това“, поради причината, която посочих. - person user207421; 10.04.2012

Все още смятам, че разходите за превключване на контекста за нишките в традиционния IO са значителни. На високо ниво вие получавате производителност само при използване на множество нишки, ако те няма да се борят толкова много за едни и същи ресурси или прекарват много повече време от превключването на контекста върху ресурсите. Причината да повдигнете това е, че с новите технологии за съхранение като SSD вашите нишки се връщат, за да се борят с процесора много по-бързо

person byte_array    schedule 19.01.2012
comment
Ако приложението ви обаче е обвързано с мрежови I/O, както при HTTP клиент или сървър, тогава всички блокирани нишки няма да се изпълняват, докато ядрото не ги събуди, така че не мисля, че ще причинят излишни разходи за превключване на контекста. Тези разходи за превключване се отнасят само за приложения, в които всички нишки се опитват да работят едновременно, за да обработят някои данни. - person Dobes Vandermeer; 24.03.2012

Няма нито един „най-добър“ начин за изграждане на NIO сървъри, но превесът на този конкретен въпрос на SO предполага, че хората смятат, че има! Вашият въпрос обобщава случаите на употреба, които са подходящи и за двете опции достатъчно добре, за да ви помогнат да вземете решението, което е правилно за вас.

Освен това са възможни и хибридни решения! Бихте могли да прехвърлите канала на нишките, когато възнамеряват да направят нещо, което си заслужава, и да се придържате към NIO, когато е по-добре.

person pawstrong    schedule 10.04.2012

Бих казал, че започнете с нишка на връзка и адаптирайте оттам, ако срещнете проблеми.

Ако наистина трябва да се справите с милион връзки, трябва да помислите за писане (или намиране) на прост брокер на заявки в C (или каквото и да е), който ще използва много по-малко памет за връзка, отколкото всяка реализация на Java. Брокерът може да получава заявки асинхронно и да ги подрежда на опашка към бекенд работници, написани на език по ваш избор.

По този начин бекендовете се нуждаят само от нишка за активна заявка и можете просто да имате фиксиран брой от тях, така че използването на паметта и базата данни е предварително определено до известна степен. Когато голям брой заявки се изпълняват паралелно, заявките трябва да изчакат малко по-дълго.

Затова мисля, че никога не трябва да прибягвате до канали за избор на NIO или асинхронен I/O (NIO 2) на 64-битови системи. Моделът нишка на връзка работи достатъчно добре и можете да направите своето мащабиране до "десетки или стотици хиляди" връзки, като използвате някои по-подходящи технологии на ниско ниво.

Винаги е полезно да избягвате преждевременната оптимизация (т.е. писане на NIO код, преди наистина да имате огромен брой връзки) и не преоткривайте колелото (Jetty, nginx и т.н.), ако е възможно.

person Dobes Vandermeer    schedule 11.04.2012

Това, което най-често се пренебрегва, е, че NIO позволява работа с нулево копиране. напр. ако слушате един и същ мултикаст трафик от множество процеси, използвайки стари сокети на един единствен сървър, всеки мултикаст пакет се копира от буфера на мрежата/ядрото към всяко слушащо приложение. Така че, ако изградите GRID от напр. 20 процеса, получавате проблеми с честотната лента на паметта. С nio можете да прегледате входящия буфер, без да се налага да го копирате в пространството на приложението. След това процесът копира само части от входящия трафик, който го интересува.

друг пример за приложение: вижте http://www.ibm.com/developerworks/java/library/j-zerocopy/ за пример.

person R.Moeller    schedule 27.01.2013