ホームページ  >  記事  >  バックエンド開発  >  マイクロサービス アーキテクチャで例外を正しく使用する方法

マイクロサービス アーキテクチャで例外を正しく使用する方法

醉折花枝作酒筹
醉折花枝作酒筹転載
2021-06-17 17:24:231919ブラウズ

この記事では、マイクロサービス アーキテクチャにおける例外の正しい使用方法を紹介します。一定の参考値があるので、困っている友達が参考になれば幸いです。

マイクロサービス アーキテクチャで例外を正しく使用する方法

例外の正しい使用法は、マイクロサービス アーキテクチャにおける重要性のトップ 3 にランクされます。コメントはありません。

#例外の正しい使用法例外の数 マイクロサービス アーキテクチャの重要性でトップ 3 にランクされています。 コメントはありません。

Curdboys さん、お久しぶりです。端午節が楽しく過ごせることを祈っています。最近例外について話したいと思います。私の思考が閉ループを形成しているように思えます。この組み合わせがあなたのビジネス コードに役立つことを願っています。

以下では、世界で最も優れた言語と、環境的に最も完全な言語についてのみ説明します。私には意見はありません。

例外の類似点と相違点

PHP PHP7 の例外の設計は Java の Exception extends Throwable と一致していますが、歴史的な理由と設計にはまだ微妙な違いがいくつかあります。コンセプト。たとえば、PHP の例外にはコード属性があるため、複数の例外が同じ例外にクラスター化され、catch ブロック内のコードに応じて異なるビジネス ロジック コードが記述されます。

しかし、Java 例外にはコードがないため、このように設計することはできません。異なる例外は、異なる状況にのみ使用できます。したがって、私たちは、外部に公開されるときに、例外の透過的な送信に直接依存するのではなく、パッケージ化クラスを通じてサービスをカプセル化することに慣れています。

均一な例外処理

Java コードで最も批判されるのは、多数のトライ キャッチです。私には異論はありません。コードの一部を取得するだけです

@Override
public DataResult<List<AdsDTO>> getAds(Integer liveId) {
    
    try {
        List<AdsDTO> adsDTO = new ArrayList<>();
        //...业务逻辑省略
        DataResult.success(adsDTO);
    } catch (Exception e) {
        log.error("getAds has Exception:{}", e.getMessage(), e);
        DataResult.failure(ResultCode.CODE_INTERNAL_ERROR, e.getMessage()); // 将异常信息返回给服务端调用方
    }
    
    return dataResult;
}

多くの場合、実行時以外の例外があるかどうかに関係なく、何も考えずに try catch を書くだけです。より良い方法は、aop を使用してすべてのサービス メソッド呼び出しをインターセプトし、例外を一律に引き継いで処理することです。

@Around("recordLog()")
public Object record(ProceedingJoinPoint joinPoint) throws Throwable {
  //... 请求调用来源记录
  
  Object result;

  try {
    result = joinPoint.proceed(joinPoint.getArgs());
  } catch (Exception e) {
    //... 记录异常日志
    
    DataResult<Object> res = DataResult.failure(ResultCode.CODE_INTERNAL_ERROR, e.getMessage());
    result = res;
  }

    //... 返回值日志记录
  
  return result;
}

小さな問題があります。サービス A の例外情報を呼び出し元 B に直接返すと、潜在的なリスクが生じる可能性があります。呼び出し元は、たとえ貧困 3 世であっても、決して信頼できません。農民です。呼び出し元がエラー メッセージをどのように処理するかが不明なため、json としてフロント エンドに直接返される可能性があります。

RuntimeException

Java の例外は、実行時例外と非実行時例外に分類できます。実行時例外はキャッチする必要も、キャッチする必要もありません。たとえば、メソッド内で guava パッケージの Preconditions ツール クラスを使用する場合、スローされる IllegalArgumentException も実行時例外になります。

@Override
public DataResult<List<AdsDTO>> getAds(Integer liveId) {
  Preconditions.checkArgument(null != liveId, "liveIds not be null");
  
  List<AdsDTO> adsDTOS = new ArrayList<>();
  //...业务逻辑省略
  return DataResult.success(adsDTOS);
}

この機能を使用して、独自のビジネス例外クラスをカスタマイズして RuntimeException を継承することもできます

XXServiceRuntimeException extends RuntimeException

ビジネス ロジックに準拠しない状況では、XXServiceRuntimeException が直接スローされます

@Override
public DataResult<List<AdsDTO>> getAds(Integer liveId) {

  if (null == liveId) {
    throw new XXServiceRuntimeException("liveId can&#39;t be null");
  }
  
  List<AdsDTO> adsDTOS = new ArrayList<>();
  //...业务逻辑省略
  return DataResult.success(adsDTOS);
}

そして、aop では統合処理が実行され、対応する最適化が行われます。前述の大まかなアプローチでは、XXServiceRuntimeException と IllegalArgumentException 以外の例外は内部的に記録され、外部に公開されなくなります。ただし、分散リンクを 1 つにまとめることを忘れないでください。 requestId.In DataResult トラブルシューティングを容易にするために戻ります。

