在CCL SBCL等开源Lisp平台上运行CL-HTTP

  • 0

在CCL SBCL等开源Lisp平台上运行CL-HTTP

Category:语法学习 Tags : 

这篇文章是田春于2011年7月发表在网易博客上的,现在网易博客即将关闭,所以就不发源地址了,上篇已找不到,这是我以前存下的,下篇重新复制的原博客的。

现在我来说明在 4 种开源 Common Lisp 平台上运行 CL-HTTP 的方法,四种平台分别是 SBCLClozure CLCMUCL 和 Macintosh Common Lisp (MCL)。我相信我所提供的这些信息对某些 Common Lisp 爱好者来说将是梦寐以求的。

背景

过去我也曾在博客里多次提到 CL-HTTP,多年来也一直在实际地学习和使用这个软件。概括地说,CL-HTTP 是一个纯 Common Lisp 实现的高级 Web 应用服务器。和 Common Lisp 社区的其他流行 HTTP 服务器,例如 Hunchentoot 和 AllegroServe 相比,CL-HTTP 不仅提供了 HTTP 1.1 协议的完整实现,还提供了一体化的解决方案,包括基于 Lisp 宏的动态 HTML/XHTML 代码生成,面向对象的 Web 交互设计,权限控制,HTTPS 和 HTTP Proxy 支持(带有缓存)等,另外它还提供 HTTP 客户端,SMTP/POP3/FTP 支持以及其他数不清的高级特性。CL-HTTP 是目前开源 Common Lisp 项目中规模最大、最复杂的,其核心 server 模块源代码 5 万多行,所有模块连同各种平台的移植在内共计 35 万行 Common Lisp 代码(bz2 压缩一下其实只有不到 7M),可谓是浩如烟海,Lisp 新手往往迷失在其庞大的目录树里。

 

CL-HTTP 最初是在 Symbolics Lisp Machine 上开发的,1994 年开始公开了源代码。作者是 MIT AI 实验室 (CSAIL) 的教授 John C. Malley,目前主要维护者还包括 Rainer Joswig 和来自几个商业 CL 平台的专家,尤其是 LispWorks 公司的 Martin Simmons。CL-HTTP 的开发工作非常缓慢,但也极度稳定可靠,不过主要是在上一代的很多商业平台上。CL-HTTP 的源代码 SVN 是不公开的,仅少量核心开发者具有访问权限,但是一般可以通过发信给邮件列表向开发者索取最新的源代码。这给追踪其源代码变化带来了一定困难,因此我自己维护了一个私有的 SVN 库将历史上获得的所有代码快照全部提交在里面,再注入我自己的一些补丁。

获得最新的 CL-HTTP 源码

CL-HTTP 网站上目前公开下载的最后版本 70.190a 已经是很老的版本了,目前的最新版本是 70.218,SVN revision 至少为 436。Rainer Joswig 定期将 SVN 上的最新版本打包放在它的个人主页上,下载地址是:

 

http://lispm.dyndns.org/distributions/

 

其中目前的最新版本的下载文件名是 cl-http-70-218-s436.tar.gz。不过由于上游合并补丁的速度非常慢,即便是这个最新版本仍然有一些问题导致 SBCL 下无法顺利加载。推荐使用的是本文附件中的版本,它来自我自行维护的 CL-HTTP 私有 SVN 库,修复了很多 bug,补丁已提交到上游但尚未被合并。有兴趣的读者可以同时下载两个文件然后自行比对其中的差异。

源代码结构

CL-HTTP 的源代码可以大致分为下列 7 个部分:

  1. 平台无关的核心代码,包括 HTTP/HTTPS 协议本身的实现和 HTML、URL 等标准的实现代码;
  2. 平台无关的周边代码,包括 HTML 解析器、客户端、SMTP/FTP 等协议的实现等;
  3. 平台无关的用户贡献代码,包括 POP3 客户端、XML 工具箱以及最重要的可移植模板 (port-template) 代码等;
  4. 平台相关的核心代码,包括适用于 CMUCL、LispWorks、MCL、SCL 等主要平台的网络和工具性代码等;
  5. 平台相关的周边代码,例如 LispWorks 和 MCL 下的图形界面控制台等;
  6. 平台相关的用户贡献代码,例如 LispWorks 下的 UTF-8 支持、远程 Listener 和数据库认证接口等;
  7. 示例站点的源代码和静态文件。

 

