Middleware와 Laavel의 Middleware 사이의 에피소드

당신은 무엇을 하고 싶습니까?


데이터베이스 트랜잭션을 Middleware로 정의하여 무의식적으로 자동으로 적용하고자 합니다.그래서 나는 불평을 했지만 의외로 수집되지 않은 정보도 있었고 갑자기 화를 낸 사람도 있었다. 다른 사람을 도울 수 있었으면 좋겠다고 생각해서 경험담을 정리했다.

결론


이걸로 주세요.
https://gist.github.com/aambrozkiewicz/5b38416fc84a824649b1a137bd4fa84c
app/Http/Middleware/DBTransaction.php
<?php
namespace App\Http\Middleware;
use Closure;
class DBTransaction
{
    public function handle($request, Closure $next)
    {
        \DB::beginTransaction();
        $response = $next($request);
        if ($response->exception) {
            \DB::rollBack();
        } else {
            \DB::commit();
        }
        return $response;
    }
}
다음은 실제 이 Middleware 방법을 통해 다음과 같이 Kernel에만 세 줄을 추가하는 것을 추천합니다.
app/Http/Kernel.php
    //...

    protected $middlewareGroups = [
        'web' => [
            //...
            'dbtransaction', // すべての web ルートに適用
        ],

        'api' => [
            //...
            'dbtransaction', // すべての api ルートに適用
        ],
    ];

    protected $routeMiddleware = [
        //...
        'dbtransaction' => \App\Http\Middleware\DBTransaction::class, // 定義
    ];

    //...

