我简单的Webrick服务器提供静态html和rhtml嵌入式ruby文件。如何使用像Thin这样的多线程或多进程Ruby服务器实现相同的目标?
Webrick设置:
#!/usr/bin/ruby
# simple_servlet_server.rb
require 'webrick'
include WEBrick
s = HTTPServer.new(:Port => 8000)
# Add a mime type for *.rhtml files
HTTPUtils::DefaultMimeTypes.store('rhtml', 'text/html')
s.mount('/', HTTPServlet::FileHandler, './public_html')
['TERM', 'INT'].each do |signal|
trap(signal){ s.shutdown }
end
s.start
我已经安装了Thin和Rack,我的config.ru读取HTML但不会渲染rhtml文档?:
use Rack::Static,
:urls => ["/images", "/js", "/css"],
:root => "public"
run lambda { |env|
[
200,
{
'Content-Type' => 'text/html',
'Cache-Control' => 'public, max-age=86400'
},
File.open('./public_html', File::RDONLY)
]
HTTPUtils::DefaultMimeTypes.store('rhtml', 'text/html')
}
TL; DR; - 动态ruby内容(即.rhtml
文件)需要由模板引擎呈现; WebRick使用的内置引擎是Ruby的ERB
引擎;在发送Rack响应之前呈现动态文件。
./public
下。
将动态文件放在./app/views
下。.rhtml
文件,我会假设这将是内置的ERB
模板引擎(关于它有一个很好的帖子here)。假设您按照提到的方式放置了动态文件和静态文件,您可能会启动一个如下所示的config.ru
文件:
require 'erb'
require 'pathname'
ROOT ||= Pathname.new(File.dirname(__FILE__)).expand_path
module APP
VIEWS = ROOT.join('app', 'views')
def self.call(env)
# prevent folder trasversal (security) and extract file name
return [403, {}, ["Not Allowed!"]] if env['PATH_INFO'].index("..") || env['PATH_INFO'].index("//")
filename = VIEWS.join(env['PATH_INFO'][1..-1])
# make sure file exists
return [404, {}, ["Not Found"]] unless File.exists?(filename)
# process dynamic content
template = IO.binread filename
data = ERB.new(template).result(binding)
return [200, {}, [data]]
end
end
run APP
接下来,您可以使用iodine运行应用程序,它将处理静态文件服务部分(在此示例中,每个核心使用单个线程工作程序):
iodine -w -1 -t 1 -www public
当然,你可以使用Rack::Static
中间件,但它应该被证明是非常慢的(自己标记它或者测试它对Thin)...
......我是碘的作者,所以我可能有偏见。
附: (关于安全性和性能的附注)
我会重新考虑模板引擎。
ERB快速有效,但它也允许在模板中执行代码。
当代码泄漏到模板中时,这可能导致难以进行项目维护,从而使代码不易读取并且难以维护。
我会考虑切换到Mustache模板,这会阻止代码在模板中运行。
更改模板引擎还可以提高性能。考虑以下基准,使用iodine flavored mustache templates(提供积极的HTML转义):
require 'iodine'
require 'erb'
require 'benchmark'
module Base
ERB_ENG = ERB.new("<%= data %> <%= Time.now %>")
MUS_ENG = Iodine::Mustache.new(nil, "{{ data }} {{ date }}")
def self.render_erb(data)
ERB_ENG.result(binding)
end
def self.render_mus(data)
h = {data: data, date: Time.now.to_s }
MUS_ENG.render(h)
end
end
puts "#{Base.render_mus("hello")} == #{Base.render_erb("hello")} : #{(Base.render_mus("hello") == Base.render_erb("hello"))}"
TIMES = 100_000
Benchmark.bm do |bm|
bm.report("Ruby ERB") { TIMES.times { Base.render_erb("hello") } }
bm.report("Iodine ") { TIMES.times { Base.render_mus("hello") } }
end
我的机器上的结果是(越低越好):
user system total real
Ruby ERB 1.701363 0.006301 1.707664 ( 1.709132)
Iodine 0.256918 0.000750 0.257668 ( 0.258190)
再说一遍,因为我是碘的作者,所以我有偏见。四处游玩,找到最适合自己的东西。