Решение для вызова функции, делающей много вещей в ней Cron? - PullRequest
1 голос
/ 28 июня 2019
function cronProcess() {
    # > 100,000 users 
    $users = $this->UserModel->getUsers();

    foreach ($users as $user) {
        # Do lots of database Insert/Update/Delete, HTTP request stuff
    }
}

Проблема возникает, когда число пользователей достигает ~ 100 000.

Я вызвал функцию CURL через CronTab.

Так что же является лучшим решением для этого?

1 Ответ

1 голос
/ 28 июня 2019

Я делаю много объемных задач в CakePHP, некоторые обрабатывают миллионы записей.Это, безусловно, возможно сделать, ключ, как другие предложили, это небольшие партии в цикле.

Если это то, что вы вызываете из Cron, возможно, проще использовать Shell (Command новее (v3.6 +), чем cURL.

Вот как я обычно разбиваю на большие партии, в том числе некоторые полезные дополнительные функции, такие как индикатор выполнения, отключение гидратации, чтобы немного ускорить процесс, и демонстрацию того, какмного пользователей / секунду сценарий смог обработать:

<?php

namespace App\Command;

use Cake\Console\Arguments;
use Cake\Console\Command;
use Cake\Console\ConsoleIo;

class UsersCommand extends Command
{
    public function execute(Arguments $args, ConsoleIo $io)
    {
        // I'd guess a Finder would be a more Cake-y way of getting users than a custom "getUsers" function:
        // See https://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html#custom-finder-methods
        $usersQuery = $this->UserModel->find('users');

        // Get a total so we know how many we're gonna have to process (optional)
        $total = $usersQuery->count();
        if ($total === 0) {
            $this->abort("No users found, stopping..");
        }

        // Hydration takes extra processing time & memory, which can add up in bulk. Optionally if able, skip it & work with $user as an array not an object:
        $usersQuery->enableHydration(false);

        $this->info("Grabbing $total users for processing");

        // Optionally show the progress so we can visually see how far we are in the process
        $progress = $io->helper('Progress')->init([
            'total' => 10
        ]);

        // Tune this page value to a size that solves your problem:
        $limit = 1000;
        $offset = 0;

        // Simply drawing the progress bar every loop can slow things down, optionally draw it only every n-loops,
        // this sets it to 1/5th the page size:
        $progressInterval = $limit / 5;

        // Optionally track the rate so we can evaluate the speed of the process, helpful tuning limit and evaluating enableHydration effects
        $startTime = microtime(true);
        do {
            $users = $usersQuery->offset($offset)->toArray();
            $count = count($users);
            $index = 0;

            foreach ($users as $user) {
                $progress->increment(1);

                // Only draw occasionally, for speed
                if ($index % $progressInterval === 0) {
                    $progress->draw();
                }

                ### WORK TIME
                # Do your lots of database Insert/Update/Delete, HTTP request stuff etc. here
                ###
            }

            $progress->draw();

            $offset += $limit; // Increment your offset to the next page
        } while ($count > 0);

        $totalTime = microtime(true) - $startTime;
        $this->out("\nProcessed an average " . ($total / $totalTime) . " Users/sec\n");
    }
}

Оформить заказ в этих разделах в Документах CakePHP:

Надеюсь, это поможет!

...