这听起来像是“potayto, potahto”的解决方案。你不是用Sidekiq的序列化,而是自己序列化。
我们来看看为什么sidekiq有这个规则:
即使它们确实序列化正确,如果您的队列备份并且引用对象同时发生变化,会发生什么? [...]
不要传递符号、命名参数、关键字参数或复杂的 Ruby 对象(如日期或时间!),因为它们无法正确地在转储/加载往返过程中存活。
我喜欢添加第三个:
序列化状态使得无法区分持久数据和空灵(内存中、记忆化、延迟加载等)数据。例如。 def sent_mails; @sent_mails ||= Mail.for(user_id: id); end 现在被序列化了:你想要那个吗?
sidekiq也提供了解决方案:
不要将状态保存到 Sidekiq,保存简单的标识符。在您的 perform 方法中真正需要它们时查找这些对象。
您的真正问题不在于在何处或如何序列化状态。因为无论您在何处以及如何执行此操作,sidekiq 都会警告不要序列化状态。
您需要解决的问题是如何将状态存储在可以正确存储的地方。或者完全避免存储状态:不在 redis/sidekiq 中,也不在给您带来问题的存储中。
延迟
您的存储速度慢吗?这不是验证、序列化、存储的一些副作用吗?
您能否通过两步改进这一点:插入状态并稍后异步更新/丰富/验证它?如果您使用的是 Rails,它在这里对您没有帮助,甚至可能对您不利,但常见的模型是将对象存储在特殊的“队列”表或事件队列中;例如kafka 就是因为这个而出名的。
例如当存储发生在慢速网络到慢速 API 上,这可能无法解决,但是当存储发生在本地数据库中时,您可以使用数十年的解决方案来提高写入性能。无论是在您的数据库内部,还是使用一些专门的状态存储队列(sidekiq 并不是这样一个专门的存储队列),具体取决于用于存储的技术。例如。 Linux 将允许您通过内存进行存储,从而非常快速地写入磁盘,但消除了它确实写入磁盘的保证。
例如在 bookkeeping api 中,我们会将经过验证的对象存储在 PostgreSQL 中,然后让异步作业稍后为其添加昂贵的属性(例如,必须从遗留 API 或通过复杂计算检索的状态)。
例如在写入繁重的 GIS 系统中,我们会将对象存储到“to_process_places”表中,该表由处理 Places 的工具监控。这完全取决于您的域和要求。
不使用状态。
一个常见的解决方案是不制作对象,而是使用客户的实际有效负载。只需发送 HTTP 有效负载(在 rails 中,params)并保留它。可能会合并到一个标头(如Request Date)或过滤掉一些数据(标头令牌或cookie)。
如果您的控制器可以使用这些数据进行操作,那么延迟作业也可以。与其在控制器中构建对象,不如将其留给延迟的工作。这甚至可以产生非常简洁和精简的控制器:他们所做的只是(然后进行一些身份验证和授权)调用适当的工作并将其传递给经过消毒的params。
显然,这需要权衡取舍,例如无法同步验证,而是通过电子邮件、推送通知或延迟响应提供此类信息,具体取决于您的要求(例如,大型 CSV 导入可以通过电子邮件发送任何验证问题,但如果登录无效,登录请求可能需要立即得到响应)。
这还需要一些思考:您可能不想将 Base64 编码的 CSV 发送到 sidekiq,而是将文件写入(临时)存储并传递文件名/url。这听起来很明显,因为它是:文件上传本质上是前面提到的“临时状态存储”的实现:您不会将整个 PDF/high-res-header-image/CSV 传递给 sidekiq,而是将其存储某个地方,以便 sidekiq 稍后可以将其拾取以进行处理。如果将其他属性传递给 sidekiq 有问题,为什么其他属性不采用相同的模式?