首先给大家看一下 NewRelic 的报表
最近 24h 的平均响应时间
流量高的那些页面 (Action)
访问量搞的几个 Action 的情况:
TopicsController#show
UsersController#show (比较惨,主要是 GitHub API 请求拖慢)
PS: 在发布这篇文章之前我有稍加修改了一下,GitHub 请求放到后台队列处理,新的结果是这样:
TopicsController#index
HomeController#index
从上面的报表来看,目前 Ruby China 后端的请求,排除用户主页之外,响应时间都在 100ms 以内,甚至更低。
我们是如何做到的?
Markdown 缓存
Fragment Cache
数据缓存
ETag
静态资源缓存 (JS,CSS,图片)
Markdown 缓存
在内容修改的时候就算好 Markdown 的结果,存到数据库,避免浏览的时候反复计算。
此外这个东西也特意不放到 Cache,而是放到数据库里面:
为了持久化,避免 Memcached 停掉的时候,大量丢失;
避免过多占用缓存内存;
class Topic field :body # 存放原始内容,用于修改 field :body_html # 存放计算好的结果,用于显示 before_save :markdown_body def markdown_body self.body_html = MarkdownTopicConverter.format(self.body) if self.body_changed? end end Fragment Cache
这个是 Ruby China 里面用得最多的缓存方案,也是速度提升的原因所在。
app/views/topics/_topic.html.erb <% cache([topic, suggest]) do %> <div class="topic topic_line topic_<%= topic.id %>"> <%= link_to(topic.replies_count,"#{topic_path(topic)}#reply#{topic.replies_count}", :class => "count state_false") %> ... 省略内容部分 </div> <% end %>
用 topic 的 cache_key 作为缓存 cache views/topics/{编号}-#{更新时间}/{suggest 参数}/{文件内容 MD5} -> views/topics/19105-20140508153844/false/bc178d556ecaee49971b0e80b3566f12
某些涉及到根据用户帐号,有不同状态显示的地方,直接把完整 HTML 准备好,通过 JS 控制状态,比如目前的“喜欢“功能。
<script type="text/javascript"> var readed_topic_ids = <%= current_user.filter_readed_topics(@topics) %>; for (var i = 0; i < readed_topic_ids.length; i++) { topic_id = readed_topic_ids[i]; $(".topic_"+ topic_id + " .right_info .count").addClass("state_true"); } </script>
再比如
app/views/topics/_reply.html.erb <% cache([reply,"raw:#{@show_raw}"]) do %> <div class="reply"> <div class="pull-left face"><%= user_avatar_tag(reply.user, :normal) %></div> <div class="infos"> <div class="info"> <span class="name"> <%= user_name_tag(reply.user) %> </span> <span class="opts"> <%= likeable_tag(reply, :cache => true) %> <%= link_to("", edit_topic_reply_path(@topic,reply), :class => "edit icon small_edit", 'data-uid' => reply.user_id, :title => "修改回帖")%> <%= link_to("", "#", 'data-floor' => floor, 'data-login' => reply.user_login, :title => t("topics.reply_this_floor"), :class => "icon small_reply" ) %> </span> </div> <div class="body"> <%= sanitize_reply reply.body_html %> </div> </div> </div> <% end %>
同样也是通过 reply 的 cache_key 来缓存 views/replies/202695-20140508081517/raw:false/d91dddbcb269f3e0172bf5d0d27e9088
同时这里还有复杂的用户权限控制,用 JS 实现;
<script type="text/javascript"> $(document).ready(function(){ <% if admin? %> $("#replies .reply a.edit").css('display','inline-block'); <% elsif current_user %> $("#replies .reply a.edit[data-uid='<%= current_user.id %>']").css('display','inline-block'); <% end %> <% if current_user && !@user_liked_reply_ids.blank? %> Topics.checkRepliesLikeStatus([<%= @user_liked_reply_ids.join(",") %>]); <% end %> }) </script>
数据缓存
其实 Ruby China 的大多数 Model 查询都没有上 Cache 的,因为据实际状况来看, MongoDB 的查询响应时间都是很快的,大部分场景都是在 5ms 以内,甚至更低。
我们会做一些比价负责的数据查询缓存,比如:GitHub Repos 获取
def github_repos(user_id) cache_key = "user:#{user_id}:github_repos" items = Rails.cache.read(cache_key) if items.blank? items = real_fetch_from_github() Rails.cache.write(cache_key, items, expires_in: 15.days) end return items end ETag
ETag 是在 HTTP Request, Response 可以带上的一个参数,用于检测内容是否有更新过,以减少网络开销。
过程大概是这样
Rails 的 fresh_when 方法可以帮助将你的查询内容生成 ETag 信息
def show @topic = Topic.find(params[:id]) fresh_when(etag: [@topic]) end
静态资源缓存
请不要小看这个东西,后端写得再快,也有可能被这些拖慢(浏览器上面的表现)!
1、合理利用 Rails Assets Pipeline,一定要开启!
# config/environments/production.rb config.assets.digest = true
2、在 Nginx 里面将 CSS, JS, Image 的缓存有效期设成 max;
location ~ (/assets|/favicon.ico|/*.txt) { access_log off; expires max; gzip_static on; }
3、尽可能的减少一个页面 JS, CSS, Image 的数量,简单的方法是合并它们,减少 HTTP 请求开销;
<head> ... 只有两个 <link href="//ruby-china-files.b0.upaiyun.com/assets/front-1a909fc4f255c12c1b613b3fe373e527.css" rel="stylesheet" /> <script src="https://ruby-china-files.b0.upaiyun.com/assets/app-24d4280cc6fda926e73419c126c71206.js"></script> ... </head>
一些 Tips
看统计日志,优先处理流量高的页面;
updated_at 是一个非常有利于帮助你清理缓存的东西,善用它!修改数据的时候别忽略它!
多关注你的 Rails Log 里面的查询时间,100ms 一下的页面响应时间是一个比较好的状态,超过 200ms 用户就会感觉到迟钝了。
如何配置我的应用程序引擎应用程序的内存(RAM)使用情况?我试图解决与超过实例内存限制相关的错误。我尝试过这些东西,到目前为止,它们不起作用或者不能提供我需要的东西。 Appstats。这不提供内存使用的详细信息 以上任何一点我都错了吗?在这个问题上,评分最高的答案(不是公认的答案)表示没有办法监控App Engine上的内存使用情况。那不可能是真的。可以吗? 编辑 我可以确认GAE mini p
问题内容: 我正在寻找 文本情感分析 (http://en.wikipedia.org/wiki/Sentiment_analysis)的开源实现,最好是python 。有谁熟悉我可以使用的开源实现? 我正在编写一个应用程序,该应用程序在Twitter上搜索某个搜索词,例如“ youtube”,并计算“快乐”推文与“悲伤”推文的数量。我正在使用Google的appengine,所以它在python
本文向大家介绍Google Analytics(分析)跟踪代码应放在哪里?,包括了Google Analytics(分析)跟踪代码应放在哪里?的使用技巧和注意事项,需要的朋友参考一下 将您的网站与Google Analytics(分析)集成时,请在代码中添加跟踪代码。这将使Google Analytics(分析)可以跟踪页面浏览量并提供其他分析。 复制代码并将其放置在您关闭网站代码中的<head>
问题内容: 我目前正在将Google Analytics(分析)实施到我的应用中。该文档没有说明在用户没有Internet连接时如何处理跟踪。数据会被扔掉还是存储在某个地方以便以后上传? 问题答案: 据我所知,SDK文档中没有明确说明,但这意味着它存储了匹配,并等待用户再次在线发送它们。 从Android SDK文档中 : 已知的问题 -可能不正确的时间戳记:时间戳记是在应用程序分发给Google
问题内容: 我知道Valgrind,但它只是检测内存管理问题。我要搜索的是一个概述的工具,程序的哪些部分确实消耗了多少内存。带有树形图的图形表示(就像KCachegrind对Callgrind所做的那样)会很酷。 我在Linux机器上工作,所以Windows工具对我没有太大帮助。 问题答案: 使用massif,这是Valgrind工具的一部分。massif- visualizer 可以帮助您绘制数
问题内容: 我想知道是否有一种方法可以在不使用内置函数的情况下实现SQL分析功能。 问题答案: 这是三个等效的表达式: 假设存在,以使行对于“ row_number”而言是唯一的。