天穹-Api接口自动化管理系列2:MiApi- 多协议接口扫描器详解

本文主要是介绍天穹-Api接口自动化管理系列2:MiApi- 多协议接口扫描器详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

开源地址

https://github.com/XiaoMi/mone/tree/master/miapi-all/mi-api 欢迎对云原生技术/研发效能感兴趣的小伙伴加入(Fork、Star)我们。

概述

在上一篇对平台的整体介绍中我们介绍了平台提供的核心能力与流程,并且在服务接口信息的加载生成中大致描述了我们获取生成接口数据信息的主要逻辑。本文将深入到源码层面为大家介绍我们是如何实现接口文档数据的扫描解析与生成的。

业务依赖包

在前文中我们提到了获取业务项目数据的关键组件/模块,即注解、扫描器、缓存

为了获取上述我们所需的业务项目接口的基本数据,我们需要业务方引入我们提供的一套依赖包,针对不同协议接口的服务我们提供了针对性的依赖包,例如对提供 Http 接口、Dubbo接口的项目,我们分别设计提供了适配 Http、适配 Dubbo 的依赖包。这些包将用于不同协议接口数据的解析、数据推送、服务注册等等。

注解定义
作用

注解用于在项目中做一些特定的标记,例如模块层标记、接口层标记、字段层标记等,用于后续的扫描器扫描解析过程。

实现

在设计之初我们首先基于我们定义的接口交付流程,明确我们需要哪些数据。我们希望业务项目在运行之后,研发人员即可在平台中搜索自身项目模块的信息,并根据选择的模块信息加载生成相应的接口数据。这里的模块我们希望是研发人员习惯的、在代码中明确定义的类。

例如对于一个Http接口,基于传统的 mvc 架构,通常该接口入口将实现于 xxxController 类下,例如:

@RestController
@RequestMapping()
public class HelloController {@RequestMapping(path = "/hello",method = RequestMethod.POST)
public Response<String> getToken() {Response<String> r = new Response<>();r.setData("hello");return r;
}
}

那么我们希望用户可以在搜索框直接搜索关键字 HelloController,即可看到该项目下所有模块、接口信息,并针对性选择加载生成文档。

而对于一个Dubbo接口如下:

public interface DubboHealthService {Result<Health> health2(AaReq aaReq);
}@DubboService(timeout = 1000, group = "staging",version = "1.0")
@Slf4j
public class DubboHealthServiceImpl implements DubboHealthService {@Overridepublic Result<Health> health(AaReq aaReq) {Health health = new Health();health.setVersion("1.0");return Result.success(health);}

我们希望可搜索的模块为接口定义,即 DubboHealthService ,用户只需要搜索该接口,即可获取到该项目下所有 interface及其方法列表,并基于这两者进行筛选加载生成文档。

综上针对我们需要获取的接口数据,在业务依赖包中我们提供了几项基础注解:@EnableApiDocs、@ApiModule、@ApiDoc、@ParamDefine。(对于不同协议接口,注解命名上略有不同)

  • @EnableApiDocs

  • @EnableApiDocs用于启动类 Bootstrap上,用作开关,用户可以根据填不添加该开关,决定是否启用数据扫描推送功能。

@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.xxx.xxx.hello", "com.xxx.xxx"})
@DubboComponentScan(basePackages = "com.xxx.xxx.hello")
@ServletComponentScan
@EnableDubboApiDocspublic class HelloBootstrap {private static final Logger logger = LoggerFactory.getLogger(HelloBootstrap.class);public static void main(String... args) {try {SpringApplication.run(HelloBootstrap.class, args);} catch (Throwable throwable) {logger.error(throwable.getMessage(), throwable);System.exit(-1);}}
}

例如,如上一个提供 dubbo 接口的项目只需要在启动类上添加 @EnableDubboApiDocs 注解,即可启用该功能。

这个启动类注解的实现也很简单:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Inherited
@Import({DubboApiDocsAnnotationScanner.class})
public @interface EnableDubboApiDocs {
}

实际上我们只是在这个注解中 @Import 入依赖包中的扫描器类,那么只要添加该注解,spring便会帮我们把扫描器类初始化进容器中,后续的行为都将由扫描器执行。

  • @ApiModule

