- /4te.me
- Установка zfs
- Создание pool-а
- Аналог RAID1 и RAID10
- Проверяем состояние пула
- Как заменить диск в zfs пуле
- Русские Блоги
- Реализация пула потоков на языке Linux C
- Во-первых, общая структура пула потоков
- Во-вторых, массив потоков
- Три, очередь задач
- В-четвертых, менеджер потока
- V. Выпуск
- 6. Интерфейс
- what is an application pool?
- 2 Answers 2
- Caching
- Pooling
- Example
- select / poll / epoll: практическая разница
- Использование select()
- Опрос с помощью poll()
- Polling with epoll()
- libevent
/4te.me
ZFS — это файловая система на стероидах. С помощью ZFS можно собрать подобие RAID-массивов, но с дополнительными функциями, которые могут быть полезны в хозяйстве. Здесь покажу как установить zfs и собрать аналог RAID-10 массива на примере Ubuntu 18.04
Установка zfs
Изначально ZFS была доступна только в Solaris, но с помощью модулей ядра можно установить и в Linux.
На последних версиях Ubuntu установка абсолютно элементарная:
Автоматически устанавливаются модули ядра, а также тулзы для создания разделов.
В терминах zfs — сначала создается pool из разделов или дисков, потом на этот pool накатывается файловая система. Все как и в случае с raid-ами.
Создание pool-а
Пока никаких пулов не создано:
Создаем пул из сырых дисков. Пул можно также создавать из разделов диска (/dev/sdb1, /dev/sdc2 etc.):
Смотрим созданные в системе пулы:
После создания пула, просиходит его автомаунт в /
Аналог RAID1 и RAID10
Выше мы создавали пул, аналогичный RAID0(stripe), без какого-либо резервирования данных. Если умрет хотя бы один диск из пула, то развалится весь пул. Чтобы повысить отказоустойчивость — создадим зеркалированный пул.
Вот так можно создать RAID1(mirror) с помощью zfs:
Создание RAID10 из шести (sdb, sdc, sdd, sde, sdf, sdg) дисков выглядит так:
Проверяем состояние пула
Проверить состояние всех пулов можно так:
Вот так выглядит zpool status когда умер один диск:
Как заменить диск в zfs пуле
Если случилась беда и умер один диск в пуле с резервированием, то его можно легко заменить. Процедура аналогична замене диска в RAID-массиве.
Вот пример из жизни когда в zfs-RAID10 массиве сломался один диск:
Здесь видим, что sdf приказал долго жить и требует замены. В данном случае — это железный сервер и пул построен на целых дисках. Находим нужный диск в железном сервере, и “на горячую” меняем старый диск на новый, такого же размера. В dmesg можно увидеть как определяется новый диск. В моем примере, я вынул /dev/sdf из сервера, вставил новый диск, и он определился в системе с таким же именем.
Теперь меняем диск в пуле. Мы говорим заменить диск, который именовался в пуле как sdf на диск, который в системе именуется /dev/sdf (так же как и старый):
Источник
Русские Блоги
Реализация пула потоков на языке Linux C
Введение: пул потоков — это форма многопоточной обработки, которая в основном используется на высококонкурентных серверах, которая может разумно и эффективно использовать ресурсы потоков на высококонкурентных серверах;
В сетевом программировании Unix потоки и процессы используются для обработки различных подфункций ветвления. Наша обычная операция: получение сообщения ==> классификация сообщений ==> создание потока ==> передача сообщения в подсеть ==> разделение потока = => Выполнить задачу в дочернем потоке ==> задача заканчивается и завершается;
Для большинства небольших локальных сетей достаточно использовать вышеуказанный метод, но при расширении диапазона связи до глобальной сети или крупных локальных сетей мы столкнемся с большим количеством сообщений, часто запрашивающих сервер В этом случае создание и уничтожение потоков стало дорогостоящим занятием, особенно для встроенных серверов, которые должны обеспечивать разумное использование ресурсов памяти;
Таким образом, возникла технология пула потоков: пул потоков позволяет многократно использовать поток, и обработка сообщений внутри мультиплексированного потока может каждый раз отличаться. Устранить накладные расходы, не открывая поток с запросом;
Объяснение структуры:
Пул потоков — это абстрактное понятие, его внутреннийОчередь задач, куча потоков, менеджер потоков;
Мы будем использовать приведенное выше изображение в качестве примера для реализации самого основного пула потоков, который будет объяснен последовательно в разделах; последовательность объяснения такова: 1. Общая структура пула потоков 2. Массив потоков 3. Задача Очередь 4. Диспетчер потока 5. Пример использования интерфейса пула потоков
Во-первых, общая структура пула потоков
Здесь объясняется логическая структура пула потоков, см. Код ниже, структура threadpool_t содержитИнформация о состоянии пула потоков, информация об очереди задач и блокировки мьютекса в многопоточных операциях; Структура задачи содержит функцию, которая может разместить множество различных функций задачи.Указатель на функциюОдин перешел в задачу функцииПараметры типа void *;
Примечание. Вам нужно изменитьФункция обработки классификации сообщенийЗагрузите задачу (* function), затем поместите ее в очередь задач и уведомите свободный поток;
Информация о состоянии пула потоков: Опишите основную информацию о текущем пуле потоков, например, включен ли он, минимальное количество потоков, максимальное количество потоков, количество активных потоков, количество занятых потоков, количество потоков, подлежащих уничтожению, и т. Д.
Информация об очереди задач: Опишите основную информацию о текущей очереди задач, такую как максимальное количество задач, очередь не заполнена условными переменными, очередь не является пустыми условными переменными и т. Д.
Многопоточный мьютекс: Убедитесь, что только один поток извлекает задачи из очереди задач одновременно, измените информацию очереди задач и измените информацию пула потоков;
Указатель на функцию: На этапе упаковки сообщений поместите функцию обработки классифицированных сообщений в (* function);
параметр типа void *: Используется для передачи информации, необходимой для функции обработки сообщений;
Во-вторых, массив потоков
Массив потока — фактически период хранения, созданный, когда пул потока инициализируетсяКуча потоков тидПространство логически формируется в пул, который содержит заранее созданные потоки, это пространство содержитРабочая нить,Темы в ожидании работы(Холостой поток),Нить ожидает уничтожения,Пространство потока объявлено, но не инициализировано;
Три, очередь задач
Очередь задач существует в форме, аналогичной массиву потоков, при инициализации пула потоков она открывает пространство на основе максимального числа переданных задач, когда запрос поступает перед сервером, сообщение классифицируется и упаковывается как задача, задача помещается в очередь задач, и свободный поток уведомляется Чтобы получитьРазница в том, что очередь задач имеет четкий порядок, сначала в порядке;Потоки в массиве потоков — это конкурентные отношения, чтобы заставить мьютекс бороться за задачу;
В-четвертых, менеджер потока
Как менеджер пула потоков, основные функции потока включают в себя: проверку статуса выживания и рабочего состояния потоков в пуле потоков, ответственность за динамическое добавление или удаление потоков в соответствии с текущим состоянием запроса сервера, чтобы гарантировать, что количество потоков в пуле потоков поддерживается на одном уровне. Разумный и эффективный баланс;
В конечном итоге это отдельный поток, который регулярно проверяется, а потоки добавляются или удаляются в соответствии с одним из наших алгоритмов балансировки.
V. Выпуск
6. Интерфейс
Спасибо за чтение этой статьи и надеюсь, что на ваш вопрос ответили
Полный исходный код выглядит следующим образом:
Источник
what is an application pool?
I’m reading this article:
It talks about how Phusion Passenger extends Apache2 to act as an application server. When an HTTP request comes in, the Phusion Passenger module checks whether the request should be handled by a Phusion Passenger-served application. If so, then the module spawns a process for the application, if necessary. Forwards the request to the application process, and forwards the response back to the client. In order to enhance the spawning process, passenger acts as a spawn server which caches Ruby on Rails framework code and application code in memory.
This way every time a new request comes in, when a process is spawned it references the cached code and spawns the process quickly. But despite caching spawning is still expensive compared to an http request. So an application pool is used. I don’t understand what an application pool is. This is what it says:
Spawned application instances are kept alive, and their handles are stored into this pool, allowing each application instance to be reused later. Thus, Passenger has very good average case performance.
What does it mean «kept alive» and their «handles are stored in this pool». I thought that’s the point of caching — to keep data alive for later. So I don’t see how this is different.
2 Answers 2
There isn’t a binary on/off situation going on here. It’s a continuum.
One extreme of the continuum is the old CGI model of web programming: the web server starts an application afresh to service each request that comes in. The application dies when it finishes handling the request. Because the application has to start afresh each time, it has to rebuild the application state on each request. This is expensive in both time and data space.
And so, over time, many options have been created to reduce the expense:
FastCGI uses the CGI model, but it keeps the CGI program alive after starting it the first time, and just keeps feeding new requests to it. This not only avoids the expense of re-launching the CGI program, it lets the app keep state in RAM.
mod_
This system you’re talking about is just one level higher than the mod_foo scheme, where it keeps a logical model of an entire application in RAM, so that even fewer things have to be re-created on each request. Exactly how this differs from the base behavior you get with mod_foo I couldn’t say. The point, though, is that it is not a difference in quality, just of amount.
The next step down the line, which may help you to understand where this Phusion Passenger is trying to go, is to keep everything running within a single long-running application instance. This is how web servers based on Erlang work, such as Nitrogen. Visit that second link for a comparison with the way Apache-based applications work.
I believe many Java based application servers work the same way.
Bottom line, all of this is an optimization game, trading RAM and runtime complexity for speed.
The terms «kept alive» and their «handles are stored in this pool» is in reference to something different than caching.
Caching
Caching is a mechanism where data that’s expensive to acquire is kept in a fast accessing location for reuse later on. You’ll see it used in a variety of places such as:
- looking something up in a database
- resolving a server’s IP address
- accessing a file from a hard drive
Pooling
The «kept alive» they’re referring to in the Phusion Passenger documentatoin is referring to forcing an application to stay up indefinitely so that we can save on the time it takes to launch the application.
When servicing a web request you want the application to be as responsive as possible. If it takes several seconds for an application to start up, then several seconds more to request data from a database, there’s no way that you’ll be able to create a responsive web application.
So instead what’s done is a light frontend is constructed that will accept connections and then keep multiple instances of the application up and running at all times, and then the frontend will do the following:
- assign an incoming connection to one of the already running instances of app
- take the results from this running instance
- pass the results back to the client
- put the running instance of app back into a «ready to process» state
Example
I like to use the example of having multiple registers at a grocery store. Each lane is an instance of the app server, and each can only process a single user at a time, but together they can service several.
If you take a look at Ruby’s Mongrel HTTP server it was designed so that it could be run in a pool in much the same way.
One popular configuration was to run Apache HTTP Server 2.2 as a load balancer using mod_proxy_balancer in conjunction with several Mongrel instances. Each Mongrel instance would run on a separate TCP port, configured via the mongrel_cluster management utility. Until recently, Twitter was a notable instance of this configuration.
Mongrel was capable of serving Ruby on Rails powered sites without requiring any other web servers, though as a single-threaded application this configuration is unsuitable for all but light loads
Источник
select / poll / epoll: практическая разница
При проектировании высокопроизводительных сетевых приложения с неблокирующими сокетами важно решить, какой именно метод мониторинга сетевых событий мы будем использовать. Их есть несколько и каждый хорош и плох по-своему. Выбор правильного метода может быть критически важной вещью для архитектуры вашего приложения.
В этой статье мы рассмотрим:
Использование select()
Старый, проверенный годами работяга select() создавался ещё в те времена, когда «сокеты» назывались «сокетами Беркли». Данный метод не вошел в самую первую спецификацию тех самих сокетов Беркли, поскольку в те времена вообще ещё не существовало концепции неблокирующего ввода-вывода. Но где-то в 80-ых годах она появилась, а вместе с ней и select(). С тех пор в его интерфейсе ничего существенно не менялось.
Для использования select() разработчику необходимо инициализировать и заполнить несколько структур fd_set дескрипторами и событиями, которые необходимо мониторить, а затем уже вызвать select(). Типичный код выглядит примерно вот так:
Когда проектировался select() никто, вероятно, не ожидал, что в будущем у нас появится необходимость писать многопоточные приложения, обслуживающие тысячи соединений. У select() есть сразу несколько существенных недостатков, делающих его плохо пригодным для работы в такого рода системах. Основными являются следующие:
- select модифицирует передаваемые ему структуры fd_sets, так что ни одну из них нельзя переиспользовать. Даже если вам не нужно ничего менять (например, получив порцию данных, вы хотите получить ещё) структуры fd_sets придётся переинициализировать. Ну или копировать из заранее сохранённого бэкапа с помощью FD_COPY. И это придётся делать снова и снова, перед каждым вызовом select.
- Для выяснения того, какой именно дескриптор сгенерировал событие, вам придётся вручную опросить их все с помощью FD_ISSET. Когда вы мониторите 2000 дескрипторов, а событие произошло лишь для одного из них (который по закону подлости будет последним в списке) — вы потратите уйму процессорных ресурсов впустую.
- Я только что упомянул 2000 дескрипторов? Это я погорячился. select не поддерживает так много. Ну, по крайней мере на обычном линуксе, с обычным ядром. Максимальное количество одновременно наблюдаемых дескрипторов ограниченно константой FD_SETSIZE, которая в линуксе жестко равна 1024. Некоторые операционные системы позволяют реализовать хак с переопределением значения FD_SETSIZE перед включением заголовочного файла sys/select.h, но этот хак не является частью какого-то общего стандарта. Тот же Linux проигнорирует его.
- Вы не можете работать с дескрипторами из наблюдаемого набора из другого потока. Представьте себе поток, выполняющий вышеуказанный код. Вот он запустился и ждёт событий в своём select(). Теперь представьте, что у вас есть ещё один поток, мониторящий общую нагрузку на систему, и вот он решил, что данные от сокета sock1 не приходят уже слишком давно и пора бы разорвать соединение. Поскольку данный сокет может быть переиспользован для обслуживания новых клиентов, хорошо бы его корректно закрыть. Но ведь первый поток наблюдает в том числе и за этим дескриптором прямо сейчас. Что же будет, если мы его всё-таки закроем? О, у документации есть ответ на этот вопрос и он вам не понравится: «Если дескриптор, наблюдаемый при помощи select(), будет закрыт другим потоком, вы получите неопределённое поведение».
- Та же проблема появляется и при попытке отправить какие-то данные через sock1. Мы ничего не отправим, пока select не закончит свою работу.
- Выбор событий, которые мы можем мониторить, достаточно ограничен. Например, для определения того, что удалённый сокет был закрыт, вам следует, во-первых, мониторить события прихода данных по нему, а во-вторых, сделать попытку чтения этих данных (read вернёт 0 для закрытого сокета). Это ещё можно назвать приемлемым при чтении данных из сокета (прочитали 0 — сокет закрыт), но что, если наша текущая задача в данный момент — отправка данных этому сокету и никакое чтение данных из него нам сейчас не нужно?
- select накладывает на вас излишнее бремя вычисления «наибольшего дескриптора» и передачу его отдельным параметром
Конечно, всё вышесказанное не является какой-то новостью. Разработчики операционных систем давно осознали данные проблемы и многие из них были учтены при проектировании метода poll. В этом месте вы можете спросить, а зачем мы вообще сейчас изучаем древнюю историю и есть ли сегодня какие-то причины использовать древний select? Да, такие причины есть и их целых две. Не факт, что они когда-то вам пригодятся, но почему бы о них не узнать.
Первая причина — портируемость. select() с нами уже миллион лет. В какие бы дебри программно-аппаратных платформ вас не занесло, если там есть сеть — там будет и select. Там может не быть никаких других методов, но select будет практически гарантированно. И не думайте, что я сейчас впадаю в старческий маразм и вспоминаю что-то типа перфокарт и ENIAC, нет. Более современного метода poll нет, например, в Windows XP. А вот select есть.
Вторая причина более экзотична и имеет отношению к тому факту, что select может (теоретически) работать с таймаутами порядка одной наносекунды (если позволит аппаратная часть), в то время как и poll и epoll поддерживают лишь миллисекундную точность. Это не должно играть особой роли на обычных десктопах (или даже серверах), где у вас всё равно нет аппаратного таймера наносекундной точности. Но всё же в мире есть системы реального времени, имеющие такие таймеры. Так что я вас умоляю, когда будете писать прошивку ядерного реактора или ракеты — не поленитесь измерять время до наносекунд. Я, знаете ли, хочу жить.
Описанный выше случай, вероятно, единственный в котором у вас и правда нет выбора, что использовать (подходит лишь select). Однако, если вы пишете обычное приложение для работы на обычном железе, и вы будете оперировать адекватным количеством сокетов (десятками, сотнями — и не больше), то разница в производительности poll и select будет не заметна, так что выбор будет основываться на других факторах.
Опрос с помощью poll()
poll — это более новый метод опроса сокетов, созданный после того, как люди начали пытаться писать большие и высоконагруженные сетевые сервисы. Он спроектирован намного лучше и не страдает от большинства недостатков метода select. В большинстве случаев при написании современных приложений вы будете выбирать между использованием poll и epoll/libevent.
Для использования poll разработчику нужно инициализировать члены структуры pollfd наблюдаемыми дескрипторами и событиями, а затем вызвать poll().
Типичный код выглядит вот так:
Poll был создан для решения проблем метода select, давайте посмотрим, как у него это получилось:
- Нет никакого лимита количества наблюдаемых дескрипторов, можно мониторить более 1024 штук
- Не модифицируется структура pollfd, что даёт возможность её переиспользования между вызовами poll() — нужно лишь обнулить поле revents.
- Наблюдаемые события лучше структурированы. Например, можно определить отключение удалённого клиента без необходимости чтения данных из сокета.
О недостатках метода poll мы уже говорили выше: его нет на некоторых платформах, вроде Windows XP. Начиная с Vista он существует, но называется WSAPoll. Прототип тот же, так что для платформенно-независимого кода можно написать переопределение, вроде:
Ну и точность таймаутов в 1 мс, которой будет недостаточно очень редко. Однако, у poll есть и другие недостатки:
- Как и при использовании select, невозможно определить какие именно дескрипторы сгенерировали события без полного прохода по всем наблюдаемым структурам и проверки в них поля revents. Что ещё хуже, так же это реализовано и в ядре ОС.
- Как и при использовании select, нет возможности динамически менять наблюдаемый набор событий
Однако, всё вышеперечисленное можно считать относительно несущественным для большинства клиентских приложений. Исключение составляют, наверное, лишь p2p протоколы, где каждый из клиентов может быть связан с тысячами других. Эти проблемы могут игнорироваться даже большинством серверных приложений. Таким образом poll должен быть вашим предпочтением по-умолчанию перед select, если только вас не ограничивает одна из двух вышеуказанных причин.
Забегая наперёд, скажу, что poll является более предпочтительным даже по сравнению с более современным epoll (рассматривается ниже) в следующих случаях:
- Вы хотите писать кросплатформенный код (epoll есть только в Linux)
- Вам не нужно мониторить более 1000 сокетов (epoll не даст вам ничего существенного в этом случае)
- Вам нужно мониторить более 1000 сокетов, но время соединения с каждым из них очень невелико (в этих случаях производительность poll и epoll будет очень близка — выигрыш от ожидания меньшего количества событий в epoll будет перечёркнут накладными расходами на их добавление/удаление)
- Ваше приложение не спроектировано таким образом, чтобы менять события из одного потока, пока другой ожидает их (или вам этого не требуется)
Polling with epoll()
epoll — это новейший и лучший метод ожидания событий в Linux (и только в Linux). Ну, не то чтобы прям «новейший» — он в ядре с 2002 года. От poll и select он отличается тем, что предоставляет API для добавления/удаления/модификации списка наблюдаемых дескрипторов и событий.
Использование epoll требует чуть более тщательных приготовлений. Разработчик должен:
- Создать дескриптор epoll с помощью вызова epoll_create
- Инициализировать структуру epoll_event нужными событиями и указателями на контексты соединений. «Контекст» здесь может быть чем-угодно, epoll просто передаёт это значение в возвращаемых событиях
- Вызвать epoll_ctl( … EPOLL_CTL_ADD ) для добавления дескриптора в список наблюдаемых
- Вызвать epoll_wait() для ожидания событий (мы указываем сколько именно событий хотим получить за раз, например, 20). В отличии от предыдущих методов — мы получим эти события отдельно, а не в свойствах входных структур. Если мы наблюдаем 200 дескрипторов и 5 из них получили новые данные — epoll_wait вернёт лишь 5 событий. Если произойдёт 50 событий — нам вернут первые 20, а остальные 30 будут ждать следующего вызова, они не потеряются
- Обработать полученные события. Это будет относительно быстрая обработка, ведь мы не просматриваем те дескрипторы, где ничего не произошло
Типичный код выглядит вот так:
Давайте начнём с недостатков epoll — они очевидны из кода. Данный метод сложнее использовать, нужно написать больше кода, он делает больше системных вызовов.
Достоинства тоже налицо:
- epoll возвращает список только тех дескрипторов, для которых реально произошли наблюдаемые события. Не нужно просматривать тысячи структур в поисках той, возможно, одной, где сработало ожидаемое событие.
- Вы можете ассоциировать некоторый значимый контекст с каждым наблюдаемым событием. В примере выше мы использовали для этого указатель на объект класса соединения — это сэкономило нам ещё один потенциальный поиск по массиву соединений.
- Вы можете добавлять или удалять сокеты из списка в любое время. Вы можете даже модифицировать наблюдаемые события. Всё будет работать корректно, это официально поддерживается и задокументировано.
- Можно завести сразу несколько потоков, ожидающих события из одной и той же очереди с помощью epoll_wait. Нечто, что никоим образом не получится сделать с select/poll.
Но нужно также помнить и о том, что epoll — это не «во всём улучшенный poll». У него есть и недостатки по сравнению с poll:
- Изменение флагов событий (например, переключение с READ на WRITE) требует лишнего системного вызова epoll_ctl, в то время как для poll вы просто меняете битовую маску (полностью в пользовательском режиме). Переключение 5000 сокетов с чтения на запись потребует для epoll 5000 системных вызовов и переключений контекста, в то время как для poll это будет тривиальная битовая операция в цикле.
- Для каждого нового соединения вам придётся вызвать accept() и epoll_ctl() — это два системных вызова. В случае использования poll вызов будет лишь один. При очень коротком времени жизни соединения это может иметь значение.
- epoll есть только в Linux. В других ОС есть схожие механизмы, но всё же не полностью идентичные. Вам не удастся написать код с epoll так, чтобы он собрался и заработал, например, на FreeBSD.
- Писать высоконагруженный параллельный код — тяжело. Многим приложениям не нужен столь фундаментальный подход, поскольку их уровень нагрузки легко обрабатывается и более простыми методами.
Таким образом, использовать epoll следует только тогда, когда выполняется всё нижесказанное:
- Ваше приложение использует пул потоков для обработки сетевых соединений. Выигрыш от epoll в однопоточном приложении будет ничтожен, не стоит и заморачиваться на реализацию.
- Вы ожидаете относительно большого числа соединений (от 1000 и выше). На небольшом количестве наблюдаемых сокетов epoll не даст прироста производительности, а если сокетов буквально несколько штук — может даже замедлить.
- Ваши соединения живут относительно долго. В ситуации, когда новое соединение передаёт буквально несколько байт данных и тут же закрывается — poll будет работать быстрее, ведь на обработку ему нужно будет делать меньше системных вызовов.
- Вы намерены запускать ваш код на Linux и только на Linux.
Если один или несколько пунктов не выполняются — рассмотрите использование poll или libevent.
libevent
libevent — это библиотека, которая «оборачивает» методы опроса, перечисленные в данной статье (а также некоторые другие) в унифицированный API. Преимущество здесь в том, что, однажды написав код, вы можете собрать и запустить его на разных операционных системах. Тем не менее, важно понимать, что libevent — это всего лишь обёртка, внутри которой работают всё те же вышеперечисленные методы, со всеми их преимуществами и недостатками. libevent не заставит select слушать более 1024 сокетов, а epoll — модифицировать список событий без дополнительного системного вызова. Так что знать лежащие в основе технологии по-прежнему важно.
Необходимость поддерживать разные методы опроса приводит к усложнению API библиотеки libevent. Но всё же его использование проще, чем вручную писать два разных движка выборки событий для, например, Linux и FreeBSD (используя epoll и kqueue).
Рассмотреть возможность использования libevent стоит при сочетании двух событий:
- Вы рассмотрели методы select и poll и они вам точно не подошли
- Вам нужно поддерживать несколько ОС
Источник