File: managing.tex

package info (click to toggle)
mongrel2 1.12.2-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 6,020 kB
  • sloc: ansic: 39,099; python: 2,833; sql: 1,555; javascript: 1,202; sh: 467; makefile: 360; asm: 189; yacc: 145; php: 73; awk: 28; sed: 5
file content (803 lines) | stat: -rw-r--r-- 56,091 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
\chapter{Управление}

Mongrel2 создан так, чтобы его можно было легко развёртывать, более
того --- \emph{автоматизировать} развёртывание. Именно поэтому он
использует \href{http://www.sqlite.org/}{SQLite}, чтобы хранить
настройки, и утилиту m2sh в качестве интерфейса к этой базе настроек.
По сути, вы можете управлять этой базой из любого языка.

В этой главе я продемонстрирую создание базовой конфигурации в m2sh, а
также другие доступные команды. Вы узнаете внутренню структуру
конфигурационной системы, которая в общем является простым и удобным
механизмом хранения данных.

\begin{aside}{SQL вводит в заблуждение}

Когда я впервые упомянул Mongrel2, я сказал, что буду хранить
настройки с помощью SQLite и создам MVC архитектуру.  Те, кто не умеют
читать, начали биться в истерике и кричать, что это равносильно
``реестру Windows'', только для доступа к данным используется язык
запросов.  Они думали, что придётся конфигурировать сервер с помощью
SQL. Естественно, звучит, мягко говоря, непривычно.

Всё это --- большое заблуждение. \emph{Никто никого} не заставляет
использовать SQL.  Я повторял это снова и снова, но многие не слышали
этого.  SQLite не имеет ничего общего с реестром Windows. Моя
изначальная цель --- создать сервер, который можно настроить из
\emph{любого языка программирования}, а затем предоставить удобный
инструмент, чтобы люди вообще \emph{навсегда} забыли про SQL.

В итоге, что мы имеем, несмотря на всю суету? Офигительный инструмент
для конфигурирования сервера с простым и элегантным дизайном, который,
вдобавок к этому, отлично работает. Если союз Mongrel2 и SQLite
кажется вам необычным, то добро пожаловать в будущее --- иногда оно
очень и очень странное (хотя Postfix делает то же самое на протяжении
десяти лет).

\end{aside}


\section{MVC}

Когда вы слышите MVC\footnote{Model-View-Controller}, вы думаете о
веб-приложениях, где этот паттерн проектирования стал стандартом. Суть его в
разделении системы на уровни по функциональному признаку. В такой системе
\emph{модель} хранит данные, \emph{контроллер} реализует бизнес-логику, а
взаимодействие с пользователем происходит посредством \emph{вида}.

Сила такой архитектуры в том, что все компоненты достаточно независимы
и автономны и их легко адаптировать к новым требованиям. Однако они
перестают быть таковыми, как только вы начинаете их смешивать.  Нет
какой-либо строгой математически обоснованной теории, подтверждающей
это. Но есть многолетний опыт сообщества, который подсказывает:
смешивать уровни --- плохая идея, иначе возникнут проблемы. Например,
вы раскидали бизнес-логику (контроллер) по веб-страницам (вид) и позже
обнаруживаете, что её достаточно сложно изменять.  Или не можете
изменить схему базы (модель), потому что это противоречит хранимым
процедурам (контроллер).

Mongrel2 нуждался в механизме автоматизации: ведь это одна из ключевых
функций сервера, если не самая ключевая. Сервер является контроллером,
т.е.  он реализует бизнес-логику и придаёт смысл модели. В качестве
модели был выбран SQLite. Он идеально подходит: внедряемый движок,
легко доступный из любого языка программирования, портируемый,
маленький, быстрый и т.п. Дело оставалось за видом --- так и появилась
утилита m2sh.

m2sh (доступная из \file{tools/m2sh}) --- это \emph{вид}, интерфейс к модели,
которая хранит конфигурацию сервера. Эта утилита только пример; вы можете
создать свой вид на любимом языке программирования. Это может быть всё что
угодно --- скрипт для автоматического развёртывания сервера, веб-интерфейс,
скрипт мониторинга, и т.п.

В общем и целом всё просто: если вы просто хотите выполнять базовые
операции с сервером --- запускать, останавливать, создавать и
загружать конфигурации --- используйте m2sh. Если же вам нужно что-то
более продвинутое, то изучите схему базы и пробуйте. Структура базы
достаточно хорошо отражает внутренню структуру сервера, так что если
вы поймёте её, то вы в принципе поймёте как работает Mongrel2.

\section{Пробуем m2sh}

Пробовать будем на конфигурационном файле, который используется в
юнит-тестах. Для разогрева выполним самые базовые команды:

\begin{code}{m2sh в действии}
\begin{lstlisting}
> m2sh servers -db tests/config.sqlite
> m2sh hosts -db tests/config.sqlite -server test
> m2sh running -db tests/config.sqlite -name test
> m2sh start -db tests/config.sqlite -host localhost
\end{lstlisting}
\end{code}

Вы только что сделали следующее: вывели настройки на экран; вывели
список серверов, затем список хостов; проверили, запущен ли сервер
(он не запущен); и запустили сервер.

Чтобы узнать больше об этой команде, вызовите справку --- \shell{m2sh
help} или \shell{m2sh help command}.

Теперь попробуем запустить-остановить сервер в sudo-режиме и
перезагрузить настройки (нажмите CTRL+c, чтобы прервать предыдущую
команду):

\begin{code}{Запускаем, останавливаем, перезагружаем}
\begin{lstlisting}
> m2sh start -db tests/config.sqlite -host localhost -sudo
> tail logs/error.log
> m2sh reload -db tests/config.sqlite -host localhost
> tail logs/error.log
> curl http://localhost:6767/
> tail logs/error.log
> m2sh running -db tests/config.sqlite -host localhost
> m2sh stop -db tests/config.sqlite -host localhost
\end{lstlisting}
\end{code}

Круто, да? Используя этот маленький инструмент можно полностью
контролировать сервер. И не нужно ковыряться в конфигурационных
файлах. Однако неплохо бы знать как всё это работает.

\subsection{А что произошло?}

А произошло следующее --- вы выполнили большинство из возможных
операций над базой настроек. Ниже приведено детальное
объяснение всех запущенных команд:

\begin{enumerate}
\item Вызов \shell{m2sh start} с опцией \shell{-sudo} транслируется в
    \shell{sudo mongrel2 tests/config.sqlite localhost} и запускает
    сервер.
\item Mongrel2 запущен и работает в фоновом процессе, как и подобает
    серверу. Но предварительно текущую директорию он сделал рутовой
    \footnote{С помощью команды chroot}, а текущего пользователя ---
    владельцем процесса. Вызовите \shell{ps aux}, чтобы убедиться, что
    сервер действительно запущен.
\item Смотрим логи в logs/error.log: там куча отладочной информации.
    Но обратите внимание на сообщения --- детальные и адекватные.
\item Горячая перезагрузка: процесс mongrel2 заново считывает и загружает
    настройки.
\item Однако из-за небольшого бага перезагрузка не осуществляется
    немедленно --- надо подождать до следующего запроса, который
    эмулируется с помощью \shell{curl http://localhost:6767/}.
\item Чтобы убедиться, что перезагрузка прошла успешно, смотрим логи и
    выполняем \shell{m2sh running}. Эта команда считывает с базы настроек
    расположение PID-файла (\file{run/mongrel2.pid}) и проверяет, запущен
    ли этот процесс.
\item Наконец, останавливаем сервер. Замечательная вещь в том, что,
    поскольку в момент запуска текущий пользователь стал владельцем
    процесса, вы можете остановить этот процесс не прибегая sudo.
\end{enumerate}

\section{Базовый конфигурационный файл}

Для создания новой базы настроек вы напишите конфигурационный файл.
Файл похож на скрипт на Питоне; изначально он и был таким скриптом,
поскольку первая версия утилиты m2sh была на написана на Питоне (и
сейчас находится в \file{examples/python}). Чуть позже она была
переписана на Си.  Несмотря на то, что она была полностью переделана,
мы сохранили первоначальный формат. И даже немного его упростили,
сделав запятые необязательными во многих местах.

Сначала надо загрузить этот файл в чистую базу с помощью команды
\shell{m2sh load}. Для нашего примера воспользуемся файлом
\file{examples/configs/sample.conf}:

\begin{code}{Простой маленький скрипт настроек}
  \lstinputlisting[language=Python]{../../../examples/configs/sample.conf}
\end{code}

Если Python вам не знаком, то код может показаться необычным, но на
самом деле он довольно прост. Мы вскоре вернёмся к нему, а пока
выполните следующее:

\begin{code}{Загружаем скрипт}
\begin{lstlisting}
m2sh load -config examples/configs/sample.conf
ls -l config.sqlite
m2sh servers
m2sh hosts -server test
m2sh start -server test
\end{lstlisting}
\end{code}

Обратите внимание, что по умолчанию база записывается в файл
\file{config.sqlite}, поэтому можно её явно не указывать в команде.
Конфигурационным файлом по умолчанию является \file{mongrel2.conf} (но
поскольку в нашем примере используется другой файл, мы его явно
передаём).

Последовательность этих команд выполняет следующее:

\begin{enumerate}
\item Создаёт чистую базу настроек \file{simple.sqlite} и загружает
в неё скрипт \file{sample.conf}.
\item Выводит список серверов из базы.
\item Выводит список хостов со всеми маршрутами.
\item Запускает сервер.
\end{enumerate}

Полагаю, у вас начала складываться картина того, как создать новую
конфигурацию --- использовать m2sh, чтобы загрузить скрипт и
сгенерировать .sqlite, с которым непосредственно работает Mongrel2.

\section{Структура базы настроек}

База настроек структурирована следующим образом:

\begin{description}
  \item[Server] Является корнем в иерархии; их может быть несколько, несмотря на то, что 
    Mongrel2 одновременно может запустить один их них.
  \begin{description}
    \item[Host] Внутри сервера --- хосты, т.е. доменные имена, на
      которые должен отвечать веб-сервер. Их тоже может быть несколько.
    \begin{description}
      \item[Route] Внутри хостов находятся маршруты, которые
        разруливают приходящие запросы в зависимости от URL. Внутри
        маршрута может быть \ident{Dir}, \ident{Handler} или \ident{Proxy}.
      \begin{description}
        \item[Dir] Самый простой маршрут --- говорит серверу, что надо
          выдавать файлы из директории. Mongrel2 поддерживает 304-й статус,
          ETag, типы данных по умолчанию, и вообще всё необходимое для
          обслуживания статических файлов.
        \item[Proxy] Прокси берёт запрос и перенаправляет его на
          другой http-сервер. Mongrel2 держит соединения открытыми в keep-alive
          режиме, если браузер поддерживает их.
        \item[Handler] Это самая лучшая часть Mongrel2. Обработчик
          получает http-запрос, заворачивает его в ZeroMQ сообщение и передаёт
          его в бэкенд, но делает это асинхронно.
      \end{description}
    \end{description}
  \end{description}
\end{description}

Каждый из этих ``объектов'' имеет свой набор атрибутов для более
тонкой настройки и большинству из этих атрибутов присваиваются
достаточно адекватные значения по умолчанию.

\subsection{Server}

Этот раздел хранит настройки, характерные для любого сервера: какой
порт слушать, куда писать логи и т.п.

\begin{description}
\item[uuid] Уникальный идентификатор сервера внутри вашей
    инфраструктуры. Это может быть любая строка, но традиционно
    используется UUID --- универсальный уникальный идентификатор, который
    можно сгенерировать с помощью \shell{m2sh uuid}.
\item[chroot] Директория, которую Mongrel2 делает рутовой.
\item[access\_log] Путь к лог-файлу запросов.
    Путь задаётся относительно \emph{рутовой директории}. Обычно
    начинается с `/'. Убедитесь, что этот и другие подобные файлы
    недоступны из браузера.
\item[error\_log] Путь к лог-файлу ошибок.
\item[pid\_file] Путь к pid-файлу.
\item[default\_host] Сервер может иметь произвольное количество
    хостов, но среди них должен быть один по умолчанию. С помощью этого
    хоста удобно ссылаться на весь сервер.
\item[port] Порт, который должен слушать сервер.
\end{description}


\subsection{Host}

Раздел Host хранит информацию о каждом хосте, доступном из сервера.

\begin{description}
\item[name] Имя хоста.
\item[matching] Паттерн, который используется, чтобы сопоставить с
    хостом в заголовке для принятия решения о дальнейшей маршрутизации запроса.
\item[server] Используйте этот атрибут, если хотите назначить сервер отдельно.
\item[maintenance] Настройка для будущего использования. Если сервер
    некоторое время недоступен из-за проведения профилактических работ,
    этот атрибут будет указывать на временную страничку, которая будет
    уведомлять об этом посетителей.
\item[routes] Хеш-таблица, которая хранит данные по маршрутизации запросов.
\end{description}


\subsection{Route}

Route --- или маршрут --- рабочая лошадка всей системы. Маршруты
используют довольно интересный, но простой код, чтобы транслировать
путь в http-заголовке в один из пунктов назначения на сервере:
\ident{Handler}, \ident{Dir}, \ident{Proxy}.

\begin{description}
\item[path] \emph{Паттерн} соответствия маршруту. Mongrel2 использует
    упрощённую версию системы сопоставления паттернов из Lua.
\item[reversed] Определяет, является ли паттерн реверсивным. Этот
    атрибут полезен для сравнения расширений файлов, хостов и пр., где
    окончание фактически является префиксом. Обычно этот атрибут игнорируется.
\item[host] Используется, чтобы задать хост вручную.
\item[target] Пункт назначения запроса, т.е. что-либо из списка:
\ident{Dir}, \ident{Handler}, \ident{Proxy}.
\end{description}

Позже вы узнаете о системе сравнения паттернов, используемой в
сервере, но по сути это упрощённая версия регулярных выражений c
некоторыми добавлениями. При конфигурировании маршрута пишется
что-то вроде \file{/images/(.*.jpg)}. Всё, что идёт до первой скобки,
используется как префикс (который легко сравнить); в то время как всё
после скобки --- собственно паттерн. Когда приходит запрос, Mongrel2
ищет самый длинный подходящий префикс и проверяет паттерн. Если
паттерн подходит, то запрос идёт дальше. В противно случае --- 404.

\subsection{Dir}

\ident{Dir} --- инструктирует веб-сервер обслуживать файлы из
директории. Поддерживает If-Modified-Since, ETag и прочие странные
механизмы кэширования согласно протокола HTTP. Также поддерживает
индексные файлы и типы данных по умолчанию.

\begin{description}
\item[base] Базовая директория \emph{внутри рутовой}, из которой будут
    выдаваться файлы.
\item[index\_file] Файл по умолчанию; он выдаётся, если в запросе файл явно
    не указан. \ident{Dir} также делает редирект, если запрашивается
    директория, а на конце нет слэша.
\item[default\_ctype] Content-Type по умолчанию; используется, если в
    таблице типов нет подходящего MIMEType.
\end{description}

На данный момент это все доступные параметры, но со временем мы будем
добавлять больше настроек, чтобы управлять работой
\ident{Dir}-маршрутов.

\subsection{Proxy}

Прокси добавлены в Mongrel2 для того, чтобы вы могли использовать
существующую инфраструктуру. Мы приложили все усилия, чтобы сделать
систему проксирования быстрой и надёжной внутри Mongrel2, тем не
менее, она не конкурент обработчикам ZeroMQ. Я вижу один сценарий
развития --- вы добавляете прокси, чтобы Mongrel2 перенаправлял
запросы на ваши текущие серверы; но постепенно вы переводите всё на
обработчики.

\begin{description}
\item[addr] DNS-адрес сервера.
\item[port] Порт подключения.
\end{description}

Mongrel2 распарсивает запросы, которые попадают под проксирование,
поэтому ваши серверы должны получать эти запросы в нормальном виде.
Ответы же от прокси передаются как есть в браузер.

\subsection{Handler}

Ну вот, мы подошли к самой интересной части:
\ident{ZeroMQ-обработчикам}, которые получают асинхронные запросы от
Mongrel2. Для их конфигурирования нужно использовать ZeroMQ-синтаксис.
Плюс его в том, что разные транспортные протоколы --- UDP, TCP, Unix,
PGM --- одинаково настраиваются. Мы в основном тестировали TCP.

\begin{description}
\item[send\_spec] Спецификация отправителя в формате 0MQ. Например,
    строка ``tcp://127.0.0.1:9999'' говорит, что надо соединяться с
    сервером по адресу 127.0.0.1 на порт 9999, используя протокол TCP.
    Отправители используют PUSH-сокеты, чтобы обработчики получали запросы
    в стиле round-robin.
\item[send\_ident] Этот идентификатор (обычно UUID) используется для
    регистрации отправителя. Если ваш сервер рухнет по каким-то причинам,
    этот идентификатор используется, чтобы восстановить необработанные
    сообщения.
\item[recv\_spec] Подобно спецификации отправителя, только для
    получения ответов от обработчиков. Получатели используют SUB-сокеты,
    т.е. подписчики. Это позволяет кластеру из серверов Mongrel2 слушать
    ответы, но только одному из них с подходящим recv\_ident --- реагировать.
\item[recv\_ident] Ещё один UUID. Полезен, если необходимо подписаться
    и слушать ответы. В ответах обработчики передают send\_ident ---
    идентификатор отправителя. Так что у вас два варианта: присвоить
    recv\_ident то же значение, что и send\_ident и таким образом
    подписаться на ответы; либо ничего не присваивать и не подписываться.
\end{description}

Примечательный факт --- веб-серверу абсолютно \emph{безралично} где
расположены бэкенды. Вы обратили внимание на то, что не нужно
прописывать кластеры из прокси, или методы выбора прокси-сервера, или
ещё что-либо в этом духе? Всё что нужно определить --- две конечные
точки и их идентификаторы. Это потому что Mongrel2 \emph{связывается}
с сокетами и слушает. Mongrel2 не подключается к бэкэндам; напротив
--- бэкенды подключаются к нему. Это значит, что если вам нужно ещё 10
обработчиков, чтобы справиться с нагрузкой, вы просто их запускаете и
всё. Не надо перегружать или заново конфигурировать веб-сервер.

\subsection{Другое}

Есть также объекты/таблицы \ident{MIMEType}, \ident{Log}, и
\ident{Setting}. Но они не нужны, чтобы понять структуру сервера,
поэтому мы вернёмся к ним позже.

\section{Более сложный пример}

Теперь, вооружившись всеми необходимыми знаниями о структуре Mongrel2,
можно взглянуть на более сложный пример. Я буду комментировать, вы ---
сопоставлять мои комментарии с кодом.  Файл с примером находится
здесь: \file{examples/configs/mongrel2.conf}:

\begin{code}{Скрипт настроек mongrel2.org}
  \lstinputlisting[language=Python]{../../../examples/configs/mongrel2.conf}
\end{code}

Если вы ещё не догадались, этот скрипт используется для сайта
\url{http://mongrel2.org}. И вот что в нём есть:

\begin{enumerate}
\item Простой сервер и в нём mongrel2.org как хост по умолчанию.
\item Маршруты хранятся в отдельных переменных, а не в одной куче как
    в примере \file{sample\_conf.py}.
\item Первый маршрут --- Dir --- обслуживает файлы из директории
    \file{tests} и использует \file{index.html} в качестве индексного файла.
\item Создаём прокси и направляем его на локальный веб-сервер; удобно
    для тестирования \ident{Proxy}-маршрута.
\item Далее идет \ident{Dir} для \url{http://mongrel2.org:6767/chatdemo/}. Посмотрим позже.
\item Также для демо-чата создаём \ident{Handler}, который и реализует всю логику.
\item После этого следует небольшой \ident{Handler} для тестирования
    обращений к обработчикам. Обратите внимание, несмотря на то, что они
    используют разные протоколы (демо-чат использует JSSockets) не нужно
    это явно указывать. Mongrel2 определяет это не из конфигурации, а по
    поведению.
\item Теперь, имея все переменные, мы можем создать хост
\ident{mongrel2}. Обратите внимание, я поленился выносить настройки
    для mp3stream-демо в отдельную переменную, и объявил их прямо в хэш-таблице. Это вполне
    допустимо. Запомните также, что можно использовать строки вида
    \verb|'blah'|, чтобы не экранировать обратные слэши.
\item Добавляем все хосты из переменной \ident{mongrel2} в \ident{main}-сервер.
\item Переменной \ident{settings} присваимваем массив глобальных
    настроек. В данном случае мы только увеличиваем количество рабочих
    потоков, которые 0MQ использует для своих нужд.
\item Наконец, заносим конфигурацию в базу.
\end{enumerate}

И это, пожалуй, самая сложная конфигурация на сегодняшний день.


\section{Маршрутизация и паттерны}

Код для работы с паттернами был взят из
\href{http://www.lua.org/}{Lua} и это, пожалуй, самый быстрый код
такого рода. Он очень похож на регулярные выражения, за исключением
того, что из него выкинуто всё лишнее и не нужное для маршрутизации
запросов. Также, в отличие от регулярных выражений, паттерны для
URLов всегда сравниваются с начала строки.

Вот как Mongrel2 ищет подходящий маршрут для заданного URLа. Он
разбивает маршруты на две части: префикс и паттерн. Затем ищет среди
маршрутов подходящий с самым длинным префиксом и анализирует паттерн.
Если паттерн подходит --- маршрут найден. Если паттерна нет вообще, то
маршрут снова подходит, поскольку фиксированная часть маршрута ---
префикс --- подошла.

Единственный нюанс, о котором надо помнить --- паттерны надо брать в
скобки. Собственно, здесь скобки используются исключительно для этих
целей --- отделять паттерн (в отличие от регулярных выражений, где они
обозначают группу). Таким образом, например, вместо
\shell{/images/.*.jpg} надо писать \shell{/images/(.*.jpg)}

Ниже приведён более детальный список символов, которые можно
использовать в паттернах:

\begin{itemize}
\item \verb|.| (точка) Все символы.
\item \verb|\a| Буквы.
\item \verb|\c| Управляющие символы.
\item \verb|\d| Цифры.
\item \verb|\l| Строчные буквы.
\item \verb|\p| Символы пунктуации.
\item \verb|\s| Пробелы.
\item \verb|\u| Заглавные буквы.
\item \verb|\w| Буквы и цифры.
\item \verb|\x| Шестнадцатиричные цифры.
\item \verb|\z| Символ окончания строки (null terminator).
\item \verb|[set]| Как и в регулярных выражениях --- [] обозначает
множество символом, например, [0-9] --- все цифры.
\item \verb|[^set]| Обратное множество, т.е. \verb|[^0-9]| --- всё что
угодно, кроме цифр.
\item \verb|*| Максимально возможное вхождение предыдущего символа; от
0 и больше.
\item \verb|+| Максимально возможное вхождение предыдущего символа; от
1 и больше.
\item \verb|-| Минимально возможное вхождение предыдущего символа; от
0 и больше.
\item \verb|?| 0 или 1 предыдущий символ.
\item \verb|\b|\emph{xy} Сбалансированное сопоставление подстроки,
начинающейся с \emph{x} и заканчивающейся на \emph{y}. Паттерн
\verb|\b()|, например, сопоставляет сбалансированные скобки.
\item \verb|$| Конец строки.
\end{itemize}

Экранированный символ в верхнем регистре работает с точностью до
наоборот, в отличие от своего младшего брата в нижнем. Т.е. \verb|\A| сопоставляет
любой символ, который \emph{не является} буквой. Обратный слэш
экранирует следующий за ним символ, отменяя, таким образом, его
специальные возможности (например, \verb|\\| сопоставляет бэкслэш).

Всё что здесь не упомянуто сопоставляется как есть.

\begin{aside}{Извините, юникодеры, но здесь всё в ASCII}
Да, я понимаю, всё должно быть в UTF-8 или какой другой разновидности
юникода. И вас, видимо, раздражает тот факт, что вы не можете
создавать URLы на том же языке, на котором говорите.

Что я могу сказать на это? Протоколы --- достаточно сложно
разрабатывать и если бы вам пришлось писать свой протокол, то,
вероятно, вы бы не хотели в конечном итоге иметь противоречивый
формат, который имеет несколько интерпретаций, который иногда сложно
переводить в верхний или нижний регистр, и который нужно транслировать
каждый раз, чтобы что-то с ним сделать. Поэтому юникоду здесь не
место. С ASCII жизнь сильно упрощается --- каждый компьютер просто
знает что это такое и с чем его едят.

Вот почему в интернете нередко приходится транслировать URLв в ASCII;
например, кодировать с помощью \%. Так говорит нам стандарт и это
весьма разумная вещь. Честно, я не хочу знать в чём разница между
крышечкой, двоеточием и прочими акцентами. Я хочу работать с
фиксированным набором символов. Не вините меня или Mongrel2 в этом;
это стандарт и, к тому же, хороший способ получить в итоге стабильный
сервер.

Протоколы работают тем лучше, чем меньше в них политики. Это значит,
что вы не можеет использовать юникод в паттернах. То есть вы можете,
конечно, но никто не гарантирует стабильный результат.

\end{aside}

Вот примеры маршрутов для изучения --- они помогут вам прочувствовать
систему немного лучше:

\begin{itemize}
\item \verb|"/images/"|  Сопоставляет любой путь, который содержит
/images/ без всяких паттернов.
\item \verb|"/"| Самый быстрый маршрут из всех возможных.
\item \verb|"/images/(.*.jpg)"| Сопоставляет картинки в формате jpg в
папке images. Обратите внимание на то, что паттерн не смотрит внутрь
директории, он только проверят паттерн \ident{(.*.jpg)}.
\item \verb|"/images/(\a-\-\d+\.jpg)"| Пример посложнее: он
сопоставляет короткую последовательность из 0 или больше букв (помните
-); затем тире (\verb|\-| экранирует -); далее длинная
последовательносить из 1 и больше цифр; и, наконец, .jpg, где
\verb|\|. экранирует точку.
\end{itemize}

Этого должно быть достаточно, чтобы понять как использовать паттерны.

\section{Логи}

m2sh ведёт логи всех операций, выполненных с помощью этой утилиты.
Кроме этого, с помощью команды \shell{m2sh commit} можно добавлять
свои комментарии. Все эти логи сохраняются, даже если вы выполняете
загрузку из скрипта (\shell{m2sh load}). В логах хранится подробная
информация: кто и что сделал с конфигурацией, когда это было сделано и
для какого сервера.

Чтобы посмотреть логи для нашего маленького примера, запустите
\shell{m2sh log -db simple.sqlite}.

Ниже --- пример для mongrel2.org:

\begin{code}{Пример логов}
\begin{lstlisting}
> m2sh log
[2010-07-18T04:14:53, mongrel2@zedshaw, init_command] /usr/bin/m2sh init
[2010-07-18T04:15:06, mongrel2@zedshaw, load_command] /usr/bin/m2sh load
[2010-07-18T04:22:06, mongrel2@zedshaw, load_command] /usr/bin/m2sh load
[2010-07-18T04:23:32, mongrel2@zedshaw, load_command] /usr/bin/m2sh load
[2010-07-18T04:26:16, mongrel2@zedshaw, upgrade] Latest code for Mongrel2.
[2010-07-18T18:05:59, mongrel2@zedshaw, load_command] /usr/bin/m2sh load
[2010-07-18T20:09:01, mongrel2@zedshaw, init_command] /usr/bin/m2sh config
[2010-07-18T20:09:02, mongrel2@zedshaw, load_command] /usr/bin/m2sh config
> m2sh commit -what mongrel2.org -why "Testing things out."
\end{lstlisting}
\end{code}

На сегодняшний день наблюдается тенденция хранить серверные
конфигурации в системах управления версиями, такими как git или
mercurial. Эти системы хранят историю изменений, но ничего не говорят
о причинах изменений. Логи восполняют этот пробел; собственно поэтому
эта фича и была добавлена в m2sh.

В будущем Mongrel2 будет вести статистику, которая привяжет изменения
в конфигурации с негативными изменениями в поведении сервера: частые
сбои, ошибки, медленная работа и прочие проблемы.

В общем, всё тайное становится явным. Mongrel2 позволит админам
вычислить, кто ``уронил'' Твиттер и кого надо за это уволить :-)

\section{Порт управления}

Перед самым релизом 1.0 мы добавили фичу под названием ``порт
управления'', которая позволяет соединиться с сервером через unix-сокет
и посылать команды. Вот что можно делать с помощью этих команд:
получать статус запущенных задач, получать список открытых соединений
и узнать как долго они открыты, принудительно закрыть соединение. С
помощью этого порта можно реализовать любой инструмент мониторинга.

По умолчанию этот порт находится в файле \file{run/control} в рабочей
директории (та, которую веб-сервер делает рутовой, помните?), но можно
изменить этот путь с помощью настройки \ident{control\_port}. Можно
использовать любую спецификацю ZeroMQ, но в целях безопасности я
рекомендую использовать IPC.

Как только Mongrel2 запущен, можно подключиться к нему с помощью
\file{m2sh} и управлять сервером с помощью простых команд. На данный
момент сервер возвращает не очень читабельный текст, но со временем мы
его улучшим.

Ниже приведён список команд:

\begin{description}
\item[status tasks] Возвращает список задач в JSON-формате. Что-то вроде
    внутренней команды \shell{ps}.
\item[status net] Возвращает список идентификаторов соединений и время
    (в секундах) с момента последней активности: если это HTTP-соединение,
    то время с момента подключения; если же сокет --- время с момента
    последнего пинга.
\item[time] Выводит время с сервера. Полезно для синхронизации.
\item[kill ID] Принудительно закрывает сокет с заданным
    идентификатором (его можно получить из \ident{status net}). Это
    достаточно жёсткий способ, поэтому используйте его как можно реже.
\item[control stop] Закрывает порт управления. Открыть сможет только
    перезагрузка сервера.
\end{description}

И пример того, как управлять с помощью \ident{m2sh}:

\begin{Verbatim}
m2sh control -db config.sqlite -name test
CONNECTING...
> status net
{"total": 0}
> time
{"time": 1282980306}
\end{Verbatim}

Эта фича ещё в разработке и со временем у неё будет более удобный
интерфейс.

Если вы хотите вручную соединиться и управлять сервером, то вот пример
на Питоне:


\begin{code}{Управление сервером: пример на Питоне}
\begin{lstlisting}
import zmq

CTX = zmq.Context()

addr = "ipc://run/control"

ctl = CTX.socket(zmq.REQ)

print "CONNECTING"
ctl.connect(addr)

while True:
    cmd = raw_input("> ")

    ctl.send(cmd)

    print ctl.recv()

ctl.close()
\end{lstlisting}
\end{code}

\section{Несколько серверов}

Процесс Mongrel2 не может хостить несколько серверов; при запуске вы
передаёте два параметра: базу настроек и UUID сервера, который надо
запустить. Такой подход упрощает код и позволяет поддерживать его в
рабочем состоянии.

Однако.

Утилита m2sh позволяет запускать несколько серверов из одной и той же
конфигурационной базы. Передавая параметр \ident{-every} вы сообщаете
о том, что надо выполнять действия над всеми серверами, определёнными
в настройках. Наряду с этим можно указать один сервер с помощью
идентификатора, хоста или названия. Если передаваемый параметр нельзя
трактовать однозначно (например, вы ищите с помощью \shell{-host
localhost} и вашей базе задано два сервера, которые обслуживают этот
домен), то m2sh выведет список соответствующих серверов и попросит
выбрать один.

Например:

\begin{Verbatim}
> m2sh start -db config.sqlite -every
Launching server localhost 9f0cbd7d-aeff-4195-921e-2ce1c25512d3 on port 6768
...
Launching server localhost 3d815ade-9081-4c36-94dc-77a9b060b021 on port 6767
...

> m2sh start -db config.sqlite -host localhost
Not sure which server to run, what I found:
NAME HOST UUID
--------------
localhost localhost 3d815ade-9081-4c36-94dc-77a9b060b021
localhost localhost 9f0cbd7d-aeff-4195-921e-2ce1c25512d3
* Use -every to run them all.

> m2sh start -db config.sqlite -uuid 3d815ade-9081-4c36-94dc-77a9b060b021
Launching server localhost 3d815ade-9081-4c36-94dc-77a9b060b021 on port 6767
...

> m2sh running -db config.sqlite -every
Found server localhost 3d815ade-9081-4c36-94dc-77a9b060b021 RUNNING at PID 28525
PID file run/mongrel2.pid not found for server localhost 9f0cbd7d-aeff-4195-921e-2ce1c25512d3

> m2sh stop -db config.sqlite -every
\end{Verbatim}

\section{Продвинутые настройки}

Mongrel2 можно конфигурировать с помощью настроек. Некоторые из них
нужно менять осторожно, поэтому тестируйте, прежде чем внести
изменения в рабочую систему. Все они имеют приемлемые значения по
умолчанию, так что, возможно, ничего и не надо менять, если нет острой
необходимости.

Чтобы настройки вступили в силу, их нужно передать в команду
\ident{commit}, при сохранени конфигурации, как в этом примере:

\begin{code}{Изменение настроек}
\begin{lstlisting}
settings = {"zeromq.threads": 1, "limits.url_path": 1024}
mimetypes = {".txt": "text/superawesome"}

servers = [main]
\end{lstlisting}
\end{code}

Mongrel2 считывает настройки на лету и для каждой из них создаёт
INFO-сообщение в логах со значением переменной --- это облегчает
отладку, в случае возникновения проблем.

Итак, полный список настроек:

\begin{description}
\item[control\_port=ipc://run/control] Mongrel2 слушает этот порт с
помощью 0MQ на предмет управляющих команд. Вам следует использовать
\verb|ipc://| для этих целей, чтобы только локальные пользователи
могли управлять сервером.
\item[limits.buffer\_size=2 * 1024] Внутренний буфер для ввода-вывода.
Используется для обработки запросов и проксирования. Это \emph{очень}
консервативное значение, так что если сервер получает HTTP-запросы
больше этого размера, то его надо увеличить. Возможно вы захотите
пристрелить того, кто посылает такие запросы: в среднем запрос обычно
не привышает 400-600 байт.
\item[limits.client\_read\_retries=5] Количество попыток полностью прочесть
HTTP-заголовок. Эта настройка предотвращает тип атак, когда клиент продолжает
посылать заголовок частично, пока сервер не исчерпает все ресурсы для
обслуживания этого клиента.
\item[limits.connection\_stack\_size=32 * 1024] Размер стэка для
корутин.
\item[limits.content\_length=20 * 1024] Максимальный размер контента,
который сервер принимает от клиентов. Сейчас это жёстко прописанный
лимит и все запросы, превышающие его, отвергаются. Будущие версии
позволят загружать файлы любого размера.
\item[limits.dir\_max\_path=256] Максимальная длина пути в
Dir-обработчиках.
\item[limits.dir\_send\_buffer=16 * 1024] Максимальный размер буфера
для пересылки файлов.
\item[limits.fdtask\_stack=100 * 1024] Размер стека подсистемы
управления соединениями и состояниями сервера. Это сердце веб-сервера
и, как в любом организме, оно одно, поэтому для него можно выделить
побольше ресурсов.
\item[limits.fdtask\_stack=100 * 1024] Размер стэка для одного
обработчика. Поскольку их обычно не очень много, то можно установить
достаточно большое значение. 
\item[limits.handler\_targets=128] Максимальное количество
обработчиков в бэкенде для получения сообщений. Не очень разумно
устанавливать большое значение.
\item[limits.header\_count=128 * 10] Максимальное количество
заголовков в клиентском соединении.
\item[limits.host\_name=256] Максимальная длина хоста.
\item[limits.proxy\_read\_retries=100] Максимальное количество считываний с
проксируемого бэкенда. Дело в том, что некоторые сервера не используют
буферизацию, а выводят данные по мере поступления. Это может отрицательно
сказаться на производительности. Если Mongrel2 не может получить заголовки за
заданное количество считываний (100 по умолчанию), он вообще отвергает ответ от
сервера.
\item[limits.proxy\_read\_retry\_warn=10] Если количество считываний с
прокси-бэкенда достигает этого значения, то выводится сообщение о том, что этот
бэкенд может испытвать проблемы производительности. Полезно для предотвращения
подобных проблем.
\item[limits.mime\_ext\_len=128] Максимальная длина MIME-типа.
\item[limits.url\_path=256] Максимальная длина URLа. Длина параметров
не включена, только длина пути.
\item[superpoll.hot\_dividend=4] Доля ``горячих'' соединений (т.е.
1/4, 1/8). Чем больше простаивающих соединений, тем выше должно быть
это число. И наоборот, чем больше активных соединений, тем оно ниже.
\item[superpoll.max\_fd=10 * 1024] Максимально возможное количество
открытых файлов. Не устанавливайте больше чем 64 * 1024, и учтите, что
серверу понадобится некоторое время, чтобы создать необходимые
структуры данных.
\item[upload.temp\_store=None] Единственная переменная без значения по
умолчанию. Если вы планируете обрабатывать большие запросы, то задайте
временную папку, в которую сервер может писать. В секции
\ref{sec:async_file_upload_demo} эта настройка рассматривается более подробно.
\item[zeromq.threads=1] Количество потоков для 0MQ. Осторожно! У нас
возникали проблемы, если установить большое значение.
\end{description}