  • 该注解用于标注模块类,其实现如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ApiModule {/*** 用于自定义模块名*/    String value();/*** 用于定位模块类的类型* dubbo api interface class* 若为http接口,则该选项为 apiController*/    Class<?> apiInterface();}

该注解将在扫描器的扫描过程中用于从spring容器中筛选需要解析的类信息,同时也提供关于这些类的基本信息。

对于Http接口通常用法如下:

@RestController
@RequestMapping()
@HttpApiModule(value = "这是一个controller类HelloController", apiController = HelloController.class)
public class HelloController {
}

对于Dubbo接口通常用法如下:

@DubboService(timeout = 1000, group = "staging",version = "1.0")
@Slf4j
@ApiModule(value = "健康检测服务", apiInterface = DubboHealthService.class)public class DubboHealthServiceImpl implements DubboHealthService {
}
  • @ApiDoc

  • 该注解用于标注具体需要生成文档的接口,实现如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface ApiDoc {/*** api name.用于自定义接口名*/    String value();/*** api description.自定义接口描述文档*/    String description() default "";}

通常用法如下:

@Override
@ApiDoc(value = "健康监测方法",description = "这是一个用于健康监测的方法")
public Result<Health> health(AaReq aaReq) {Health health = new Health();health.setVersion("1.0");return Result.success(health);
}
  • @ParamDefine

  • 该注解用于具体参数字段的定义,实现如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Documented
@Inherited
public @interface ParamDefine {/*** 参数名*/String value();/*** 该参数是否必填*/boolean required() default false;/*** 是否忽略该字段,若是,在生成文档时该字段将被忽略*/boolean ignore() default false;/*** 字段说明描述*/String description() default "";/*** 默认值,若有默认值,将以该值生成mock数据*/String defaultValue() default "";}

通常用法如下:

@Data
public class AaReq implements Serializable {@ApiDocClassDefine(value = "用户名a",required = true,description = "这里是用户名参数",defaultValue = "dongzhenixng")private String username;@ApiDocClassDefine(value ="年龄",required = false,description = "用户年龄",defaultValue = "23")private Integer age;/**
* 也可以不使用该字段,平台将默认提取该字段基本信息,如参数名、类型等*/    private double age21;}
扫描器
作用

扫描器(scanner)为本项目的核心,我们基于jdk的反射能力,在运行时获取项目的服务、接口信息数据。

实现

扫描器由 @EnableApiDocs 开关导入 spring容器,这里由于我们需要基于 spring 容器初始化后的bean数据作为解析目标,因此扫描解析的动作必须发生在spring完成基本初始化操作后,因此这里实现了 spring 开放的 ApplicationListener 接口,该接口能够接收 spring 的项目触发的一系列事件。这里我们接收 ApplicationReadyEvent,即在 spring 框架初始化完成项目基本信息后触发。

public class ApiDocsScanner implements ApplicationListener<ApplicationReadyEvent> {@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {//扫描解析逻辑......
}
}

spring 初始化完成后,项目的 bean 信息等都在spring的上下文 ApplicationContext 中维护,那么在扫描器中即可从中获取标识了 @ApiModule 的模块集合,之后通过反射逐一获取标识了该注解的某块类信息,构造模块数据结构,再通过反射获取每个模块下的方法列表,筛选带有 @ApiDoc 注解的方法,构造接口方法层的数据结构,再对标识了该注解的方法进行进一步处理。

在方法层级的处理中,同样使用反射的方式获取具体参数字段信息,这里根据字段的类型递归构造字段级的数据结构。当然,这里的解析操作较为复杂繁琐,我们需要区分针对不同的参数类型做针对性的解析,例如对于基本类型的参数怎么处理?对于对象object、List、Map、Set、Queue甚至是嵌套的泛型参数怎么处理?哪些类型不能循环递归?哪些需要针对性忽略......这些细节我们在开发以及测试的过程中做了大量的斟酌与兼容处理,这里我们不做详细介绍,感兴趣的朋友可以翻看我们开源的代码。

完成以上几个步骤的数据扫描与解析后,根据一定的规则聚合所有数据,再获取项目运行时本地 ip及使用的端口,调用平台提供的开放接口,将以上数据统一推送到平台,平台将以 ip:port 为唯一索引将数据存入平台数据库中。这里之所以使用 ip:port 作为唯一索引,是由于一般微服务业务项目不管是在开发过程中或者发布到测试环境、生成环境,它们大概率都将拥有多个实例,即同一个项目在多方、多处运行,不同实例的代码版本、开发进度可能不完全一致,因此我们希望用户在平台上可以选择指定的实例,针对性加载生成该实例的接口数据。

扫描器执行流程图

这篇关于天穹-Api接口自动化管理系列2:MiApi- 多协议接口扫描器详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/713789

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP