搜索
首页后端开发PHP问题在微服务架构中异常如何正确使用

本篇文章给大家介绍一下在微服务架构中异常正确使用的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

在微服务架构中异常如何正确使用

异常的正确使用在微服务架构中的重要性排前三,没什么意见吧

异常的正确使用在微服务架构中的重要性排前三,没什么意见吧

Curdboy 们好久不见,先祝大家端午节快乐。最近想说说异常,我的思考俨然形成了闭环,希望这套组合拳能对你的业务代码有所帮助。

下面只讨论世界上最好的语言和生态最完整的语言,没什么意见吧。

异常的异同

PHP 在 PHP7 异常的设计和 Java 保持一致了 Exception extends Throwable  ,不过在历史原因和设计理念上还是有一些细微的差别。比如 PHP 中的异常是有 code 属性的,这样就存在多种异常聚类为同一个异常,然后在catch 区块里根据 code 写不同的业务逻辑代码。

而 Java 异常则没有code ,不能这样设计,只能针对不同的情况使用不同的异常。所以我们习惯服务对外暴露的通过包装类来封装,而不是直接依赖异常的透传。

统一异常的处理

在 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,可能存在一些潜在的风险,永远不能相信调用者,即使他根正苗红三代贫农也不行。因为不能确定调用者会将该错误信息作何处理,可能就直接作为 json 返回给了前端。

RuntimeException

在 Java 中异常可以分为运行时异常和非运行时异常,运行时异常是不需要捕获的,在方法上也不需要标注 throw Exception,比如我们在方法里使用 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;
}

异常监控

说好的闭环呢,使用了自定义异常类之后,对异常日志的监控报警的阈值就可以降低不少,报警更加精准,以阿里云 SLS 的监控为例

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

这里监控的是记录异常日志① 的日志

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 里有一些历史包袱,起初设计的时候很多运行时异常都是作为 Notice,Warning 错误输出的,但是错误的输出缺少调用栈,不利于问题的排查

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 Fatal error:  Uncaught TypeError,PHP7 新增了
class Error implements Throwable,则在 PHP 系统错误日志里会有 Stack,但是不能和整个业务系统串联起来,这里就又不得不说日志的设计,我们期望像 Java 那样通过一个 traceId 将所有的日志串联起来,从 Nginx 日志到 PHP 里的正常 info level 日志以及这些Uncaught TypeError,所以接管默认输出到系统错误日志,在 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就能接受Error和Exception了。

但是 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。如有侵权,请联系admin@php.cn删除
酸与基本数据库:差异和何时使用。酸与基本数据库:差异和何时使用。Mar 26, 2025 pm 04:19 PM

本文比较了酸和基本数据库模型,详细介绍了它们的特征和适当的用例。酸优先确定数据完整性和一致性,适合财务和电子商务应用程序,而基础则侧重于可用性和

PHP安全文件上传:防止与文件相关的漏洞。PHP安全文件上传:防止与文件相关的漏洞。Mar 26, 2025 pm 04:18 PM

本文讨论了确保PHP文件上传的确保,以防止诸如代码注入之类的漏洞。它专注于文件类型验证,安全存储和错误处理以增强应用程序安全性。

PHP输入验证:最佳实践。PHP输入验证:最佳实践。Mar 26, 2025 pm 04:17 PM

文章讨论了PHP输入验证以增强安全性的最佳实践,重点是使用内置功能,白名单方法和服务器端验证等技术。

PHP API率限制:实施策略。PHP API率限制:实施策略。Mar 26, 2025 pm 04:16 PM

本文讨论了在PHP中实施API速率限制的策略,包括诸如令牌桶和漏水桶等算法,以及使用Symfony/Rate-limimiter之类的库。它还涵盖监视,动态调整速率限制和手

php密码哈希:password_hash和password_verify。php密码哈希:password_hash和password_verify。Mar 26, 2025 pm 04:15 PM

本文讨论了使用password_hash和pyspasswify在PHP中使用密码的好处。主要论点是,这些功能通过自动盐,强大的哈希算法和SECH来增强密码保护

OWASP前10 php:描述并减轻常见漏洞。OWASP前10 php:描述并减轻常见漏洞。Mar 26, 2025 pm 04:13 PM

本文讨论了OWASP在PHP和缓解策略中的十大漏洞。关键问题包括注射,验证损坏和XSS,并提供用于监视和保护PHP应用程序的推荐工具。

PHP XSS预防:如何预防XSS。PHP XSS预防:如何预防XSS。Mar 26, 2025 pm 04:12 PM

本文讨论了防止PHP中XSS攻击的策略,专注于输入消毒,输出编码以及使用安全增强的库和框架。

PHP接口与抽象类:何时使用。PHP接口与抽象类:何时使用。Mar 26, 2025 pm 04:11 PM

本文讨论了PHP中接口和抽象类的使用,重点是何时使用。界面定义了无实施的合同,适用于无关类和多重继承。摘要类提供常见功能

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境