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}
|