• 浅谈尾递归 at 2018年06月05日

    请教一个问题, 既然尾递归可以 一定程度 上解决递归导致的内存泄漏问题, 为什么这个参数没有默认开启呢? 尾递归的开销是什么呢?

  • 数据结构之 HashMap at 2018年06月04日

    明白了, 谢谢楼主分享.

    一致性哈希算法很赞, 学习了新的知识, 非常感谢!

  • 数据结构之 HashMap at 2018年06月03日

    在Ruby中some_hash[some_key] = some_value这种哈希操作虽然有多个步骤(取哈希值=>做映射=>再赋值),但整个操作在底层是由C语言实现的,而GIL会保证这段C代码执行的原子性。这里面并没有真正的并发。

    楼主您好,请问这块有源码分析吗?您说的GIL,只是用不到多核而已,并发并不是指用到了多核才是并发。

    ruby的并发同样涉及到上下文切换,所以并发中的竞争问题(race condition)同样存在的。

    GIL 不意味着 线程安全

    建议可以看下 working with ruby threads 第五章的内容

  • 感谢分享, 我们会参考一下.

    我们在设计的时候会考虑几点:

    1. Sidekiq Worker不需要去管理job的状态, 也就是并发的任务本身对job状态是无感知的, 也没有状态的概念
    2. 工具是通用的, 不仅仅在导出场景, 任何批量多任务处理, 都可以使用.
    

    在新版本的Rails中, 有ActiveJob, 里面的callback可以做到这些功能, 但是我们并未在上面做扩展, 因为

    • 我们使用的Rails版本太低了(<5)
    • 我们希望这个更底层一些, 所以选择使用了Sidekiq的Middleware

    您说的这个也是一个解决方案, 但是个人觉得太偏向于某一种场景了, 如果换一种场景的话, 可能又得再实现一遍; 建议可以抽离出一个插件, 做成一个执行引擎, 相信从可读性和实用性而言会更好一些.

  • 嗯, 是的.

    这个文档解决的问题, 是希望耗时的任务 高性能并且状态可跟踪, 跟高可用关系不大, 所以没有考虑自己实现 RPOPLPUSH

  • 是的,您说得对。

    但是这一块是专门为sidekiq定制的,数据结构设计还包括 sidekiq_class 等,这些其实也可以用在 kafka 中。

  • Pro版本使用到了, 用来做高可用, 保证数据不丢的.

    Sidekiq Pro offers an alternative fetch strategy, super_fetch, for job processing using Redis' RPOPLPUSH command which keeps jobs in Redis.

    wiki: 这里

  • 是的, 我们是这样做的.

    job_id 在执行

    job_id = AWorker.perform_async(params)
    

    的时候, 已经生成了. 但是需要利用 Sidekiq 的 Middleware, 去更新这个 job 的执行情况.

    对于前端, 只需要轮询状态就可以了.

  • each_slice 这块我理解得不对, 我们在实际业务的做法跟您所说的是一致的.

  • 因为我们需要一个job的状态, 以及业务失败时, 这个job的错误信息.

    如下面的信息:

    { status: failed, message: "发生错误: 您的资质不满足报名条件" }
    

    我们是通过 raise NormalException 来中断任务, 并将信息存储在异常中的.

    所以说, 我们需要通过job_id, 来找到这个job, 访问这个job的状态(status), 并拿到业务逻辑的错误信息(message)

    这儿其实跟 Erlang 的 Let It Crash 有点儿像, 但是使用上会稍微简单一点.

    另外, 所有跟job相关的参数, 状态信息也都会入到数据库. 因为Sidekiq 的 任务是使用 Redis#brpop 命令的, 消费了之后, 消息就没有了, 就找不到这个 job 了.

  • 我的理解如下:

    1. 消息的生成和消费, 是在不同的经常里面的, 如果需要用 job_id, 则还是需要将 job_id 存储起来
    2. Sidekiq的 API 通过 job_id 寻找这个job 是非常低效的, 这在前文已经说过
    3. each_slice 本身还是在一个进程里面, 无法使用 多核 来进行操作. 虽然在一个进程内的Sidekiq多线程处理, 是无法使用多核的(这里任务是耗CPU的), 另外, 如果是用each_slice 处理, 真的是很慢, 要是中途不小心报错, 整个任务就失败了.
    4. job_id生成是有成本的.
    def push_bulk(items)
       ...
    
      raw_push(payloads) if !payloads.empty?
    
      # 如果有十万个任务, 这里需要循环十万次得到jid
      payloads.collect { |payload| payload['jid'] }
    end
    

    我们部署Sidekiq多个进程, 就有一定概率使用到多核.

    50万数据我们是分成多个sidekiq job处理, 并且有一个task_id跟这些job关联. 这样可以用到了多核, 也能观察到各个任务进行的进度, 并且可以重试.

    当然单独针对这个业务逻辑, 也可以设计单独的表结构来存储任务.

  • 嗯, 可以贴一下链接或者设计思路吗? 我们也都学习一下.

    这些其实都是造轮子, 我们的痛点是一些资源申请不下来.

  • Sidekiq 任务调度流程分析 at 2018年05月28日

    非常赞!

    楼主可以对比一下 Sidekiq 和 Kafka 或者其他的队列吗? 如Sidekiq 是怎么做到 客户端 和 服务端的高可用的呢?

  • 五问 Sidekiq at 2018年05月28日

    楼主很棒, 感谢分享!

    希望楼主也能分析一下 sidekiq 中 线程的监控, 以及 sidekiq 由原来的 Celluloid 换成 Raw Threads 的方式后, 线程之间的通信情况.

    还有一些队列的基本问题: 如 为什么 Sidekiq 是 At Least Once 等, 我们能做到 Exactly Once 吗

    等等之类的问题.

  • 可以拿到 job 情况. 但是在sidekiq的存储结构中, 并没有将job_id当做key来存储

    通过sidekiq 的api 获取 job 是非常低效的, 在 API 文档 中提到

    Find a job by JID (WARNING: this is very inefficient if your queue is big!)

    另外, 我们需要获取到 job 的整个状态, 包括 进入队列, 处理中, 成功, 业务校验的失败, 系统的失败 等, 这是 sidekiq 的 api 不具备的.

    sidekiq-pro 提供了类似 batches 功能, 但是我们因为一些原因没有使用.