调用API构建前端
一般情况下,rails应用都是从数据库进行数据获取,而采用API调用的话,就不再需要model层了,只需要在控制器中用http库进行接口调用就可以了,大大简化了结构。 example:
require 'open-uri'
require 'json'
require 'net/http'
def index
uri = URI("http://test.csdn.net/api/format/json")
req = Net::HTTP.get(uri)
@slides = JSON.parse(req)["data"]["contents"]
end
问题:多个API调用时的阻塞问题
def index
uri_one = URI("http://test.csdn.net/api/1/format/json")
uri_two = URI("http://test.csdn.net/api/2/format/json")
uri_three = URI("http://test.csdn.net/api/3/format/json")
......
req_one = Net::HTTP.get(uri_one)
req_two = Net::HTTP.get(uri_two)
req_three = Net::HTTP.get(uri_three)
......
@slides = JSON.parse(req)["data"]["contents"]
......
end
如果有10个API调用,每个Net::HTTP.get(uri)
通信耗时30ms,那么就是300ms的时间。
解决方案一:多线程处理
threads = []
threads << Thread.new {@req_one = Net::HTTP.get(uri_one)}
threads << Thread.new {@req_two = Net::HTTP.get(uri_two)}
threads << Thread.new {@req_three = Net::HTTP.get(uri_three)}
......
threads.each { |t| t.join }
data = JSON.parse(@req_one)["data"]
多进程是抢占资源的一个模型,并非并行计算,比如线程1执行一个http通信后中间有30ms的I/O时间,那么这段阻塞的时间就会被线程2抢占。 开启多线程并行处理,可以很大程度上提高处理速度。但是有两个问题: 一、开多个线程以及线程间的切换造成资源开销增加,实际上降低了应用的吞吐能力。 二、由于Ruby存在GIL (Global Interpreter Lock),并不能真正利用多线程进行并行计算。(JRuby 去除了 GIL,是真正意义的多线程,既能应付 IO Block,也能充分利用多核 CPU 加快整体运算速度。)
解决方案二:采用异步模型处理
Ruby的异步解决方案:Eventmachine
在Gemfile中添加:
gem 'eventmachine'
gem 'em-http-request', :require => 'em-http'
控制器中的代码:
EventMachine.run {
http = EM::HttpRequest.new("http://test.csdn.net/api/1/format/json").get
http.errback {
render :text "接口连接失败";
EM.stop
}
http.callback {
req = http.response
@slides = JSON.parse(req)["data"]["list"]
@page_count = JSON.parse(req)["data"]["count"].to_i/18+1
EM.stop
}
}
这种方式不仅解决了阻塞问题,而且可以有效的提高系统的吞吐能力。但带来的问题是代码量增加,复杂逻辑下代码的复杂度成倍增加,不利于开发和维护。
引入纤程(Fiber),用同步代码编写异步程序
在Gemfile中添加gem 'em-synchrony'
。
控制器代码:
EM.synchrony do
http = EM::HttpRequest.new("http://test.csdn.net/api/1/format/json").get
req = http.response
@slides = JSON.parse(req)["data"]["list"]
EM.stop
end
多个接口调用实例:
require "em-synchrony"
require "em-synchrony/em-http"
require "em-synchrony/fiber_iterator"
EM.synchrony do
concurrency = 2
urls = ['http://url.1.com', 'http://url2.com']
results = []
EM::Synchrony::FiberIterator.new(urls, concurrency).each do |url|
res = EventMachine::HttpRequest.new(url).get
results << res.response
end
puts results # all completed requests
EventMachine.stop
end