Про эволюцию языков с точки зрения серверной разработки:
"...Ну смотри, если ты сервера пишешь, то обычно в рамках запроса у тебя будет дофига IO (запросы к базе, запросы к сторонним API и т.п.). Но тебе нужно как-то утилизировать процессор в это же время, обсулживая других клиентов. И у тебя опции:
- PHP - никаких опций, только увеличивая количесто процессов PHP интерпретатора в пуле fpm (т.е. воркеров). Каждый новый процесс будет полностью блокироваться на вызовах IO и в любой момент времени ты не сможешь обсулживать больше скажем 50 HTTP запросов, зависших на IO.
- олдскул Python та же картина, но обычно с потоками, вместо процеесов в качестве воркеров. Но можно хакнуть его с помощью gevent и перестать использовать потоки ОС, а начать использовать “зеленые” потоки и асихронный IO на самом деле. Тогда можно число зависших на IO запросов увеличить до тысяч, потому что зеленые потоки на пару порядок легче потоков ОС.
В обоих этих случаях (PHP, Python, Python + gevent) твой код будет выглядеть как обычный “сихронный код”, без всяких колбеков повсюду или await-ов.
Потом начинается магия - в питоне начинают писать с использованием callback-based стиля (всякие twisted и tornado). Т.е. у тебя всего один процесс, вместо десятков воркеров. Весь IO асинхронный. Каждый вызов "условного" read/write() принимает callback функцию, которую он вызовет при получении результата IO и тут же возвращает управление. Код больше не выглядит “последовательным/синхронным”, его становится труднее понимать. Альтернативный, но не более понятный подход - использовать модный asyncio, где ты пишешь
И тут припирается JavaScript nodejs и говорит, что она асихронная их коробки. Все кидаются на ней писать, потому что типа она решает эти проблемы с IO, но… Там все те же колбеки и всего один процесс, и нечитаемый код. Даже несмотря на то, что из этого "всего один процесс" не может быть проблем с race condition. Потом в JavaScript добавляют async/await синтаксический сахар, код становится “линейным” (без колбеков). Тот же путь, что и у питона, короче.
А вот в Go очень интересным путем пошли. Они придумали горутины (которые на самом деле корутины). И ты запускаешь свою прогу на Go, это один процесс. Но под капотом она может запустить до GO_MAX_PROCESS процессов (обычно по числу ядер). Код в твоем основном процессе выполняется последовательно и синхронно. Но когда ты пишешь
"...Ну смотри, если ты сервера пишешь, то обычно в рамках запроса у тебя будет дофига IO (запросы к базе, запросы к сторонним API и т.п.). Но тебе нужно как-то утилизировать процессор в это же время, обсулживая других клиентов. И у тебя опции:
- PHP - никаких опций, только увеличивая количесто процессов PHP интерпретатора в пуле fpm (т.е. воркеров). Каждый новый процесс будет полностью блокироваться на вызовах IO и в любой момент времени ты не сможешь обсулживать больше скажем 50 HTTP запросов, зависших на IO.
- олдскул Python та же картина, но обычно с потоками, вместо процеесов в качестве воркеров. Но можно хакнуть его с помощью gevent и перестать использовать потоки ОС, а начать использовать “зеленые” потоки и асихронный IO на самом деле. Тогда можно число зависших на IO запросов увеличить до тысяч, потому что зеленые потоки на пару порядок легче потоков ОС.
В обоих этих случаях (PHP, Python, Python + gevent) твой код будет выглядеть как обычный “сихронный код”, без всяких колбеков повсюду или await-ов.
Потом начинается магия - в питоне начинают писать с использованием callback-based стиля (всякие twisted и tornado). Т.е. у тебя всего один процесс, вместо десятков воркеров. Весь IO асинхронный. Каждый вызов "условного" read/write() принимает callback функцию, которую он вызовет при получении результата IO и тут же возвращает управление. Код больше не выглядит “последовательным/синхронным”, его становится труднее понимать. Альтернативный, но не более понятный подход - использовать модный asyncio, где ты пишешь
response = await http.GET(‘https://ya.ru’). И код типа все еще выкглядит синхронно (т.е. без колбеков), но на самом деле ты должен париться, когда и кому вернется управление после await.И тут припирается JavaScript nodejs и говорит, что она асихронная их коробки. Все кидаются на ней писать, потому что типа она решает эти проблемы с IO, но… Там все те же колбеки и всего один процесс, и нечитаемый код. Даже несмотря на то, что из этого "всего один процесс" не может быть проблем с race condition. Потом в JavaScript добавляют async/await синтаксический сахар, код становится “линейным” (без колбеков). Тот же путь, что и у питона, короче.
А вот в Go очень интересным путем пошли. Они придумали горутины (которые на самом деле корутины). И ты запускаешь свою прогу на Go, это один процесс. Но под капотом она может запустить до GO_MAX_PROCESS процессов (обычно по числу ядер). Код в твоем основном процессе выполняется последовательно и синхронно. Но когда ты пишешь
go foo(), запускается горутина и тут же возвращает управление твоему основному процессу. Скорее всего она будет выполняться в новом процессе. И если все твои горутины не делают IO, а считают цифры, то ты очень быстро съешь все ядра своего CPU и будешь "играть в ОСь", заменяя ее планировщик планировщиком из Go рантайма. Но вот если твои горутины подвисают на IO, то ты можешь запускать десятки тысяч горутин (фактически зеленых потоков). И код внутри каждой горутины выглядит последовательно и синхронно (и так оно и есть). И ты все еще пишешь в "классическом стиле" (т.е. без колбеков), но при этом можешь очень легко средствами языка утилизировать весь свой CPU. Минусы - race condition, ты можешь из разных корутин модифицировать shared данные и никто тебе по рукам не надает. Но нужно просто channel использовать, а не писать в shared данные )) Т.е. Go - это что-то среднее между классическим воркеро-ориентированном способом параллелить задачи и асинхронно-колбеко-ориентированным."Forwarded from Ivan Velichko
Только ща осознал, что все open space офисы неправильно приготовлены )) людей сажают вместе, мотивируя это тем, что ничто не должно затруднять коммуникации. При этом как только начинается обсуждение чего-то, весь опен спейс шикает на вас и вы пиздуете в переговорную. Каждое рабочее место в опен Спейсе оборудовано именно для работы, у всех по два монитора и куча доп барахла типа док станций для работы. И в офисах нет обычно мест, где можно спрятаться и покодить в тишине. В итоге преимущества опен Спейса не используются, но и сосредоточиться в нем нельзя, потому что вокруг тьма людей и коммуникаций
Всегда недолюбливал module-level переменные. Они лишь немногим лучше просто глобальных, т.е. почти равны "злу", скрывают зависимости и состояние и усложняют тестирование. И всегда считал, что в таких случаях всегда лучше в функции передавать объект, представляющий состояние. И думал, что module-level переменными грешат только высокоуровневые скрипто-подобные языки вроде python, JS и Go. Но тут загляунл в glibc... https://github.com/bminor/glibc/blob/09533208febe923479261a27b7691abef297d604/libio/iopopen.c#L50 Тадам! Похоже, что это традиционная техника, используемая уже полвека )) Может пересмотреть свое отношение к проблеме?
Также интересные размышления на тему от Dave Cheney https://dave.cheney.net/2017/06/11/go-without-package-scoped-variablesтут
Также интересные размышления на тему от Dave Cheney https://dave.cheney.net/2017/06/11/go-without-package-scoped-variablesтут
GitHub
glibc/libio/iopopen.c at 09533208febe923479261a27b7691abef297d604 · bminor/glibc
Unofficial mirror of sourceware glibc repository. Updated daily. - bminor/glibc
Примерно раз в год я задумываюсь над тем, какие именно качества важны для программиста (software developer). И вот уже почти 10 лет я неизменно приходил к выводу, что важно именно уметь круто писать код, т.е шарить в алгоритмах, шаблонах, подходах к организации вычислений и прочей тёплой ламповой программистской SICP идиллии. И тогда ты будешь крутым гуру, а все эти ребята, что используют готовые модули, когда строке нужно сделать трим, вместо того, чтобы самому написать 5 строк кода, навсегда должны остаться второсортными кодерами со средними зарплатами... В то же время, пару лет назад MIT отказался от SICP в пользу более прикладного курса на основе python. А мотивация была в том, что в современном мире все меньше и меньше вещей требуют собственной реализации в коде и все больше и больше задач решаются комбинированием готовых решений. И крут теперь тот, кто умеет эти готовые решения уметь проанализировать (в научном понимании этого слова, как чёрный или белый ящик), а затем написать правильный интеграционный код. Который очень редко похоже бывает алгоритмически сложным. Проблема в том, что слепить из нескольких говнопакетов свой говносервис очень легко. И отличить настоящих гениев анализа и интеграции от copy/paste-from-stackoverflow-based-девелоперов становится все труднее и труднее. Нет больше той искусственной академической границы между профессионалами в написании кода и любителями. Но ведь так хочется сохранить привычную картину мира. Куда податься ? Разработка для встраиваемых систем? Интернет вещей? В каких ещё областях недостаточно готовых решений и все ещё приходится писать клевые штуки с нуля?
Forwarded from Anton Bukov
> Закон дырявых абстракций означает, к сожалению, что абстракции не так сильно упрощают нашу жизнь, как хотелось бы. Если я обучаю программистов C++, было бы здорово, если бы мне не нужно было рассказывать им про char* и арифметику указателей, а можно было сразу перейти к строкам из стандартной библиотеки темплейтов. Но в один прекрасный день они напишут "foo"+"bar", и возникнут странные проблемы, а мне придётся всё равно объяснить им, что такое char*. Или они попытаются вызвать функцию Windows с параметром типа LPTSTR и не смогут, пока не выучат char* и указатели и Юникод и wchar_t и хедерные файлы TCHAR — все то, что просвечивает через дырки в абстракциях.
Продолжая тему прошлого поста. Тут друзья подсказывают про закон Дырявых абстракций Джоэла Спольски. Речь в нем о том, что любая абстракция имеет свои «дырки», т.е места, где она протекает на более низкий уровень в силу своей несовершенности. Абстракции призваны упростить нашу жизнь, перенести мышление на более высокий уровень, освободив его от несущественных на данном этапе деталей. Это позволяет решать все более и более высокоуровневые проблемы, решение которых было бы невозможным, если бы мы, скажем, все ещё продолжали писать на ассемблере, вместо языков следующего поколения или каждый раз писали свою сортировку, вместо использования библиотек и модулей. И в то же время, абстракции понижают естественный порог входа в профессию программиста. А вместе с ним и средний IQ сообщества. И вот тут нас атакуют дырявости в наших абстракциях. Прежде чем использовать абстракцию, правильно было бы научиться разбираться на один-два уровня ниже в вещах, на которых эта абстракция основана. Потому что в ситуациях, когда она протечет «в бою», уже поздно будет читать про TCP или ревьювить код импортированных пакетов. Добавим тягу к фундаментальному пониманию происходящих процессов и их анализ в копилку навыков настоящего программиста.
Итак, после десяти лет в консоли… десяти лет боли и страдания от emacs-стиля:
set -o viКомпетенции программиста:
- алгоритмы и структуры данных (основа основ, необходимый, но не достаточный элемент для решения более высокоуровневых задач);
- архитектура [программ] (декомпозиция, знание парадигм, подходов и шаблонов. В комбинации со знанием алгоритмов и структур данных даёт возможность реализовывать широкий спектр прикладных программ);
- операционные системы/платформы (знание среды, в которой выполняется код, её примитивов и API. В совокупности с первыми двумя пунктами расширяет круг возможностей программиста);
- архитектура распределённых систем (CAP теорема, осознание невозможности двухфазного коммита, распределённый консенсус плюс набор шаблонов для построения распределённых систем) - вжух, и ты - инженер.
- алгоритмы и структуры данных (основа основ, необходимый, но не достаточный элемент для решения более высокоуровневых задач);
- архитектура [программ] (декомпозиция, знание парадигм, подходов и шаблонов. В комбинации со знанием алгоритмов и структур данных даёт возможность реализовывать широкий спектр прикладных программ);
- операционные системы/платформы (знание среды, в которой выполняется код, её примитивов и API. В совокупности с первыми двумя пунктами расширяет круг возможностей программиста);
- архитектура распределённых систем (CAP теорема, осознание невозможности двухфазного коммита, распределённый консенсус плюс набор шаблонов для построения распределённых систем) - вжух, и ты - инженер.
nil leads to panic
panic leads to fear
fear leads to…
functional programming!
https://speakerdeck.com/campoy/understanding-nil
panic leads to fear
fear leads to…
functional programming!
https://speakerdeck.com/campoy/understanding-nil
Speaker Deck
Understanding Nil
Opening Keynote at GopherCon 2016
Is it a constant? A variable? Where is it defined? What is its type? It has no type? It has all the types? Those ar…
Is it a constant? A variable? Where is it defined? What is its type? It has no type? It has all the types? Those ar…
Интересная мысль у товарища. Он утверджает, что запоминание - это ненужная вещь. Речь идет про математику, формулы и определения, но, похоже, можно обобщить до любой области. Товарищ говорит, что нужно банально кучу раз решить на практике одни и те же задачи, тогда они станут как бы частью твоего мыслительного процесса. Как разговор на естесственном языке. Мы же не пытаемся вспоминать слова, а просто автоматически говорим. И так же нужно поступать с математикой - механически вдалбливать себе знания путем повторения пока не начнешь думать на этом языке.
https://math.stackexchange.com/a/33987/417969
https://math.stackexchange.com/a/33987/417969
Mathematics Stack Exchange
What's better strategy to handle tons of formulas, definitions?
I'm freshman, bachelor of Math major. When I read and learn the textbook, there're lots of formulas, laws, definitions and so forth. But what is the better way to handle it ?
I mean, is it necessa...
I mean, is it necessa...
Все языки программирования, кроме C - от лукавого. Они делают слишком много абстракций поверх примитивов операционной системы, годами вводя сеньоров девелоперов в заблуждения.
У линуксовых сокетов нет никаких таймаутов, которые можно было бы настраивать из приложения. Только всякие обще-системные TCP счетчики. Типа максиальное кол-во передачи SYN, задержка между передачами и т.п.
И когда программист создает сокет в синхронном моде и делает на нем коннект, результата такого вызова можно прождать пару минут, все зависит от TCP настроек оси...
И поэтому всегда с сокетами работают в асинхронном виде (не тюнить же ось ради каждой проги). Создают асихронный сокет, вызывают коннект, который тут же заврешается с ошибкой. А дальше poll-ят на дескрипторе этого сокета, не произошло ли там коннетка. И если с точки зрения приложения коннекта нет слишком долго, то poll-инг прекращается с ошибкой уровня приложения, типа connection timed out.
Аналогичная история с операциями чтения и записи.
А всякие выскороуровневые языки, типа python, берут и делают всю эту магию под капотом, возвращая на уровень публичного API сокетов “блокирующие” операции коннекта, чтения и записи, которые доп параметром принимают timeout. Или еще круче - такой таймаут можно выставить глобально (все будущим сокетам) или для каждого сокета отдельно в момент создания. А JS пошел еще дальше - он впилил idle timeout. То есть если на сокет не приходит ничего (или с сокета не уходит ничего) дольше idle timeout milliseconds, вызывается socket.onTimeout коллбек.
Пруфы:
https://stackoverflow.com/a/2597774/1201488
https://docs.python.org/3/library/socket.html#notes-on-socket-timeouts
https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback
https://github.com/nodejs/node/blob/master/lib/net.js#L424
https://golang.org/pkg/net/#TCPConn.SetDeadline
У линуксовых сокетов нет никаких таймаутов, которые можно было бы настраивать из приложения. Только всякие обще-системные TCP счетчики. Типа максиальное кол-во передачи SYN, задержка между передачами и т.п.
И когда программист создает сокет в синхронном моде и делает на нем коннект, результата такого вызова можно прождать пару минут, все зависит от TCP настроек оси...
И поэтому всегда с сокетами работают в асинхронном виде (не тюнить же ось ради каждой проги). Создают асихронный сокет, вызывают коннект, который тут же заврешается с ошибкой. А дальше poll-ят на дескрипторе этого сокета, не произошло ли там коннетка. И если с точки зрения приложения коннекта нет слишком долго, то poll-инг прекращается с ошибкой уровня приложения, типа connection timed out.
Аналогичная история с операциями чтения и записи.
А всякие выскороуровневые языки, типа python, берут и делают всю эту магию под капотом, возвращая на уровень публичного API сокетов “блокирующие” операции коннекта, чтения и записи, которые доп параметром принимают timeout. Или еще круче - такой таймаут можно выставить глобально (все будущим сокетам) или для каждого сокета отдельно в момент создания. А JS пошел еще дальше - он впилил idle timeout. То есть если на сокет не приходит ничего (или с сокета не уходит ничего) дольше idle timeout milliseconds, вызывается socket.onTimeout коллбек.
Пруфы:
https://stackoverflow.com/a/2597774/1201488
https://docs.python.org/3/library/socket.html#notes-on-socket-timeouts
https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback
https://github.com/nodejs/node/blob/master/lib/net.js#L424
https://golang.org/pkg/net/#TCPConn.SetDeadline
Stack Overflow
C: socket connection timeout
I have a simple program to check if a port is open, but I want to shorten the timeout length on the socket connection because the default is far too long. I'm not sure how to do this though. Here's...
Окей, ещё одна житейская мудрость... Если у тебя есть пул коннектов к какому угодно серверу (БД, HTTP, etc), проверь дважды, что keep-alive таймаут на твоей стороне хотя бы на несколько секунд меньше keep-alive таймаута этих соединений на стороне сервера. Иначе ошибок записи в сокет по причине race conditions в закрывающихся TCP соединениях не избежать.
Когда накрыла ностальгия - старый добрый C, но на этот раз в браузере, спасибо WebAssembly! Играть тут http://micromind.me/golife/?preset=spaceship, код смотреть тут https://github.com/iximiuz/golife.c.
GitHub
GitHub - iximiuz/golife.c: Conway's Game of Life written in C and compiled to WebAssembly
Conway's Game of Life written in C and compiled to WebAssembly - iximiuz/golife.c
О вычислительной сложности... Все конечно слышали про класс задач NP (задача коммивояжёра, укладка рюкзака, и пр.). Задачи, которые *похоже* (если P != NP) не возможно решить за полиномиальное время на обычном компе. Но очень много людей ошибочно считают, что NP расшифровывается как Non-Polynomial, ожидаемо противопоставляя этот класс классу задач P, aka Polynomial. Так вот, оказывается, был вариант назвать NP класс вовсе не NP, а PET (i.e. Probably Exponential Time). И в случае доказательства P != NP переименовать PET в Provably Exponentioal Time. Или в Previously Exponential Time, если вдруг P == NP. Но нет, назвали-таки Non-deterministic Polynomial...
После долгих лет в разработке, начавшихся с абсолютно бездарного теоретического старта основ ООП в универе, картинка сложилась!
Чему учили в универе: ООП - это абстракция, инкапсуляция, полиморфизм и наследование.
Что говорит Alan Kay (https://en.wikipedia.org/wiki/Alan_Kay): ООП - это общение посредством передачи сообщений, состояние [объектов в широком понимании этого слова], его (состояния) сокрытие и защита [aka инкапсуляция], и [максимально] позднее связывание.
И вот со вторым определением никаких проблем. Все, что обладает состоянием - объект. Как такие объекты создаются (с помощью классов, модулей, копирования и цепочек прототипов, etc) - абсолютно не важно. Как объекты общаются между собой? Передачей сообщений. По сути, даже простой
Что мне не нравится в первом (и общеизвестном) определении ООП: полиморфизм и наследование представлены как объекты одного уровня. По факту же, наследование - это лишь один из вариантов реализации полиморфизма (https://en.wikipedia.org/wiki/Subtyping_polymorphism). Существуют и другие - ad hoc polymorphism (https://en.wikipedia.org/wiki/Ad_hoc_polymorphism), parametric polymorphism (https://en.wikipedia.org/wiki/Parametric_polymorphism). При этом полиморфизм сам по себе является одним из вариантов создания абстракций, делая вышеупомянутое определение еще более наивным.
Чему учили в универе: ООП - это абстракция, инкапсуляция, полиморфизм и наследование.
Что говорит Alan Kay (https://en.wikipedia.org/wiki/Alan_Kay): ООП - это общение посредством передачи сообщений, состояние [объектов в широком понимании этого слова], его (состояния) сокрытие и защита [aka инкапсуляция], и [максимально] позднее связывание.
И вот со вторым определением никаких проблем. Все, что обладает состоянием - объект. Как такие объекты создаются (с помощью классов, модулей, копирования и цепочек прототипов, etc) - абсолютно не важно. Как объекты общаются между собой? Передачей сообщений. По сути, даже простой
myUser.setAge(42) - это передача сообщения setAge объекту myUser. В некоторых языках это прослеживается лучше, в некоторых хуже. Объекты должны иметь возможность прятать и защищать (от модификации) свое внутреннее состояние (C++/Java private, protected атрибуты классов такой же способ сокрытия и защиты, как JavaScript замыкания в module pattern или заглавные/строчные буквы в Go). Сообщения, передаваемые между объектами должны быть диспетчеризуемыми. На основе имени сообщения (связывание на этапе компиляции), типа получателя (single dispatching, т.е. связывание происходит уже в runtime), типа получателя и аргументов (double dispatching, опять же runtime). И из общения посредством передачи сообщений и позднего связывания возникает полиморфизм и дополнительный уровень абстракции.Что мне не нравится в первом (и общеизвестном) определении ООП: полиморфизм и наследование представлены как объекты одного уровня. По факту же, наследование - это лишь один из вариантов реализации полиморфизма (https://en.wikipedia.org/wiki/Subtyping_polymorphism). Существуют и другие - ad hoc polymorphism (https://en.wikipedia.org/wiki/Ad_hoc_polymorphism), parametric polymorphism (https://en.wikipedia.org/wiki/Parametric_polymorphism). При этом полиморфизм сам по себе является одним из вариантов создания абстракций, делая вышеупомянутое определение еще более наивным.
Немного мыслей про readable streams в современном Node.js. В этот раз в форме статьи http://micromind.me/posts/nodejs-readable-streams-distilled
Продолжаем про Node.js стримы - коротко про writable streams https://micromind.me/posts/nodejs-writable-streams-distilled?msrc=tc