【Apache ShenYu源码】看看贡献者如何实现支持提醒通知设计

相信大家碰到源码时经常无从下手🙃,不知道从哪开始阅读,面对大量代码晕头转向,索性就读不下去了,又浪费了一次提升自己的机会😭。

我认为有一种方法 ,可以解决大家的困扰!那就是通过阅读某一次开源的【PR】,从这个入口出发去阅读源码!!

至此,我们发现自己开始从大量堆砌的源码中脱离开来😀,柳暗花明又一村。

一、前瞻

Ok,开始我们今天的PR阅读

翻译过来大致意思就是添加提醒通知的功能。翻译如下:

支持提醒通知设计

  • Shenyu admin 提供警报报告 API,/alert/report用于从网关 pulgin 接收警报内容
  • 网关在警报触发时发送警报消息
  • 神宇仪表板支持管理警报接收者名称,警报类型(电子邮件,钉钉,微信...)

我们可以思考下今天的阅读线索了

  1. 什么情况下会触发该警报信息
  2. 要支持多种警报类型,贡献者的代码是怎么设计成可扩展的

二、探索

话不多说,先整体看下本次PR的整体提交,从全局看下做了哪些修改。

还有很多提交没有截图下来,本次提交代码量可谓巨大了,那我们就先看看最核心的功能shenyu-alert模块

先思考下,既然是告警,核心接口就是发送告警接口 ,可以看到AlertNotifyHandler的send接口 ,我们就从这个入口开始探索,看看线索1的答案。

java 复制代码
public interface AlertNotifyHandler {
    
    /**
     * send alert.
     *
     * @param receiver Notification configuration information
     * @param alert    Alarm information
     * @throws AlertNoticeException when send receiver error
     */
    void send(AlertReceiverDTO receiver, AlarmContent alert) throws AlertNoticeException;
    
    /**
     * alert type.
     *
     * @return type
     */
    byte type();
}

通过引用来查询 ,发现send()方法最终的调用者是通过Controller来触发告警,很奇怪,告警不是应该内部触发吗?

java 复制代码
@RestApi("/alert/report")
public class AlertReportController {
    
    @Autowired
    private AlertDispatchService alertDispatchService;
    
    /**
     * report new alert content.
     *
     * @param alarmContent AlertContentDTO
     * @return row int
     */
    @PostMapping
    public ShenyuAdminResult reportAlert(@Valid @RequestBody final AlarmContent alarmContent) {
        alertDispatchService.dispatchAlert(alarmContent);
        return ShenyuAdminResult.success(ShenyuResultMessage.CREATE_SUCCESS);
    }
    
}

我们重新回顾下贡献者在PR写下的注释,有提到下面这一条:

Shenyu admin 提供警报报告 API,/alert/report用于从网关 pulgin 接收警报内容

也就是说告警是通过http请求触发,同时触发对象是网关的各个pulgin插件。

那我们再看看pulgin插件什么情况下会发送告警的http请求呢。

我们直接通过全局搜索/alert/report,看看哪些地方触发该http请求。

再找到对应执行的代码:

java 复制代码
public class GlobalErrorHandler implements ErrorWebExceptionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(GlobalErrorHandler.class);

    /**
     * handler error.
     *
     * @param exchange  the exchange
     * @param throwable the throwable
     * @return error result
     */
    @Override
    @NonNull
    public Mono<Void> handle(@NonNull final ServerWebExchange exchange, @NonNull final Throwable throwable) {
        LOG.error("handle error: {} formatError:{} throwable:", exchange.getLogPrefix(), formatError(throwable, exchange.getRequest()), throwable);
        HttpStatus httpStatus;
        Object errorResult;
        String errorMsg = "";
        if (throwable instanceof IllegalArgumentException) {
            httpStatus = HttpStatus.BAD_REQUEST;
            errorResult = ShenyuResultWrap.error(exchange, httpStatus.value(), throwable.getMessage(), null);
            errorMsg = throwable.getMessage();
        } else if (throwable instanceof ResponseStatusException) {
            httpStatus = ((ResponseStatusException) throwable).getStatus();
            String errMsg = StringUtils.hasLength(((ResponseStatusException) throwable).getReason()) ? ((ResponseStatusException) throwable).getReason() : httpStatus.getReasonPhrase();
            errorResult = ShenyuResultWrap.error(exchange, httpStatus.value(), errMsg, null);
            errorMsg = errMsg;
        } else {
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
            errorResult = ShenyuResultWrap.error(exchange, httpStatus.value(), httpStatus.getReasonPhrase(), null);
            errorMsg = httpStatus.getReasonPhrase();
        }
        exchange.getResponse().setStatusCode(httpStatus);
        Map<String, String> labels = new HashMap<>(8);
        labels.put("global", "error");
        labels.put("component", "gateway");
        AlarmSender.alarmMediumCritical("ShenYu-Gateway-Global-Error", errorMsg, labels);
        return WebFluxResultUtils.result(exchange, errorResult);
    }

}

可以看到调用者实现了Spring的ErrorWebExceptionHandler 类,也就是说这个警报的实际调用者是用Spring内置的web错误处理器

到这里我们就解决了我们的阅读线索1了。

什么情况下会触发该警报信息

还没完呢,我们继续阅读线索2的探索:要支持多种警报类型,贡献者的代码是怎么设计成可扩展的

既然要可扩展,肯定有底层接口在设定规则,我们找下这个底层接口。

这个底层接口其实还是我们上文提到的send接口 ,可以看到send方法的子类实现有钉钉、邮箱通知。

两个子类实现都是实现相同的底层接口AlertNotifyHandler ,只要在配置上配置哪个通知实现,ShenYu alert模块便会实例化对应的通知实现,也就能达到可扩展的目的。

java 复制代码
@Component
final class EmailAlertNotifyStrategy implements AlertNotifyHandler { }
java 复制代码
@Component
final class EmailAlertNotifyStrategy implements AlertNotifyHandler { }

三、总结

在阅读中,还发现了有个html文件忘记加了开源协议,我们提下PR修复下,又收获了一次开源贡献!!

PR提交戳这。

未完待续。。。

好了,今天的分享就到这🤔。大家能否感受到通过PR这种方式来阅读源码的乐趣呢

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

相关推荐
0和1的舞者12 分钟前
基于Spring的论坛系统-前置知识
java·后端·spring·系统·开发·知识
Maggie_ssss_supp1 小时前
Linux-MySQL主从复制
github
invicinble1 小时前
对于springboot
java·spring boot·后端
码界奇点2 小时前
基于Spring Boot与Vue的校园后台管理系统设计与实现
vue.js·spring boot·后端·毕业设计·源代码管理
爱编程的小庄2 小时前
Rust 发行版本及工具介绍
开发语言·后端·rust
cg50173 小时前
Continue插件实现本地部署一个“cursor”或“github copilot”
github·copilot·curcor
Apifox.3 小时前
测试用例越堆越多?用 Apifox 测试套件让自动化回归更易维护
运维·前端·后端·测试工具·单元测试·自动化·测试用例
sunnyday04263 小时前
Nginx与Spring Cloud Gateway QPS统计全攻略
java·spring boot·后端·nginx
康王有点困4 小时前
Link入门
后端·flink
妙娲种子4 小时前
gittee连接github镜像
gitee·github