首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
timeromantic
V2EX  ›  程序员

摸完鱼就放国庆,祝大家国庆快乐

  •  
  •   timeromantic · 121 天前 via Android · 2675 次点击
    这是一个创建于 121 天前的主题,其中的信息可能已经有所发展或是发生改变。
    摸鱼鱼塘 2.0 版本发布

    https://printf520.com/hot.html

    欢迎体验新版

    更新说明
    https://printf520.com/single.html?id=69


    之前一直打算优化热榜的历史架构问题,在讲优化点前,先看一看热榜遗留的历史问题:

    热榜数据由一整个 json 串组成,造成的问题就是后期无法对热榜的历史数据查看,和每一条数据后期进行条件性筛选。
    高强度的零散数据写入和读取,关系型数据库在表结构的扩展和数据库的写入造成问题
    一次性获取几百条热榜数据,api 接口需要传输接近 20kb 的数据量,在用户高峰时期造成了热榜的卡顿
    解决问题一:

    对于造成上面三点的历史原因分析在于在初期创立热榜这个页面,基于快捷开发,产品先上,用户体验优化后期跟进的原则。所以遗留了这些问题,目前正是后期的优化时间点。先看第一个点,前期为了快速开发,数据的存储放在 mysql,并且由表的一个字段存储。表的结构如下图 1.0:



    1.0 表结构图

    可以看到在之前的设计里面每次 api 返回的数据其实就是一个 str 字段,当然这样的设计对于数据的读取和写入都是十分方便的,但是如果后期想要对热榜单独的每一条数据进行分类,这个 json 数据就显得十分的不方便了,加之综合第二点关系型数据库并不适合这样零散的数据,所以为了解决这一点,我选择了 nosql 领域比较成熟的一款数据库软件——mangodb。

    解决问题二:

    其实若不是第二点的原因,想要单独的存储每个数据不一致的热榜信息,mysql 也是可以胜任的,不过主要是因为不同的网站热榜所带的参数不一致,所以这个时候 mangodb 的面向文档存储就很适用了。对于文档型存储,其数据是用二进制的 Json 格式 Bson 存储的。数据就像 Ruby 的 hashes,或者 Python 的字典,或者 PHP 的数组,适用这样的数据类型完全可以满足每个热榜的数据差异性,并且 mangodb 没有固定的表结构,不用为了修改表结构而进行数据迁移,比如如果业务需要在原有的表增加一个字段用于记录每行数据的点击量,如果使用 mysql 我们需要修改整个表结构,然后再更新整个字段,但是对于 mangodb 来说,基于文档的存储可以不用修改表结构,直接把带有用户点击量的字段插入到表里即可。

    解决问题三:

    之前有不少的用户反馈一次性获取 200 条热榜数据,会响应一段时间,接到这个问题后,发现其中一个原因就是因为 api 每次需要返回的数据量过大,在不增加服务器带宽和使用加速 CDN 的情况下,我使用的解决方式是开启 API 的 gzip 模式,开启 gzip 后 api 返回的不再是原滋原味的原生数据,而是通过服务器端压缩后传输的 gzip 压缩数据,带来最直接的效果就是数据被压缩了不少,对比图如下:



    3.0 压缩后的 api 数据大小



    3.1 压缩前的 api 数据大小

    从 2 张图的对比可以看到,开启 gzip 后的 api 返回数据明显比未开启前小了不少,数据越小传输速度越快,当然压缩带来的成本是 API 在每次返回数据前 CPU 会执行压缩算法,不过在当前带宽不足,而 CPU 足余的情况下可以忽略不计,并且压缩后的数据可以直接存放在 redis 的缓存里面,避免二次压缩。值得一提的是 Golang 语言并不需要使用 Nginx 和 tomcat 的服务器软件,所以开启 Golang 的 http gzip 需要由自己实现,下面贴出 Golang 开启 Gzip 的关键代码段:

    package main

    import (
    "compress/gzip"
    "io"
    "net/http"
    "strings"
    )

    type gzipResponseWriter struct {
    io.Writer
    http.ResponseWriter
    }

    func (w gzipResponseWriter) Write(b []byte) (int, error) {
    return w.Writer.Write(b)
    }

    func makeGzipHandler(fn http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
    if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
    fn(w, r)
    return
    }
    w.Header().Set("Content-Encoding", "gzip")
    gz := gzip.NewWriter(w)
    defer gz.Close()
    gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w}
    fn(gzr, r)
    }
    }

    func handler(w http.ResponseWriter, r *http.Request) {

    w.Header().Set("Content-Type", "text/plain")
    jsonStr := `{"Code":0,"Message":"获取数据成功","Data":"热榜数据","CreateTime":1569673821}]}`
    w.Write([]byte(jsonStr))
    }

    func main() {
    http.HandleFunc("/a", makeGzipHandler(handler)) // 设置访问的路由
    http.ListenAndServe(":1113", nil)
    }
    32 回复  |  直到 2019-10-08 09:49:57 +08:00
    xuromky
        1
    xuromky   121 天前
    还有三个小时就下班的我已无心工作
    charleyking
        2
    charleyking   121 天前
    终于更新了,又可以快乐的摸鱼了
    timeromantic
        3
    timeromantic   121 天前 via Android
    @charleyking 哈哈,更新了一些遗留问题。欢迎常来
    timeromantic
        4
    timeromantic   121 天前 via Android
    @xuromky 同,无心工作
    Understarry
        5
    Understarry   121 天前
    无心工作+1
    fyxtc
        6
    fyxtc   121 天前
    赞,收藏了
    jy02201949
        7
    jy02201949   121 天前
    我洗好手,剥好橘子就等着下班了现在,谁也不别想让我今天下午工作!!!
    kkshell
        8
    kkshell   121 天前
    无心工作
    timeromantic
        9
    timeromantic   121 天前 via Android
    @jy02201949 下午人体宕机
    jy02201949
        10
    jy02201949   121 天前
    @timeromantic #9 老铁你这网站我打不开
    timeromantic
        11
    timeromantic   121 天前 via Android
    @jy02201949 建议用 chrome 打开
    sunziren
        12
    sunziren   121 天前
    你这网站,真的太卡了。
    unco020511
        13
    unco020511   121 天前
    这加载有点太慢了啊老哥
    timeromantic
        14
    timeromantic   121 天前 via Android
    @sunziren 今天下午人流量太大了。看来要加服务器了
    @unco020511
    hikarumx
        15
    hikarumx   121 天前
    非常好,简单清爽
    G2838
        16
    G2838   121 天前
    这加载有点太慢了啊老哥
    SSW
        17
    SSW   121 天前
    你在今天放出来,这摸鱼的人也太多了,鱼塘都炸了
    doveyoung
        18
    doveyoung   121 天前
    我昨天还在摸,今天就打不开了烙铁,是带宽不够了吗
    MX123
        19
    MX123   121 天前
    为什么不用电报群?
    timeromantic
        20
    timeromantic   121 天前 via Android
    鱼塘炸鱼了,人太多。哈哈,该升级服务器了
    @SSW
    @doveyoung
    @G2838

    多刷新还是可以打开
    timeromantic
        21
    timeromantic   121 天前 via Android
    @MX123 好的,建议不错,可以加电报群,不过不是所有人都能顺利翻墙
    VensonEEE
        22
    VensonEEE   121 天前
    @timeromantic
    大佬,有没有能缓存这些文章的 app ;
    各自的排版,风格不同,大部分网页打开需要多一次展开操作、或者跳转到 app 的提示;
    虽然说法律上不可行,但真的体验难受。
    Saszr
        23
    Saszr   121 天前
    建议那个二维码不放在热点页,放在联系页(页面首次加载出现二维码影响美感 2333
    timeromantic
        24
    timeromantic   121 天前 via Android
    @VensonEEE 抱歉,确实从法律上来说,如果直接抓取内容或者显示是侵权的。
    mcluyu
        25
    mcluyu   121 天前
    摸了一圈才过了 29 分钟
    timeromantic
        26
    timeromantic   121 天前 via Android
    @mcluyu 摸一圈这么快!
    qsbaq
        27
    qsbaq   121 天前
    无心工作+1
    t1o1
        28
    t1o1   121 天前
    一直在用。。。
    byfz
        29
    byfz   121 天前
    一直在刷,刚试了,标签可以自由拖动,挺好的
    byfz
        30
    byfz   121 天前
    可惜每次刷新都会重置为默认顺序
    timeromantic
        31
    timeromantic   121 天前
    @byfz 拖到了后需要点击右下方的保存按钮
    mcluyu
        32
    mcluyu   113 天前
    @timeromantic 7 天前仿佛就在昨天,好像一瞬之间😭
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   894 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 33ms · UTC 19:26 · PVG 03:26 · LAX 11:26 · JFK 14:26
    ♥ Do have faith in what you're doing.