Яка відповідь Haskell на Node.js?


217

Я вважаю, що спільнота Erlang не заздрить Node.js, оскільки вона не блокує введення-виведення вихідних даних і має способи легко масштабувати розгортання на більш ніж один процесор (щось навіть не вбудовано в Node.js). Детальніше на http://journal.dedasys.com/2010/04/29/erlang-vs-node-js та Node.js або Erlang

Що з Haskell? Чи може Haskell надати деякі переваги Node.js, а саме чисте рішення, щоб уникнути блокування вводу / виводу без використання багатопотокового програмування?


З Node.js є багато привабливих речей

  1. Події: Ні маніпулювання потоком, програміст надає лише зворотні дзвінки (як у Snap Framework)
  2. Гарячі зворотні дзвінки гарантовано будуть виконані в один потік: не можливі умови перегонів.
  3. Приємний і простий API UNIX. Бонус: Відмінна підтримка HTTP. Також доступний DNS.
  4. Кожен I / O за замовчуванням асинхронний. Це полегшує уникнення замків. Однак занадто велика обробка процесора в зворотному дзвінку вплине на інші з'єднання (у цьому випадку завдання має розділитися на менші підзадачі та переплановано).
  5. Однакова мова для клієнтської та серверної. (Я не бачу надто великого значення в цій, але jQuery і Node.js поділяють модель програмування подій, але решта дуже відрізняється. Я просто не бачу, як міг ділитися кодом між стороною сервера та клієнтом. бути корисним на практиці.)
  6. Все це упаковано в один продукт.

17
Я думаю, вам слід поставити це питання замість програмістів .
Йонас

47
Якщо включити фрагмент коду, це не робить це суб'єктивним питанням.
gawi

20
Я мало знаю про node.js, але одне вразило мене вашим питанням: чому ви вважаєте перспективу ниток такою неприємною? Нитки повинні бути саме правильним рішенням для мультиплексування вводу / виводу. Тут я широко використовую термін "нитки", включаючи процеси Ерланга. Можливо, ви турбуєтесь про замки та стан, що змінюється? Вам не потрібно робити такі речі - використовувати передачу повідомлень або транзакції, якщо це має більше сенсу для вашої програми.
Саймон Марлоу

9
@gawi Я не думаю, що це звучить дуже просто - без попередження вам доведеться мати справу з голодом і довгими затримками. В основному потоки - це правильна абстракція для веб-сервера - немає необхідності мати справу з асинхронним введенням-виведенням і всіма труднощами, які пов'язані з цим, просто зробіть це в потоці. До речі, я написав документ про веб-сервери в Haskell, який вам може бути цікавим: haskell.org/~simonmar/papers/web-server-jfp.pdf
Саймон

3
"Гарячі зворотні дзвінки гарантуються в одному потоці: не можливі умови перегонів." Неправильно. Ви можете легко створити перегони в Node.js; просто припустимо, що одна дія вводу-виводу завершиться перед іншою та BOOM. Що це дійсно неможливо один конкретний вид умов гонки, а саме одночасно unsynchronised доступ до того ж байт в пам'яті.
праворуч

Відповіді:


219

Добре, тому переглянувши трохи презентації node.js, на яку @gawi вказував мені, я можу сказати трохи більше про те, як Haskell порівнюється з node.js. У презентації Райан описує деякі переваги "Зелених ниток", але далі говорить, що не вважає відсутність абстракції нитки недоліком. Я не погоджуюся з його позицією, особливо в контексті Haskell: Я вважаю, що абстракції, які надаються потоками, мають важливе значення для полегшення отримання коду сервера і більш надійного. Зокрема:

  • використання одного потоку за з'єднання дозволяє писати код, що виражає зв'язок з одним клієнтом, а не код запису, який одночасно стосується всіх клієнтів. Подумайте про це так: сервер, який обробляє декілька клієнтів з потоками, виглядає майже так само, як той, який обробляє одного клієнта; головна відмінність - forkдесь у колишньому. Якщо протокол, який ви реалізуєте, зовсім складний, управління державною машиною для декількох клієнтів одночасно стає досить складним, тоді як потоки дозволяють просто сценарій спілкування з одним клієнтом. Код простіше правильно підібрати, і простіше зрозуміти та підтримувати.

  • Зворотні виклики на одному потоці ОС - це спільна багатозадачність, на відміну від попередньої багатозадачності, що саме ви отримуєте з потоками. Основним недоліком кооперативного багатозадачності є те, що програміст відповідає за те, щоб не було голоду. Він втрачає модульність: помиляється в одному місці, і це може викрутити всю систему. Це дійсно те, про що ви не хочете турбуватися, а виплата - це просте рішення. Більше того, зв’язок між зворотними дзвінками неможливий (це буде тупиком).

  • паралельність не є складною в Haskell, тому що більшість кодів є чистими, а тому конструкція безпечна для потоків. Є прості комунікативні примітиви. Набагато складніше застрелити себе в ногу з одночасністю в Haskell, ніж мовою з необмеженими побічними ефектами.


