Yêu cầu thg 7 29, 2019 6:45 SA 825 2 2
  • 825 2 2
0

Return exception vs throw exception in php ?

Chia sẻ
  • 825 2 2

Mọi người cho mình hỏi sự khác nhau giữa return exception và throw exception trong php ạ ?

Mình làm 1 chức năng là khi có lỗi thì sẽ notice đến nhiều chatapp trên laravel, vấn đề là nếu 1 trong các lệnh gửi notification cũng có exception thì sẽ tạo 1 vòng lặp vĩnh viễn trong queue.

Mình khắc phục bằng cách tạo ra 1 exception : ChatAppException, cho nó vào property dontReport của App/Exceptions/Handler và try catch khối lệnh gửi notification và throw exception này nhưng nó không chạy như mong muốn.

Sau khi mình thay throw bằng return thì nó lại chạy đúng, tuy fix được rồi mình mình vẫn lăn tăn là tại sao lại vậy, mong mọi người giải đáp.

UPDATE: Mình đã fix được lỗi này chỉ tại không có dòng

use Throwable;

😦(

Avatar No Naem @Naem
thg 7 29, 2019 6:53 SA

cho mình hỏi nó chạy ko như mong muốn ở đây là nó chạy ntn được ko :v

Avatar Quiet @simple1805
thg 7 29, 2019 6:58 SA

Bạn có thể cho mình xem đoạn try catch trogn code để throw ra exception k

thg 7 29, 2019 7:00 SA

@Naem là nó vẫn chạy ra vòng lặp vĩnh viễn, đáng lẽ nó phải hoạt động như này: chương trình xảy ra lỗi 1, shouldReport = true -> dispatch event -> listener được push vào queue -> lệnh gửi notify 1 hoạt động, lệnh 2 lỗi -> throw ra ChatAppException, shouldReport = false -> không dispatch event -> thread dừng hoạt động.

Avatar No Naem @Naem
thg 7 29, 2019 7:03 SA

@Plumpboy should report này đặt ở đâu vậy bạn + cơ chế bắt cái shouldReport này như thế nào vậy. bạn có chắc là cái dispatch event kia là thằng xử lý Exception này ko :-? hay là thằng khác xử lý

thg 7 29, 2019 7:06 SA

@huusu1996 Nguyên nhân đã được tìm ra là return dừng chương trình chứ không phải dừng ở hàm report của handler.

Exception là event nên nếu nó không đc throw thì event không đc kích hoạt, return chỉ trả về object.

Nhưng khó hiểu là tại sao laravel exception handler lại không hoạt động bình thường hay sai chỗ nào không biết, dùng supervisor thì nó trả lại lỗi trong lệnh catch thứ 2 của Queue Woker, FatalThrowableError.

Thằng handle được wrap trong runJob của class này, đang debug, hơi khó hiểu

Avatar No Naem @Naem
thg 7 29, 2019 7:17 SA

@Plumpboy bởi vì bạn throw ra ý, thì handle catch chứ ko phải event của bạn catch, nếu muốn xử lý thì bạn phải xử lý ở trong Handler chứ =))

thg 7 29, 2019 7:18 SA
Avatar No Naem @Naem
thg 7 29, 2019 7:57 SA

@Plumpboy bạn có public repo hay có thể gửi đoạn xử lý của bạn lên đây ko, mình xem hộ cho, có thể bạn chưa hiểu cách hoạt động của laravel lắm :v

2 CÂU TRẢ LỜI


Đã trả lời thg 7 29, 2019 5:02 CH
Đã được chấp nhận
+2

Về sự khác nhau giữa return và throw trong php

  • return được sử dụng để kết thúc việc hoàn tất thực thi thành công một chương trình con (ở đây là method chẳng hạn) và gửi trả lại một giá trị về cho chương trình chính. Giá trị bạn trả lại cho chương trình chính có thể ở bất cứ kiểu dữ liệu nào: Object, Array, Boolean .etc.

VD:

function sum(int $a, int $b)
{
    return $a + $b;
}

// Main:
$firstNumber = 5;
$secondNumber = 10;
$result = sum($a, $b);

echo "\$a + \$b = {$result}";
  • throw cũng được dùng để kết thúc một việc hoàn tất thực thi một chương trình con (nhưng ở đây là kết thúc với một exit code lỗi). throw luôn bắn lại về chương trình chính một Exception (đại diện cho một lỗi từ một trường hợp ngoại lệ nào đó mà logic trong chương trình con không xử lý được).
function divide($a, $b)
{
    if ($b === 0) {
        throw new \LogicException('Can not divide by zero');
    }

    return $a / $b;
}

// Main
try {
    $firstNumber = 10;
    $secondNumber = 0;

    echo divide($firstNumber, $secondNumber);
} catch(\LogicException $exception) {
    // handle error
}

