Пользователь
0,0
рейтинг
7 января 2011 в 01:23

Разработка → Настоящий веб-сайт на Common Lisp за 9 шагов

Введение





Эта вводная статья предназначена для желающих попробовать применить 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!
Глухов Михаил @Rigidus
карма
26,5
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (53)

  • +2
    метод с ошибкой не корректен. Т.к. надо разделять production и не production окружения. А когда разработка идет на production окружение сразу… За это, в приличных местах, кандилябром по лицу бьют!
    • +2
      Лисп немного другой :)
      Я согласен, что нужно разделять окружения, но метод с ошибкой совершенно ортогонален. NASA «Deep Space 1» был вполне себе production, но и в production бывают ошибки, требующие исправления на живой системе. В этом отношении подход лиспа в том, чтобы не превращать production в застывший монолит, расчитывая что он будет более или меннее совершенным, а оставлять его пластичным и чуточку сырым, чтобы при случае можно было внести изменения. Впрочем, я еще не настоящий джедай и возможно чего-то не понимаю :)
      • –2
        Есть два способа разработки софта. Я предлагаю разработку коробочного продукта, а не проекта с поддержкой, вот и все.
      • +5
        Ты будешь учить Катапа, что из себя представляет лисп??? :D
        • +3
          Я могу, да — наглости хватит :)))
          В глубокой древности мудрец Уолл сформулировал три добродетели, равно необходимых и программисту, и магу: лень, нетерпение и наглость — и функциональное программирование способствует достижению каждой из них.
      • 0
        помогите: в какой части этой статьи было описано создание веб-сайта? Или будет продолжение?
        • 0
          Последние четыре формы в последнем куске кода — Hello World.
          • +1
            Спасибо. Я не заметил Hello world — а так бы подумал что настройки сервера.
        • 0
          Продолжение будет. Здесь описана установка всего необходимого, чтобы запустить Hello World.
        • +1
          Написано же, «за 9 шагов». Это был первый :)
  • +2
    отличная статья, еще раз доказывает то, что не главное на чем написано, а как написано!
  • +6
    Не увидел ни одного из 9 общеанных шагов. «Запускаем имакс, настраиваем SLIME и SWANK», конечно, относятся к веб-сайту, но с таким же успехом можно было написать «покупаем ПеКа для учебы и ставим на нем линукс» — вроде бы тоже имеет отношение, но как-то слабо раскрывает тему.
  • –16
    слишком много сраных скобок
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        вы путаете его с clean или попсовым haskell
        • 0
          Что значит попсовым? То, что им в последнее время заинтересовались — это положительный факт. Вашей логикой С++ — вообще попсовейшее УГ.
      • +2
        Ничего подобного :) Понять Лисп даже проще, чем разобраться в дебрях синтаксиса С++. Всё очень логично и незапутано.
    • +2
      Лисп смеётся мне в лицо!

      Но на самом деле, имхо, код достаточно приятен. И эти смайлики только поднимают настроение.
    • +1
      Как будто религия запрещает скобки развернуть

      (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)
                      )
                  )
              )
          )
      )
      
      • 0
        как-будто, это уменьшит количество скобок
    • –3
      ЛИСП всегда иронично называли кучей дурацких скобок, а уж когда ты пишешь, не «трех строчный код», а программы, это превращается в целый ужас — вспомнить где что. Вспоминая карточную игру с «аи», которую заставляли писать в качестве ДЗ на нем, аж слезятся глаза.
      fat0troll, о чем с тобой говорить, если ты думаешь что он чисто академический?
      • +1
        «На любом языке можно писать как на Фортране» Ⓒ
        В следующих постах будет много примеров кода — как понятных, так и запутанных и все их можно будет обсудить как с точки зрения читабельности, так с точки зрения архитектуры.

        Мне кажется, что я смогу показать, что лисп — читабельный, понятный и выразительный язык
      • 0
        Я так смотрю, вы и sendmail.cf без помощи m4 не писали…
  • +1
    «В данный момент я разворачиваю его вместе с вами… и в конце статьи вы сможете убедиться в том, что по крайней мере у меня все заработало.»
    • 0
      Минимум один внимательный читатель, спасибо :)
      89.108.75.126/
  • +4
    Ошибка многих статей про Common Lisp — это сваливать в одну кучу ASDF и ASDF-Install/Quicklisp/clbuild/…. Надо понимать, что это инструменты разного класса:
    • ASDF загружает код из файлов в «живой» образ лиспа. Он не является пакетным менеджером.
    • ASDF-Install/Quicklisp/clbuild и прочие используются для доставки кода на жесткий диск, после чего ASDF загружает код с жесткого диска.
  • +4
    <? print «Hello»; ?>
    :))
    • 0
      Нет. Пока только эквивалент установки php, apache, настройки виртуальных хостов, подключению mod_rewrite и тому подобного. Завтра будет. Лучше :)
  • 0
    Как то не вяжется описанный метод установки с аватаркой вначале поста. Этот человек не будет устанавливать лисп как трус: сначала с пакетного менеджера, а потом компилить. Я вообще не уверен что его пакетный менеджер сразу бинарные пакеты устанавливает.
    • +1
      Конечно можно со старта поразить всех умищем, но кто повторит все эти шаги тогда? А мне хотелось бы получить людей способных поставить себе на сервере все что нужно чтобы продолжить погружение в мир лиспа
  • 0
    Позвольте полюбопытствовать: чем обусловлен выбор Common Lisp? Я имею ввиду именно этот диалект.
    • 0
      • Распространенность
      • Хорошая поддержка юникода — было важным доводом в сравнении с php, например :)
      • Cтабильность, в сочетании с частыми минорными релизами
      • Кроссплатформенность (в том числе и в среде разработки — Емакс есть везде) вместе с быстродействием моей реализации — SBCL компилирует в машинный код
      • Значительное количество библиотек (см. CLiki)
      • Обширное лисп-комьюнити, в том числе множество квалифицированных рускоязычных пользователей(archimag dmitry_vk swizard.livejournal.com и другие)
      • Множество вменяемых руководств (Practical Common Lisp, OnLisp, Мир Лиспа)
      • Мощность макросов
      • Развитая объектная подсистема (CLOS)
      • Репутация языка
      • 0
        Я сейчас на перепутье какой из трех диалектов лиспа учить — Scheme, Common Lisp или Clojure. Scheme мне почему-то очень нравится + сейчас смотрю SICP где он используется.
        Clojure сравнительно молодой и динамино развивающийся, он есть для JVM и CLR.
        Common Lisp очень старый и умеет все, но чем-то он мне не нравится (cкорее всего словом defun).
        • 0
          В таком случае при прочих равных стоит брать Common Lisp как наиболее зрелый и содержащий большее количество интересных идей.
          • 0
            Как раз таки с идеями в CL напряг. Посмотрите на Typed Racket и syntax-parse — CL до этого как до Китая.
            • 0
              Хочу увидеть пост об этом
              • 0
                Хорошая идея, спасибо.
        • 0
          Аналогично. Плюс лично мне дико не нравятся
          -убогие макросы
          -два неймспейса (привет, функол)
          -стрёмные названия функций
          В свою очередь, мне сильно нравится тамошний for. Но его можно переписать на какой угодно макросистеме при желании.
          Только тогда уж не Scheme, а Racket. Для него тоже есть веб-фреймворки, причём один из них «из коробки».
          • 0
            Собственно у меня Racket и стоит, хотя от изменения названия в нем меньше Scheme не стало (при #lang racket он все равно поддерживает R6RS).
          • 0
            В убогости макросов CL их сила. В сложности макросов Racket их слабость.

            Два неймспейса — это спорный момент. Но практика показала что проблемы LISP-1 принципиальные, а LISP-2 — эстетические, как и название defun.

            Идей действительно интересных много, но, похоже, кроме PLT они никому не нужны… к сожалению.
            • 0
              >В убогости макросов CL их сила. В сложности макросов Racket их слабость.
              1) Макросы Racket вполне себе простые. Что вообще может быть проще паттерн-матчинга?
              2) Само утверждение напоминает приснопамятное «Война — это мир. Свобода — это рабство. Незнание — сила».
              >проблемы LISP-1 принципиальные
              Например?
              • 0
                > Что вообще может быть проще паттерн-матчинга?

                Жонглирование символьными выражениями. В нетривиальных случаях оказывается куда проще.

                > Например?

                Вы правы, недостатки и там и там чисто эстетические.
                • 0
                  «жонглирование символьными выражениями» ещё более нетривиально. Попробуйте описать рекурсивный макрос для какого-нибудь факториала без gensym'ов и прочих хаков. В Racket'е о таких вещах задумываться не нужно — автоматическая гигиена всё сделает сама. Плюс ещё много нужных и приятных вещей: www.cs.indiana.edu/~chaynes/danfest/dyb.pdf
                  • 0
                    Автоматическая гигиена Racket — точно такой же хак как и gensym.
                    • 0
                      В одном случае это надо держать в уме, в другом — нет. Плюс к тому, система макросов Racket умеет в точности то же самое, что и CL (в либе даже defmacro есть для любителей), но умеет и многое другое — потому как оперирует не голыми s-expr, а s-expr с дополнительной лексической информацией. Именно поэтому макросистема CL является строгим подмножеством макросистемы Racket.
  • 0
    а почему не используете nohup?
    • 0
      Можно поднять несколько сессий screen и удобно переключаться между ними. В принципе — вопрос привычки, можно и nohup
  • 0
    У коллег ровно один аргумент против CL — отсутствие бесплатных хостингов. Пожалуйста, скажите что я просто не умею их искать.
    • 0
      Мы же серьезные, взрослые люди или стремимся произвести такое впечатление :) При развитии проекта в любом случае потребуется хостинг и лучше сразу начинать с хорошего. Можно поспрашивать у друзей, мне например catap предлагал сравнительно дешевый хостинг под Common Lisp. Никто не запрещает кооперироваться. Ну а если хочется попробовать — все можно делать на своей машине.
  • +1
    * (ql:quckload 'swank)
    опечатка, пропущена i
    • 0
      спасибо
  • 0
    И дабы не захламлять систему, я бы предложил собрать свежий sbcl самому, благо uupdate (лежит в пакете devscripts) существенно облегчает этот процесс:
    $ apt-get source sbcl
    $ sudo aptitude build-dep sbcl #тут много всего, в т.ч. и texlive
    $ wget http://downloads.sourceforge.net/project/sbcl/sbcl/1.0.45/sbcl-1.0.45-source.tar.bz2
    $ cd sbcl-1.0.40.0/
    $ uupdate -v 1.0.45 ../sbcl-1.0.45-source.tar.bz2
    $ cd ../sbcl-1.0.45
    $ debuild -b -us -uc
    #и устанавливаем полученный пакет
    $ sudo dpkg -i ../sbcl_1.0.45-0ubuntu1_amd64.deb #скорее всего имя будет отличаться
    ну вот как-то так.
  • 0
    Начиная с hunchentoot 1.2.0 HUNCHENTOOT:ACCEPTOR-REQUEST-DISPATCHER более нету , также нету поля hunchentoot:acceptor-dispatch-request. Вместо них можно использовать
    (defmethod hunchentoot:acceptor-dispatch-request ((acceptor debuggable-acceptor) request) ...)
    Пожалуйста, исправьте.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.