以下简要介绍 CL-HTTP 的源代码目录结构,最顶层各目录的说明如下表所示:

 

目录名类别用途说明
 acl 平台相关代码 Allegro CL 移植
 client 平台无关的周边代码 HTTP 客户端
 clim 平台无关的周边代码 基于 CLIM 的图形控制台
 cmucl 平台相关代码 CMU Common Lisp 移植
 contrib 平台无关的用户贡献代码 各种有用的扩展,尤其包括可移植模板 (port-template)
 examples 示例站点 示例站点的源代码
 ftp 平台无关的周边代码 FTP 客户端(代理)
 html-parser 平台无关的周边代码 HTML 解析器
 lambda-ir 平台无关的周边代码 全文索引
 lcl 平台相关代码 Lucid/Liquid Common Lisp 移植
 lispm 平台相关代码 最初在 Symbolics Lisp Machine 上开发的全部代码
 lw 平台相关代码 LispWorks 移植
 mcl 平台相关代码 Macintosh Common Lisp 移植
 proxy 平台无关的核心代码 HTTP Proxy(正反向都有)
 scl 平台相关代码 Scieneer Common Lisp 移植
 server 平台无关的核心代码 核心 HTTP 协议实现和 HTML 生成代码等
 smtp 平台无关的周边代码 SMTP 协议实现
 standards 静态文件 相关 RFC 标准的文本,通过示例站点可访问到
 w3p 平台无关的周边代码 W3P (Presentation-based Interface)
 w4 平台无关的周边代码 W4 Constraint-Guided Web Walker
 www 示例站点 示例站点的静态页面和其他数据文件

SBCL 上启动 CL-HTTP 的方法

正确在 SBCL 上启动 CL-HTTP 的先决条件是必须拥有一个支持多线程 (sb-thread) 的 SBCL,其次一定要使用比较新的版本(目前最后发布的 1.0.49 经测试是可用的)。遗憾的是,目前 SBCL 官方站点提供的二进制版本只在 Linux/x86 和 Linux/AMD64 上默认打开了多线程支持,其他平台要么不支持多线程,要么就是默认关闭的。SBCL 目前官方的 Windows 版本多线程支持不好,推荐改用 Anton Kovalenko 维护的 sbcl-win32-threads 分支,遗憾的是,我一时找不到预编译版本的下载地址了。Mac OS X 用户需要先安装官方的二进制版本,然后使用 SBCL 源代码重新编译支持多线程的版本。这需要在源代码顶层目录里放置一个名为 customize-target-features.lisp 的文件,内容如下所示:

 

(lambda (features)

(flet ((enable (x)

(pushnew x features))

(disable (x)

(setf features (remove x features))))

(enable :sb-thread)))

 

具体的编译和安装方法可参见 SBCL 源代码目录里的 INSTALL 文件。以下假设一个支持多线程的 SBCL 已顺利安装。

 

在 SBCL 下编译 CL-HTTP 最重要的一环是加载启动文件,它的相对路径是 contrib/kpoeck/port-template/load.lisp。

 

首先进入 CL-HTTP 源代码根目录,然后启动一个 SBCL 环境并加载上述文件:

 

binghe@binghe-mac:~/Lisp/self/cl-http$ sbcl

This is SBCL 1.0.49, 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.

* (load “contrib/kpoeck/port-template/load.lisp”)

 

; loading #P”/Users/binghe/Lisp/self/cl-http/contrib/kpoeck/port-template/load.lisp”

;; loading #P”/Users/binghe/lib/sbcl/sb-introspect/sb-introspect.asd”

;; loading #P”/Users/binghe/lib/sbcl/sb-introspect/introspect.fasl”

;; loading #P”/Users/binghe/Lisp/self/cl-http/contrib/kpoeck/port-template/translations.lisp”

 

