Plack 代码和结构分析一[译]
我有写大量的代码, 但我想要是能更快更好的读代码的能力也很重要. 我和 @ranguard 有一起共事的殊荣, 我发现他象一个读代码的猎豹, 非常让人羡慕. 所以我现在开始分析各种 CPAN 的模块源代码来进行练习. 先从 Plack 开始.
Plack
Plack 的文档中介绍自己只是一个 PSGI 的工具集 PSGI (the Perl Server Gateway Interface).
Plack 在 CPAN 上最高的版本是 2009-10-13 号的 0.9000. 头两年每周会有几次 releases. 在 2012 年基本上每周仍然有一次更新. 在 2013 年好象慢下来, 基本一个月才会更新一次.
代码本身写得非常的简洁和非常注重细节. 基本没有多少注释. 基本 71 个文件中有 43 个文件小于 3 个注释. 当然, 代码本身写得很优美, 就使得不需要多少注释, 现在有很多优秀的 POD 也是这样, 都是代码是自解释的.
这些代码使用了大量的回调 ( 代码引用 ). 这也就是说它是重度事件驱动的. 这对于现在流行的 Web 服务器事件驱动模型, 这是合理的. 对大家来讲, 这很有 JavaScript 的味道. 例如 Plack::Util::foreach 就很象 jQuery.each() 通过遍历数组调用每个元素的代码引用.
Plack::Util::foreach([1,2,3], sub{ printshift}); # prints "123"
背景了解
去读读最重要的 PSGI spec 非常重要. Plack 是为了处理 PSGI 的实现. 这个写得非常好并很清晰, 但是可能有点枯燥, 因为缺乏上下文. 不过这个对于读代码本身非常有帮助.
开始
安装和取得代码
~/code ⚡ git clone git@github.com:plack/Plack.git ~/code ⚡ cd Plack
这个项目的依赖列表是在 cpanfile 中. 如果读代码和执行测试最好是使用 Carton 来安装和执行.
~/code/Plack ⚡ carton ~/code/Plack ⚡ prove-rlt
Plack 有些什么人为它工作?
让我们了解一下谁参与了该项目:
~/code/Plack ⚡ git shortlog --summary --numbered | head 1567 Tatsuhiko Miyagawa 70 Kazuho Oku 68 Tokuhiro Matsuno 20 Daisuke Murase 20 Jesse Luehrs 19 yappo 16 Karen Etheridge 16 Mark Stosberg 12 hiratara 11 Stevan Little
它有多大?
~/code/Plack ⚡ tree lib | tail-1 17 directories, 70 files
分别有什么语言
~/code/Plack ⚡ cloc . 2>/dev/null | tail -13 ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Perl 82 2889 3341 5309 Bourne Shell 8 55 138 251 YAML 1 0 0 19 HTML 3 2 0 14 Python 1 2 1 6 Javascript 1 0 0 1 CSS 1 0 0 1 ------------------------------------------------------------------------------- SUM: 97 2948 3480 5601 -------------------------------------------------------------------------------
我们看看 lib 目标结构?
我们可能很难从所有的 71 个文件中去了解怎么样工作的, 但我们可以扫一眼看看, 这样相当有助于我们组织和理解代码. 这个看起来有 3 个大类.
1 - 模块的加载和 PSGI 服务器的运行
plackup
Plack::Handler
Plack::Handler::*
Plack::Loader
Plack::Loader::*
Plack::Runner
2 - 用于构建 PSGI apps 的模块.
Plack::App::*
Plack::Builder
Plack::Component
Plack::Middleware
Plack::Middleware::*
3 - 测试用的模块
Plack::Test
Plack::Test::*
Plack::HTTP::Message::PSGI
Plack::LWPish
我们来接着看这些 lib 目录下所有的东西, 我在后面都加了一个简短的描述, 这认为这还是比较有参考意义的. 下面的数字对应上面所列出来的类别.
~/code/Plack ⚡ tree lib lib ├── HTTP │ ├── Message │ │ └── PSGI.pm # (3) 转换 HTTP::Request 变成 PSGI 环境变量 env 的哈希结构 │ └── Server │ └── PSGI.pm # (1) PSGI web 服务器所需要的引用; 没有依赖; 通常不用于生产环境 ├── Plack │ ├── App # (2) 这个 lib 继承自 Plack::Component; 这是 PSGI web apps │ │ ├── Cascade.pm # 为每个请求, 尝试调用一些 PSGI 的 app 直到响应正常 │ │ ├── CGIBin.pm # 目录中有多个 CGI 的脚本时, 通过 WrapCGI 来创建多个 CGI 的 PSGI 的应用 │ │ ├── Directory.pm # 服务一个目录下文件 │ │ ├── File.pm # 服务普通文件 │ │ ├── PSGIBin.pm # 从目录中的 .psgi 文件来创建 PSGI apps │ │ ├── URLMap.pm # 映射 url 到 PSGI app │ │ └── WrapCGI.pm # 从 CGI 的脚本来创建单个 PSGI app │ ├── Builder.pm # (2) 使用 DSL 风格来构建 Plack 的中间件 │ ├── Component.pm # (2) 一个可选的工具, 用于构建 PSGI web apps │ ├── Handler │ │ ├── Apache1.pm │ │ ├── Apache2 │ │ │ └── Registry.pm │ │ ├── Apache2.pm │ │ ├── CGI.pm │ │ ├── FCGI.pm │ │ ├── HTTP │ │ │ └── Server │ │ │ └── PSGI.pm # HTTP::Server::PSGI 中的 Plack::Handler │ │ └── Standalone.pm # Plack::Handler::HTTP::Server::PSGI 的别名 │ ├── Handler.pm # (1) 用于实例化和运行 PSGI 兼容的服务 │ ├── HTTPParser │ │ └── PP.pm # 通过 XS 解析 HTTP headers │ ├── HTTPParser.pm # (1) 解析 HTTP headers; used by HTTP::Server::PSGI │ ├── Loader │ │ ├── Delayed.pm # 延迟编译 PSGI app 直到第一个请求到达 │ │ ├── Restarter.pm # 当检查到本地文件变化时, 重起服务 │ │ └── Shotgun.pm # 为每个请求重新编译 PSGI app │ ├── Loader.pm # (1) 载入 PSGI 兼容的 web 服务 │ ├── LWPish.pm # (3) 轻量版本的 LWP 用于测试的 │ ├── Middleware │ │ ├── AccessLog │ │ │ └── Timed.pm # 写 access logs 但可以处理fake File::IO 的内容 │ │ ├── AccessLog.pm # 写 access logs │ │ ├── Auth │ │ │ └── Basic.pm # Basic authentication │ │ ├── BufferedStreaming.pm # 打开 streaming 的服务 │ │ ├── Chunked.pm # 用于实现 HTTP/1.1 的部分 - chunked HTTP transfer encoding │ │ ├── ConditionalGET.pm # 用于实现 HTTP/1.1 的部分 - Conditional GET │ │ ├── Conditional.pm # 根据指定的条件来运行指定的中间件 │ │ ├── ContentLength.pm # 如果可以,就添加一个 Content-Length header │ │ ├── ContentMD5.pm # 设置 Content-MD5 header 当 body 是一个数组引用的时候 │ │ ├── ErrorDocument.pm # 不同的 HTTP 错误的时候, 显示不同的错误文档 │ │ ├── Head.pm # 如果是 HEAD 请求, 会删除所有的响应 body │ │ ├── HTTPExceptions.pm # 当出现 HTTP::Exceptions 的时候, 重定向到错误页面 │ │ ├── IIS6ScriptNameFix.pm # Fix for IIS │ │ ├── IIS7KeepAliveFix.pm # Fix for IIS │ │ ├── JSONP.pm # 如果指定了 callback 的参数, 就给 JSON 的响应转换成 JSONP 的响应 │ │ ├── LighttpdScriptNameFix.pm # Fix for Lighttpd │ │ ├── Lint.pm # 检查 input/output 是否兼容 w/PSGI spec │ │ ├── Log4perl.pm # Log with Log::Log4Perl │ │ ├── LogDispatch.pm # Log with Log::Dispatch │ │ ├── NullLogger.pm # 清除日志处理函数 │ │ ├── RearrangeHeaders.pm # Fix for very old MSIE and broken HTTP proxy servers │ │ ├── Recursive.pm # 允许应用程序将请求转发到不同的路径(url) │ │ ├── Refresh.pm # 类似 Plack::Loader::Restarter 但有少量的不一样 │ │ ├── Runtime.pm # 设置 'X-Runtime' HTTP response header = app's response time │ │ ├── SimpleContentFilter.pm # 过滤响应的内容 │ │ ├── SimpleLogger.pm # 日志信息 │ │ ├── StackTrace.pm # 显示 PSGI 应用 die 的堆栈 │ │ ├── Static.pm # 服务静态文件 │ │ ├── XFramework.pm # 增加 X-Framework HTTP response header │ │ └── XSendfile.pm # 增加 X-Sendfile HTTP response header │ ├── Middleware.pm # (2) 封装 PSGI apps; 能修改进入的请求 / 输出的响应 │ ├── MIME.pm # 全部的 MIME 类型(mostly) │ ├── Request │ │ └── Upload.pm # Plack::Request 有关文件上传的一个子类 │ ├── Request.pm # (2) 低级的 request 对象, 用于中间件和 web 应用 │ ├── Response.pm # (2) 低级的 Response 对象, 用于中间件和 web 应用 │ ├── Runner.pm # (1) plackup 的核心 -- 用于 Plack::Loader 和 Plack::Handler │ ├── TempBuffer.pm # 用于向后兼容, 存储数据到内存, 如果文件很大会自动存储到文件; │ ├── Test │ │ ├── MockHTTP.pm # 不使用 server 来用于测试 PSGI app (最快的) │ │ ├── Server.pm # 使用简单的 server 来测试 PSGI app (比较快的) │ │ └── Suite.pm # 确保 Web 服务器符合 PSGI spec │ ├── Test.pm # (3) test objects 的工厂 │ ├── Util │ │ └── Accessor.pm # 轻量的 Class::Accessor 用于向后兼容 │ └── Util.pm # 混杂的但整个代码库使用的重要工具 └── Plack.pm # 这没有代码 -- 只有 POD 文件各版本号
建议继续学习:
- Plack 代码和结构分析-Plack::Builder[译] (阅读:621)
- Plack 代码和结构分析-PSGI Application Architecture[译] (阅读:610)
- Plack 代码和结构分析-plackup Architecture[译] (阅读:587)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:扶 凯 来源: 扶凯
- 标签: Plack
- 发布时间:2015-02-26 22:22:28
- [55] IOS安全–浅谈关于IOS加固的几种方法
- [53] 如何拿下简短的域名
- [52] 图书馆的世界纪录
- [52] android 开发入门
- [50] Go Reflect 性能
- [50] Oracle MTS模式下 进程地址与会话信
- [48] 【社会化设计】自我(self)部分――欢迎区
- [47] 读书笔记-壹百度:百度十年千倍的29条法则
- [36] 程序员技术练级攻略
- [29] 视觉调整-设计师 vs. 逻辑