@Around("recordLog()")
public Object record(ProceedingJoinPoint joinPoint) throws Throwable {
  //... 请求调用来源记录
  
  Object result;

  try {
    result = joinPoint.proceed(joinPoint.getArgs());
  } catch (Exception e) {
    //... 记录异常日志①
    log.error("{}#{}, exception:{}:", clazzSimpleName, methodName, e.getClass().getSimpleName(), e);
    
    DataResult<Object> res = DataResult.failure(ResultCode.CODE_INTERNAL_ERROR);
    if (e instanceof XXServiceRuntimeException || e instanceof IllegalArgumentException) {
       res.setMessage(e.getMessage());
    }
 
    result = res;
  }

  if (result instanceof DataResult) {
      ((DataResult) result).setRequestId(EagleEye.getTraceId()); // DMC 
  }

    //... 返回值日志记录
  
  return result;
}

異常監視

閉ループに関しては、カスタム例外クラスを使用すると、異常ログの監視と警告のしきい値を大幅に下げることができます。アラーム より正確には、Alibaba Cloud SLS の監視を例に挙げます。

* and ERROR not XXServiceRuntimeException not IllegalArgumentException|SELECT COUNT(*) AS count

ここで監視されるのは、例外ログを記録するログです。①

PHP の例外

Java で前述した問題は PHP にも存在します。AOP をシミュレートするために 3 つのメソッドを使用しない場合、PHP が世界で最高の言語であることを反映することはできません。

//1. call_user_func_array
//2. 反射
//3. 直接 new
try {
  $class = new $className();
  $result = $class->$methodName();
} catch (\Throwable $e) {
    //...略
}

上記のアーキテクチャ ロジックと同様に、疑似コードは繰り返しません。基本的には一貫しています。独自のビジネス例外クラスをカスタマイズしてRuntimeExceptionを継承し、外部出力処理を行うことも可能です。

ただし、PHP には歴史的な問題がいくつかあります。当初の設計時には、多くのランタイム例外が通知エラーと警告エラーとして出力されていましたが、エラー出力にはコール スタックが欠如しており、トラブルシューティングに役立ちませんでした。

function foo(){
  return boo("xxx");
}

function boo($a){
  return explode($a);
}

foo();
Warning: explode() expects at least 2 parameters, 1 given in /Users/mengkang/Downloads/ab.php on line 8

特定のパラメーターや呼び出しスタックを確認することはできません。 set_error_handler ErrorException を使用すると、それが非常に明確になります。

set_error_handler(function ($severity, $message, $file, $line) {
    throw new ErrorException($message, 10001, $severity, $file, $line);
});

function foo(){
  return boo("xxx");
}

function boo($a){
  return explode($a);
}

try{
  foo();
}catch(Exception $e){
  echo $e->getTraceAsString();
}

最後に出力された情報は

Fatal error: Uncaught ErrorException: explode() expects at least 2 parameters, 1 given in /Users/mengkang/Downloads/ab.php:12
Stack trace:
#0 [internal function]: {closure}(2, &#39;explode() expec...&#39;, &#39;/Users/mengkang...&#39;, 12, Array)
#1 /Users/mengkang/Downloads/ab.php(12): explode(&#39;xxx&#39;)
#2 /Users/mengkang/Downloads/ab.php(8): boo(&#39;xxx&#39;)
#3 /Users/mengkang/Downloads/ab.php(15): foo()
#4 {main}
  thrown in /Users/mengkang/Downloads/ab.php on line 12

です。上記の関数

function boo(array $a){
  return implode(",", $a);
}

を変更すると、PHP 致命的エラー: Uncaught TypeError がスローされるため、キャプチャされません。PHP7 では新しい

class Error は Throwable を実装しており、PHP システム エラー ログには Stack が存在しますが、ビジネス システム全体と直列に接続することはできません。ここでは、ログの設計について説明する必要があります。 Java のようなトレース ID。Nginx ログから PHP の通常の情報レベルのログ、およびこれらの Uncaught TypeErrors まで、すべてのログが連結されるため、デフォルトの出力はシステム エラー ログに引き継がれ、catch コード ブロック内の統一された場所に記録されます。次に、ここを

set_error_handler(function ($severity, $message, $file, $line) {
    throw new ErrorException($message, 10001, $severity, $file, $line);
});

function foo(){
  return boo("xxx");
}

function boo(array $a){
  return implode(",", $a);
}

try{
  foo();
}catch(Throwable $e){
  echo $e->getTraceAsString();
}

catch Throwable に変更して、エラーと例外を受け入れるようにするだけです。

ただし、set_error_handler は E_PARSE エラーなどの一部のエラーを処理できません。register_shutdown_function を使用してカバーできます。

值得注意的是register_shutdown_function的用意是在脚本正常退出或显示调用exit时,执行注册的函数。
是脚本运行(run-time not parse-time)出错退出时,才能使用。如果在调用register_shutdown_function的同一文件的里面有语法错误,是无法注册的,但是我们项目一般都是分多个文件的,这样就其他文件里有语法错误,也能捕获了
register_shutdown_function(function(){
    $e = error_get_last();
    if ($e){
        throw new \ErrorException($e["message"], 10002, E_ERROR, $e["file"], $e["line"]);
    }
});

如果你想直接使用这些代码(PHP的)直接到项目可能会有很多坑,因为我们习惯了系统中有很多  notice 了,可以将 notice 的错误转成异常之后主动记录,但是不对外抛出异常即可。

推荐学习:php视频教程

以上がマイクロサービス アーキテクチャで例外を正しく使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。