; file: /Users/binghe/Lisp/self/cl-http/contrib/kpoeck/port-template/load.lisp

; in: DEFUN LOAD-THE-THING

;     (POTENTIALLY-COMPILE-FILE VALUE)

;

; caught STYLE-WARNING:

;   undefined function: POTENTIALLY-COMPILE-FILE

;

; compilation unit finished

;   Undefined function:

;     POTENTIALLY-COMPILE-FILE

;   caught 1 STYLE-WARNING condition

 

; in: DEFUN LOAD-CL-HTTP-FILES

;     (LOAD-CL-HTTP-FILES-INTERNAL *FILES-CL-HTTP*)

;

; caught STYLE-WARNING:

;   undefined function: LOAD-CL-HTTP-FILES-INTERNAL

;

; compilation unit finished

;   Undefined function:

;     LOAD-CL-HTTP-FILES-INTERNAL

;   caught 1 STYLE-WARNING condition

 

Excecute (COMPILE-ALL) to compile the system.

 

Excecute (LOAD-CL-HTTP-TESTER) to test the system without sockets.

 

To start the simple-server do (http::start-examples) .

T

 

忽略那些警告,注意结尾处的三句话:执行 (COMPILE-ALL) 可以编译整个系统,执行 (LOAD-CL-HTTP-TESTER)可以在不使用网络的情况下测试整个系统;使用 (HTTP::START-EXAMPLES) 启动示例站点。

 

正确的启动方法是,首先执行 (COMPILE-ALL) 然后跳过测试,直接使用 (HTTP::START-EXAMPLES) 来启动示例站点。一次完全编译可能需要花几分钟时间,并且每次只能做完全编译(以后会说明怎样节省时间)。启动成功后有类似下面的提示:

 

[2011-06-30 23:42:32]  HTTP Proxy Service Enabled on port 8000 for NIL.

[2011-06-30 23:42:32]  HTTP service enabled for: http://localhost:8000/

 

如果顺利走到这一步了,那么恭喜你,可以打开浏览器访问 http://localhost:8000/,然后就可以看到本文插图中的示例站点 Web 页面了。

 

(后记:附件的源代码有所更新,修复了一个 port-template 下的小问题,请看到的读者下载新版本 (r75))

 

新的下载地址:

cl-http-70-218-s436-binghe-r75-fixed-v.part1.rar

cl-http-70-218-s436-binghe-r75-fixed-v.part2.rar

 

or files.me.com/binghe.lisp/aw4rfb

以下是下篇

在 Clozure CL 上启动 CL-HTTP

CL-HTTP 在 Clozure CL (CCL) 上跑得比 SBCL 更好一些,因为 CCL 的多线程 API 特性更加丰富,与 CL-HTTP 的可移植兼容层的吻合度也比较高。CCL 是我最喜爱的开源 Common Lisp 实现。它由商业公司维护,性能稳定可靠,并且在所有支持的 OS/CPU 组合上都有多线程支持以及 32/64 位版本 (ARM 除外,只有 32 位),在 Mac OS X 上甚至还有一个 IDE。目前最后发布的 CCL 版本 1.6 支持下列五种操作系统:
  • Mac OS X (PowerPC and x86)
  • Linux (ARM, PowerPC and x86)
  • FreeBSD (x86)
  • Solaris (x86)
  • Microsoft Windows (x86)
安装 Clozure CL 的过程非常简单,首先按照用户所在的平台将对应的源代码和二进制文件一次性地从 SVN 上 checkout 下来。例如,对于 Mac OS X 上的版本 1.6,相应的 SVN URL 是:
http://svn.clozure.com/publicsvn/openmcl/release/1.6/darwinx86/ccl
只需调整 URL 中的版本号部分即可获得 CCL 的其他版本 (目前的可用范围是 1.2 – 1.7)。对于版本 1.6 来说,平台目录除 darwinx86 以外还包括下列平台组合:
  • darwinppc
  • freebsdx86
  • linuxarm
  • linuxppc
  • linuxx86
  • solarisx86
  • windows
