转载

Spring Boot高效数据聚合之道

来源丨Feego

juejin.im/post/5d064b90e51d45777540fda7

Spring Boot高效数据聚合之道

富士山和双岩,日本松崎 (© Tommy Tsutsui/Getty Images)

背   景 

接口开发是后端开发中最常见的场景, 可能是RESTFul接口, 也可能是RPC接口. 接口开发往往是从各处捞出数据, 然后组装成结果, 特别是那些偏业务的接口.

如何方便快速的开发高性能的接口, 是一个必须思考的问题.

例如, 我现在需要实现一个接口, 拉取 用户基础信息 + 用户的博客列表 + 用户的粉丝数据 的整合数据, 假设已经有如下三个接口可以使用, 分别用来获取 用户基础信息 , 用户博客列表 , 用户的粉丝数据 .

用户基础信息:

用户博客列表:

用户的粉丝数据:

注意, 每一个方法都 sleep了 1s以模拟业务耗时.

我们需要再封装一个接口, 来拼装以上三个接口的数据.

PS:这样的场景实际在工作中很常见, 而且往往我们需要拼凑的数据, 是要走网络请求调到第三方去的. 另外可能有人会想, 为何不分成3个请求? 实际为了客户端网络性能考虑, 往往会在一次网络请求中, 尽可能多的传输数据, 当然前提是这个数据不能太大, 否则传输的耗时会影响渲染. 许多APP的首页, 看着复杂, 实际也只有一个接口, 一次性拉下所有数据, 客户端开发也简单.

串 行 实 现 

编写性能优良的接口不仅是每一位后端程序员的技术追求, 也是业务的基本诉求. 一般情况下, 为了保证更好的性能, 往往需要编写更复杂的代码实现.

但凡人皆有惰性, 因此, 往往我们会像下面这样编写串行调用的代码

很明显, 上面的代码, 效率低下, 起码要3s才能拿到结果, 且一旦用到某个接口的数据, 便需要注入相应的 service, 复用麻烦.

并 行 实 现 

有追求的程序员可能立马会考虑到, 这几项数据之间并无强依赖性, 完全可以并行获取嘛, 通过 异步线程 + CountDownLatch + Future 实现, 就像下面这样.

上面的代码, 将串行调用改为并行调用, 在有限并发级别下, 能极大提高性能. 但很明显, 它过于复杂, 如果每个接口都为了并行执行都写这样一段代码, 简直是噩梦.

优雅的注解实现 

熟悉 java的都知道, java有一种非常便利的特性 ~~ 注 解 . 简直是黑魔法. 往往只需要给类或者方法上添加一些注解, 便可以实现非常复杂的功能.

有了注解, 再结合Spring依赖自动注入的思想, 那么我们可不可以通过注解的方式, 自动注入依赖, 自动并行调用接口呢? 答案是肯定的.

首先, 我们先定义一个聚合接口 (当然也可以不定义聚合类, 所有代码写在原Service 类中同样可以)

其中:

  • @DataProvider 表示这个方法是一个数据提供者, 数据  Id 为  userFullData

  • @DataConsumer 表示这个方法的参数, 需要消费数据, 数据  Id 分别为  userpostsfollowers .

当然, 原来的3个原子服务 用户基础信息 ,用户博客列表, 用户的粉丝数据, 也分别需要添加一些注解

其中:

  • @DataProvider 与前面的含义相同, 表示这个方法是一个数据提供者

  • @InvokeParameter 表示方法执行时, 需要手动传入的参数

这里注意 @InvokeParameter@DataConsumer 的区别, 前者需要用户在最上层调用时手动传参; 而后者, 是由框架自动分析依赖, 并异步调用取得结果之后注入的.

最后, 仅仅只需要调用一个统一的门面( Facade  )接口, 传递数据 Id , InvokeParameters ,以及返回值类型. 剩下的并行处理, 依赖分析和注入, 完全由框架自动处理.

如何用在项目中 

上面的功能, 已经封装为一个 spring boot starter , 并发布到 maven 中央仓库.

只需在你的项目引入依赖.

并在 application.properties 文件中声明注解的扫描路径.

之后, 就可以使用如下注解和 Spring Bean 实现聚合查询

  • @DataProvider

  • @DataConsumer

  • @InvokeParameter

  • Spring Bean  DataBeanAggregateQueryFacade

注意, @DataConsumer@InvokeParameter 可以混合使用, 可以用在同一个方法的不同参数上. 且方法的所有参数必须有其中一个注解, 不能有没有注解的参数.

特   性 

  • 异步获取依赖

所有 @DataConsumer 定义的依赖将异步获取. 当 provider 方法参数中的所有依赖获取完成, 才执行 provider 方法

  • 不限级嵌套

依赖关系支持深层嵌套. 上面的示例只有一层

  • 异常处理

目前支持两种处理方式: 忽略 or 终止

忽略是指 provider 方法在执行时, 忽略抛出的异常并 returnnull 值; 终止是指一旦有一个 provider 方法抛出了异常, 将逐级向上抛出, 终止后续处理.

配置支持 consumer 级或者全局, 优先级 : consumer 级 > 全局

  • 查询缓存

在调用 Facadequery 方法的一次查询生命周期内, 方法调用结果可能复用, 只要方法签名以及传参一致, 则默认方法是幂等的, 将直接使用缓存的查询结果. 但这个不是绝对的, 考虑到多线程的特性, 可能有时候不会使用缓存

后   记

若有错误或者不当之处,可在本公众号内反馈,一起学习交流!

更多热文在此:

   ●   Spring Boot 系列实战文章合集(源码已开源)

●   程序员写简历时必须注意的技术词汇拼写

●   基于Spring Security OAuth2 的SSO单点登录+JWT权限控制实战

●   从一份配置清单详解Nginx服务器配置

●   如何在Windows下像Mac一样优雅的开发

●   Docker容器可视化监控中心搭建

●   利用ELK搭建Docker容器化应用日志中心

●   RPC框架实践之:Google gRPC

●   一文详解 Linux系统常用监控工具

更多 务实、能看懂、可复现的 技术文章尽在公众号 CodeSheep ,欢迎扫码订阅,第一时间获取更新 :arrow_down::arrow_down::arrow_down:

Spring Boot高效数据聚合之道

原文  http://mp.weixin.qq.com/s?__biz=MzU4ODI1MjA3NQ==&mid=2247484263&idx=1&sn=7728befa701450392f1490fb0e11d173
正文到此结束
Loading...