性能文章>我在新公司做的第一个技术优化>

我在新公司做的第一个技术优化原创

297933

你好,我是yes。


我相信大部分人到新公司都会有阵痛,不管是环境的改变、流程的不适、新同事的磨合还是项目上的熟悉。

关于这方面的我到时候再写一篇文章,今天来盘盘我看到的项目里面的一个小小优化点。

起因

入职后比较重要一点就是快速上手项目,也就是熟悉需求以及代码,作为一个新人我肯定是能毫无感情的发现项目里诸多奇怪的点(不会管历史遗留包袱,因为我根本不知道)。


当然,作为新人你不能过于激进地用批判的眼光去看一切,存在即合理。


不过我发现公司项目里有一系列类确实是冗余的,这些类的功能纯粹是为了包装 dubbo 的接口来对 RPC 调用进行统一的 catch。


所以只要有一个依赖外部项目的 rpc 接口,对应就有一个包装的 service。


我简化下包装的 serivce 逻辑,咱们看下代码就清晰了。


比如要调用远程的订单服务,对应的项目里就创建了一个订单服务包装类,拿通过 id 获取订单方法来看,内部逻辑如下所示:

 

public OrderDTO getOrderById(Long id) {
    Response res;
    try {
        res = orderSerivce.getOrderById(id); //orderService是外部服务接口
    } catch (Exception e) {
        logger.error("远程服务抛错,根据 ID 获取订单异常, ID: {}", id);
        throw new RpcInvocationException(e);
    }
    if (!res.isSuccess()) {
        logger.error("远程服务业务抛错,根据 ID 获取订单失败, ID: {}", id);
        throw new RpcBusinnessException(res.getErrorInfo());
    }
    return res.getResult();
}


包装类大概就是上面这个意思,这个包装意义就是为了判断调用下游接口的报错到底异常还是失败。


如果是异常的话说明下游出现了预知外的错误,属于 RPC 调用出错,而失败指的是预知内的错误,属于业务错误。


这样一来通过报错就可以很方便的辨别调用下游接口的情况,便于问题的排查。


但是我们想想,一个服务依赖诸多下游接口,每来一个我们都为之建一个类来封装之,是不是很麻烦?


100 个外部接口我们就需要创建 100 个包装类。


解决方案

那如何解决这个麻烦呢?我们想想可以有哪几种方案:

  • 代码生成器
  • aop
  • dubbo filter

代码生成器这个属于定制化插件开发了。


想必我们应该都用过 idea 里面的 Generate 吧?就是在这里做个扩展。

像没 lombok 前, getter 和 setter 都是用它来生成的,还有我们的单元测试类的生成等等。

089CD1FD-4A9D-4082-AE76-5B379A4B0108.png

也就是说写个插件,在此扩展,根据模板解析对应的 dubbo 接口,一键生成对应的类。

这个方案比较麻烦,要熟悉插件的开发,这是一个成本,而且虽然重复的代码不需要人为去写,但是项目中还是会生成很多里面的逻辑看起来都是重复的包装类。


当然,如果你们项目组已经在维护一个自己的插件了,那么可以基于此扩展,这种生成出来的代码也更容易定制修改逻辑,比如可能要为某个 rpc 接口编写专门特殊的逻辑等。


AOP

这个就不说了,常规代理操作,想必大家项目里或多或少都会用到 Spring AOP 功能。


Dubbo Filter

这是我们今天这篇文章要讲的重点。


其实到公司一堆包装类的代码,我脑子第一个蹦出来想法就是基于 Dubbo 的 Filter 进行扩展。


因为我们用的是 dubbo 框架,其次上述包装 service 仅是为了解析返回的 rpc 结果,里面的逻辑高度重复。


从高度重复我们就要想到抽象封装,减少重复冗余代码。


从目的来说我们仅为了解析 rpc 结果进行 catch,方便判断是下游异常还是业务抛错。


所以我们只要统一在 dubbo 调用结果返回之前进行拦截判断,就可以去除这些冗余的代码。


此时就要联想到 dubbo 遗留了哪些扩展点供我们增强功能呢?


那就是 dubbo spi,在这个场景再具体些就是里面的 Filter 机制。


在这篇文章dubbo 靠它崭露头角里,我已经详细剖析了 spi 机制,这篇不深入 spi 逻辑,仅结合 Filter 简单讲下用法。

 

自定义 Filter 实战

首先我们看下 Dubbo 提供的  Filter 接口,这里的源码是 2.7.5 版本。

9EE1894C-FD4E-4ABA-92DD-94F2AE3AE9C3.png


我们的第一步就是实现 Filter 接口,写一个自定义的异常类。


ps:这里的为了方便说明,我写的自定义 Filter 逻辑比较简单,生产上使用的话还是要细化很多东西的

需要注意的几个点我已经用红框标注了。


@Activate(group = CommonConstants.CONSUMER) 这个表明仅在消费端生效,我们的目的就是消费端消费的时候对结果进行解析。


实现 Filter 和 Filter.Listener,老版本的例子应该会实现 Filter 里面的 onResponse ,不过既然已经标注废弃了,我们就别用了。


onMessage 里面就是上面例子的逻辑了,可以看到还是很简单的,dubbo 也封装的很好,直接根据 appResponse 就能判断是否有异常,然后进行日志的记录和异常的封装。


实现完自定义 Filter 之后,如何应用到我们的项目中呢?


也非常简单,SPI 都帮我们封装好了。


