질문을 삭제하지 말아주세요.!
 
1
0
-1

라라벨의 queue를 사용하기 위해 aws sqs를 만들었습니다.

라라벨 큐에 해당하는 https://laravel.kr/docs/5.4/queues 문서를 보고 작업하고 있는데

supervisor를 사용했습니다.

laravel.log를 보니

exception 'ReflectionException' with message 'Class  does not exist' in /home/upumb/up_umb/vendor/laravel/framework/src/Illuminate/Container/Container.php:729

이런 에러가 나타납니다.


일단 .env 파일에다가 sqs key, secret key, profix, name 등을 설정해놓았고

config/queue.php 파일에 sqs 설정 하는 부분도 해당 env 파일에 설정해 놓는 걸로 바꾸었구요.


$job = (new ProcessPodcast($podcast))->onConnection('sqs'); dispatch($job);

이런식으로도 작업을 해놨습니다.


해당 에러가 왜 나타나는 지 계속 찾고 있지만 찾는데 어려움이 있어 이곳에다 글을 남깁니다...

도움의 손길이 필요합니다.

부디 도와주세요ㅠ

    CommentAdd your comment...

    1 answer

    1.  
      2
      1
      0

      저 에러는 서비스 컨테이너(IoC)가 TypeHint로 객체를 생성할 수 없을 때 발생합니다. Job을 큐로 dispatch(직렬화해서 큐에 넣을 때)할 때 문제가 발생하는지, 큐를 읽어서 Job 객체로 재생할 때 발생하는지, 주신 정보만 가지고는 알 수가 없습니다. 로그를 더 자세히 살펴보시고, 어디서 발생하는 지 중간중간에 로그를 더 찍어 보시기 바랍니다.

      1. lzao

        답변 감사합니다.

        해당 로그는 supervisor를 실행할 때 나타납니다.

        supervisor/conf.d/filename.conf 로 conf 파일을 만들었고

        해당 내용은

        [program:filename]
        command=php /home/user/workspace/artisan queue:work sqs
        autostart=true
        autorestart=true
        user=user
        redirect_stderr=true
        stdout_logfile=/home/user/workspace/worker.log 

        로 되어 있습니다.

        sudo supervisorctl start filename:*

        인 명령어를 실행하면 storage/log/laravel.log 파일에 해당 ReflectionException 이 나타나구요...

        dispatch를 호출 하는 부분은 Controller부분이고

        해당 코드는 

        dispatch(new SenderJob($data));

        dispatch를 호출합니다. SenderJob은 php artisan make:job SenderJob  명령어로 만든 파일입니다.

        각  SenderJob에는 메일이나 sms 등의 sender 기능이 있습니다.


        exception 'ReflectionException' 에러는 supervisor를 실행하면 1초마다 계속 나타납니다. supervisor를 stop 하기 전까지 나타납니다.


        더 필요한 정보가 있으면 알려드리도록 하겠습니다.

      2. 김주원

        $data의 타입이 뭔지 모르겠으나, 추가 정보를 본 첫 느낌은 Job 클래스의 생성자에서 $data 타입을 찾지 못해서 발생한 것이 아닐까 싶습니다. 우선 Job 클래스 전체적으로 문법 오류, 네임스페이스, use 구문, 타입 힌트 등이 정확한지 확인해보세요.


        또 하나 조언 드리고 싶은 부분은, sqs, supervisor 등 본질과 무관한 군더더기가 생기기 전에 이 문제가 발견되었어야 하는 데, 한 번에 너무 많이 진도를 내서 중간 중간에 테스트를 안하셔서 생긴 결과로 보입니다.

        1) Job 클래스를 만들고, sync 드라이버로 로컬 머신에서 작동을 확인한다.

        2) sqs로 드라이버를 전환하고, 작동을 확인한다.

        3) supervisor를 이용해서 워커 프로세스를 구동하고, 작동을 확인한다.

        순으로 진행했어야 한다고 생각합니다. 주신 정보로 추정컨대 2), 3)은 작동하는 것 같습니다.

      3. lzao

        추가 답변 정말 감사합니다.

        많은 도움이 될 것 같습니다.

        일단 동기처리로 dispatch job을 실행했을 때는 문제가 없었습니다.

        $data type은 그냥 단순 array 입니다.

        동기처리로 했을 때 문제가 없어서 sqs를 이용하여 비동기 처리를 하려고 할 때

        해당문제가 나타났습니다.

        그래도 답변해 주신 정보로 힌트를 얻을 수 있어 문제를 해결할 수 있는 데 많은 도움이 될 것 같습니다.

        정말 감사합니다. 문제가 해결이 되면 꼭 답변으로 남기도록 하겠습니다.

      4. 김주원

        네. 해결 댓글을 꼭 남겨주세요.

        sync 드라이버로 로컬에서 dispatch()까지 문제 없단 얘기는 Job 직렬화에는 문제가 없다는 말입니다. Job을 역직렬화해서 객체로 재생할 때 문제가 있는 겁니다. 로컬 컴퓨터에서 php artisan queue:work --daemon 으로 워커를 실행시켜놓고, dispatch()를 실행하면 똑같은 문제가 발생할 거라 생각합니다.

        큐 잡(라라벨 콘솔 코맨드를 실행하는 크론 잡도 마찬가지)은 UI 없이 콘솔에서 실행되므로, Job::handle() 메서드에 \Log::info(__CLASS__ . ' started'), \Log::info(__CLASS__ . ' finished'), \Log::error(__CLASS__ . ' failed'), .. 와 같이 로그를 잘 찍고, try... catch를 이용해서 예외 처리를 잘 해야 합니다.

        코드가 변경되면 워커는 무조건 다시 실행해야 합니다. Ctrl/Cmd+c를 눌러 워커를 종료시키고 php artisan queue:work --daemon 다시 실행하면 됩니다. supervisor 를 사용한다면 supervisorctl restart all 명령을 주면 됩니다.

      5. lzao

        오류는 해결이 됐습니다만, 큐가 비동기로 잘 돌아가고 있는 지 확인을 하려면 어떻게 하나요?


        해당 문제가 나타났던 원인은

        config/queue.php 파일의

        'default' => env('QUEUE_DRIVER', 'sqs')

         이런식으로 해놔서 나타났던 문제 같습니다.

        기존의 sync로 변경했더니 php artisan queue:work --daemon 으로 했을 때는 에러가 나타나지 않았습니다.

        그리고 

        'sqs' => [
        'driver' => 'sqs',
        'key' => env('SQS_KEY'),
        'secret' => env('SQS_SECRET'),
        'prefix' => env('SQS_PREFIX'),
        'queue' => env('SQS_NAME'),
        'region' => 'us-east-2',
        ],

        이 부분에 env('SQS_KEY') 만 선언해놓고 별다른 값을 지정을 안해놨습니다.

        env('SQS_KEY','abababaabrbrbrbrrb') 이런식으로 Key값을 설정하니 문제가 나타나지 않았습니다.

        나머지 env 값들도 마찬가지구요..

        제가 생각했던건 env('SQS_KEY') 만 해놓으면 .env 파일에 키값과 매칭이 되는 줄 알았는데 잘 모르겠습니다.


        그리고 supervisor/conf.d/filename.conf 파일도 수정했습니다.

        기존에는 command=php /home/user/workspace/artisan queue:work sqs 만 해놓았는데

        command=php /home/user/workspace/artisan queue:work sqs --sleep=3 --tries=2 로 수정했습니다.

        기존에 동기로 했을 때 문제가 생기지 않아서 supervisor를 실행시켜놓고 해봤는데

        별다른 문제가 없어서 제대로 돌아가고 있는지가 의문입니다.

        AWS도 처음사용해보는 거라 aws console 페이지도 많이 어렵네요....

        답변주신 김주원님 감사드립니다. 덕분에 많이 배우고 갑니다 (smile)

      6. 김주원

        그랬군요. AWS의 KEY와 SECRET를 넣고(~/.aws/config 파일에 있는 그것, 계정 설정에서 csv로 받을 수 있는 그것입니다. 없다면 검색해 보세요), SQS_PREFIX 등 모든 정보를 넣어야 SQS가 작동합니다.

        SQS 설정이 정확하다면, AWS SQS 콘솔에서 해당 큐 네임을 선택하고 receive/delete 머시깽이하는 Action을 실행해서 30초 동안 큐 폴링을 하는 상태에서 dispatch()를 실행하면 큐에 메시지가 들어오는 것을 확인할 수 있습니다. 안들어왔다면 큐 커넥션 문제입니다. 특정 큐 서비스의 내용이고, AWS에서 스폰서를 안해줘서 더 언급은 안 할려구요 ㅎㅎ.

        아래 그림은 일반적인 상용 서비스의 구성인데, 작은 서비스라면 웹 서버와 워커를 같은 서버를 쓰고, 스케일 아웃이 불요하다면 로드밸런서를 붙이지 않을 수도 있습니다. 그럼에서는 웹 요청만 썼는데, 크론 잡이 커맨드를 수행하면서 큐 잡을 dispatch()했을 수도 있습니다.

        여튼 핵심은 웹 요청은 UI를 통해 상호작용으로 하므로 사용자든 개발자든 기능의 동작과 오작동을 눈으로 확인할 수 있습니다. 반면에 큐 잡은 비동기로 실행되고 사용자든 개발자가 비주얼 피드백을 받을 수 없습니다. 네네, 그래서 로그에 의존해야 하는 겁니다.


        class SomeJob
        {
            use Log;
            use Exception;
            use \...\SpecialException;
        
            public function handle()
            {
                Log::info(__CLASS__ . ' started');
                
                // 본질을 수행하기 위한 준비 작업
        
                try {
                    // 핵심 작업
                    // 예외가 날 수 있는 구문들, 특히 외부 API나 DB 작업
                    // 문제는 항상 경계에서 난다는 사실
                    Log::info(__CLASS__ . ' finished');
                } catch (SpecificException $e) {
                    // DB 트랜잭션을 사용한다면 롤백
                    Log::error(__CLASS__ . ' failed', [$e]);
                    throw $e; // 또는 커스텀 예외 (e.g. throw new SomeJobFailedExcpetion($message = '메시지', $code = 123, $previous = $e));
                } catch (Exception $e) {
                    Log::error(__CLASS__ . ' failed', [$e]);
                    // 위의 캐치 블록에서 처리하지 못한 예외 처리
                }
            }
        }
      CommentAdd your comment...