• 基于 rails guides,自己打造了一下 https://github.com/dengqinghua/roses

    评论用的 gitalk

    最终生成静态页面,服务部署在 github pages 中,再在域名上面配置 cname

    博客经过打造后,支持 流程图,树结构,五线谱和吉他和弦 😀

    https://blog.dengqinghua.net/example_markdown.html

  • 浅谈尾递归 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 功能,但是我们因为一些原因没有使用。