• @ruohanc 我非常想帮助别人,但你这样的说法真让人寒心。

    如果你觉得我的位置太高,会不会因为你的位置太低?如果你觉得每个人都在教育我,会不会因为你太敏感了,为不存在的事情替我着急?如果你觉得我的反应有些偏激,会不会因为你承受别人指出(别人的)错误的压力阀太低了?

    ETag 应该前置校验,这样除了减少网络传输外,(基本)不会产生性能下降。另外,你不要偷换概念,这不是“这点”,不进行前置校验,绝不会是“这点”。

    当然,如果我设计一个 WebFrame,应该也会引入类似Rack::ETag的中间件,作为通用组件,它的局限性是可以被容忍的。

    对于动态请求来说,etag 和 last modified 都是需要后置校验

    @ruohanc 我非常希望你也能明白,@quakewang 类似的说法是错误的,这并不是基础知识,而是错误的知识。

    @quakewang 进行分享,这是好的事情,分享的意义,并不单纯帮助了别人,也给了别人帮助自己的机会。

    另外,需要请教下的:实际上 last_modified 就是字符串,并非一定要时间戳或者其它什么格式,因为 HTTP 处理的是超文本;真有限制,也是各种 WebFrame 内建的限制。我这样的理解是不是错误的?

    貌似没有人能帮助我,为了回复 @ruohanc 你的这个帖子,我仔细翻阅了http://www.ietf.org/rfc/rfc2616.txt的协议,这个论述,我估计会在某些浏览器中不支持。好吧,也算你帮助了我。

    我已经到了俯首而为孺子牛的年纪,所以,并不太容易有类似生气的过激反应,倒是高中时,同学跑过来问问题,然后辅导之,然后她开心地说自己就应该不耻下问,这不是耍流氓么……

    我在文前的反问,并非 troll 行为,只是当年自己跟下属说类似的话时,那时没有人告诉我,这样是错的。


    @aptx4869 测试的前提比结果更重要些,比如确保 status 回来的都是 200,确保 GZip 压缩的 level 是一致的,比如 text 类型的 js/css/html 几个分开比对一下;另外,结果还是感觉不科学,再看看 Rails 对 Gzip 的实现是纯 Ruby 还是用了 C。如果,结果还是不科学(这就真的太不科学了),哈哈,欢迎来笑话我。


    一说真话,就遭人嫌,继续感受下吧。

    @luikore

    nginx 只用 gzip_static on 发送静态资源就够啦,绝大部分动态内容都是不压缩的比较快

    gzip_static 是预压缩,就是 Nginx 会检测.gz 的对应已经压缩好的文件是否存在……

    gzip on 的话也可以配置哪些内容 gzip 哪些不 gzip

    这个是必须要配置的。

    像压缩过的 js, png 图片,gzip 对瘦身效果很不明显,但是耗费很多服务器和客户端的 CPU, 还是不 gzip 的好

    压缩过的 js如果是指自己下面这句话的,呃,有效果才奇怪…… png 图片怎么敢用 GZip?不,不,没有特殊情况下,text 类的都应该开启 GZip,它的效率还是很高的。

    用 gzip_static 还有个好处:在编译 asset 时就比较压缩效果,压缩比少于一定量就不 gzip

    唉,Nginx 有个gzip_min_length参数可以设置……


    还是那句话,希望对大家有所帮助。 但坦率地说,有点后悔参合进来。

  • Rack::ETag 不是累赘,对于非性能瓶颈的请求,没必要手工加 fresh_when/stale 等判断,就算多生成一次 response body,浪费一些服务器资源也无所谓,客户端还是能够享受到 304 带来的好处.

    是如此,我当然知道……

    相信看到这里的朋友对 304 有了更好的理解。

    如果真要做优化,这种通用的 MiddleWare,确实非常鸡肋。我保留这个看法,因为这是正确的。

    但多数的产品用通用的就可以了,因为性能问题不会如此突出;如果要针对自己产品做类似Rack::ETag的处理,就需要确定自己的规则,当然,代码并不会增加太多,估计就 10 行内的事情。

    好了,我该说的都说完了。希望对诸位有所帮助。

  • #61 楼 @quakewang

    不仅仅是 markdown 转 html,就是单纯的 rails helper 生成 html,或者复杂页面 view template 渲染,也需要花费 10ms 级别的时间(没办法,ruby 不够快),对于访问量大的页面,片段缓存能够节约掉这部分时间也是相当可观的

    评论可以修改的,所以刚才没有看到这段。其它方式生成 HTML,这个怎么做缓存都不过分,脚本语言,没有办法的;我特指 Markdown 转 HTML,这个真的没有必要。

    对于动态请求来说,etag 和 last modified 都是需要后置校验,没办法提前到 nginx 这层面校验.我不选择用 last modified 是因为 etag 更加灵活,可以用更新时间+其他参数,比如我文章中提到过的当前用户 id,评论数等来做.

    前置校验,不是指放在 Nginx 中处理,而是指 request 过来的时候,先校验头部信息,如果 match cache 的,直接 status=304,并且返回空内容;而不需要再继续运算完整的 response

    我总感觉有些奇怪的,是动态请求来说这样的前提把自己画地为牢了?

    我不选择用 last modified 是因为 etag 更加灵活

    另外,需要请教下的:实际上 last_modified 就是字符串,并非一定要时间戳或者其它什么格式,因为 HTTP 处理的是超文本;真有限制,也是各种 WebFrame 内建的限制。我这样的理解是不是错误的?

    我始终觉得,因为 ETag 跑去改 Nginx 的源码,太草率了。当然,实际自己遇到这样的问题,可能也会类似的做法,dirty but quick. 但当做知识进行分享,感觉不是很好。

  • #62 楼 @aptx4869 你要把我搞疯掉了。

    你这个不用做测试就知道什么结果了呀,我没有说refresh_when的问题呀,我是说Rack::ETag这种 ETag 的 MiddleWare 的逻辑就是累赘。

    是不是我的表达能力太弱了?还是已经冒犯到了你?

  • 我可能表达不够清楚,ETag 本身的作用是节省带宽(就是下载时间),但是校验 ETag 如果是这种后置的算法,那这个就很难算上优化的手段。 但是很多人会有这样的误解(304意味着)直接用客户端的缓存,而无需在服务器端再生成一次内容,程序没有特殊处理,不是这样的。

    @quakewang 所以我才说在知道可以用last_modified的前提下,实在没有必要去修改Nginx源码;再则,Nginx处理Gzip的性能高很多,不是么?

    in 查询一般都是主键查询,或者是有 index,不会有全表查询问题.

    这个你是对的,不过建议还是说明下,因为 ORM 的调用还是很容易进入子查询的逻辑,这个时候,对于那些还没有完全理解索引概念的朋友们,优化优化,结果越来越糟。

    @huacnlee @quakewang 提个建议,应该能看出我并不是一名新手或者不知所以的人,所以,一些基本的概念,不用说明的。

  • #53 楼 @aptx4869 我们不是“试”,而是实践了很久,复杂程度远超过本文的。

    但你这个问法很奇怪,知识本身没有那么强烈的感情色彩,多数时候,对的就是对的,错的就是错的。如果我说错了,请告诉我;如果自己没有能力分辨什么是对什么是错,可能最好的办法,就不要用这样的态度去『随便问问』(希望没有冒犯到你)。

  • #54 楼 @huacnlee 是的,ETag 在 request 过来的时候就进行校验,这样基本上执行的时间就接近于 0 了;但rails框架默认使用Rack::ETag middleware,它会自动给无etag的response加上etag这样的 MiddleWare,很令人费解,如果是后置计算,response 都已经得到了(完整得运行了一次),hash 虽然效率很高,但这个逻辑就很鸡肋。

    在这方面来说,ETag 和 last_modified 扮演的角色是一样的。

  • 楼主是在传播错误的知识,为什么没有人指出呢?

    (304)直接用客户端的缓存,而无需在服务器端再生成一次内容。

    这个表述不严谨,如程序内没有特殊处理,照样会生成一次内容(楼主自己后面也举了用例,但未说明原因),只是 status=304 而非 200,这个区别。而 HTTP 在传输的时候,是先给状态,再传内容的,304 的则是空内容,从而节约网络传输的时间。

    Gzip 之后,etag 就变了,这个 Nginx 处理得是复合逻辑的,并且在知道可以用last_modified的前提下,实在没有必要去修改 Nginx 源码;再则,Nginx 处理 Gzip 的性能高很多,不是么?

    自动 etag 能够节省的只是客户端时间,服务器端还是一样会执行所有的代码

    如果 etag 是这样被中间件处理的,实是累赘至极,它或许为某些特殊场景设计的吧,如果通用场景这样使用,实际上必然带来性能的下降。

    HTTP 本身的协议允许各种 Headers 的定制,etag 和 last_modified 能起到缓存校验的作用,是因为它们属于默认的 Headers,浏览器基本上都是支持的。


    节约了生成 markdown 语法转换到 html 时间,这里用文章最后更新时间作为 cache key 的一部分

    Markdown 转 HTML,这种结果接近固定值的逻辑,为什么要用 cache?


    另外数据查询缓存这段,传统的 SQL,如果用 IN 查询,会不会有潜在的全表扫描的可能?

    个人意见:ORM 的缓存应该信任 ORM 本身,因为本来比较难处理,通过外部调用的缓存颗粒度控制,可控性反倒更高。比如下面所言,在多 Unicorn 同时运行的前提下,如果请求周期过长(不知道你们实际场景下是多久),那么数据的不一致性会十分离谱... 如果请求周期是偏短的,那应该是 ORM 设计的一个标配,短时缓存,实在不算是应用场景下优化的手段。

    rails 内置了 query cache( https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb ),在同一个请求周期内,如果没有 update/delete/insert 的操作,会对相同的 sql 查询进行缓存,如果文章类别都是相同的话,真正去查询数据库只会有 1 次。