• Data Service 设计分享 at 2017年02月20日

    #4 楼 @huacnlee 感谢指教.

    学习了一下 second_level_cache, data service 的确做了一些和 second_level_cache 类似的事情, 场景的区别主要因为我们前后端项目分离后, Write-Through 在 ruby, Read-Through 需要在 node 端发起. 在单体 ruby 项目中 SecondLevelCache 是个非常不错的选择!

    不过有些场景还是不能容忍数据在缓存中 miss 后再去触发生成, 这个节点容易造成 5XX, data service 主要就是加强缓存异步刷新, 在 redis 数据没有 expire 之前去刷新数据.

  • Data Service 设计分享 at 2017年02月20日

    #5 楼 @i5ting 感谢指教. 这里和直接 RPC 还是有点区别, 在文中的最后一张图「周内 TP 变化情况」, 该页面是在 22 号之前很多数据是走的 http/thrift RPC, 22 号之前的很多不可用 (超时/5XX) 都是因为 RPC 调用偶尔的失败导致, 而 22 后接入了 data service 后, 基本保证所有数据在 redis 中都有, 基本不会发生 miss 的情况, 页面很稳定.

    对比 RPC 封装缓存和 data service 采用的异步更新, 忘了哪里看来的比喻还挺形象的:

    1. 发现缓存过期时, 线程都去获取并更新缓存, 容易引发雪崩, 5XX 的原因:

      5 个工人(线程)去港口取同样 Key 的货(get),发现货已经过期被扔掉了,这时 5 个工人各自分别去对岸取新货,然后返回

    2. 异步更新数据:

      5 个工人(线程)去港口取同样 Key 的货(get), 货一直存在, 即使货过期了也不会扔掉 (很长的有效期). 但港口的货物还是比较新鲜, 因为对岸有个家伙在不时更新着货物, 这个家伙在 data service 中就是 ruby 端的 after_commit 或者定时 rake

  • Data Service 设计分享 at 2017年02月20日

    #1 楼 @huacnlee 看了代码, 目前的确是用的after_commit 感谢提醒~ 稍后更新文档.

    1) 数据更新有 2 种途径:

    对于需要实时更新的数据, 使用after_commit+sidekiq , 只是稍微进行了封装, 调用方式形如: Monitor.watch(Product, SomeSidekiqWorker, :title, :price) 会给 Product 加上after_commit, after_commit中判断如果 title 或 price 有变化, 会扔到 sidekiq 中更新. 对于旧数据有一定容忍度的业务, 采用 rake 定时更新.

    2) 缓存存储前面需要有一个应用服务, 两者都可能成为瓶颈, Redis 自然高效, 应用服务使用 node 的好处之一在于异步 IO:

    let data = yield {
      '商品':  dataService.fetch(...),
      '广告':  dataService.fetch(...),
      '友情链接': dataService.fetch(...),
    }
    

    dataService.fetch返回的是 promise, 3 次 IO 的时间为: max(查商品时间, 查广告时间, 查友链时间)

    如果采用阻塞 IO 的实现, 时间将是: 查商品时间 + 查广告时间 + 查友链时间

    3) 缓存时间是各模型必须设置的, 是在 redis cluster 中的缓存时间, 我们有用 varnish 作为反向代理, 主要用于页面缓存.

    Data Service 的目的在于管理那些可缓存的数据, Data Service 提供的数据, 前端在 Node 中还可能进行加工再展示, 这也是之前引入 node 的一个原因 -- 前端希望管理 view 层.

    另外不同页面的 Data Service 数据可以在 node 端方便的组合, 比如很多页面都需要「友情链接」的数据, 在 Data Service 中只需要存一份.

    采用 redis 的另一个原因是可以比较容易的进行分布式扩展, 可选哨兵 + 主从或者 cluster.

  • 记一次 Rails 项目异常排查 at 2017年01月24日

    #16 楼 @killernova 2 个 puts 之间其实有 200 多行代码, 篇幅原因, 在文章中省略掉了中间代码. 当时是希望通过 singleton_methods 去判断类方法是否加上了, 意外发现了第二个 puts 没有输出.

  • 信渊格格,得永生