我们仅需在项目的 /resources/META-INF/dubbo 目录下添加一个 SPI 配置文件,文件名就是 Filter 路径 org.apache.dubbo.rpc.Filter,表明我们要扩展的是 Filter。


而文件里面的内容就是我们自定义的 Filter 路径,具体如下图所示:

2BD96307-D8E0-4DEC-B93C-B24CCE3FE2CE.png


图片dubbo 在加载的时候会扫描上述的目录,找到 Filter 扩展,并根据文件内容加载我们自定义 Filter ,这样过滤链上就有我们的罗家啦,即自定义 Filter 就生效了。


自定义 Filter 使用示例

我在消费端简单写个消费逻辑,就是调用 provider 的方法:

然后在 provider 端对应方法里模拟业务抛错:

2BD96307-D8E0-4DEC-B93C-B24CCE3FE2CE.png


调用消费端方法可以看到 Filter 生效了:

63E1065A-209D-44C9-A209-75667BCCE187.png


然后我们再在 provider 端对应方法里模拟异常抛错:

86B90A39-B870-478A-A325-F69A59FEC315.png


此时再调用消费端方法可以看到也成功处理了:

最后

好了,这个小优化到此结束。


通过开源框架提供的扩展机制其实可以方便的处理很多问题,一般脱颖而出的框架肯定会预留很多供我们定制化的手段,所以我们需要好好掌握这些框架,这样可以提高我们平日开发的效率,写起代码来更加得心应手与灵活。


这里提一下,其实 dubbo 自身已经实现了很多 Filter 类供我们使用:


图片其实看名字能猜出很多 Filter 的作用,这里我就不一一介绍了,有兴趣的小伙伴可以自己去看看,避免重复造轮子,使用方法和自定义的一样,也是利用 SPI 机制。


好嘞,今天的分享到此结束,这段时间我在负责一个反射框架的开发,等后面写完好了我再来分享下。

我是yes,从一点点到亿点点,我们下篇见~

💥看到这里的你,如果对于我写的内容很感兴趣,有任何疑问,欢迎在下面留言📥,会第一次时间给大家解答,谢谢!

 

分类:标签:
请先登录,查看3条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

Java如何进行线上问题排查?
导语Java的线上故障排查,是每个程序员必备的技能。我们项目上线后,不是随时都有条件debug,也不是随时都能查到对应的日志文件,有时关键位置也不一定打日志,所以我们需要一个完成的排查过程,来解决这些突发事件。本文是一篇干货文章,从多个方面来解决Java的线上情况。正文内存瓶颈freefr
让bug无处藏身,Java 线上问题排查神器分享!
导语本文总结了一些常见的线上应急现象和对应排查步骤和工具。分享的主要目的是想让对线上问题接触少的同学有个预先认知,免得在遇到实际问题时手忙脚乱。 正文这里先提示一下。在线上应急过程中要记住,只有一个总体目标:尽快恢复服务,消除影响。不管处于应急的哪个阶段,我们首先必须想到的是恢复问
我在新公司做的第一个技术优化
你好,我是yes。我相信大部分人到新公司都会有阵痛,不管是环境的改变、流程的不适、新同事的磨合还是项目上的熟悉。关于这方面的我到时候再写一篇文章,今天来盘盘我看到的项目里面的一个小小优化点。起因入职后比较重要一点就是快速上手项目,也就是熟悉需求以及代码,作为一个新人我肯定是能毫无感情的发现项
腾讯云和阿里云tcp三次握手的区别
前言近日同事遇到一个诡异的问题,帮忙进行了排查,好家伙不查不知道,一查让我知道了,腾讯云和阿里云TCP三次握手居然还有差异,没有想到云厂商这种Iass级别的服务,还有不同的标准~问题现象•客户是半托管客户,我们部署服务请求阿里云的nginx,nginx作为LB,反向代理了N个java
99%的Java程序员会踩的6个坑
大家好,我是苏三,又跟大家见面了。前言作为Java程序员的你,不知道有没有踩过一些基础知识的坑。有时候,某个bug,你查了半天,最后发现竟然是一个非常低级的错误。有时候,某些代码,这一批数据功能正常,但换了一批数据就出现异常了。有时候,你可能会看着某行代码目瞪口呆,心里想:这行代码为什么会出错?
【全网首发】一次jstack 的排查之旅
前言今天阳光明媚,天气真好,奈何是工作日,不然大睡一场,岂不美哉。还残留在昨天的美梦中,突然群内的告警把我拉回了现实,奈何还在车上,不能拿上电脑,疯狂输出。群内告警,接口频繁超时,运维查看了机器情况,超时的接口的应用的CPU,居高不下,如下图好家伙,一大早就给我喂饭,其实这个问题也没什么,
【全网首发】JavaAgent寄生在目标进程中引起的ClassNotFoundException
引起ClassNotFoundException异常的原因有很多种,今天我们来介绍一种原因,有因有果有解决方法。
【全网首发】chrome自动更新到104版本居然引起Java服务内存泄漏
前言近期在工作中,遇到了一次很有意思的内存泄漏,把排查过程和思路记下来,供大家参考和学习,如有不正确的,欢迎指正。起因最近几天很多半托管客户,突然报连接服务失败,登上服务器后查看内存很高,为了让客户尽快恢复业务,运维同事第一时间选择了重启。重启后,内存肉眼可见的速度涨了上来,研发同事判断后
3
3