Quay lại giải pháp bạn đang dùng

  • Web app có lỗi -> notice về chatapp trong queue
  • Job notice chatapp trong queue có lỗi -> dẫn tới bị quay lại bước trên -> một kiểu như hell callback
  • Cách 1: Sử dụng một Custom Exception. Với cách này, khối try cần catch chính xác một exception được định danh cụ thể, như ví dụ trên là \LogicException. Hoặc bạn có thể catch toàn bộ exception với class \Exception. Sau đó trong catch bạn throw Custom Exception để ErrorHandler nhận biết được và không report. Cách này không được ổn lắm.
  • Cách 2: Theo mình hiểu, bạn đổi cái throw trong cái catch ở Cách 1 thành return. Việc này không khác gì dấu bug cả. Và càng không hay ho hơn so với cách 1.
  • Cách 3: Bạn nên tìm hiểu nguyên nhân bị lỗi là do đâu và sửa nó. Nếu nguyên nhân throw ra exception như do đường truyền mạng ConnectException, NetworkException trong quá trình notice thì với những những cái này bạn có ignore nó. Có thể là log lại exception ra file logs và kết thúc job notice này tại đây thay vì lại report và một job notice khác sẽ chạy tiếp. Vì có cố thì cũng ko thể gửi được bởi mất kết nối mà. Tóm lại bạn phải tìm nguyên nhân gây lỗi và fix cái lỗi đấy chứ không nên quan tâm quá vào việc ignore error để dấu bug.
Chia sẻ
thg 7 30, 2019 1:24 SA

@huukimit mình đang tìm cách fix nhưng giờ chắc chỉ có duy nhất 1 cách là log lỗi đó, và force stop cái job đó, nếu không wrap bên ngoài của job, process nó sẽ throw ra các exception khác, vòng lặp sẽ xảy ra.

thg 7 30, 2019 2:32 SA

@Plumpboy không biết exception bạn gặp phải như thế nào nhỉ? :-?

thg 7 30, 2019 2:39 SA

@huukimit đã fix được rồi bạn ơi, thiếu mỗi dòng

use Throwable;

nên khi catch

try {
            resolve('chatapp')
                ->handle('chatwork')
                ->withEnv()
                ->withMessage(generate_message('notification_template.chatwork', $event))
                ->dispatch();
            resolve('chatapp')
                ->handle('slack')
                ->withEnv()
                ->withMessage(generate_message('notification_template.slack', $event))
                ->dispatch();
        } catch (Throwable $e) {
            throw new ChatAppException();
        }

thì không chạy vào block catch vì $e không phải instance của Throwable (do mình chưa khai báo nó, nên Throwable ở đây là undefined) 😦

thg 7 30, 2019 2:45 SA

@Plumpboy Theo cách bạn làm như vậy thì tương đương với việc catch(\Exception $e). Làm như này không được hay lắm vì nếu có bất kỳ exeption nào trong try thì nó sẽ bị ngậm mất vì bạn ignore cái ChatAppException rồi. 🤔

thg 7 30, 2019 2:47 SA

@huukimit bắt buộc phải thế còn lỗi thì mình sẽ log ra thôi, throw ra cái này để dừng job, trong queue thì lỗi cũng chỉ log đc thôi

Đã trả lời thg 7 29, 2019 8:45 SA
+1

Return exception giống như return value bình thường thôi bạn, nó chỉ dừng function đang chạy và coi như function bạn chạy không có lỗi gì. Lúc này queue cũng xác định là job chạy success và không chạy lại nữa.

Throw exception thì nó dừng function, nếu bạn không try/catch thì exception handler sẽ đứng ra xử lý và do có exception nên queue worker sẽ thực hiện retry. Việc có vòng lặp vĩnh viễn theo mình suy đoán là do bạn không set số lần retry tối đa, ví dụ command php artisan queue:work có option là --tries và giá trị mặc định là 0 tức là retry cho đến khi job chạy success, bạn có thể thử set value cho option này:

php artisan queue:work --tries=3

dontReport tức là để nó không log hay report exception đến các log channels thôi bạn, chứ nó không liên quan đến queue có lặp vĩnh viễn hay không.

Bạn có thể đọc tạm bài này https://viblo.asia/p/how-laravel-handle-exception-L4x5xdeb5BM để hiểu hơn về error handler.

Bonus quote from docs https://laravel.com/docs/5.8/queues#error-handling

Error Handling

If an exception is thrown while the job is being processed, the job will automatically be released back onto the queue so it may be attempted again. The job will continue to be released until it has been attempted the maximum number of times allowed by your application.

Chia sẻ
thg 7 29, 2019 8:54 SA

nó vòng lặp vĩnh viễn vì mỗi lần nó bắn ra 1 lỗi trong queue thì lại có 1 event đc dispatch và nó sinh ra job mới. try time mình đã đặt rồi.

Cái khó ở đây là mình try catch trong queue để throw ra 1 exception đã được đăng ký trong dontReport để dừng vòng lặp. Tuy nhiên queue lại được wrap trong runJob trong Queue Woker, và ở đây nó throw những exception khác những cái này mình không thể ném vào dontReport vì như thế sai logic.

Cách tạm thời là Log vào file log của queue và return, chạy đúng mặc dù code hơi ma giáo

thg 7 29, 2019 9:00 SA

vì mình làm 1 cái manager để notice khi có exception trong hệ thống mà chính cái để gửi exception lại lỗi nên nó thành vòng lặp, mà nếu nó lỗi ở cái manager thì không sao, ở đây nó lại lỗi ở 1 driver trong khi các driver khác vẫn chạy

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí