147 lines
5.7 KiB
PHP
147 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace App\Commands;
|
|
|
|
use CodeIgniter\CLI\BaseCommand;
|
|
use CodeIgniter\CLI\CLI;
|
|
|
|
use App\Models\Entities\NaverWorkerLogModel; // 새로 만든 테이블용 모델
|
|
|
|
// 헬퍼 로드 (app/Helpers/log_helper.php 가 있어야 함 autoload 설정 넣어놓았음)
|
|
|
|
class NaverWorker extends BaseCommand
|
|
{
|
|
protected $group = 'Workers';
|
|
protected $name = 'naver:worker';
|
|
protected $description = 'Redis에서 데이터를 꺼내 DB에 저장하고 네이버 API를 호출합니다.';
|
|
|
|
// DB 객체를 담을 변수 선언
|
|
protected $db;
|
|
|
|
public function run(array $params)
|
|
{
|
|
helper('log'); // 여기서 로드 완료!
|
|
|
|
$this->db = \Config\Database::connect();
|
|
$logModel = model(NaverWorkerLogModel::class);
|
|
$naverService = new \App\Services\NaverService(); // 서비스 생성
|
|
|
|
$redis = new \Redis();
|
|
try {
|
|
// 두 가지 환경 변수 형식 지원 (REDIS_HOST 또는 redis.default.host)
|
|
$redisHost = getenv('REDIS_HOST') ?: (getenv('redis.default.host') ?: '127.0.0.1');
|
|
$redisPort = getenv('REDIS_PORT') ?: (getenv('redis.default.port') ?: 6379);
|
|
$redisDatabase = getenv('REDIS_DATABASE') ?: (getenv('redis.default.database') ?: 9);
|
|
|
|
$redis->connect($redisHost, (int)$redisPort);
|
|
$redis->select((int)$redisDatabase);
|
|
CLI::write(CLI::color('🟢 Naver Worker running... (Redis: ' . $redisHost . ':' . $redisPort . ' DB:' . $redisDatabase . ')', 'green'));
|
|
} catch (\Exception $e) {
|
|
CLI::error("Redis 연결 불가: " . $e->getMessage());
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
$result = $redis->brPop(['naver:raw_queue'], 30);
|
|
|
|
if (!$result) {
|
|
// 데이터가 없어서 타임아웃 난 경우.
|
|
// 굳이 sleep 안 해도 바로 다음 brPop이 다시 30초 대기를 시작함.
|
|
continue;
|
|
}
|
|
|
|
if ($result) {
|
|
$rawData = $result[1];
|
|
|
|
// [1] 꺼내자마자 DB에 원문 저장 (2차 임시 저장) - 실패 시 재시도
|
|
try {
|
|
$logId = $logModel->insert([
|
|
'raw_payload' => $rawData,
|
|
'status' => 'INIT'
|
|
]);
|
|
} catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) {
|
|
// MySQL gone away 에러 시 재연결 후 재시도
|
|
if (strpos($e->getMessage(), 'MySQL server has gone away') !== false) {
|
|
CLI::write(CLI::color('⚠️ MySQL gone away, reconnecting...', 'yellow'));
|
|
$this->db->close();
|
|
$this->db = \Config\Database::connect();
|
|
$logModel = model(NaverWorkerLogModel::class);
|
|
|
|
// 재시도
|
|
$logId = $logModel->insert([
|
|
'raw_payload' => $rawData,
|
|
'status' => 'INIT'
|
|
]);
|
|
} else {
|
|
throw $e; // 다른 에러면 그대로 throw
|
|
}
|
|
}
|
|
|
|
try {
|
|
$responseJson = json_decode($result[1], true);
|
|
$payload = $responseJson['request_data'] ?? [];
|
|
|
|
if (empty($payload)) {
|
|
throw new \Exception("빈 페이로드 데이터");
|
|
}
|
|
|
|
// 서비스의 함수 하나로 모든 처리 완료
|
|
$insertId = $naverService->processArticle($payload);
|
|
|
|
// [3] 성공 시 로그 업데이트 (재연결 처리 포함)
|
|
$this->safeUpdateLog($logModel, $logId, [
|
|
'atcl_no' => $payload['articleNumber'] ?? null,
|
|
'status' => 'SUCCESS',
|
|
'target_db_id' => $insertId
|
|
]);
|
|
|
|
CLI::write("✅ Success! DB ID: $insertId", 'cyan');
|
|
|
|
} catch (\Exception $e) {
|
|
CLI::error("❌ Task Failed: " . $e->getMessage());
|
|
// 실패 로그는 여기서 남김
|
|
// 1. DB 상태를 FAIL로 업데이트 (필수) (재연결 처리 포함)
|
|
$this->safeUpdateLog($logModel, $logId, [
|
|
'status' => 'FAIL',
|
|
'error_msg' => $e->getMessage()
|
|
]);
|
|
|
|
// 2. Redis 실패 큐에 백업 (선택 - 나중에 모아서 다시 던질 때 편함)
|
|
$redis->lPush('naver:failed_queue', $rawData);
|
|
helper('log');
|
|
write_custom_log("FAILED_DATA | Error: " . $e->getMessage(), 'ERROR', 'failed');
|
|
|
|
// 루프 과부하 방지 (연속 에러 시)
|
|
sleep(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MySQL gone away 에러 발생 시 재연결 후 재시도하는 안전한 update
|
|
*/
|
|
protected function safeUpdateLog($logModel, $logId, $data)
|
|
{
|
|
try {
|
|
return $logModel->update($logId, $data);
|
|
} catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) {
|
|
if (strpos($e->getMessage(), 'MySQL server has gone away') !== false) {
|
|
CLI::write(CLI::color('⚠️ MySQL gone away on update, reconnecting...', 'yellow'));
|
|
$this->db->close();
|
|
$this->db = \Config\Database::connect();
|
|
$logModel = model(\App\Models\Entities\NaverWorkerLogModel::class);
|
|
|
|
// 재시도
|
|
return $logModel->update($logId, $data);
|
|
} else {
|
|
throw $e;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} |