是谁的请求导致我的系统一直抛异常?

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 微服务引擎MSE面向业界主流开源微服务项目, 提供注册配置中心和分布式协调(原生支持Nacos/ZooKeeper/Eureka)、云原生网关(原生支持Ingress/Envoy)、微服务治理(原生支持Spring Cloud/Dubbo/Sentinel,遵循 OpenSergo 服务治理规范)能力。

在线上环境中,请求错综复杂,如果有某个请求出现了不符合预期的情况,我们往往会先需要确定这个请求在实际环境中是由哪个Controller来处理的。通常情况下,我们需要去查阅文档或是代码,这个过程往往比较繁琐,并且不一定是准确的,可能由于一些问题会导致我们的请求没有被预期的Controller处理。而借助微服务洞察的能力,能够快速地定位特定的请求在真实环境中是由哪个Controller处理的。


流程分析

本文的demo包含log-demo-spring-cloud-zuul、log-demo-spring-cloud-a、log-demo-spring-cloud-b、log-demo-spring-cloud-c四个应用,采用最简单的 Spring Cloud 标准用法依次调用,可以直接在 https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/mse-simple-demo 项目上查看源码。


以SpringCloud为例,请求到达后会由org.springframework.web.servlet.DispatcherServlet#doDispatch方法处理请求的整体流程。

  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
      ModelAndView mv = null;
      Exception dispatchException = null;
      try {
        processedRequest = checkMultipart(request);
        multipartRequestParsed = (processedRequest != request);
        // Determine handler for the current request.
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
          noHandlerFound(processedRequest, response);
          return;
        }
        // Determine handler adapter for the current request.
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        // Process last-modified header, if supported by the handler.
        ……
        // Actually invoke the handler.
        ……
      }
    }
  }

可以看到决定由哪个handler来处理请求的功能由org.springframework.web.servlet.DispatcherServlet#getHandler方法负责。该方法所返回的org.springframework.web.servlet.HandlerExecutionChain类的实例,就是最后实际处理该请求的handler。也就是说我们只需要借助微服务洞察的能力观测这个方法的返回值即可。


微服务洞察


首先,简单介绍一下要使用的微服洞察能力,该能力基于规则的模型,以动态增强的方式为我们获取真实的现场信息。它的规则模型如下图所示:

image.png

arget:

  • ResourceTarget: 目标接口,支持Web、Rpc、SQL 以及任意的自定义方法
  • WorkloadTarget: 目标实例,可以选择所有机器或指定机器 IP
  • TrafficCondition: 是否仅针对异常、慢调用、全链路灰度标签

Action:

  • 相关上下文诊断信息的收集,参数、返回值、线程上下文、Target对象、类加载器信息等
  • 后续链路是否日志打印
  • 流量染色、限流降级等治理动作

在本文的场景中,我们需要将org.springframework.web.servlet.DispatcherServlet#getHandler设为我们的Target,目标实例为默认的全部,不添加流量过滤条件。


随后,我们选择想要打印的内容,在这个场景中我们所需要是该方法的返回值,其他的内容可以根据需要选择。

image.png

在开启规则之后,我们可以在控制台中看到如图的调用链展示,可以看到/a这个请求在log-demo-spring-cloud-a中的调用栈在基础的框架记录中新增了我们所选择的方法org.springframework.web.servlet.DispatcherServlet#getHandler的相关记录,从右侧的Attributes中的mse.return字段中可以看到该请求会由java.lang.String com.alibabacloud.mse.demo.AApplication$AController.a来处理。

image.png


辅助异常分析


线上的请求出现了异常,在定位问题的过程中,我们往往会需要知道调用的堆栈信息,进而去排查堆栈上的方法。借助微服务洞察的能力,我们也可以很方便的进行这些操作。

本场景在前文的Demo中增加了对数据库的访问,使用了Druid作为连接池组件

