站务 什么是 Etag?

恒温 · 2015年11月01日 · 最后由 陈恒捷 回复于 2015年11月01日 · 1575 次阅读

今天发现 topic 页的关注和屏蔽,不能时时刷新。比如,我关注了@chenhengjie123,刷新下页面,还是没有关注,一开始以为我给自己加了特权,只能关注女生,后来又关注了 @chichimei,发现刷新了也不能更新,郁闷。

调试了下,发现每次刷新的时候,页面都会从缓存里读取,比如:

  CACHE: read site_config:custom_head_html
  CACHE: read site_config:footer_html
  Cache digest for app/views/topics/_topic_info.html.erb: 1c4ccc947740545d8e562f6acd7a10b8
  Cache digest for app/views/topics/_buttons.html.erb: 30469afb69031cbbd037f1c98dfe014e
  Cache digest for app/views/topics/_ban_reason.html.erb: 6efd1f9b4c7f1c901bb6ce43dcfa05f1
  Cache digest for app/views/replies/_reply.html.erb: 2ae716af07ea0f52c9bbfd87dbe4a04e
  Cache digest for app/views/topics/_editor_toolbar.html.erb: 112cb7af45235d3b3ea1cd98d89998e5
  Cache digest for app/views/topics/translation/_need_login_to_reply.zh-CN.html.erb: 5f8a6c9b24c6fc6f06b301922920ff32
  Cache digest for app/views/topics/_sidebar_box_user_info.html.erb: d6ac9fed43857ce95c2bf1e3a5dd68e7
  Cache digest for app/views/topics/_sidebar_box_tips.html.erb: c7bb423509069cec5d8bb757434a71d0
  Cache digest for app/views/topics/_sidebar_box_node_recent_topics.html.erb: cf602a9b7f54a9293ae22e4a41b14436
  Cache digest for app/views/topics/show.html.erb: 1d877fbbac7bf272672f32b3c0426f2c
Completed 304 Not Modified in 26ms

app/views/topics/_sidebar_box_user_info.html.erb 这个 fragment 就是应该有动作了就刷新的,那问题在哪里呢?打开代码一看,topic#show 方法:

def show
  @threads = []
  @topic = Topic.without_body.includes(:user).find(params[:id])

  @threads << Thread.new do
    @topic.hits.incr(1)
  end
  @threads << Thread.new do
    @node = @topic.node
  end

  @show_raw = params[:raw] == '1'

  @per_page = Reply.per_page
  # 默认最后一页
  params[:page] = @topic.last_page_with_per_page(@per_page) if params[:page].blank?
  @page = params[:page].to_i > 0 ? params[:page].to_i : 1

  @threads << Thread.new do
    @replies = @topic.replies.unscoped.without_body.asc(:_id)
    @replies = @replies.paginate(page: @page, per_page: @per_page)

    check_current_user_liked_replies
  end

  check_current_user_status_for_topic
  set_special_node_active_menu

  @threads.each(&:join)

  set_seo_meta "#{@topic.title} &raquo; #{t('menu.topics')}"

  fresh_when(etag: [@topic, @has_followed, @has_favorited, @replies, @node, @show_raw])
end

关键点在 fresh_when 这个方法,原来只有在@topic, @has_followed, @has_favorited, @replies, @node, @show_raw这些发生变化的时候,才会刷新对应页面。于是我们就加了两个判断:


  def check_current_user_status_for_topic
    return false unless current_user

    @threads << Thread.new do
      # 通知处理
      current_user.read_topic(@topic)
    end

    # 是否关注过
    @has_followed = @topic.followed?(current_user.id)
    # 是否收藏
    @has_favorited = current_user.favorited_topic?(@topic.id)
    # 读者是否关注作者
    @has_focused = current_user.followed?(@topic.user)
    @has_baned = current_user.blocked_user?(@topic.user)
  end

然后在 fresh_when 里面加上:
fresh_when(etag: [@topic, @has_followed, @has_favorited, @replies, @node, @show_raw, @has_focused, @has_baned])

再试试看,就实现了,当关注和屏蔽状态发生变化的时候,会刷新 app/views/topics/_sidebar_box_user_info.html.erb 这个 fragment。

那为啥这样修改呢?于是就引出了 ETag。

什么是 Etag?

ETag 是在 HTTP Request, Response 可以带上的一个参数,用于检测内容是否有更新过,以减少网络开销。Etags 是一种 Web 缓存验证机制,并且允许客户端进行缓存协商,能够更加高效的利用客户端的缓存。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 3 条回复 时间 点赞

大赞!学习了。我找错方向了,没找到 topic#show 这块。代码还是不熟啊。。。

我这边发现如果从外部跳转到 topic 页面会读缓存(304),如果直接在本页面刷新就会返回最新渲染结果(200)。看了最后一个参考文章后才知道原来两者还有这么大区别。

#1 楼 @chenhengjie123 修一个 bug 需要多深的技术栈。。。

#2 楼 @lihuazhang 感觉好深,至少是一个有独立做过类似系统的开发的水平吧。否则很多东西光定位就很累。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册