gpt4 book ai didi

php - Laravel:将大量任务放入队列中

转载 作者:行者123 更新时间:2023-12-01 22:08:31 25 4
gpt4 key购买 nike

我正在使用 Laravel 5 构建一个 web 应用程序,该应用程序创建指向 web 应用程序的链接,当访问该链接时,会显示学生进度报告的表单。这些链接由 Web 应用程序发送到学生就读机构的联系人的电子邮件,以便收件人完成通过电子邮件中的链接访问的进度报告。

我面临的问题是创建和发送链接时。我有一些代码适用于几百名学生,但在现实世界中使用该应用程序可能一次创建和发送 3000 多个链接。我写的代码根本无法及时处理这么大的数字,应用程序崩溃了。奇怪的是,虽然我没有通过 laravel 收到任何超时错误(我需要仔细检查 php 日志)。

尽管我非常欢迎其他建议,但我相信问题的答案是利用队列。我在发送电子邮件时已经使用了队列(请参阅代码),但我想将代码的其他部分处理到队列中,但我有点不确定如何执行此操作!

简要数据库架构
Student hasMany LinkStudent hasMany InstitutionContact (根据我的申请限制为两个)
Link hasMany InstitutionContact (根据我的申请限制为两个)
Email多对多 Link
我正在努力完成的工作

  • 获取所有 Student需要一个新的 Link
  • 创建一个 Link每个Student
  • 分配 Student的当前 InstitutionContact s 到 LinkInstitutionContact (A Student 的机构联系方式可能会发生变化,因此如果需要重新发送,我会将 InstitutionContact 链接到该链接。
  • 循环遍历所有新创建的 Links为了通过共享将它们组合在一起 InstitutionContact s - 这是不是每个链接都发送一封电子邮件(因此可能会发送许多带有一个链接到同一地址的电子邮件),而是应该将链接按相同的电子邮件/联系人分组在一起,并在适用的情况下一起发送
  • 遍历所有 Link按电子邮件/联系人分组,以及:
  • 发送包含 Link 的电子邮件的信息(网址、学生姓名等)到指定的 InstitutionContact的电子邮件地址
  • 写一份 Email到数据库
  • 加入Email在前一步中创建的 Link (s) 在其中发送的(因此该应用程序可用于搜索在哪个电子邮件中发送的链接)

  • 所以我面临的主要挑战是使用大型数据集执行上述任务。我已经考虑过创建和发送 Link通过队列一个接一个,但是这不允许我将所有 Link 分组通过联系方式/电子邮件联系在一起。由于该任务不会定期执行,我愿意考虑执行该任务,因为它会增加进程的内存和时间,但是我在尝试使用 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'
    ]);
    }

    app\Models\Link.php
    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;
    }

    app\Emails\LinkEmailer.php
    namespace App\Emails;

    use App\Emails\EmailComposer;

    class LinkEmailer
    {
    protected $emailComposer;

    public function __construct(EmailComposer $emailComposer)
    {
    $this->emailComposer = $emailComposer;
    }

    public function send($links, $emailDetails, $emailType)
    {
    $contactsAndLinks = $this->arrangeContactsToLinks($links);

    foreach ($contactsAndLinks as $linksAndContact) {

    $emailData = array_merge($linksAndContact, $emailDetails);

    // send/queue email
    \Mail::queue('emails/queued/reports', $emailData, function ($message) use ($emailData) {
    $message
    ->to($emailData['email'], $emailData['formal_name'])
    ->subject($emailData['subject']);
    });

    // compose email message, returns text of the email
    $emailMessage = $this->emailComposer->composeMessage($emailData);

    // // create Email
    $email = \App\Models\Email::create([
    'to' => $emailData['email'],
    'from' => 'report@domain.org',
    'subject' => $emailData['subject'],
    'body' => $emailMessage,
    'type' => $emailType,
    'user' => $_SERVER['REMOTE_USER']
    ]);

    foreach ($linksAndContact['links'] as $link) {
    $link->emails()->attach($email->id);
    }
    }
    }

    // group links by contact
    public function arrangeContactsToLinks($links)
    {
    $contactsForLinks = [];
    $assigned = false;
    $match = false;

    foreach ($links as $link) { // 1, n

    if ($link->contacts) {

    foreach ($link->contacts as $contact) { // 1, 2

    if ($contactsForLinks) {

    $assigned = false;

    foreach ($contactsForLinks as $key => $contactLink) { // n
    // assign links to existing email in array
    if ($contactLink['email'] === $contact->institutionContact->email) {
    $match = false;

    // check link hasn't already been included
    foreach ($contactsForLinks[$key]['links'] as $assignedLink) {
    if ($assignedLink === $link) {
    $match = true;
    }
    }

    // if there was no match add to list of links
    if (!$match) {
    $contactsForLinks[$key]['links'][] = $link->load('student');
    $assigned = true;
    break;
    }
    }
    }

    if (!$assigned) {
    $contactsForLinks[] = [
    'email' => $contact->institutionContact->email,
    'formal_name' => $contact->institutionContact->formal_name,
    'requires_id' => $contact->institutionContact->institution->requires_id,
    'requires_course_title' => $contact->institutionContact->institution->requires_course_title,
    'links' => [$link->load('student')],
    ];
    }
    } else {
    $contactsForLinks[] = [
    'email' => $contact->institutionContact->email,
    'formal_name' => $contact->institutionContact->formal_name,
    'requires_id' => $contact->institutionContact->institution->requires_id,
    'requires_course_title' => $contact->institutionContact->institution->requires_course_title,
    'links' => [$link->load('student')],
    ];
    }
    }
    }
    }

    return $contactsForLinks;
    }
    }

    编辑 1

    我现在可以使用 set_time_limit(0);ini_set('memory_limit','1056M');做 3000 名学生用了 8 分钟。

    编辑 2

    我正在运行 Laravel Framework 版本 5.1.6 (LTS),用于 DB 的 MySQL。

    编辑 3

    感谢到目前为止所有的答案,谢谢大家。我想我可以工作 link创建过程到一个队列中,该队列将在数据库中有一个相关实体,名为 Batch当那个 Batch链接已完成创建,然后将所有 Link 分组来自那个 Batch并发送它们。

    我可以使用@denis-mysenko 建议的方法,方法是使用 sent_at Link 中的字段s 表并有一个预定的进程来检查 Link s 尚未发送,然后发送它们。但是,使用上述方法我可以发送 BatchLink s 当它们都完成创建时,而使用 sent_at使用预定进程寻找 Link 的方法尚未发送的 s 可能会在尚未创建所有链接时发送一些链接。

    最佳答案

    如果您使用少量数据测试了您的代码并且它成功而没有崩溃,那么很明显问题是(如您所说)您正在处理的记录数量非常多。为什么不使用 处理您的收藏?大块 方法?

    根据 Laravel 文档:

    If you need to process thousands of Eloquent records, use the chunk command. The chunk method will retrieve a "chunk" of Eloquent models, feeding them to a given Closure for processing. Using the chunk method will conserve memory when working with large result sets



    无论如何,我认为在这种情况下需要使用队列。我认为,由于请求超时的高风险,应该绝对避免在 HTTP 请求上处理大量数据。排队的进程没有执行时间的限制。

    为什么不在集合中一起使用队列和块方法?这将使您能够:
  • 避免超时错误
  • 在进程执行期间使用合理数量的内存(这取决于传递给块闭包的数据数量)
  • 让您优先控制这个排队的进程和(最终)其他排队的进程

  • Laravel 文档涵盖了您需要的所有内容: Eloquent - Retrieving multiple models (请参阅“分块结果”一章以更深入地了解处理大型数据集时如何节省内存)和 Queues用于创建作业并分离不应在网络服务器下运行的某些软件部分,从而避免超时风险

    关于php - Laravel:将大量任务放入队列中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35648386/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com