alibaba / Sentinel

A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)
https://sentinelguard.io/
Apache License 2.0
22.42k stars 8.03k forks source link

使用sentinel-sofa-rpc-adapter,在被调用方抛出InvocationTargetException时,如何使用自定义falback接收。 #2508

Open wentfar opened 2 years ago

wentfar commented 2 years ago

你好,我这边使用了SofaRpc,目前正在集成sentinel,目前使用了sentinel-sofa-rpc-adapter进行集成,调试发现程序执行到了SentinelSofaRpcProviderFilter,但是当被调用逻辑抛出异常时,被调用方接收到了异常信息,但是却没有异常的回调。

我的问题是,如何使用sentinel-sofa-rpc-adapter集成sentinel,并且能够在provider抛出InvocationTargetException时,进入到自定义的fallback函数中去。

环境: windows10,sofastack,sentinel

@sczyh30 @cdfive

cdfive commented 2 years ago

当被调用逻辑抛出异常...

是业务异常吗,还是限流异常(BlockException

如果是业务异常,会根据异常类型是否是SofaRpcException直接抛出,或者包装为SofaRpcException向上抛 处理逻辑在AbstractSofaRpcFilter#traceOtherException方法里

如果是限流异常,默认会使用DefaultSofaRpcFallback,里面抛出SentinelRpcException, 可通过SofaRpcFallbackRegistry#setProviderFallback指定自定义的限流fallback处理逻辑

wentfar commented 2 years ago

如果是业务异常,会根据异常类型是否是SofaRpcException直接抛出,或者包装为SofaRpcException向上抛 处理逻辑在AbstractSofaRpcFilter#traceOtherException方法里

是业务异常,我这边调试下来调用链大概如下:FilterChain->ProviderExceptionFilter->FilterInvoker->RpcServiceContextFilter->FilterInvoker->ProviderBaggageFilter->。。。->SentinelSofaRpcProviderFilter->。。。->ProviderInvoker

业务异常会在ProviderInvoker中被catch住,然后转换成正常响应,依次经过各个filter,最终返回到SentinelSofaRpcProviderFilter,不能进入AbstractSofaRpcFilter#traceOtherException。

难道我有啥特殊配置?

cdfive commented 2 years ago

是的,你描述的调用链没问题,SentinelSofaRpcProviderFilter类上面注解里配置了order = -1000,会在SofaRPC所有系统的Filter之后,ProviderInvoker之前执行。 如果是Service接口实现里的业务异常,会在ProviderInvoker反射调用catch住InvocationTargetException, 抱歉。。我前面描述是错的,由于ProviderInvoker对业务异常有处理不会向上抛,确实不会进AbstractSofaRpcFilter#traceOtherException

业务异常从语义讲跟Sentinel限流是无关的,项目中自行处理或者根据框架提供的扩展统一处理业务异常即可。 比如在consumer端,可以捕获到provider端抛的业务异常。 (例:在sentinel-demo-sofa-rpc里运行DemoProviderDemoConsumer,修改DemoServiceImpl#sayHello,里面抛一个业务异常)

或考虑在项目Provider端单独实现一个SofaRPC的Filter,里面判断SofaResponse是否有业务异常来实现项目的统一异常处理。 (判断可参考AbstractSofaRpcFilter#traceResponseException)

我的问题是,如何使用sentinel-sofa-rpc-adapter集成sentinel,并且能够在provider抛出InvocationTargetException时,进入到自定义的fallback函数中去。

这里自定义的fallback(实现SofaRpcFallback接口),它主要是用于处理限流异常(Sentinel的BlockException) 即当系统或者接口发生限流时的fallback处理,而不是业务异常的fallback处理。

因此实现是发生限流异常时才会进限流fallback中去, Sentinel对其它框架适配也是如此哈(如sentienl-dubbo-adapter、sentinel-spring-webmvc-adapter等)

如果要实现业务异常进入到自定义的fallback,可参考上面业务异常的处理思路。

wentfar commented 2 years ago

感谢,感谢,回答很专业,我再吸收理解一下。

wentfar commented 2 years ago

(例:在sentinel-demo-sofa-rpc里运行DemoProviderDemoConsumer,修改DemoServiceImpl#sayHello,里面抛一个业务异常)

我又真正触发了BlockException,在SentinelSofaRpcProviderFilter中的确进入了DefaultSofaRpcFallback,但是最终到/com/alipay/sofa/rpc/server/bolt/BoltServerProcessor.java:182,该行:MessageBuilder.buildSofaErrorResponse(e.getMessage()),如下:

// 真正调用
        response = doInvoke(serviceName, invoker, request);
         if (bizCtx.isRequestTimeout()) { // 加上丢弃超时的响应的逻辑
            throwable = clientTimeoutWhenSendResponse(appName, serviceName, bizCtx.getRemoteAddress());
            break invoke;
        }
    }
} catch (Exception e) {
    // 服务端异常,不管是啥异常
    LOGGER.errorWithApp(appName, "Server Processor Error!", e);
    throwable = e;
    response = MessageBuilder.buildSofaErrorResponse(e.getMessage());
}

// Response不为空,代表需要返回给客户端
if (response != null) {
    RpcInvokeContext invokeContext = RpcInvokeContext.peekContext();
    isAsyncChain = CommonUtils.isTrue(invokeContext != null ?
        (Boolean) invokeContext.remove(RemotingConstants.INVOKE_CTX_IS_ASYNC_CHAIN) : null);
    // 如果是服务端异步代理模式,特殊处理,因为该模式是在业务代码自主异步返回的
    if (!isAsyncChain) {
        // 其它正常请求
        try { // 这个try-catch 保证一定要记录tracer
            asyncCtx.sendResponse(response);
        } finally {
            if (EventBus.isEnable(ServerSendEvent.class)) {
                EventBus.post(new ServerSendEvent(request, response, throwable));
            }
        }
    }
}
} catch (Throwable e) {

最终在Consumer端得到SofaRpcException,而不是BlockException。