42
Гаразд, тому я розумію, що node.js є вирішенням двох проблем: 1 - одночасність важка для більшості мов, 2 - використання потоків ОС є експансивним. Рішення Node.js полягає у використанні одночасності (w / libev) на основі подій, щоб уникнути зв'язку між потоками та уникнути проблем масштабованості потоків ОС. У Haskell немає чистоти №1 через чистоту. Для №2 Haskell має легкі потоки + менеджер подій, який нещодавно оптимізовано в GHC для масштабних контекстів. Крім того, використання Javascript просто не може сприйматися як плюс для будь-якого розробника Haskell. Для деяких людей, які використовують Snap Framework, Node.js "просто погано".
gawi

4
Обробка запитів - це більшість часу послідовності взаємозалежних операцій. Я схильний погоджуватися, що використання зворотних викликів для кожної операції блокування може бути громіздким. Нитки для цього краще підходять, ніж зворотний дзвінок.
гаві

10
Так! А абсолютно новий мультиплексування вводу-виводу в GHC 7 робить сервери запису в Haskell ще кращими.
andreypopp

3
Перша ваша думка не має для мене сенсу (як стороння особа) ... Під час обробки запиту в node.js ваш зворотний виклик має справу з одним клієнтом. Управління станом стає лише тим, про що слід хвилюватися, коли масштабується на декілька процесів, і навіть тоді досить просто використовувати наявні бібліотеки.
Рікардо Томасі

12
Це не окреме питання. Якщо це запитання - це справжній пошук найкращих інструментів для роботи в Haskell, або перевірка того, чи існують прекрасні інструменти для роботи в Haskell, то непряме припущення, що багатопотокове програмування буде непридатним, потрібно оскаржити, оскільки Haskell робить нитки досить інакше, як вказує Дон Стюарт. Відповіді, які пояснюють, чому співтовариство Haskell також не ревнує Node.js, дуже важливе для цього питання. Відповідь gawi дозволяє сказати, що це відповідна відповідь на його запитання.
AndrewC

154

Чи може Haskell надати деякі переваги Node.js, а саме чисте рішення, щоб уникнути блокування вводу / виводу без використання багатопотокового програмування?

Так, насправді події та теми об'єднані в Haskell.

  • Ви можете програмувати в явних легких потоках (наприклад, мільйони ниток на одному ноутбуці).
  • Або; Ви можете запрограмувати в стилі асинхронного події на основі сповіщення про масштабовані події.

Нитки фактично реалізовані з погляду подій та проходять через декілька ядер, безперебійну міграцію потоків, із задокументованою продуктивністю та програмами.

Напр

Одночасні колекції нікого на 32 ядра

alt текст

У Haskell у вас є і події, і теми, і як і всі події під кришкою.

Прочитайте документ, що описує реалізацію.


2
Дякую. Мені потрібно переварити все це ... Це здається специфічним для GHC. Я здогадуюсь це нормально. Мова Haskell коли-небудь, як все, що може скласти GHC. Аналогічним чином "платформа" Haskell - це більш-менш час роботи GHC.
гаві

1
@gawi: Це та всі інші пакети, які потрапляють у нього прямо, так що це корисно прямо з коробки. І це той самий образ, який я бачив у своєму курсі CS; і найкраще - це те, що в Haskell важко досягти подібних приголомшливих результатів у власних програмах.
Роберт Массайолі