当发现某个url的请求部分报错,但我们并没有预先编写能够记录有效信息的日志,这时我们就可以通过一条规则来打印现场的堆栈信息,以获取我们需要排查的方法列表,再进一步对逐个方法进行分析。假设是/sql的请求出现了部分报错,我们选择/sql作为Target,如果不知道具体的接口,也可以选择全部。

image.png

由于我们只需要分析错误的请求,所以在过滤规则条件中开启异常过滤,在打印内容中选中调用堆栈,其他的内容可以根据需要选择。

image.png

开启该规则后,可以在控制台看到堆栈信息。

image.png

at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:989)
  at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeQuery(DruidPooledPreparedStatement.java:213)
  at com.alibabacloud.mse.demo.service.DruidCon.doCommond(DruidCon.java:57)
  at com.alibabacloud.mse.demo.service.DruidService.query(DruidService.java:15)
  at com.alibabacloud.mse.demo.BApplication$AController.sql(BApplication.java:89)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

截取其中一部分可以发现com.alibabacloud.mse.demo.service.DruidCon.doCommond以及com.alibabacloud.mse.demo.service.DruidService.query都是我们自身的业务逻辑方法,也是我们需要关注的方法,我们可以继续借助微服务洞察的能力,去获取这些方法的现场信息,比如参数、返回值、类加载器等等。

这边以获取com.alibabacloud.mse.demo.service.DruidCon.doCommond方法的参数信息为例。同样只需要一条规则,类似前文,将该方法设为Target,打开异常过滤标签,在打印内容中选中请求参数,开启规则后,便可以在控制台看到该方法的参数信息。

image.png

以上只是简单的例子,但是能够由此发现,微服务洞察的能力能够让我们在Java方法任意点位收集信息,将排查工作变成零代码且动态的,由于不需要在测试环境中重复增加日志代码并不断重启应用,能够大大减小某些难以在测试环境中复现的问题的排查难度。


在非预期的情况下,工作在最优解


微服务洞察能力提供了Java方法任意点位的可观测能力,本文通过该能力演示了:观测请求的匹配、观测异常场景下请求的上下文等场景,可以帮助我们在系统出现异常时进行问题定位,除了本文的场景还有很多微服务的场景能够借助微服务洞察能力来观测,将原本复杂且交织的微服务场景清晰地展现在我们面前。微服务洞察能力我们还在持续地打磨与完善,而发现并定位问题并不是目的,对于系统稳定来说只是迈出了第一步,我们还需要进一步结合治理能力才能够真正让系统稳定运行,比如本文的场景中可以针对异常的请求进行限流,或是快速地隔离故障,或是针对不稳定的服务进行容错降级等等,从而保护我们的系统。

在真实环境中,系统的不稳定性不仅仅来自于我们自身的业务逻辑错误,还有很多可能比如突发的大流量,上下游的不稳定等等。而这些问题都可以交给MSE,使系统在非预期的情况下仍能工作在最优解。

image.png

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
相关文章
|
27天前
|
Java 应用服务中间件 测试技术
NoInitialContextException 异常通常会在哪些场景下出现
NoInitialContextException 异常通常在尝试使用 JNDI(Java 命名和目录接口)进行资源查找时,因缺少必要的环境配置或初始化上下文未正确设置而出现。常见于 Java EE 应用中。
40 5
|
7月前
|
SQL 安全 程序员
C++:异常
C++:异常
64 7
|
运维 编译器 C语言
异常(C++)
异常(C++)
75 1
|
7月前
|
Java 程序员 数据库连接
|
7月前
|
C语言 C++
C++异常
C++异常
53 0
|
7月前
|
C++
C++中的异常
C++中的异常
|
7月前
|
程序员 编译器 Shell
C++『异常』
C++『异常』
65 0
|
安全 Java 程序员
C++ 异常
C++ 异常
48 0
|
安全 程序员 C语言
一日一技:不使用 try...except 掩盖一些已知异常
一日一技:不使用 try...except 掩盖一些已知异常
71 0