Clozure CL 项目以一种奇特的方式将所有文件放在 SVN 里管理,所有二进制目录里使用 svn:external 属性链接到相应的源代码目录和预处理头文件 (cdb) 目录上。由于 CCL 是完全自举架构的,即新版本的 CCL 二进制文件是从老版本的 CCL 加载新的源代码以后生成的,因此二进制文件是作为构建 CCL 新版本的原材料来管理的。另外每当一些产生一些重要的源代码修改时,自举过程可能短暂地需要人工干预,这些工作通常是由 CCL 核心开发者来处理并提交修复后的二进制文件。
将 CCL 随便安装在任何目录里(所谓安装也就是找个地方就地 svn checkout 上述 URL),然后要做的事情包括以下两点:
  1. 定义一个环境变量 CCL_DEFAULT_DIRECTORY,使其内容等于 CCL 的安装路径。
  2. Windows 上需要设置环境变量,将 %CCL_DEFAULT_DIRECTORY% 写入 PATH 环境变量中;其他平台上推荐将 $CCL_DEFAULT_DIRECTORY/scripts 目录下的 ccl 和 ccl64 两个脚本软链接到 PATH 环境变量所包含的某个目录里,例如 /usr/local/bin。
CCL 的启动方法是,Windows 下打开一个命令提示符窗口,然后根据自己系统的实际情况执行 wx86cl 或者 wx86cl64;其他平台下根据实际情况执行 ccl 或 ccl64。如果顺利启动的话应该可以看到类似下面的提示:
binghe@binghe-mac:~$ ccl64
Welcome to Clozure Common Lisp Version 1.6-r14820  (DarwinX8664)!
 
关于 Clozure CL 使用方法的其他问题,尤其是当源代码出现更新以后的重编译方法,请参阅官方文档
在 Clozure CL 上启动 CL-HTTP 的方法跟在 SBCL 上是完全一样的。即首先加载 contrib/kpoeck/port-template/load.lisp,然后再用 (compile-all) 编译所有源代码,(http::start-examples) 启动示例站点(浏览器中的效果如本文插图所示)。CCL 的编译速度非常快。
不过需要注意的是,CL-HTTP 的 SBCL 和 CCL 移植是通过 kpoeck 的 port-template 框架进行的,这个框架的缺点在于 HTTP 请求的多线程处理相关代码还很不成熟。当 HTTP 服务器启动以后,Lisp 环境中只有一个 HTTP 监听器线程:
* (sb-thread:list-all-threads)
 