1
Привіт Дон, чи ти думаєш, що ти можеш зв’язатись із веб-сервером haskell, який працює найкраще (Warp), відповідаючи на такі запитання? Ось досить релевантний орієнтир проти Node.js: yesodweb.com/blog/2011/03/…
Грег Вебер,

4
Просто теоретично. "Легкі нитки" Haskell не такі легкі, як ви думаєте. Реєстрація зворотного дзвінка в інтерфейсі epoll набагато набагато дешевше, ніж планування так званої зеленої нитки, вони, звичайно, дешевші, ніж потоки ОС, але вони не безкоштовні. Для створення 100 000 з них використовується ок. 350 Мб пам’яті та займе деякий час. Спробуйте 100 000 з'єднань з node.js. Не проблема взагалі . Це було б магічно, якби не швидше, оскільки ghc використовує еполу під кришкою, тому вони не можуть бути швидшими, ніж безпосередньо використовувати epoll. Хоча програмування з інтерфейсом потоків дуже приємне.
Kr0e

3
Крім того: новий менеджер вводу-виводу (ghc) використовує алгоритм планування, який має (m log n) складність (де m - кількість запущених потоків і n загальна кількість потоків). Epoll має складність k (k - кількість читабельних / записаних fd's =. Отже, ghc має O (k * m log n) по всій складності, що не дуже добре, якщо ви стикаєтеся з високими трафіковими зв’язками. Node.js має лише лінійну складність і просто давайте не будемо говорити про продуктивність Windows ... Node.js набагато швидше, оскільки він використовує IOCP.
Kr0e,

20

По-перше, я не вважаю, що node.js робить все правильно, виявляючи всі ці зворотні виклики. Ви закінчуєте писати свою програму в CPS (стиль проходження продовження), і я думаю, що робота компілятора повинна зробити цю трансформацію.

Події: Ні маніпулювання потоком, програміст надає лише зворотні дзвінки (як у Snap Framework)

Отже, маючи це на увазі, ви можете писати, використовуючи асинхронний стиль, якщо цього хочете, але, роблячи це, ви пропустите написання в ефективному синхронному стилі, по одному потоку на запит. Haskell чудово дієвий при синхронному коді, особливо в порівнянні з іншими мовами. Це все події внизу.

Гарячі зворотні дзвінки гарантовано будуть виконані в один потік: не можливі умови перегонів.

Ви все ще можете мати стан перегонів у node.js, але це складніше.

Кожен запит - це власна тема. Коли ви пишете код, який повинен зв’язуватися з іншими потоками, дуже просто зробити його безпечним для потоків завдяки примітивним примітивам Haskell.

Приємний і простий API UNIX. Бонус: Відмінна підтримка HTTP. Також доступний DNS.

Погляньте на злом і переконайтеся самі.

Кожен ввід / вивід за замовчуванням є асинхронним (хоча це часом може дратувати). Це полегшує уникнення замків. Однак занадто велика обробка процесора в зворотному дзвінку вплине на інші з'єднання (у цьому випадку завдання має розділитися на менші підзадачі та переплановано).

У вас немає таких проблем, ghc розподілить вашу роботу серед реальних потоків ОС.

Однакова мова для клієнтської та серверної. (Я не бачу занадто великої цінності в цій. Однак JQuery і Node.js поділяють модель програмування подій, але решта дуже відрізняється. Я просто не бачу, як міг ділитися код між стороною сервера та клієнтом. бути корисним на практиці.)

Haskell тут не може перемогти ... правда? Подумайте ще раз, http://www.haskell.org/haskellwiki/Haskell_in_web_browser .

Все це упаковано в один продукт.

Завантажте ghc, підпаліть кабал. Є пакет на кожну потребу.


Я просто грав захисника диявола. Отже, так, я згоден на ваші моменти. За винятком об'єднання мови на стороні клієнта та сервера. Хоча я думаю, що це технічно можливо, я не думаю, що він може врешті-решт замінити всю існуючу сьогодні екосистему Javascript (JQuery та друзі). Хоча це аргумент, висунутий прихильниками Node.js, я не думаю, що це дуже важливий. Вам дійсно потрібно поділити стільки коду між вашим презентаційним шаром та вашим бекендом? Чи дійсно ми прагнемо, щоб програмісти знали лише одну мову?
гаві

Справжня виграш полягає в тому, що ви можете візуалізувати сторінки як на сервері, так і на стороні клієнта, що полегшує створення сторінок у реальному часі.
dan_waterworth

@dan_waterworth точно, дивіться метеор чи derby.js
mb21

1
@gawi У нас є виробничі послуги, де 85% коду ділиться між клієнтом і сервером. Це відомо як універсальний JavaScript у спільноті. Ми використовуємо React для динамічного відтворення вмісту на сервері, щоб зменшити час на перше корисне візуалізацію в клієнті. Хоча я усвідомлюю, що ви можете запустити Haskell у веб-переглядачі, я не знаю жодного набору "універсальних Haskell" найкращих практик, які дозволяють надати серверну та клієнтську візуалізацію, використовуючи ту саму базу коду.
Ерік Елліотт

8

Я особисто бачу Node.js і програмування з зворотними дзвінками як надмірно низький рівень і трохи неприродну річ. Навіщо програмувати з зворотними дзвінками, коли хороший час виконання, наприклад, знайдений у GHC, може обробляти зворотні дзвінки для вас і робити це досить ефективно?

Тим часом час роботи GHC сильно покращився: тепер він має "нового нового менеджера вводу- виводу" під назвою MIO, де "M" означає багатоядерний я вірю. Він будується на основі існуючого менеджера вводу-виводу і його головна мета - подолати причину погіршення продуктивності ядер 4+. Показники продуктивності, представлені в цій роботі, є досить вражаючими. Побачити себе:

Завдяки Mio реалістичні HTTP-сервери в масштабі Haskell до 20 процесорних ядер, що досягають пікової продуктивності до коефіцієнта 6,5x порівняно з тими ж серверами, що використовують попередні версії GHC. Також покращується затримка серверів Haskell: [...] при помірному навантаженні скорочує очікуваний час реакції на 5,7 разів порівняно з попередніми версіями GHC

І:

Ми також показуємо, що за допомогою Mio, McNettle (контролер SDN, написаний на Haskell) може ефективно масштабувати до 40 ядер, досягти досконалої потужності понад 20 мільйонів нових запитів в секунду на одній машині, а отже, стати найшвидшим з усіх існуючих контролерів SDN .

Mio ввійшов до випуску GHC 7.8.1. Я особисто бачу це як головний крок вперед у виконанні Haskell. Було б дуже цікаво порівняти продуктивність існуючих веб-додатків, складених попередньою версією GHC та 7.8.1.


6

ІМХО події хороші, але програмування за допомогою зворотних викликів - ні.

Більшість проблем, що викликають особливе кодування та налагодження веб-додатків, пов’язані з тим, що робить їх масштабованими та доступними. Найважливіший, бездержавний характер HTTP. Це підвищує керованість, але це накладає інверсію управління, де елемент IO (в цьому випадку веб-сервер) викликає різні обробники в коді програми. Ця модель події - або точніше сказана модель зворотного виклику - кошмар, оскільки зворотні виклики не поділяють змінні області застосування, і інтуїтивно зрозумілий режим навігації втрачається. Серед інших проблем дуже важко запобігти всі можливі зміни стану, коли користувач переміщається вперед і назад.

Можна сказати, що проблеми схожі на програмування графічного інтерфейсу, де модель події працює чудово, але у графічних інтерфейсів немає навігації та кнопки повернення назад. Це примножує можливі переходи стану у веб-додатках. Результатом спроби вирішити цю проблему є важкі рамки зі складною конфігурацією безліччю розповсюджених магічних ідентифікаторів, не ставлячи під сумнів корінь проблеми: модель зворотного виклику та притаманний йому відсутність спільного використання змінних діапазонів та відсутність послідовності, тому послідовність повинна бути побудовані за допомогою зв'язування ідентифікаторів.

Існують послідовні основи, такі як ocsigen (ocaml) узбережжя (smalltalk) WASH (припинено, Haskell) та mflow (Haskell), які вирішують проблему управління державою при збереженні судноплавності та повноти REST. У цих рамках програміст може виражати навігацію як імперативну послідовність, коли програма надсилає сторінки та чекає відповідей в одному потоці, змінні знаходяться в області застосування, а кнопка "назад" працює автоматично. Це по суті створює коротший, більш безпечний і читабельний код, де навігація чітко видна програмісту. (справедливе попередження: я розробник mflow)


У node.js зворотні виклики використовуються для обробки асинхронного вводу / виводу, наприклад, до баз даних. Ви говорите про щось інше, що, хоча й цікаво, не дає відповіді на запитання.
Робін Грін

Ти правий. Минуло три роки, щоб відповісти, що я, сподіваюся, задовольнить ваші заперечення: github.com/transient-haskell
agocorona

Тепер Node підтримує функції асинхронізації, а це означає, що ви можете написати код імперативного стилю, який насправді є асинхронним. Він використовує обіцянки під кришкою.
Ерік Елліотт

5

Питання є досить смішним, оскільки 1) Haskell вже вирішив це питання набагато кращим чином і 2) приблизно таким же чином, як і Ерланг. Ось орієнтир проти вузла: http://www.yesodweb.com/blog/2011/03/prepreme-warp-cross-language-benchmarks