주의점

  • 한 번의 요청은 모두 BEGIN과 COMMIT로 묶기 때문에 처리 과정에서 BEGIN과 COMMIT를 사용하는 기존 시스템에 잘 쓰이지 못할 수 있습니다.
  • 그나저나 한 번에 요청하는 처리 수는 최소화하자.
  • DB::beginTransaction() 연결 단위이기 때문에 여러 연결을 사용하는 시스템에서 해당하는 노력(투)
  • 오류 로그나 세션 정보 등 오류가 발생하더라도 보존해야 하는 정보는 반대로 이 정보를 이용해 따로 연결하는 것이 좋을 수 있다.상세한 상황은 후술하다.
  • 중간부품의 적용 순서에 따라 순조롭게 진행됩니다!나는 이것이 라벨의 중간부품 디자인 사상에 어긋난다고 생각하기 때문에 추천하지 않는다(´)ε` ).순서에 의존하는 중간부품을 용서해 주십시오.
  • 해설


    데이터베이스에서'사무'는 무엇입니까?


    데이터베이스에는'사무'라는 개념이 있는데 만약 처리가 중도에 실패하면 이전에 진행된 중간 처리를 모두 취소할 수 있다.예를 들어, 송금 처리가 있는 경우
  • A 계좌에서 10000엔
  • 인출
  • 미스터 B 계좌로 1000엔 입금
  • 장소
  • A 계좌에서 10000엔
  • 인출
  • 어둠의 힘에 의외의 예외가 생겼다!
  • 이러면 쉽지 않을 거야.만 엔이 사라졌다.이 때문에
  • 여기서부터 처리(BEGIN TRANSACTION)
  • A의 계좌에서 10000엔을 인출
  • 미스터 B 계좌로 1000엔 입금(임시집행)
  • 모든 임시 실행 결정(COMMIT)
  • 또는
  • 여기서부터 처리(BEGIN TRANSACTION)
  • A의 계좌에서 10000엔을 인출
  • 어둠의 힘에 의외의 예외가 생겼다!
  • 처음부터 없음(ROLLBACK)
  • 이렇게 하면 오류가 발생하더라도 정보에'모순'이 남아 있는 것을 방지할 수 있다.

    중간부품


    이는 사용자와 라벨이 대화할 때'요청'에 대한 예처리와'응답'에 대한 후처리를 공통화하고 간단하게 관리하기 위한 구조다.

    Middleware와 실제 처리(컨트롤러 등)의 위치 관계는 이렇게 병렬 연결된 이미지이지만 실제 Middleware의 처리handle 방법에는 다음 중간부품이 포함되어 있기 때문에 아래의 인상은 실제 상황과 비슷하다.(그림에 쓰기 어렵지만)

    이번 경우 요청의 예처리로'거래의 시작', 응답의 후처리로'제출'또는'롤백'이 좋다.

    추천하지 않는 방법론try&catch


    예를 들면 여기.
    https://gist.github.com/rodrigopedra/a4a91948bd41617a9b1a
    try &catch에서 예외를 포착하려고 합니다.일반적인 PHP 방식이라 구글로 검색하면 처음 나오기 때문에 처음에는 이것을 사용했지만 조건에 따라 잘 되지 않았다.
        public function handle($request, Closure $next)
        {
            \DB::beginTransaction();
            try {
                $response = $next($request);
            } catch (\Exception $e) {
                \DB::rollBack();
                throw $e;
            }
            if ($response instanceof Response && $response->getStatusCode() > 399) {
                \DB::rollBack();
            } else {
                \DB::commit();
            }
            return $response;
        }
    

    왜요?실제 운행을 시도해 보면 라벨의 구조를 보면 알 수 있다. 모든 중간부품과 프로그램은 미리 적당한 사이즈의try&catch로 포장되어 있기 때문이다.무엇을 하고 있는지 말하자면, 나는 처리 과정에서 발생하는 모든 예외를 표준적인 오류 처리 프로그램에 맡기고 싶다.

    따라서 상기try&catch의 중간부품의 이 부분
            } catch (\Exception $e) {
                \DB::rollBack();
                throw $e;
            }
    
    실제로는 통과되지 않는다.
    그렇다면그리고 실제 행동으로 이 단계에서 나온 엑셉션의 내용은 실제 발생한 오류의 내용이 아닌 인터넷 서버 에로로 바뀌었다.오류 처리 프로그램이 오류를 깨끗이 처리했다.
    그렇다면 깊숙이 파고들수록 이 연선을 사용한 것이 틀렸다는 것을 알아차리려면 더 많은 시간이 필요하다.
    따라서, 중간부품을 사용하여try &catch를 함부로 추가하면 표준 오류 처리 프로그램에 좋지 않은 영향을 미칠 수 있으므로 추천하지 않습니다.(말은 그렇지만 오류 로그가 정상적으로 표시되지 않는 것은 어느 정도의 영향일 뿐이라고 생각한다.)

    오류 로그 없음


    정도의 영향만 받는다고 하지만 우리 시스템에서는'버그 로그가 DB에 쓰기'때문에 중간부품이 이동하면 버그 로그도 롤백에 의해 아무것도 나오지 않는다.
    .env
    DB_LOG_TABLE=logs
    DB_LOG_CONNECTION=mysql
    DB_LOG_FLUSH_RATIO=100
    DB_LOG_PRESERVE_DAYS=30
    
    오류 로그를 데이터베이스로 설정하고 Laavel 표준 방법으로 테이블을 준비합니다.ev만 추기하기 때문에 간단해요.
    가장 간단한 방법은 DB 연결을 분리하는 것입니다.
    라벨의 거래는'연결 단위'로 연결을 분리하면 롤백의 영향을 받지 않는다.그리고 방법은 간단해..
    cinfig/database.php
        'connections' => [
    
            'mysql' => [
               // この内容を
            ],
    
            // 新しくコネクションを追加して
            'mysql_log' => [
               // まるごとコピペ。全く同じ内容でOK
            ],
    
    .env
    DB_LOG_TABLE=logs
    DB_LOG_CONNECTION=mysql_log  # 変更する
    DB_LOG_FLUSH_RATIO=100
    DB_LOG_PRESERVE_DAYS=30
    
    연결을 분리하고 싶지 않은 경우, 예를 들어 중간부품 안에서 오류가 발생하면 다시 한 번 던질 수 있다.
    app/Http/Middleware/DBTransaction.php
            if ($response->exception) {
                \DB::rollBack();
                throw $response->exception; // 追記
            } else {
    
    이렇게 되면 DB를 뒤집은 후에 다시 오류 처리를 합니다.그러나 이렇게 쓴 것은'마지막 오류'뿐이다.처리 중 INFO와 NOTICE로 기록된 오류 로그가 폐기되었기 때문에 분리 연결을 추천합니다.

    복사본을 읽는 여러 구조 사용


    Laravel은 전용 주인 1대를 기록하고, 전용 복제품 여러 대의 부하 분산 구성을 읽지만, 거래를 시작하면 이를 무시하고 모두 주인에게 연결하는 일을 한다.(하고 있다. 나도 모르게 이 중간부품을 넣을 때 모든 연결이 메인보드에 집중되어 서버가 떨어질 것이다. 이것은 비밀이다.)
    소스 코드 여기 있습니다.transactions1 이상일 때 ReadPdo의 방법을 getPdo(주인)로 되돌려 주세요.
    vendor/laravel/framework/src/Illuminate/Database/Connection.php
        public function getReadPdo()
        {
            if ($this->transactions >= 1) {
                return $this->getPdo();
            }
            // ...
        }
    
    이런 상황에서 비록 매우 번거롭지만, 나는 쓴 종점만 중간 소프트웨어를 통해 하는 것이 어떻다고 생각한다.
    (원래 읽기 전용 단점은 거래가 필요 없고 쓴 단점은 일치하지 않도록 읽기도 주인을 참조해야 한다. 이것은 매우 합리적이다.)
    하지만 귀찮아서 다른 방법이 있는지 확인하고 있어요.
    routes/api.php
    Route::get   ('order_number',  ['uses' => 'Api\OrderController@order_number']);
    Route::get   ('order_summary', ['uses' => 'Api\OrderController@order_summary']);
    Route::post  ('/',             ['uses' => 'Api\OrderController@store',   'middleware' => 'dbtransaction']);
    Route::delete('{id}',          ['uses' => 'Api\OrderController@destroy', 'middleware' => 'dbtransaction']);
    Route::put   ('{id}',          ['uses' => 'Api\OrderController@update',  'middleware' => 'dbtransaction']);
    

    감상


    반드시'프레임 표준화'!아무것도 모르는 사람이 아무것도 모르는 상태에서 시스템을 구축하면 거래가 순조롭게 진행될 수 있다는 점이 기대된다♡

    좋은 웹페이지 즐겨찾기