我的rails应用程序遇到了一些加载时间问题,并从URL源加载JSON数据,然后使用lazy_high_charts gem将其解析为图形。目前,每次加载页面需要7到10秒。
我有三个使用Oj gem解析的JSON数据网址(@data,@ forecast,@ moisture),因为有人建议它会加快这个过程。
我正在寻找一种方法来加快这个过程,如果可能的话。
JSON数据结构如下:
{"status"=>"ok", "data"=>[{"2014-08-11 11:00:00"=>14.9},{"2014-08-11 11:30:00"=>15.1}]}
控制器
@temperature = Temperature.find(params[:id])
@temps = Temperature.find(:all, :conditions => ["id != ?", params[:id]])
@hash = Gmaps4rails.build_markers(@temperature) do |data, marker|
marker.lat data.lat
marker.lng data.long
end
@data = Oj.load(open(@temperature.url).read)
@forecast = Oj.load(open(@temperature.air_forecast).read)
@moisture = Oj.load(open(@temperature.moisture).read)
data = []
moisture = []
forecast = []
@data['data'].flatten.each do |d|
data << [DateTime.parse(d.keys.first).to_i * 1000, d.values.first]
end
@moisture['data'].each do |d|
moisture << [DateTime.parse(d.keys.first).to_i * 1000, d.values.first]
end
@sevendays = LazyHighCharts::HighChart.new('graph') do |f|
f.chart(:height => '400', width: '860', plotBackgroundImage: ActionController::Base.helpers.asset_path("chartbg.png"))
f.yAxis [:title => {:text => "Soil Temperature (\u00B0C)", :margin => 20, style: { color: '#333'}}, min: 0, plotLines: [{ color: '#b20838', width: 2, value: 28 }], ]
f.series(:type => 'line', :name => 'Soil Temperature', data: data, marker: {enabled: false}, :color => '#00463f' )
f.xAxis(:type => 'datetime', tickInterval: 1.day.to_i * 1000, :dateTimeLabelFormats => { day: "%b %e"}, :tickmarkPlacement => 'on', :startOnTick => true, min: 1.weeks.ago.at_midnight.to_i * 1000, labels: { y: 20 } )
f.legend({:align => 'center', :verticalAlign => 'top', :y => 0, :borderWidth => 0, style: {color: "#333"}})
end
@day = LazyHighCharts::HighChart.new('graph') do |f|
f.chart(:height => '400', width: '860', plotBackgroundImage: ActionController::Base.helpers.asset_path("chartbg.png"))
f.yAxis [:title => {:text => "Soil Temperature (\u00B0C)", :margin => 20, style: { color: '#333'}}, min: 0, plotLines: [{ color: '#b20838', width: 2, value: 28 }]]
f.series(:type => 'line', :name => 'Soil Temperature', data: data, marker: {enabled: false}, :color => '#00463f' )
f.xAxis(:type => 'datetime', tickInterval: 5.hour.to_i * 1000, :dateTimeLabelFormats => { day: "%b %e"}, :tickmarkPlacement => 'on', :startOnTick => true, min: 1.day.ago.at_midnight.to_i * 1000, labels: { y: 20 } )
f.legend({:align => 'center', :verticalAlign => 'top', :y => 0, :borderWidth => 0, style: {color: "#333"}})
end
@month = LazyHighCharts::HighChart.new('graph') do |f|
f.chart(:height => '400', width: '860', plotBackgroundImage: ActionController::Base.helpers.asset_path("chartbg.png"))
f.yAxis [:title => {:text => "Soil Temperature (\u00B0C)", :margin => 20, style: { color: '#333'}}, min: 0, plotLines: [{ color: '#b20838', width: 2, value: 28 }]]
f.series(:type => 'line', :name => 'Soil Temperature', data: data, marker: {enabled: false}, :color => '#00463f' )
f.xAxis(:type => 'datetime', tickInterval: 2.day.to_i * 1000, :dateTimeLabelFormats => { day: "%b %e"}, :tickmarkPlacement => 'on', :startOnTick => true, min: 1.month.ago.at_midnight.to_i * 1000, labels: { rotation: 90, y:20 })
f.legend({:align => 'center', :verticalAlign => 'top', :y => 0, :borderWidth => 0, style: {color: "#333"}})
f.plotOptions({line: {turboThreshold: 1500}})
end
@moisture_graph = LazyHighCharts::HighChart.new('graph') do |f|
f.chart(:height => '400', width: '860', plotBackgroundImage: ActionController::Base.helpers.asset_path("chartbg.png"))
f.yAxis [:title => {:text => "Litres of Water Per Litre of Soil", :margin => 20, style: { color: '#333'}}]
f.series(:type => 'line', :name => 'Surface Moisture Volume', pointInterval: 1.day * 1000, data: moisture, marker: {enabled: false}, :color => '#00463f' )
f.xAxis(:type => 'datetime', tickInterval: 1.day.to_i * 1000, :dateTimeLabelFormats => { day: "%b %e"}, :tickmarkPlacement => 'on', :startOnTick => true, min: 1.weeks.ago.at_midnight.to_i * 1000, labels: { y: 20 } )
f.legend({:align => 'center', :verticalAlign => 'top', :y => 0, :borderWidth => 0, style: {color: "#333"}})
end
respond_to do |format|
format.html # show.html.erb
format.json { render json: @temperature }
end
end
查看
<ul class="nav nav-pills">
<li class="active"><a href="#seven" data-toggle="tab">Last 7 Days</a></li>
<li><a href="#forecast" data-toggle="tab">7 Day Forecast Temperature</a></li>
<li><a href="#day" data-toggle="tab">Last 24 Hours</a></li>
<li><a href="#month" data-toggle="tab">Last 30 Days</a></li>
<li><a href="#moisture" data-toggle="tab">Surface Moisture Volume</a></li>
<li><a href="#location" data-toggle="tab">Location of <%= @soil_temperature.property %></a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="seven" style="width: 100%;">
<%= high_chart("chart", @sevendays) %>
</div>
<div class="tab-pane" id="forecast" style="width: 100%;">
<table class="table table-bordered table_forecast" width="100%">
<th></th>
<% Time.use_zone('Sydney'){(0.day.from_now.to_date..6.day.from_now.to_date)}.each do |d| %>
<th><%= d.strftime("%a %d, %b") %></th>
<% end %>
<tr>
<td width="12.5%"><b>Maximum</b></td>
<% Time.use_zone('Sydney'){(0.day.from_now.to_date..6.day.from_now.to_date)}.each do |d| %>
<% if @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.max { |a,b| a[1] <=> b[1] }[1] > 20 %>
<td width="12.5%" bgcolor="#ed1c24"><font color="#ffffff">
<% elsif @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.max { |a,b| a[1] <=> b[1] }[1] > 14 %>
<td width="12.5%" bgcolor="#f58233"><font color="#fff">
<% elsif @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.min { |a,b| a[1] <=> b[1] }[1] < 14 %>
<td width="12.5%" bgcolor="#00a1e4"><font color="#fff">
<% else %>
<td width="12.5%"><font color="#333333">
<% end %>
<%= @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.max { |a,b| a[1] <=> b[1] }[1] %>°C</font></td>
<% end %>
</tr>
<tr>
<td width="12.5%"><b>Minimum</b></td>
<% Time.use_zone('Sydney'){(0.day.from_now.to_date..6.day.from_now.to_date)}.each do |d| %>
<% if @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.min { |a,b| a[1] <=> b[1] }[1] > 20 %>
<td width="12.5%" bgcolor="#ed1c24"><font color="#ffffff">
<% elsif @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.min { |a,b| a[1] <=> b[1] }[1] > 14 %>
<td width="12.5%" bgcolor="#f58233"><font color="#fff">
<% elsif @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.min { |a,b| a[1] <=> b[1] }[1] < 14 %>
<td width="12.5%" bgcolor="#00a1e4"><font color="#fff">
<% else %>
<td width="12.5%"><font color="#333333">
<% end %>
<%= @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.min { |a,b| a[1] <=> b[1] }[1] %>°C</td>
<% end %>
</tr>
<tr>
<td width="12.5%"><b>Average</b></td>
<% Time.use_zone('Sydney'){(0.day.from_now.to_date..6.day.from_now.to_date)}.each do |d| %>
<% if (@forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.sum { |sum| sum[1] } / @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.count) > 20 %>
<td width="12.5%" bgcolor="#ed1c24"><font color="#ffffff">
<% elsif (@forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.sum { |sum| sum[1] } / @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.count) > 14 %>
<td width="12.5%" bgcolor="#f58233"><font color="#fff">
<% elsif (@forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.sum { |sum| sum[1] } / @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.count) < 14 %>
<td width="12.5%" bgcolor="#00a1e4"><font color="#fff">
<% else %>
<td width="12.5%"><font color="#333333">
<% end %>
<%= "%.1f" % (@forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.sum { |sum| sum[1] } / @forecast['data']['temperatures'].select { |temp| temp[0].to_date == d }.count) %>°C</td>
<% end %>
</tr>
</table>
</div>
<div class="tab-pane" id="day" style="width: 100%;">
<%= high_chart("chart2", @day) %>
</div>
<div class="tab-pane" id="month" style="width: 100%;">
<%= high_chart("chart3", @month) %>
</div>
<div class="tab-pane" id="moisture" style="width: 100%;">
<%= high_chart("chart4", @moisture_graph) %>
</div>
<div class="tab-pane" id="location" style="width: 100%;">
<div id="map" style='width: 100%; height: 600px;'></div>
</div>
<hr>
</div>
<script type="text/javascript">
$('a[href="#location"]').on('shown', function(e) {
var mapOptions = { mapTypeId: google.maps.MapTypeId.HYBRID, Zoom: 9 };
handler = Gmaps.build('Google');
handler.buildMap({ provider: mapOptions, internal: {id: 'map'}}, function(){
markers = handler.addMarkers(<%= raw @hash.to_json %>);
handler.map.centerOn(markers[0]);
handler.getMap().setZoom(9);
google.maps.event.trigger(map, "resize");
});
});
</script>
答案 0 :(得分:2)
听起来好像是&#34;网址来源&#34;从第三方网站提供天气预报信息。网址本身需要7到10秒才能加载吗?如果是这样的话,你可以做的很少,以加快他们的速度。即使并行请求也只能与最快的请求一样快。相反,您可以尝试移动延迟,以便最终用户不会遇到它。
据推测,这些信息并非每分钟都有显着变化。如果您的潜在预测位置数量有限,则可以尝试预先加载和存储数据。特别是,您可以使用cron作业每隔X分钟提取数据并将其存储在数据库中,您可以在页面加载时查询数据。
或者,您可以加载没有数据的页面(显示简单的&#34;正在加载...&#34;文本),然后通过Ajax填充数据。这不会使查看数据变得更快,但它可以提供比盯着空白页7-10秒更好的用户体验。
答案 1 :(得分:1)
@romainsalles是对的,如果获取数据是慢速的一部分,那么最好的办法是并行化。我对赛璐珞没什么经验。使用线程,您可以这样做:
t1 = Thread.new{ @data = Oj.load(open(@temperature.url).read) }
t2 = Thread.new{ @forecast = Oj.load(open(@temperature.air_forecast).read) }
t3 = Thread.new{ @moisture = Oj.load(open(@temperature.moisture).read) }
t1.join
t2.join
t3.join
如果您请求数据的位置支持压缩响应,则可以切换到接受gziped响应的http客户端。根据数据的不同,这可能会将传输时间减少2倍到10倍。
我相信httpclient默认会这样做。
require 'httpclient'
clnt = HTTPClient.new
clnt.get_content(url) #instead of open(url).read