• 在 Rails 中集成第三方登录 at 2020年08月18日

    哈哈哈 史诗级问题

  • 高并发的电商 at 2020年08月14日

    其实就是个秒拍系统

    秒拍系统的核心理念就是

    能不动数据库就不动数据库

    那么如何在不动数据库的情况下,解决用户高并发的问题呢? 高并发下,要守住两个底线:

    • 库存不能为负
    • 下单成功,且付款成功的,一定要购买成功

    将秒拍抢购进行进一步的分析:

    1. 用户首先获得购买资格(库存足够 + 用户下单)
    2. 用户付款,安排发货

    其实并发压力就在 1 上,2 无所谓,毕竟付款成功后回调大多数系统都有 retry 机制,问题不大。 至于解决并发压力最好的方式就是让这些变成串行通过,

    最简单方式自然是 利用 redis 的原子性来进行操作

    方案 1:硬来

    redis 记录库存数量,待秒杀开始时,进来的请求进行下单操作,DECR 后的读书如果>=0,则建立订单,注意,这里一定要利用 decr 的原子性,在此判断之前,不要有任何数据库操作,否则数据库会爆

    if redis.decr(SKU_STOCK_GATE) >= 0 # 在此之前不要有数据库读写操作
      create_order(current_user, sku) #这里最好是个异步, 否则也可能造成数据库抖动
    end
    

    这种最基础的方式有一个麻烦之处,如果有一个买家想一次购买 N 个商品,就比较头疼了,因为我们很可能遇到这样的情况,库存只有 1, 但是此时用户要买 2 个或者更多

    如果需求是:如果是多个购买,则允许最小购买数,那就无所谓,系统可以将其转化为单位为 1 的订单,但是大部分是:如果数量不够,就建立订单失败。

    因为包含了多个购买,所以就不能用 decr 而是要用 decrby 2 了 此时 SKU_STOCK_GATE 为 1,redis 操作完毕后,GATE 值为 -1 不满足 GATE 条件,不能购买了。 库存不变

    但是此时就有个麻烦的地方:
    STOCK_GATE 已经小于 0 了,但是真实的库存是 1

    此时,如果再有人购买一个库存,也会提示失败了

    也就是说,这种方案的弊病就是:

    有可能出现 库存还有,但是无法进行购买了

    其实我们可以发现,大于 1 的购买,STOCK_GATE 有几种可能

    1. 在 STOCK_GATE 操作之前,STOCK_GATE 就已经为负数。这种分俩情况,第一种情况是,前置操作是 单一购买,以及前置操作是大于 1 的购买,用递归的思想,后者一定开始于前置是单一购买或 0 购买,而没人买的话,不存在库存不足的问题,所以 这种问题简化成:前置是单一购买,且操作前 STOCK_GATE 已经为负,这种情况很好理解,就是没库存了,购买失败

    2. STOCK_GATE 在操作前后,都为正,所以没啥说的,正常购买

    3. STOCK_GATE 在操作钱为 正,操作后为负,这种是库存不够,购买失败,理应回退库存

    但是这里就有个问题,因为要拿到 decr 之前的数据,所以要先 get 一下数据,超过 1 次操作 redis 的话, 整个操作就不保证原子性了,当并发高的时候,就会出大问题,例如:

    账户 A 和账户 B 请求同时 一起进来

    有可能出现这样的情况,假设 key 的值是 0,当 k <0 的时候,跳出判断,否则执行减 1, A 和 B 一起发生 GET 请求,A 先返回 GET 请求,ruby 执行下一句 让 redis 执行 decr - 1,但是正当 ruby 要通知 redis,但 redis 还没执行的时候 B 的 ruby 的 GET 命令先到达了 redis , 此时 redis 也返回 0 于是根据 ruby 的逻辑,B 也会执行 -1

    解决这类问题,自然是锁

    改进后的方案 2:lock

    使锁来保证判断的时候只有单线程,不会出现数据脏读

    begin
      block_result = lock_manager.lock!("SKU_LOCK_KEY", 2000) do
        pre_gate_value = redis.get('SKU_STOCK_GATE')
        return false if pre_gate_value <= 0  # 库存没有了
    
        after_gate_value = redis.decrby(SKU_STOCK_GATE, num
        if after_gate_value >=0 
          return true  #可以购买
        else
          redis.incrby(SKU_STOCK_GATE, num) # 将扣除的加回来
          return false  #
        end  
      end
    rescue Redlock::LockError
      # 锁超时, 理论上来说这里只有redis 和最简单的判断操作, 很难超时, 最好接入人工检查
    end
    

    通过锁可以解决问题

    但是这种方案有没有啥其他的问题呢?

    比较麻烦,自己维护锁,万一锁超时,检查起来也很烦躁

    于是到了我觉得最简单的方案 3,排队。

    方案 3:排队

    即当发生抢购的时候,利用 redis,在一定时间内,给每个参加购买的人一个号码即可,之后入队。

    因为 push 本身靠的是 redis 来保持原子性,所以无所谓。

    之后拿一个进程单独处理这个队列,不符合的就直接 next,因为是独立处理,所以压根不存在并发问题,也就没有锁的概念。

    但是这种的缺点也很明显,即订单生成时间与用户无关,只与 进程处理时间有关,但是其实这个时候秒杀用户基本也都在线,就算建立的订单最后他没支付,重新将库存加上即可。

  • 国际团队用的真不少

    国内介于网络原因 估计不太适用普通公司

    我是被 command + j 震惊的 无敌好用

  • Rails 单元测试相关 at 2020年07月29日

    咱俩应该是一个症状 我这也是 406 的 bug

  • "至少开发过几百行的 worker"

    将几百行的逻辑都塞到一个 worker 里,不太好测试,出错了 retry 也比较难定位

    建议抽象出来,一一测试

  • 😂 😂 😂 知道真相的我眼泪掉下来 谁说 rails 耗机器的

  • 先弄,弄出来了市场上跑一圈,效果好就好 效果不好就当练手

  • 恰好自己拿 esp 8266 实现了个 mqtt

    楼主可以发一个 跟 ruby 相关的 实现 mqtt client 的内容,纯介绍的话 容易被当做广告贴处理

  • ps 是使用的 swarm 的

    类似这样的配置么

    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 5s
    

    另外还有个地方还想请教下

    就是 docker 下 静态文件夹 public 的管理。

    因为配置等原因,我们还是期望 nginx 不在容器中运行 puma 提供的服务,本身我们可以依赖 upstream, 但是静态文件这块就有点尴尬,

    之前我们的做法是 有个 container 专门接受从外层主机挂载的目录 /tmp/public, 之后这个镜像 run 的指令就是 cp -r public /tmp/public

    这样外层的机器就获得了镜像里的 public 文件夹,但是我觉得这种方式属于取巧的邪道

    能否参考下您的解决方案 或者 有人能好心分享下么?

  • 哈哈 我正准备问 嘛时候可以把 backbone 换一下口味

  • anycable 已经更新到了 1.0 版本 看着性能还是不错的 10000 个 client 95% 也就 1 秒

    clients:  1000    95per-rtt: 196ms    min-rtt:   2ms    median-rtt:  33ms    max-rtt: 252ms
    clients:  2000    95per-rtt: 296ms    min-rtt:   2ms    median-rtt:  48ms    max-rtt: 596ms
    clients:  3000    95per-rtt: 462ms    min-rtt:   2ms    median-rtt: 101ms    max-rtt: 694ms
    clients:  4000    95per-rtt: 670ms    min-rtt:   3ms    median-rtt:  72ms    max-rtt: 1382ms
    clients:  5000    95per-rtt: 712ms    min-rtt:   3ms    median-rtt: 101ms    max-rtt: 1526ms
    clients:  6000    95per-rtt: 739ms    min-rtt:   3ms    median-rtt:  99ms    max-rtt: 2901ms
    clients:  7000    95per-rtt: 745ms    min-rtt:   2ms    median-rtt: 231ms    max-rtt: 2800ms
    clients:  8000    95per-rtt: 950ms    min-rtt:   2ms    median-rtt: 173ms    max-rtt: 3109ms
    clients:  9000    95per-rtt: 1133ms   min-rtt:   3ms    median-rtt: 185ms    max-rtt: 4084ms
    clients: 10000    95per-rtt: 1033ms   min-rtt:   3ms    median-rtt: 248ms    max-rtt: 2671ms
    

    https://github.com/anycable/anycable/blob/master/benchmarks/2020-06-30.md

  • 目前使用 docker-compose 的时候出现一个问题

    在升级 image 完毕后, 重新运行 docker-compose 的时候 这个时候新的 docker image 有启动时间 这段时间会 502

    大概几秒,docker 里的 puma 启动完毕后就没这个现象了

  • Twitter 上被 Hey 刷屏了 at 2020年06月17日

    感觉自己用邮件还没有用到那么强....

  • 最近大家开始折腾 ActionCable 了呀

  • 是重启 docker 后恢复原始吧

    docker 的特性

    如果不想 建议将 homeland 的页面挂载出来

  • 弹幕类的应该 go 会好一些

    视频主要还是靠 cdn 抗

  • 这个有点屌

    必须资瓷

  • 我只是好奇

    类似 ruby-china 的项目

    用前端渲染和后端渲染,大家能感受到运行时间差异么?

    我自己用 react 的理由主要有两点

    1. 直接操作 dom 变来变去,还要考虑状态很麻烦,react 的 data 直接确定 ui 在处理复杂应用时不用考虑过多细节与状态。

    2. react 有丰富的 UI 组件可以用

    至于页面渲染时间,10-50ms 我自己是感觉不到区别

  • 这个 stimulusreflex 就是仿照这 liveview 来的

  • 官方的解释就是 scale 问题就靠 anycable go 来撑了

    因为扩展性做的改变不是很大

    算能接受吧

    另外一个网站同时在线 1w 配 4 台机器也算能解决一部分问题吧

  • 刚好也在看另外一个项目 chatwoot, 也是基于 websocket

    包括我在内,大家都认为 "Rails 原生的 websocket 比较弱 "

    但是确实我也不太了解这块应该怎样去做 benchmark , 也不太知道这个极限值 大概是多少

    不知道是否有什么有效的方式,能够知道 这个 rails 的 websocket 的极限是多少呢?

  • stimulusrelex 基于 stimulus 和 websocket

    stimulus 本身跟 react 之类的并无冲突,直接可以用

    我用 react 其实就是懒得搞 ui

  • gems.ruby-china.com 挂了 at 2020年04月13日

    着急的时候可以试试

    https://mirrors.tuna.tsinghua.edu.cn/help/rubygems/

    这个的原理 跟 gems.ruby-china.com 不一样,

    清华大学的这个好像是主动拉取

  • 直接看代码这种方式 简直就是暴力美学

  • 350 行实现一个简单酸酸 at 2020年03月17日

    之前我在 1024 看了个帖子 某监控屏幕上 居然出现了 443 端口 wss 方式都被监控出来了

    我还在想这玩意是怎么检测出来的

    只不过后面我一看备注是 移动端

    我在想估计是华为手机系统级别出来的,

    而不是通过协议拆出来的吧,要不 ssl 就不可靠了还怎么玩

  • 确实正如您说的,这套东西又烧钱又耗人力

    主要是之前对技术难度预估不足,盲目自信,最后特别后悔

    后来我自己的想法是,微服务这种搞法,估计运维 > 5 才能平衡吧

    这里也可以麻烦大家统计下 大家上微服务,运维大概多少人,研发大概多少人

    docker 化我倒是有一些使用 我们有一些后台任务挺烦挺长的,而且都是瞬时来,平时常备的两台 sidekiq 服务器不够,我是 将 sidekiq 进程做成了一个 docker 服务,同时检测 sidekiq 队列堆积情况 >1000 后 就在阿里开 docker 容器跑 效果还行

  • 在 Rails 里如何使用 vue? at 2020年03月16日

    只要不用 vue router 管理就行,该咋用 rails 还是咋用 就是一个页面一个 vue 的组件

  • 之前我吹捧过一阵子微服务,

    拆了几十个小的服务,变成了一个伪微服务

    之后

    各种监控,各种部署,还有各种神烦的日志跟踪问题 数据库的一致性 还有该死的同步异步问题超时问题

    可能是技术不够,让我深切的体会到了

    啥叫搞服务发现,搞网关,搞流量控制,搞服务降级,搞异步部署

    最后

    我觉得我就是个弱智

  • 开源中国的代码托管估计是

    可能参考了 gitlab 比较多吧

  • 我比较好奇 是 ruby 开发的么