(#<SB-THREAD:THREAD “Stupid cl-http-accept-thread” RUNNING {1004BABBD1}>
 #<SB-THREAD:THREAD “initial thread” RUNNING {10048393C1}>)
对于每个新的 HTTP 请求,CL-HTTP 都会临时派生一个新线程处理,一旦处理完毕线程就会退出;对于多并发的请求来说性能成问题。因此 CL-HTTP 的 SBCL 和 CCL 移植目前还无法胜任产品级的应用需求。不过一旦学会了 CL-HTTP 的使用方法,以后可以进而使用 LispWorks、MCL、SCL 甚至 CMUCL 等平台运行产品级的 CL-HTTP 应用。
注意到示例站点的 URL 为 http://localhost:8000。如果想要在其他 IP 地址或者端口上运行示例站点,可以带参数调用 HTTP::START-EXAMPLES 函数,语法是:
http::start-examples &key host port (type :stupid-multi)
其中 host 是字符串格式的主机名或 IP 地址,port 是数值格式的端口,type 是一个关键字参数,除了 :stupid-multi 以外另一个可用的值是 :single,这导致 CL-HTTP 可以运行在单线程模式下,每次处理一个请求,并且上述函数将不会返回。单线程模式易于调试,甚至有可能把 CL-HTTP 跑在不支持多线程的 SBCL 版本上。
(有资料表明,很多 Lisp 程序员将 CL-HTTP 与 Hunchentoot 一起混用,Hunchentoot 用来提供多线程处理和 HTTP 协议的实现,CL-HTTP 负责生成动态 HTML/XHTML 代码。本系列文章的后续部分会简要介绍 CL-HTTP 的 HTML 生成宏。)

保存 SBCL 和 CCL 下的编译成果

在前述的 CL-HTTP 加载过程中,每次执行 (COMPILE-ALL) 时都要重新编译 CL-HTTP 的所有源代码。通常,使用 ASDF 或者 MK:DEFSYSTEM 的 Lisp 程序是可以做到按需编译的,但 CL-HTTP 的 port-template 似乎有意关闭了按需编译以确保 CL-HTTP 代码的加载过程具有某种确定性。对于 CL-HTTP 这种规模比较大并且代码相对稳定的项目来说,每次加载不同的用户站点代码时都要重新编译整个 CL-HTTP 是不值得的(我最近在基于 MIPSel 的嵌入式系统上使用 SBCL 1.0.28 完整编译 CL-HTTP 花掉了 1 小时 36 分,而在我最好的电脑上这个过程只需 40 秒),因此推荐的做法是在着手开发用户站点之前,先将 CL-HTTP 中除了示例站点以外的代码以二进制形式导出到一个 core (或 image,不同的 Lisp 平台有各自推荐的扩展名,导出的文件不能跨平台使用) 文件中,从而实现快速加载。
SBCL 下的做法如下:
首先进入 port-template 的根目录(也就是 CL-HTTP 源代码的 contrib/kpoeck/port-template 目录),然后不加载用户初始化文件启动 SBCL,然后依次输入红色标识的三个命令:
binghe@binghe-mac:~/Lisp/cl-http/contrib/kpoeck/port-template$ sbcl –no-userinit
This is SBCL 1.0.49.78-0bce932, 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.
(load “load.lisp”)
(compile-all)
(save-lisp-and-die “cl-http.core”)
这样就在当前目录下得到了一个 cl-http.core 文件,以后可以使用命令行 sbcl –core cl-http.core 从该文件启动 SBCL (如果从其他目录启动 SBCL 则需要提供该文件的全路径)。事实上 SBCL 在不使用 –core 命令行参数启动时也会加载一个默认的 sbcl.core 文件,其中含有 Common Lisp 语言的实现和一些 SBCL 自己的语言扩展。从某种意义上来说,一个 Lisp 程序本质上就是对 Lisp 语言的扩展,用户代码和语言本身的实现代码在本质上是一样的,在 core 文件中的地位也是均等的。当然,诸如 car 和 cdr 这些底层 Lisp 函数最终上会被映射到 C 语言编写的底层 runtime 函数上。对 SBCL 的内部结构感兴趣的读者可以参考 SBCL Internals wiki 站点。关于 sb-ext:save-lisp-and-die 的详细说明请参见 SBCL 官方手册
Clozure CL 下的过程是类似的,不过 CCL 将 core 文件称为 image 文件。导出方法是类似的,首先不加载用户初始化文件启动 CCL,然后执行同样的前两个命令编译 CL-HTTP,最后用一个不同的命令导出 image 文件:
binghe@binghe-mac:~/Lisp/cl-http/contrib/kpoeck/port-template$ ccl64 -n
Welcome to Clozure Common Lisp Version 1.6-r14820  (DarwinX8664)!
(load “load.lisp”)
(compile-all)
(save-application “cl-http.image”)
最后得到的 cl-http.image 文件可以使用 CCL 命令行 -I cl-http.core 来加载,注意 32 位和 64 位的 image 不能通用。关于 CCL:SAVE-APPLICATION 命令的其他细节请参见 CCL 官方手册
一旦以带有 CL-HTTP 的 core 或 image 文件启动了 SBCL 或 CCL,仍然可以继续使用 (HTTP::START-EXAMPLES) 来启动示例站点。这样可以大幅节省启动时间,并且为后面开发用户站点打下了一个良好的基础。
本站下载地址,可加群免费下载或者捐助下载
资源下载此资源下载价格为5雪花,请先
本资源为虚拟产品,充值或购买后不退换。 客服邮箱:zhlisp@aliyun.com
http://mip.i3geek.com

Leave a Reply

搜索

分类目录

公 告

本网站学习论坛:

www.zhlisp.com

lisp中文学习源码:

https://github.com/zhlisp/

欢迎大家来到本站,请积极评论发言;

加QQ群学习交流。