>  기사  >  백엔드 개발  >  마이크로서비스 아키텍처에서 예외를 올바르게 사용하는 방법

마이크로서비스 아키텍처에서 예외를 올바르게 사용하는 방법

醉折花枝作酒筹
醉折花枝作酒筹앞으로
2021-06-17 17:24:231830검색

이 기사에서는 마이크로서비스 아키텍처에서 예외를 올바르게 사용하는 방법을 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

마이크로서비스 아키텍처에서 예외를 올바르게 사용하는 방법

예외의 올바른 사용은 마이크로서비스 아키텍처의 중요성 상위 3위에 랭크됩니다.

예외의 올바른 사용은 마이크로서비스 아키텍처의 중요성 3위에 속합니다. 어떤 의견도 없습니다. Curdboy 오랜만입니다. 모두 행복한 단오절을 보내시기 바랍니다. 최근 예외에 대해 이야기하고 싶습니다. 제 생각은 폐쇄 루프를 형성한 것 같습니다. 이 조합이 귀하의 비즈니스 코드에 도움이 되기를 바랍니다.

다음은 세계 최고의 언어와 생태학적으로 가장 완전한 언어에 대해서만 논의합니다.

예외의 유사점과 차이점

PHP7의 PHP 예외 디자인은 Java의 Exception 확장 Throwable과 일치하지만 역사적 이유와 디자인 개념에는 여전히 미묘한 차이가 있습니다. 예를 들어, PHP의 예외에는 코드 속성이 있으므로 동일한 예외로 클러스터된 여러 예외가 있으며, catch 블록의 코드에 따라 다른 비즈니스 논리 코드가 작성됩니다.

Java 예외에는 코드가 없으며 이와 같이 설계할 수 없습니다. 다양한 예외는 다양한 상황에만 사용할 수 있습니다. 따라서 우리는 투명한 예외 전송에 직접 의존하는 대신 외부 세계에 노출될 때 패키징 클래스를 통해 서비스를 캡슐화하는 데 익숙합니다.

균일한 예외 처리

Java 코드에서 가장 비판받는 점은 수많은 try catch가 있다는 점입니다. 그냥 코드 조각을 가져오세요

@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 이외의 예외를 내부적으로 기록하고 더 이상 외부에 노출하지 않는 것입니다. 그러나 문제 해결을 용이하게 하려면 requestId를 통해 분산 링크를 함께 연결하고 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 모니터링으로 더 정확합니다. an example

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

여기서 모니터링하는 것은 예외 로그를 ​​기록하는 로그입니다. ①

Exceptions in PHP

위의 Java에서 언급한 문제는 PHP에도 존재합니다. 3가지 방법으로 AOP를 시뮬레이션하지 않으면, PHP가 세계 최고의 언어라는 것을 반영할 수 없습니다

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

위와 유사한 구조 로직은 의사 코드로 반복해서 작성할 필요가 없으며 기본적으로 동일합니다. RuntimeException을 상속하도록 자체 비즈니스 예외 클래스를 사용자 정의한 다음 외부 출력 처리를 수행하는 것도 가능합니다.

하지만 PHP에는 원래 설계되었을 때 많은 런타임 예외가 알림 및 경고 오류로 출력되었지만 오류 출력에는 문제 해결에 도움이 되지 않는 호출 스택이 부족했습니다. 특정 매개변수 및 호출 스택을 볼 수 없습니다. set_error_handler + ErrorException을 사용하면 매우 명확해집니다.

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(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();
}

를 수정하면 캡처할 수 없습니다. 던진 내용이 PHP이기 때문입니다 Fatal error: Uncaught TypeError, PHP7에는

class Error가 Throwable을 구현한 다음 PHP에서 추가되었습니다. 시스템 오류 로그에는 Stack이 있지만 전체 비즈니스 시스템과 직렬로 연결할 수는 없습니다. 여기서는 Java와 같은 TraceId를 통해 모든 로그를 직렬로 연결하려고 합니다. Nginx 로그에서 PHP 정보 수준 로그 및 이러한 Uncaught TypeError의 일반 로그까지 시스템 오류 로그의 기본 출력을 인계받아 catch 코드 블록의 통합 위치에 기록합니다. 그런 다음 여기에서 오류 및 예외를 허용하도록

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

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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제