taurus2790
@taurus2790
Я не программер я только учусъ

Как парсить большие объёмы Yii2?

Привет, всем.

Есть код который парсит данные по api цены на предмет в разных городах

foreach (Items::find()->where(['in_market' => 1])->all() as $item){
                foreach (Cityes::find()->all() as $city){

                    /**
                     * @var $city Cityes
                     */
                    $price = $this->getApiPrice($item->unique_name , $item->enchantment_level , $city->unique_name , $item->quality);

                    if ($price){
                        $model = new Prices();
                        $model->item_id = $item->id;
                        $model->city_name = $city->unique_name;
                        $model->sell_price_min = $price[0]['sell_price_min'];
                        $model->sell_price_max = $price[0]['sell_price_max'];
                        $model->sell_price_date = '';
                        $model->buy_price_min = $price[0]['buy_price_min'];
                        $model->buy_price_max = $price[0]['buy_price_max'];
                        $model->buy_price_date = '';
                        $model->save();
                    }

                }
            }


Его проблема в том, что он упирается во время выполнения скрипта. Я его немного допилил.

foreach (Items::find()->where(['in_market' => 1])->limit(1)->offset(Yii::$app->request->get()['offset'])->all() as $item){
                foreach (Cityes::find()->all() as $city){

                    /**
                     * @var $city Cityes
                     */
                    $price = $this->getApiPrice($item->unique_name , $item->enchantment_level , $city->unique_name , $item->quality);

                    if ($price){
                        $model = new Prices();
                        $model->item_id = $item->id;
                        $model->city_name = $city->unique_name;
                        $model->sell_price_min = $price[0]['sell_price_min'];
                        $model->sell_price_max = $price[0]['sell_price_max'];
                        $model->sell_price_date = '';
                        $model->buy_price_min = $price[0]['buy_price_min'];
                        $model->buy_price_max = $price[0]['buy_price_max'];
                        $model->buy_price_date = '';
                        $model->save();
                    }

                }
            }

            if (Items::find()->where(['in_market' => 1])->count() > Yii::$app->request->get()['offset']){
                return Yii::$app->response->redirect(['site/import-data' , 'data' => 'price' , 'offset' => Yii::$app->request->get()['offset'] + 1]);
            }


Но таким образом я упираюсь в количество редиректов.

Подскажите можно ли как то запросы бить на группы, либо ещё какие то варианты.
Продлевать время выполнения скрипта, это плохой вариант, есть ли ещё варианты.
  • Вопрос задан
  • 147 просмотров
Решения вопроса 2
myks92
@myks92 Куратор тега Yii
Нашёл решение — пометь вопрос ответом!
1. Использовать консольные контроллеры вместо web и установить периодичность его работы.

2. Использовать очереди. Кидаете задачу в очередь. Консольная команда проходит по всем очередям и выполняет задачу. Схоже с первым вариантом.

3. Самый не актуальный, но действенный в вашей ситуации - использовать для запросов не объекты и Active Query, а «голые» SQL запросы которые выдают данные в массиве. AR потребляет не мало памяти на объекты. На какое то время вас это спасёт. Но лучше использовать консольные команды. Это больше подходит для подобный задач.

4. Подготавливать денормализованные данные для быстрого запроса в отдельную таблицу СУБД или использовать быстрые NOSQL базы данных. И уже из них считывать ваши данные.
Ответ написан
TheRikipm
@TheRikipm
Backend middle
Сначала определитесь нужно ли вам именно быстро выполнять парсинг, либо просто асинхронно продолжать выполнение скрипта не дожидаясь результатов парсинга.

Если первое - то самый очевидный вариант: делать голые запросы в обход ActiveRecord. Большей идей в рамках Yii приложения нет, только с структурой базы данных работать.

Но я предполагаю что вам нужно именно второе, так что создавайте отдельный консольный контроллер для парсинга и вызывайте его когда нужно. Возможно будет хорошей идеей как уже выше сказали прикрутить очереди (RabbitMQ например), а возможно нет, зависит от конкретного случая.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы