今天 shanghai on rails 三周年聚会时提到一个问题,一个程序员能否只会一种语言,只会一种语言会带来什么样的技术风险?
我的观点是只会一种语言确实会带来风险,风险主要来自于程序员会变得偏执以及语言本身会跟不上需求的演化。前一点比较主观,所以主要说说语言本身跟不上需求演化的问题。
在 C 和 Fortran 时代, 编程的一个主要指导思想是如何充分利用 CPU 和内存,所以我们会推崇 blas, lapack, atlas 之类的数学库, 会去研究如何微调编译器选项, 会说程序就是算法+数据结构,会去提醒朋友使用 minmax 算法因为他只需比较 ~1.5N 次而不是 2N 次。
逐渐地,需求变更了, 我们觉得面向对象的设计很重要, 他能让我们能处理一些更复杂的业务,为此我们愿意牺牲一点性能, 所以 C++ 出现了。C++ 尽管声称性能不妥协,但在虚函数,异常,RTTI 上毕竟对性能做了一些妥协,而且由于 C++ 工程上难度的提高, 导致在实际实施中会损失了更多的运行效率。这时期我们会推崇 MFC, QT, WX 这类的 UI 库, 以及 ACE 之类的网络库。为了降低 C++ 工程上的风险,我们会去读 Effective C++, Exceptional C++ 这类书,会去尽量找有较长工作经验的工程师。
需求再次变更了,我们无法容忍 C++ 的低鲁棒性. 变量未初始化, 提前释放,缓冲区溢出, 复杂的多线程实践, 没有底层安全机制, 这些问题导致了一个又一个的项目失败。所以 Java 出现了, Java 抛弃了性能不妥协,引入了 GC, 引入了更方便的同步机制, 更堵住了一些让 C++ 程序崩溃的漏洞。这时期我们会推崇 J2EE,几乎霸占了整个企业应用市场。我们会教导我们的同事要使用 TDD, 接口要和实现分离, 要使用检查异常。
接着需求就出现分支了, 一支说 Web 开发很重要, 我们需要开发更有效率, 语言需要更灵活, 所以 Ruby, Python 这类的语言开始流行,诞生了 Rails, Django 这类优秀的 Web 框架。特别是 Ruby, 在引入 MixIn, Block, DSL, BDD, Bundler 后,极大程度地提高了开发效率。
Java 也做了一个很大规模的演化: Java 5, 再加上 SSH 框架, 引入 Annotation, 大规模使用非检查异常, 引入 Aspect, 引入 cglib, ORM, 尽管仍然叫 Java, 但是跟我们之前的 Java 已经有了很大的不同, 设计目标不再是安全,鲁棒,而是高效开发,甚至产生了 Spring Roo, Play framework 之类相当背离 Java 原始开发风格的框架。尽管很成功, 但是作为一种静态语言,他背了太多的包袱,开发效率上始终比不上动态语言。同时由于当初为了讨好 C++, 保留了原始类型和数组, 导致反射的使用异常痛苦,普通开发者很少使用反射来简化自己的设计。所以 Web 开发方面,尽管有 Structs 2, Spring 3 之类优秀的 MVC 框架,还是有更多的程序员选择逃离 Java。
另一支则说高并发很重要, 所以 Scala, Erlang, Concurrent Python 之类语言出现了.....(太晚了,不写了)。
所以只会一种语言的风险在哪儿?在于需求的变化很快, 语言的演化很慢(比如C++ 1x), 很痛苦(比如 Java 7), 失败率也很高(比如 Perl 6)。只会一门语言就意味着你把自己局限在了这种语言所擅长的问题圈子,你无法解决这个圈子之外的问题。
至于偏执, 这是我个人观察后得出的结论,你可以看看你周围的程序员,是否有这个问题
thanks.
PS. 为了避免不必要的纠纷,先说一句, lisp rocks.
Hello, I am LI Daobing(first name is family name). You can talk with me in English or Chinese.
Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts
2010-11-01
2010-10-19
[读书笔记] Rails 中的 Rack 中间件
0. 背景知识
0.1 Rack 协议
Rack 起源于 Python 的 WSGI 协议, 是一个语言相关的 HTTP 服务端接口 (CGI, FCGI, SCGI 是语言无关的接口, WSGI, Rack, Servlet 则属于语言相关的接口), 该协议下一个简单的应用形式如下
# config.ru
class Helloworld
def call(env)
[200, {'Content-Type' => 'text/plain'}, ['hello world!']]
end
end
run Helloworld.new
将该文件存为 config.ru, 然后在该目录下运行 rackup 即可启动该应用, 详细规范可以参考 http://rack.rubyforge.org/doc/files/SPEC.html
Rails 3.0, Sinatra 都使用了 Rack 作为自己的底层, 也就是说这两个 web 框架可以共用 Rack 中间件
0.2 Rack 中间件(middleware)接口
Rack 的协议本身很简单, 需要通过中间件来支持常见的 HTTP 功能,比如 cookie, session, flash, log, cache, ...
中间件本身也是一个 Rack 应用程序, 与简单 app 略有不同的是中间件通常以下一个 app 作为初始化参数
如下的代码就是 Rack::Runtime 中间件,用于记录应用程序的执行时间:
module Rack
class Runtime
def initialize(app)
@app = app
@header_name = "X-Runtime"
end
def call(env)
start_time = Time.now
status, headers, body = @app.call(env)
request_time = Time.now - start_time
if !headers.has_key?(@header_name)
headers[@header_name] = "%0.6f" % request_time
end
[status, headers, body]
end
end
end
中间件的使用也很方便, 只需加一句 "use Rack::Runtime" 即可, 如下所示
# config.ru
class Helloworld
def call(env)
[200, {'Content-Type' => 'text/plain'}, ['hello world!']]
end
end
use Rack::Runtime
run Helloworld.new
1. 如何在 Rails 程序中查看使用了哪些中间件
$ rake middleware
(in /path/to/project)
use ActionDispatch::Static
use Rack::Lock
use Rack::Runtime
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
use ActionDispatch::BestStandardsSupport
use OpenIdAuthentication
run ProjectName::Application.routes
这些中间件大部分在 $GEM_PATH/railities-3.0.0/lib/rails/application.rb 的 default_middleware_stack 函数中定义。
2. 这些中间件有什么用
- ActionDispatch::Static, 静态文件(即 public/ 下的文件)支持, 一般在生产环境下会禁用此功能
Rack::Lock, 线程锁, 保证 rails 代码单线程运行, 同时设置 env['rack.multithread'] = false, 你可以通过设置 config.allow_concurrency = true 来去掉该中间件 - Rack::Runtime, 统计运行时间, 放在 response 的 "X-Runtime" header 中
- Rails::Rack::Logger, 比如 log/development.log 中的这一行 "Started GET "/" for 127.0.0.1 at Wed Sep 15 21:46:51 +0800 2010"
- ActionDispatch::ShowExceptions, 截获异常,把异常转换为 HTTP 错误号 (一般转为 500, 但一些特殊异常转到相应的错误号,比如 "ActionController::MethodNotAllowed" 会被转为 405, 同时显示对应的错误页面,对应开发环境,会显示异常的 backtrace, 对于生产环境,则会显示 public/500.html, 对于测试环境,该中间件会被禁用,直接把异常抛出
- ActionDispatch::RemoteIp, 解决服务器转发, 代理导致客户端真实 IP 丢失的问题,用户的真实IP放在 env["action_dispatch.remote_ip"]
- Rack::Sendfile, 如果返回数据已经放在一个文件里边了(比如生成的 PDF), 则可以让 nginx 服务器直接从该文件读取,降低系统消耗
- ActionDispatch::Callbacks, 测试环境下用于检测源文件是否改变, 产品环境下作用不明 (TODO)
- ActionDispatch::Cookies, cookie 支持
- ActionDispatch::Session::CookieStore, session 支持,此处使用 cookie store
- ActionDispatch::Flash, flash 支持, 参见 http://guides.rubyonrails.org/action_controller_overview.html#the-flash
- ActionDispatch::ParamsParser, 分析XML, JSON参数,放到 env["action_dispatch.request.request_parameters"]
- Rack::MethodOverride, 支持用 POST 来模拟 PUT, DELETE, ..., 可以在 POST 使用 _method 参数,也可以使用 HTTP 头 "HTTP_X_HTTP_METHOD_OVERRIDE"
- ActionDispatch::Head, 把 HEAD 请求转为 GET 请求, 同时设置 env["rack.methodoverride.original_method"] = "HEAD"
- ActionDispatch::BestStandardsSupport, 设置 HTTP 头: X-UA-Compatible
- ProjectName::Application.routes, 终于进入你的 rails 程序了, 开始路由, 同时开始使用 rails 的协议栈
3. 如何配置中间件
3.1 通过 config 来配置
可以参考 $GEM_PATH/railities-3.0.0/lib/rails/application.rb 的 default_middleware_stack 函数, 源码显示了如何使用 config 来控制中间件
3.2 直接修改中间件
你可通过 config.middleware (如果在配置外则需使用 Rails::Application.middleware) 来访问 middleware, 这是一个 ActionDispatch::MiddlewareStack 对象, 你可以通过他的方法来操纵 middleware, 比如你要删除 Rack::Runtime, 就可以新添 config/initializers/stack.rb 文件,内容如下
Rails::Application.middleware.delete Rack::Runtime
3.3 覆盖 default_middleware_stack 函数
修改 config/application.rb, 把 Application 类改为如下所示
module MyApp
class Application < Rails::Application
def default_middleware_stack
ActionDispatch::MiddlewareStack.new.tap do |x|
x.use Rack::Runtime
end
end
end
end
再运行 rake middleware, 可以看到大部分 middleware 都不见了
3.4 通过 rails 的插件体系
比如 vendor/plugins/open_id_authentication/init.rb 中间就有一句:
config.middleware.use OpenIdAuthentication
这句话就把 OpenIdAuthentication 这个中间件放到了 MiddlewareStack 的栈顶
Subscribe to:
Posts (Atom)