要处理业务系统一张表里面的数据,需要把每一个记录都遍历一次才能任务 job,大概有 10w 数据,通过前台发送一个请求后台使用 find_in_batches 来处理,因为 job 任务处理有些耗时,处理这些数据的时候服务器不能接受其他请求,想把任务转为后台处理,看到网上有说线程,也有说用进程的,试了试 sidekiq 但是效果好些没有什么改进,就采用下面的方法:但是任务处理的很慢
User.find_in_batches(:batch_size => 2000) do |users|
sleep(50)
users.each { |user|
job(usreport)
}
end
请教各位改怎么改进才能让任务转为后台处理或者加快处理?thx! 后来觉得没有必要采用 sidekiq,现在想只用 rack 来完成。
追问:(附 job 主要完成的任务) (1)判断数据库每个记录对应的两个文件 pdf 和 png 是否存在 (2)如果不存在要调用 Http 请求去生成对应的文件,并且必须有 pdf 才能生成 png
为了完成上面的任务,(经过@nickcen @mystery @martin @liwei78 的指导)首先采用 rack 任务,把文件不存在的查找一遍,并且标记文件文件是否存在(大概 500s 完成)。 之后再去遍历这个记录表,根据文件查找结果记录去请求 url 生成 pdf 和 png;
目前遇到的问题: 每次发送 Http 请求需要等待 500 - 700ms 才能执行完请求生成文件,随着程序执行时间越来越长(1000ms 以上),请求会花费更长时间。但是记录里面还有 4w 个文件没有生成的,如果一次性执行这么多请求会出现两个问题:(1)4G 内存可能爆掉,请求终端;(2)总的执行时间太长,大概 8-10h 完成(服务器用的是 Unicorn,多进程服务器)目前采用同步发送请求的方式断断续续的生成了 2w 多 pdf 文件(内存溢出,程序经常中断,花费好几天)
理想的程序执行结果: 采用采用多线程异步异步发送 Http 请求缩短程序总的发送请求时间,发送完成后接收方继续执行即可(缩短总的文件生成时间,数分钟即可执行完成); 如果请求失败再次执行请求,直到生成为止;
于是找到了 EventMachine 和 EM-HTTP-Request 这样的异步请求处理工具: 采用如下处理过程: 先将 pdf 没有生成的记录获取出来,并构造出请求 url 放在 urls 数组中, 然后用 EventMachine 发送请求,每次取出 25 个,发送完成后执行下一组 url 请求
代码如下:
desc 'use slice to perform http request '
task gpdf: :environment do
urls = []
a = Time.now.to_i
p 'generate use event machine' << a.to_s
report_ids = UsLog.find_by_sql("select distinct id ,xml from us_logs where pdf ='false' ")
#store all url in urls
report_ids.each_with_index do |rep, index|
id = rep.id
xml = rep.xml
gpdf_url =URI("#{HOSTURI}/create_pdf?uuid=#{xml}")
urls << gpdf_url
#record time
if (index % 1000 == 0)
b = Time.now.to_i
c = b - a
puts index
puts c
end
end
EventMachine.run {
multi = EventMachine::MultiRequest.new
urls.each_slice(25).with_index do |bulk_urls ,arr_index|
EM.next_tick do
#bulk_urls.each {|url| multi.add(url,EventMachine::HttpRequest.new(url).get(:timeout => 5))}
bulk_urls.each_with_index {
|url,index|
# abc is the unique key of multi http request
abc = arr_index* 25 + index
# add multiple requests to the multi-handler
multi.add(abc,EventMachine::HttpRequest.new(url).get(:timeout => 5))
}
end
end
multi.callback {
p multi.responses[:succeeded]
p multi.responses[:failed]
EventMachine.stop
}
}
end
程序执行后出现堆栈调用异常,如下:
f42cb438000-7f42cb45a000 r-xp 00000000 08:01 269491 /lib/x86_64-linux-gnu/ld-2.15.so
7f42cb53b000-7f42cb641000 rw-p 00000000 00:00 0
7f42cb64c000-7f42cb64d000 rw-p 00000000 00:00 0
7f42cb64d000-7f42cb654000 r--s 00000000 08:01 3809707 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
7f42cb654000-7f42cb655000 ---p 00000000 00:00 0
7f42cb655000-7f42cb65a000 rw-p 00000000 00:00 0 [stack:26307]
7f42cb65a000-7f42cb65b000 r--p 00022000 08:01 269491 /lib/x86_64-linux-gnu/ld-2.15.so
7f42cb65b000-7f42cb65d000 rw-p 00023000 08:01 269491 /lib/x86_64-linux-gnu/ld-2.15.so
7ffff0940000-7ffff0962000 rw-p 00000000 00:00 0
7ffff09b2000-7ffff09b3000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
不知为何会出现上面的错误,并且 IDEshell 中和系统的终端执行结果也不一样; Ubuntu 自带系统终端中没有错误输出,最终 callback 中打印的信息为 null IDE(rubymine)的 shell 输出如上,提示段转储错误
最终请求没有发送成功,接收方的 server 没有任何日志输出,请教做过类似多请求并发处理的大大们给些指导建议,再次感谢! @hooopo @pzgz @skandhas @luikore @zw963