Дайте Haskell 4 ядра, і він може робити 100 к (простих) запитів за секунду в одній програмі. Вузол не може виконати стільки і не може масштабувати одну програму по ядрах. І вам не доведеться нічого робити, щоб скористатися цим, оскільки час виконання Haskell не блокує. Єдиною іншою (відносно поширеною) мовою, яка має незаблокований IO, вбудований у час виконання, - Erlang.


14
Смішно? Питання не в тому, чи має Haskell відповідь, а в тому, що таке відповідь Haskell. На момент запитання, GHC 7 навіть не був випущений, тому Haskell ще не був "в грі" (за винятком, можливо, для фреймворків, які використовують libev, як Snap). Крім цього, я згоден.
gawi

1
Я не знаю, чи це було правдою, коли ви опублікували цю відповідь, але тепер насправді є вузлові модулі, які дозволяють додаткам вузлів легко масштабувати по ядрах. Крім того, це посилання порівнює node.js, що працює на одному ядрі, і haskell, що працює на 4 ядрах. Мені б хотілося, щоб він знову працював у більш справедливій конфігурації, але, на жаль, github repo вже немає.
Тім Готьє

2
Haskell, що використовує більше 4 ядер, погіршує продуктивність програми. Була робота над цим питанням, над якою активно працювали, але це все ще є проблемою. Отже, запуск 16 екземплярів Node.js на 16-ядерному сервері, швидше за все, буде набагато кращим, ніж одна програма ghc з використанням + RTS -N16, що дійсно буде повільніше, ніж + RTS -N1 через цю помилку виконання. Це тому, що вони використовують лише один IOManager, який сповільниться при використанні з багатьма потоками ОС. Я сподіваюся, що вони виправлять цю помилку, але вона існує з тих пір, тому я б не дуже сподівався ...
Kr0e

Кожен, хто дивиться на цю відповідь, повинен знати, що Node може легко обробляти 100 к. Простих запитів на одному ядрі, і це маловільно легко масштабувати додаток Node без стану на багатьох ядрах. pm2 -i max path/to/app.jsавтоматично розширюватиметься до оптимальної кількості примірників на основі наявних ядер. Крім того, Node також не блокує за замовчуванням.
Ерік Елліотт

1

1
Як це відповідає на питання?
dfeuer

1
@dfeuer Посилання повинно читати так, як Snap Haskell Web Framework впав libev, я не знаю, чому форматування не вдається. Виконання сервера вузлів стосувалося Linux libev, коли він починався, і так само було Snap Web FrameWork. Haskell with Snap - це як ECMAscript з nodejs, тому те, як Snap розвивається уздовж nodejs, є більш релевантним, ніж Haskell, що в цьому контексті може бути більш справедливо порівняно з ECMAscript.
Chawathe Vipul S
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.