Введение

Эта вводная статья предназначена для желающих попробовать применить Common Lisp в задачах веб-программирования. Я не буду останавливаться на преимуществах этого языка, за меня это сделал ababo в своем вводном посте Разработка web-приложений на языке Common Lisp (часть первая)
Я занимаюсь вопросами разработки веб-приложений на Common Lisp немногим более года и сделал на Common Lisp крупный интернет-магазин, что, как мне кажется, предупредит возражения тех, кто считает, что лисп бесполезен для коммерческого применения.
Сегодня моя задача — рассказать в подробностях о применяемом мной способе развертывания всей необходимой инфраструктуры. Используя эту статью как пошаговое руководство внимательный читатель сможет развернуть свой собственный сайт на лиспе.
Возможно мои подходы не идеальны — в таком случае я буду рад конструктивной критике — пожалуйста, не стесняйтесь если вам что-то не нравится — одной из целей написания этой статьи было исправление собственных ошибок.
Для тех, кто любит проматывать скучные процедуры установки — в конце статьи размещена небольшая вкусность, которая, возможно, расширит ваш взгляд на веб-программирование, если до этого момента вы не имели дела с лиспом. Ищите по ключевым словам SLIME и SWANK :)
Установка последней версии SBCL
Я использую SBCL как наиболее удобную, распространенную и свободную реализацию Common Lisp, тесно интергрированную со средой Emacs, обеспечивающей удобную и комфортную работу. В репозиториях, как правило, находится не самая последняя версия SBCL, поэтому вы можете скомпилировать его из сорцов или установить бинарник для вашей архитектуры. Последнее тривиально, поэтому в этой статье я опишу компиляцию из исходников.
Чтобы скомпилировать свежий SBCL можно воспользоваться старым, который мы тут же поставим, используя пакетный менеджер:
$ apt-get install sbcl
Исходники SBCL можно получить тут: sbcl.sourceforge.net/platform-table.html
# Скачиваем
$ wget http://downloads.sourceforge.net/project/sbcl/sbcl/1.0.45/sbcl-1.0.45-source.tar.bz2
# Распаковывываем архив:
$ bzip2 -cd sbcl-1.0.45.tar.bz2 | tar xvf -
# Заходим внутрь каталога и компилим
$ cd sbcl-1.0.45/
$ sh make.sh
# Удаляем старый SBCL поставленный из репозиториев
$ apt-get remove sbcl
# Устанавливаем скомпилированный sbcl
$ sh install.sh
# Проверяем, все ли в порядке:
$ sbcl
This is SBCL 1.0.45, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
*
Поздравляю, теперь у вас есть свежая версия sbcl. В данный момент я разворачиваю его вместе с вами на своем сервере, чтобы избежать возможных неточностей и в конце статьи вы сможете убедиться в том, что по крайней мере у меня все заработало. Ну, будем надеяться, что это будет так :)
Ставим quicklisp
Для управления библиотеками наиболее часто используются два пакетных менеджера — ASDF и QuickLisp. Последний гораздо более дружелюбен, а первый уже предустановлен вместе с SBCL, поэтому сейчас мы установим себе QuickLisp. На quicklisp.org размещена все вводная информация, поэтому я не буду повторяться и мы перейдем сразу к установке:
$ wget http://beta.quicklisp.org/quicklisp.lisp
$ sbcl --load quicklisp.lisp
* (quicklisp-quickstart:install)
* (ql:add-to-init-file)
Ставим hunchentoot
Теперь, когда у нас есть библиотечный менеджер, мы устанавливаем
веб-сервер hunchentoot, вместе со всеми его зависимостями одной командой:
* (ql:quickload 'hunchentoot)
Ставим swank и slime на сервер
«И даже более впечатляющий пример удаленной отладки произошел в миссии NASA «Deep Space 1» в 1998 году. Через полгода после запуска космического корабля, небольшой код на Lisp должен был управлять космическим кораблем в течении двух дней для проведения серии экспериментов. Однако, неуловимое состояние гонки (race condition) в коде не было выявлено при тестировании на земле и было обнаружено уже в космосе. Когда ошибка была выявлена в космосе (100 миллионов миль от Земли) команда смогла произвести диагностику и исправление работающего кода, что позволило завершить эксперимент. Один из программистов сказал об этом следующее:
— Отладка программы, работающей на оборудовании стоимостью 100 миллионов долларов, которая находится в 100 миллионах миль от вас, является интересным опытом. REPL, работающий на космическом корабле, предоставляет бесценные возможности в нахождении и устранении проблем.»
Я услышал об этом инциденте задолго до того как стал заниматься лиспом сам, и честно говоря воспринял это как байку, которой уже не суждено повториться в наш современный мир многомегабайтных исполняемых файлов и не менее тяжеловесных динамически связываемых библиотек. Однако познакомившись с возможностями удаленного управления лисп-образом я убедился в том, что это ничуть не сложнее чем работать с кодом у себя на машине. И никаких инкрементальных сборок, длительной компиляции или закачек скриптов через ftp — с помощью slime я подключаюсь к работающей системе и могу видеть и менять практически все, например реализовать горячую замену кода, или проинспектировать любой объект, функцию или макрос используя мощные средства интроспекции.
Как это работает? — спросите вы. Внутри лисп образа на удаленном сервере работает SWANK — специальная библиотека, предоставляющая собой бэкэнд, который предоставляет доступ ко всем рычагам управления лисп образом. SWANK написан на Common Lisp и общается со SLIME по довольно простому текстовому протоколу.
В моем Emacs-e работает SLIME, написанный на Emacs Lisp, который позволяет мне в момент редактирования файла с кодом отправлять команды, куски кода, определения объектов и структур удаленному образу Common Lisp. Таким образом, вы можете даже вообще не иметь копии исходного кода на удаленном сервере — и в этом случае никакой злоумышленник не сможет изменить его там, например чтобы обеспечить себе backdoor.
А учитывая развитые средства для кодогенерации, которыми отличается лисп, можно вообще не иметь практически никакого кода — пусть он сам будет генерироваться по данным — лучше самурая, который не сражается, может быть только программист, который не пишет код… Хм, что-то я тут увлекся, вернемся к установке :)
Итак, если у вас есть удаленный сервер, как у меня — вы можете ставить SWANK на него, а SLIME — на свою рабочую машину. Или
поставить и то и другое и туда и туда — главное потом не перепутать. Устанавливаем SWANK:
$ sbcl
* (ql:quickload 'swank)
… и SLIME
$ wget http://common-lisp.net/project/slime/snapshots/slime-current.tgz
$ tar xvzf slime-current.tgz
$ cd slime-2011-01-06/
Внимательно читаем README в этом каталоге и добавляем в свой
~/.emacs/init.el следующий код, следя за правильным указанием путей
;; SBCL
(setq inferior-lisp-program "/opt/sbcl/bin/sbcl") ; your Lisp system
(setq slime-lisp-implementations '((sbcl ("sbcl"))))
(setq slime-startup-animation nil)
;; SLIME
(add-to-list 'load-path "~/.emacs.d/slime") ;; Путь к slime
(require 'slime)
(setq slime-net-coding-system 'utf-8-unix)
(slime-setup '(slime-fancy))
(setq slime-enable-evaluate-in-emacs t)
Ставим screen
Поскольку мои сервера никогда не падают (ну да :) — я использую screen, чтобы держать вечно запущенную копию SBCL, хотя, насколько мне известно, существуют и более лучшие практики (о которых компетентные читатели, несомненно, напомнят в комментариях)
Если он у вас еще не стоит — самое время поставить:
$ apt-get install screen
Запускаем sbcl в screen на сервере и стартуем swank-сервер на 4005 порту
$ screen -S sbcl
$ sbcl
* (require 'asdf)
* (asdf:oos 'asdf:load-op 'swank)
* (setq swank:*use-dedicated-output-stream* nil)
* (swank:create-server :coding-system "utf-8-unix" :dont-close t :port 4005)
Подключаемся из Emacs-a запущенного на своей машине к лисп-образу на сервере
В терминале прокидываем до хоста ssh-туннель
ssh -2 -N -f -L 4005:localhost:4005 user@host.tld
В Emacs-е подключаемся через этот туннель
M-x slime-connect
127.0.0.1
4005
Или, если у вас сервером является ваша домашняя машина — ничего прокидывать и поднимать SWANK не нужно — просто наберите в Emacs-e:
M-x slime
Запускаем web-server hunchentoot на 4242 порту
Теперь мы готовы поднять веб-сервер hunchentoot. Я поднимаю его на 4242 порту и использую nginx как прокси. Также nginx отдает статику и делает еще ряд вещей, для которых он предназначен как нельзя лучше.
Конфиг nginx для наших тестовых целей может быть очень простым:
server { listen 80; server_name localhost; location / { proxy_pass http://localhost:4242; proxy_redirect off; } }
Нижеследующий код, за авторством archimag-a создает специальный класс, который позволяет при возникновении ошибки на удаленном сервере сразу же пробросить эту ошибку вместе со стек-трейсом вам в уютненький emacs, где вы сможете с ней разобраться как следует. Таким образом, если вы подключены к серверу, где работает ваш сайт — вы будете всегда осведомлены о возникающих ошибках в момент их появления, в отличии от ряда других языков, используемых в веб-программировании.
Если же ошибок возникает слишком много, например на ошибочную странцицу постоянно заходят посетители — вы можете просто изменить значение *catch-errors-p* чтобы без помех разобраться из-за чего возникают уже пришедшие вам ошибки.
(defparameter *catch-errors-p* nil)
(defclass debuggable-acceptor (hunchentoot:acceptor) ())
(defmethod hunchentoot:acceptor-request-dispatcher ((acceptor debuggable-acceptor))
(if *catch-errors-p*
(call-next-method)
(let ((dispatcher (handler-bind ((error #'invoke-debugger))
(call-next-method))))
(lambda (request)
(handler-bind ((error #'invoke-debugger))
(funcall dispatcher request))))))
(defun request-dispatcher (request)
"Hello!")
(defparameter *debuggable-acceptor* (make-instance 'debuggable-acceptor
:request-dispatcher 'request-dispatcher
:port 4242))
(hunchentoot:start *debuggable-acceptor*)
(setf hunchentoot:*handle-http-errors-p* nil)
Функция request-dispatcher вызывается при каждом входящем запросе и в нашем случае просто возвращает «Hello!» — о ее расширении мы поговорим в следующей статье.
Если вы дочитали до этого места
пива
завидую! Знакомство с лиспом подарило мне почти полтора года
наслаждения этим удивительным языком — и у вас в этом отношении все
впереди. Happy hacking!