Php Laravel:将大量任务放入队列
我正在使用Laravel 5构建一个web应用程序,该应用程序创建指向web应用程序的链接,当访问该应用程序时,会显示学生进度报告的表单。这些链接由web应用程序发送到学生所在院校联系人的电子邮件中,以便收件人完成电子邮件中链接访问的进度报告 我面临的问题是在创建和发送链接时。我有一些代码可以很好地处理几百名学生,但是在现实世界中,应用程序可能会一次创建和发送3000多个链接。我编写的代码无法及时处理如此大量的数据,应用程序崩溃。奇怪的是,尽管我没有通过laravel收到任何超时错误(我需要仔细检查php日志) 尽管我非常欢迎其他建议,但我相信问题的答案是利用排队。我在发送电子邮件时已经使用了队列(请参阅代码),但我想将代码的其他部分放入队列中,但我有点不确定如何做到这一点 简要数据库模式Php Laravel:将大量任务放入队列,php,laravel,laravel-5,Php,Laravel,Laravel 5,我正在使用Laravel 5构建一个web应用程序,该应用程序创建指向web应用程序的链接,当访问该应用程序时,会显示学生进度报告的表单。这些链接由web应用程序发送到学生所在院校联系人的电子邮件中,以便收件人完成电子邮件中链接访问的进度报告 我面临的问题是在创建和发送链接时。我有一些代码可以很好地处理几百名学生,但是在现实世界中,应用程序可能会一次创建和发送3000多个链接。我编写的代码无法及时处理如此大量的数据,应用程序崩溃。奇怪的是,尽管我没有通过laravel收到任何超时错误(我需要仔细
学生
有许多链接
学生
有许多机构联系人
(根据我的申请限制为两个)
链接
有许多机构联系人
(根据我的申请限制为两个)
电子邮件
manytomy链接
我正在努力实现的目标
- 获取所有需要新链接的
学生
- 为每个
学生创建
链接
- 将
的当前学生
分配给机构联系人
的链接
(学生的机构联系人可能会更改,因此如果需要重新发送,我会将机构联系人
链接到该链接机构联系人
- 循环浏览所有新创建的
,以便通过共享链接
s将它们组合在一起-这样就不会每个链接发送一封电子邮件(因此可能会将多封带有一个链接的电子邮件发送到同一地址),而链接应通过相同的电子邮件/联系人分组在一起,并在适用的情况下一起发送机构联系人
- 循环浏览所有按电子邮件/联系人分组的
链接,并:
- 将包含
链接的
信息(url、学生姓名等)的电子邮件发送到指定的
机构联系人的电子邮件地址
- 将
电子邮件的副本写入数据库
- 将在前一步中创建的
加入其中发送的电子邮件
(因此应用程序可用于搜索在哪个电子邮件中发送的链接)链接
- 将包含
链接
,但这不允许我通过联系人/电子邮件将所有链接
分组在一起。由于任务不会定期执行,我愿意考虑在发送任何链接之前,我尝试使用set_time_limit(0);
和ini_set('memory_limit','1056M');
按原样执行任务时,内存和时间都有所增加,但没有取得太大成功
任何帮助都将不胜感激,如果你读到这里,谢谢你
代码
app\Http\Controllers\LinkController.php
public function storeAndSendMass(Request $request)
{
$this->validate($request, [
'student_id' => 'required|array',
'subject' => 'required|max:255',
'body' => 'required|max:5000',
]);
$studentIds = $request->get('student_id');
$subject = $request->get('subject');
$body = $request->get('body');
$students = $this->student
->with('institutionContacts')
->whereIn('id', $studentIds)
->where('is_active', 1)
->get();
// create link, see Link.php below for method
$newLinks = $this->link->createActiveLink($students);
// send link to student's contact(s), see LinkEmailer.php below for method
$this->linkEmailer->send($newLinks, ['subject' => $subject, 'body' => $body], 'mass');
// return
return response()->json([
'message' => 'Creating and sending links'
]);
}
public function createActiveLink($students)
{
$links = [];
foreach ($students as $student) {
$newLink = $this->create([
'token' => $student->id, // automatically hashed
'status' => 'active',
'sacb_refno' => $student->sacb_refno,
'course_title' => $student->course_title,
'university_id' => $student->university_id,
'student_id' => $student->id,
'institution_id' => $student->institution_id,
'course_id' => $student->course_id,
]);
$studentContacts = $student->institutionContacts;
if ($studentContacts) {
foreach ($studentContacts as $studentContact) {
$newLink->contacts()->create([
'type' => $studentContact->pivot->type,
'institution_contact_id' => $studentContact->pivot->institution_contact_id
]);
$newLink->save();
}
}
$links[] = $newLink->load('student');
}
return $links;
}
// Accept request, validate $students
// Send this work strait to the cue
Bus::dispatch(
new CreateActiveLinks($students));
);
// return
return response()->json([
'message' => 'Creating and sending links. This will take a while.'
]);
app\Models\Link.php
public function storeAndSendMass(Request $request)
{
$this->validate($request, [
'student_id' => 'required|array',
'subject' => 'required|max:255',
'body' => 'required|max:5000',
]);
$studentIds = $request->get('student_id');
$subject = $request->get('subject');
$body = $request->get('body');
$students = $this->student
->with('institutionContacts')
->whereIn('id', $studentIds)
->where('is_active', 1)
->get();
// create link, see Link.php below for method
$newLinks = $this->link->createActiveLink($students);
// send link to student's contact(s), see LinkEmailer.php below for method
$this->linkEmailer->send($newLinks, ['subject' => $subject, 'body' => $body], 'mass');
// return
return response()->json([
'message' => 'Creating and sending links'
]);
}
public function createActiveLink($students)
{
$links = [];
foreach ($students as $student) {
$newLink = $this->create([
'token' => $student->id, // automatically hashed
'status' => 'active',
'sacb_refno' => $student->sacb_refno,
'course_title' => $student->course_title,
'university_id' => $student->university_id,
'student_id' => $student->id,
'institution_id' => $student->institution_id,
'course_id' => $student->course_id,
]);
$studentContacts = $student->institutionContacts;
if ($studentContacts) {
foreach ($studentContacts as $studentContact) {
$newLink->contacts()->create([
'type' => $studentContact->pivot->type,
'institution_contact_id' => $studentContact->pivot->institution_contact_id
]);
$newLink->save();
}
}
$links[] = $newLink->load('student');
}
return $links;
}
// Accept request, validate $students
// Send this work strait to the cue
Bus::dispatch(
new CreateActiveLinks($students));
);
// return
return response()->json([
'message' => 'Creating and sending links. This will take a while.'
]);
app\Emails\LinkEmailer.php
public function storeAndSendMass(Request $request)
{
$this->validate($request, [
'student_id' => 'required|array',
'subject' => 'required|max:255',
'body' => 'required|max:5000',
]);
$studentIds = $request->get('student_id');
$subject = $request->get('subject');
$body = $request->get('body');
$students = $this->student
->with('institutionContacts')
->whereIn('id', $studentIds)
->where('is_active', 1)
->get();
// create link, see Link.php below for method
$newLinks = $this->link->createActiveLink($students);
// send link to student's contact(s), see LinkEmailer.php below for method
$this->linkEmailer->send($newLinks, ['subject' => $subject, 'body' => $body], 'mass');
// return
return response()->json([
'message' => 'Creating and sending links'
]);
}
public function createActiveLink($students)
{
$links = [];
foreach ($students as $student) {
$newLink = $this->create([
'token' => $student->id, // automatically hashed
'status' => 'active',
'sacb_refno' => $student->sacb_refno,
'course_title' => $student->course_title,
'university_id' => $student->university_id,
'student_id' => $student->id,
'institution_id' => $student->institution_id,
'course_id' => $student->course_id,
]);
$studentContacts = $student->institutionContacts;
if ($studentContacts) {
foreach ($studentContacts as $studentContact) {
$newLink->contacts()->create([
'type' => $studentContact->pivot->type,
'institution_contact_id' => $studentContact->pivot->institution_contact_id
]);
$newLink->save();
}
}
$links[] = $newLink->load('student');
}
return $links;
}
// Accept request, validate $students
// Send this work strait to the cue
Bus::dispatch(
new CreateActiveLinks($students));
);
// return
return response()->json([
'message' => 'Creating and sending links. This will take a while.'
]);
编辑1
我现在已经用set\u time\u limit(0);
和ini\u set('memory\u limit','1056M');
花了8分钟完成3000名学生
编辑2
我正在运行Laravel框架版本5.1.6(LTS),MySQL for DB
编辑3
感谢所有的答案,谢谢大家。我想我可以将链接
创建过程放入一个队列中,该队列在数据库中有一个类似Batch
的相关实体,当链接的Batch
创建完成后,将所有链接
从B分组atch
并发送它们
我可以使用@denis mysenko建议的方法,在
链接
的表中设置一个sent_
字段,并安排一个流程来检查尚未发送的链接
然后发送它们。然而,使用上述方法,我可以在链接的批处理
完成后发送它们已完成创建,而使用sent_at
方法,通过计划进程查找尚未发送的链接,当所有链接尚未创建时,它可能会发送一些链接。如果您使用少量数据测试了代码,并且成功而没有崩溃,那么很明显,pr问题是(正如您所说)您处理的记录数量相当多。为什么不使用chunk方法处理您的收集
根据Laravel文件:
如果需要处理数千条雄辩记录,请使用chunk命令。chunk方法将检索雄辩模型的“chunk”,并将其提供给给定的闭包进行处理。在处理大型结果集时,使用chunk方法将节省内存
无论如何,我认为在这种情况下需要使用队列。我认为,由于请求超时的高风险,应该绝对避免在HTTP请求上处理大量数据。排队过程没有执行时间的限制
为什么不在集合中同时使用queue和chunk方法呢