diff --git a/.gitignore b/.gitignore index 2bdba66..4c3195f 100644 --- a/.gitignore +++ b/.gitignore @@ -141,5 +141,6 @@ _modules/* /phpunit*.xml .history/ +/storage/ .README \ No newline at end of file diff --git a/api_receiver.php b/api_receiver.php new file mode 100644 index 0000000..eba91fe --- /dev/null +++ b/api_receiver.php @@ -0,0 +1,77 @@ + 'E003', + 'resultMessage' => 'Invalid API Key' + ]); + exit; +} + +try { + // 3. 데이터 수신 (POST JSON 또는 GET 파라미터) + $rawData = file_get_contents('php://input'); + $data = json_decode($rawData, true); + + // JSON 데이터가 비어있다면 GET 파라미터 사용 (테스트용) + if (empty($data)) { + $data = $_GET; + } + + if (empty($data)) { + throw new Exception("Empty data received"); + } + + // 4. Redis 연결 + $redis = new Redis(); + // Docker 서비스 이름인 'redis' 사용 + $success = $redis->connect('redis', 6379); + + if (!$success) { + throw new Exception("Could not connect to Redis"); + } + + $redis->select(10); // 10번 DB 사용 + + // 5. 큐에 넣을 데이터 포맷팅 + $payload = [ + 'request_data' => $data, + 'received_at' => date('Y-m-d H:i:s'), + 'client_ip' => $_SERVER['REMOTE_ADDR'] + ]; + + // 'naver:raw_queue'라는 이름의 리스트에 저장 + $redis->lPush('naver:raw_queue', json_encode($payload)); + + // 6. 네이버측에 성공 응답 (202 Accepted) + // 처리가 완료된 것은 아니지만, 접수는 완료되었음을 의미 + http_response_code(202); + echo json_encode([ + 'resultCode' => 'S000', + 'resultMessage' => 'Request accepted and queued', + 'articleNumber' => $data['articleNumber'] ?? 'N/A' + ]); + +} catch (Exception $e) { + // 7. 장애 발생 시 로그 기록 (시스템 로그) + error_log("[API_RECEIVER_ERROR] " . $e->getMessage()); + + http_response_code(500); + echo json_encode([ + 'resultCode' => 'E999', + 'resultMessage' => 'Internal System Error: ' . $e->getMessage() + ]); +} \ No newline at end of file diff --git a/worker.php b/worker.php index 2d475bc..c1d0d45 100644 --- a/worker.php +++ b/worker.php @@ -1,89 +1,35 @@ connect($redisHost, $redisPort); - -// 그룹 생성 (이미 있으면 예외 발생하므로 무시) try { - $redis->xGroup('CREATE', $stream, $group, '$'); -} catch (Exception $e) {} - -// 저장 경로가 없으면 생성 (권한 문제 주의) -if (!is_dir($storagePath)) { - mkdir($storagePath, 0777, true); + $redis->connect($redisHost, 6379); + $redis->select(10); + echo "[*] Connected to Redis. Waiting for data...\n"; +} catch (Exception $e) { + die("Redis Connection Failed: " . $e->getMessage()); } - while (true) { - // 1. Stream에서 메시지 읽기 - $res = $redis->xReadGroup($group, $consumer, [$stream => '>'], $batchSize, 5000); - if (!$res) { - continue; - } + // 1. 입구 리스트에서 데이터 꺼내기 + $item = $redis->brPop(['naver:queue'], 5); + if (!$item) continue; + + $jsonStr = $item[1]; + $data = json_decode($jsonStr, true); - $entries = $res[$stream]; - $ids = []; - $processedCount = 0; - - foreach ($entries as $id => $fields) { - $ids[] = $id; - $articleNumbr = $fields['articleNumbr'] ?? ''; + // 2. 파일 저장 + $fileName = sprintf('%s_%s.json', $data['articleNumber'], $data['requestDatetime']); + $filePath = $storagePath . '/' . $fileName; + + if (file_put_contents($filePath, $jsonStr) !== false) { + echo "✅ [SUCCESS] File Saved: $fileName\n"; - if (empty($articleNumbr)) { - // 매물 번호가 없으면 실패 스트림으로 이동 후 다음 루프로 - $fields['fail_reason'] = 'Missing articleNumbr'; - $redis->xAdd('vrfc_failures', '*', $fields); - $redis->xAck($stream, $group, $id); - $redis->xDel($stream, $id); - continue; - } - - // 2. 파일 저장 로직 실행 - - // 파일 이름: 매물번호_요청시간.json - $fileName = sprintf('%s_%s.json', $articleNumbr, $fields['requestDatetime']); - $filePath = $storagePath . '/' . $fileName; - - // JSON 데이터 (stream fields를 그대로 사용) - $jsonData = json_encode($fields, JSON_PRETTY_PRINT); - - try { - // 파일에 JSON 데이터 쓰기 - $result = file_put_contents($filePath, $jsonData); - - if ($result === false) { - throw new Exception("File write failed for: {$filePath}"); - } - - error_log("SUCCESS: Saved request for {$articleNumbr} to {$fileName}"); - $processedCount++; - - } catch (Exception $e) { - error_log("File save error: " . $e->getMessage()); - - // 3. 파일 저장 실패 시 실패 스트림으로 이동 (DLQ)111 - $fields['fail_reason'] = $e->getMessage(); - $redis->xAdd('vrfc_failures', '*', $fields); - - // 잠깐 대기 - sleep(2); - } - - // 4. 성공 및 실패 처리 후 ACK 및 삭제 - $redis->xAck($stream, $group, $id); - $redis->xDel($stream, $id); + // 3. 파일 저장 성공 시에만 전송 큐로 넘김 (중요!) + $redis->lPush('naver:worker_queue', $jsonStr); + } else { + echo "❌ [ERROR] Failed to save file: $fileName\n"; + // 실패 시 다시 큐에 넣거나 로그 처리 } } \ No newline at end of file