自动化文档系统:微服务下的 API 管理利器

2020-07-10 13:25:51 浏览数 (1)

随着微服务的盛行和服务粒度的细化,对我服务的 API 接口也越来越多。如果技术管理不到位,技术债的累积会导致服务接口数量爆炸,最后变成业务开发的沉重包袱。据说有的公司,微服务个数不超 300 但 API 接口成功超越5万,这数字估计任何人听到都会头大。

本文集合流行的API文档功能 swagger,国内开源的集中式文档管理系统 YApi 和个人之前的经验分享下微服务文档的管理和控制。

Swagger


Swagger 是一套基于 OpenAPI 规范构建的开源工具,可以帮助我们设计、构建、记录以及使用 Rest API。Swagger 主要包含了以下三个部分:

  • Swagger Editor:基于浏览器的编辑器,我们可以使用它编写我们 OpenAPI 规范。
  • Swagger UI:它会将我们编写的 OpenAPI 规范呈现为交互式的 API 文档,后文我将使用浏览器来查看并且操作我们的 Rest API。
  • Swagger Codegen:它可以通过为 OpenAPI(以前称为 Swagger)规范定义的任何 API 生成服务器存根和客户端 SDK 来简化构建过程。

目前 springfox 已整合了 swagger 开源功能,下面用基于 spring boot 的 bookinfo-service 的例子来带大家体验下 swagger ui 的强大。

maven 依赖

这里只列出 swagger 的依赖包,springboot 的省去,不会的可以在 https://start.spring.io/ 自动生成一个开箱即用的 spring boot 项目。

代码语言:javascript复制
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.4.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.4.0</version>
</dependency>

swagger 配置

API 文档信息和自动扫描的 basepackage 配置如下:

代码语言:javascript复制
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

    @Bean
    public Docket buildDocket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(buildApiInf())
                .select()
                .apis(RequestHandlerSelectors.basePackage("demo.tke.sc.bookinfo.service"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo buildApiInf(){
        return new ApiInfoBuilder()
                .title("Bookinfo Service APIs")
                .description("图书服务的 API 文档,本页面采用 swagger 自动生成,可以查看图书服务对外提供的所有 API 及其对应的方法和参数说明,同时提供了在线测试功能。关于 swagger 的详细功能,请访问其官网:https://swagger.io")
                .termsOfServiceUrl("https://cloud.tencent.com")
                .contact(new Contact("森林木", "https://cloud.tencent.com", "yuanlinbao@tencent.com"))
                .build();

    }
}

API 接口文档注解

bookinfo-service 例子中提供了两个 controller,管理用的 AdminController 和用户访问用的 UserController。

  • @Api 注解用于标注服务接口类
  • @ApiOperation 注解用于标注方法
  • @ApiParam 注解用于标注方法参数
代码语言:javascript复制
@Api(description="Admin APIs",tags={"图书管理接口"})
@RestController
@RequestMapping("/admin")
public class AdminController {

    @ApiOperation("添加图书")
    @PostMapping(value = "/addBook")
    public @ResponseBody
    Response<String> addBook(@ApiParam(name="bookInfoDto",value="Json 格式的图书信息",required=true) @RequestBody BookInfoDto bookInfoDto) {

        Response<String> resp = new Response<String>();
        resp.setCode(200);
        resp.setMsg("【添加图书】"   bookInfoDto.getTitle()   " 成功!");

        return resp;
    }

    @ApiOperation("删除图书")
    @DeleteMapping(value = "/removeBook/{isbn}")
    public @ResponseBody Response<String> removeteBook(@ApiParam(name="isbn",value="国际标准书号ISBN,长整型",required=true) @PathVariable Long isbn) {
        Response<String> resp = new Response<String>();
        resp.setCode(200);
        resp.setMsg("【删除图书】 ISBN "   isbn    " 成功!");
        return resp;
    }

    @ApiOperation("修改图书信息")
    @PostMapping(value = "/modifyBookInfo")
    public @ResponseBody Response<String> modifyBookInfo(@ApiParam(name="bookInfoDto",value="Json 格式的图书信息",required=true) @RequestBody BookInfoDto bookInfoDto) {

        Response<String> resp = new Response<String>();
        resp.setCode(200);
        resp.setMsg("【修改图书】"   bookInfoDto.getTitle()   " 成功!");

        return resp;
    }
}
代码语言:javascript复制
@Api(tags={"用户接口"}, description = "User APIs")
@RestController
@RequestMapping("/bookinfo")
public class UserController {

    @ApiOperation("查询图书信息")
    @GetMapping(value = "/{isbn}")
    public @ResponseBody
    Response<BookInfoDto> getBookInfo(@ApiParam(name="isbn",value="国际标准书号ISBN,长整型",required=true) @PathVariable Long isbn) {
    
        BookInfoDto bookInfo = new BookInfoDto();
        bookInfo.setIsbn(isbn);
        bookInfo.setTitle("Dubbo Mesh");
        bookInfo.setAuthor("jack");
        bookInfo.setPrice(100.00f);

        return new Response<BookInfoDto>(200, bookInfo);
    }

    @ApiOperation("图书列表")
    @PostMapping(value = "/bookList")
    public @ResponseBody Response<List<BookInfoDto>> listBook() {

        BookInfoDto bookInfo1 = new BookInfoDto();
        bookInfo1.setIsbn(9787111653240L);
        bookInfo1.setTitle("云原生:运用容器、函数计算和数据构建下一代应用");
        bookInfo1.setAuthor("[美] Boris,Scholl,[美] Trent,Swanson 著,季奔牛 译");
        bookInfo1.setPrice(75.1f);

        BookInfoDto bookInfo2 = new BookInfoDto();
        bookInfo2.setIsbn(9787111644682L);
        bookInfo2.setTitle("Istio服务网格技术解析与实践");
        bookInfo2.setAuthor("王夕宁");
        bookInfo2.setPrice(94.1f);

        List<BookInfoDto> bookInfos = new ArrayList<BookInfoDto>(2);
        bookInfos.add(bookInfo1);
        bookInfos.add(bookInfo2);

        return new Response<List<BookInfoDto>>(200, bookInfos);
    }
}

POJO 也可以添加文档说明,主要有 @ApiModel 用于标注 pojo class; @ApiModelProperty用于标注字段, 其中 required 可以用来标注一个字段是否必须。

代码语言:javascript复制
@Data // 不想写 get set 方法的话,保留此注解同时添加 lomok 依赖
@ApiModel(description="图书信息类" )
public class BookInfoDto {

    @ApiModelProperty(value = "国际标准书号 ISBN ", required = true)
    private Long isbn;

    @ApiModelProperty(value = "书名", required = true)
    private String title;

    @ApiModelProperty(value = "作者", required = true)
    private String author;

    @ApiModelProperty(value = "价格", required=false)
    private Float price;
}

至此,代码和配置都已完成,本地编译运行后 http://localhost:8080/swagger-ui.html (若设置了 server.port 请将 8080 改成对应的端口号)即可看到 swagger 的文档界面。

整个界面很清爽,所有的 API 一目了然。点击一个具体的接口方法,会展示参数说明等详细信息。Try it out! 按钮可以在发起线测试,同时执行结果也会详细展示。具体见下面的图片说明。

在 Model Schema 下的输入框点击鼠标,会在 Value 下的输入框中填上默认数据,简单修改下,点击 Try it out! 按钮就会发起线上真实调用,并下面展示出服务端的响应,具体见下面的图标说明:

在小步快跑的互联网行业,加上微服务的开发模式,维持一份及时更新且完整的API 文档将会极大的提高我们的工作效率。传统意义上的手工编写文档,很难落地而且也难保证文档的及时性,这种文档久了也就会失去参考意义,反而会加大我们的沟通成本。采用 Swagger 自动化维护 API 文档的方式具有如下优点:

  • 文档随代码及时变化。只需要少量的注解,Swagger 就可以根据代码自动生成 API 文档,很好的保证了文档的时效性。
  • Swagger UI 是一个交互式的 API 文档,我们可以直接在文档页面尝试 API 的调用,省去了准备复杂的调用参数的过程。

YApi


Swagger 虽然很直观,单个服务而言很易用,但服务数量一旦多起来就不够便利。总不能没查看一个服务的接口文档就要找一遍对应url,因而还需要一个集中式的文档管理服务。下面介绍一款国产开源的文档服务工具 YApi。

虽然 YApi 官网 有详细的安装说明,但在具体安装过程中,还是遇到了些坑,下面介绍下在 CVM 上具体安装过程。

安装 YApi

YApi 依赖 nodejs, mongodb 和 git,其中 nodejs 需要 7.6 版本,mongodb 需要 2.6 版本。

  • 安装 Node.js
代码语言:javascript复制
# 执行以下命令,下载 Node.js Linux 64位二进制安装包。
wget https://nodejs.org/dist/v10.16.3/node-v10.16.3-linux-x64.tar.xz

# 执行以下命令,解压安装包。
tar xvf node-v10.16.3-linux-x64.tar.xz

# 依次执行以下命令,创建软链接。
ln -s /root/node-v10.16.3-linux-x64/bin/node /usr/local/bin/node
ln -s /root/node-v10.16.3-linux-x64/bin/npm /usr/local/bin/npm

# 依次执行以下命令,查看 Node.js 及 npm 版本信息。
node -v
npm -v

# 执行以下命令,安装 git。
yum install -y git
  • 手工安装 YApi
代码语言:javascript复制
mkdir yapi
cd yapi
git clone https://github.com/YMFE/yapi.git vendors
cp vendors/config_example.json ./config.json  # 复制完成后请修改相关配置
cd vendors
npm install --production --registry https://registry.npm.taobao.org
# 安装程序会初始化数据库索引和管理员账号,管理员账号名可在 config.json 配置
npm run install-server 
# 启动服务
node server/app.js

关于 YApi 的具体配置,可参考 官方文档 。

使用 demo

下面以一个简单的图书管理系统 BMS,来展示下 YApi 的使用。BMS 由4个服务组成:

  • BMS-Web:图书管理系统的 web 服务
  • user-service:用户管理服务
  • store-service:图书信息管理服务
  • loan-service:图书借阅服务

BMS 下的服务使用 swagger 生成了文档信息,见下图:

通过 swagger-ui 界面里的文档元数据连接可以看到下面的 API 文档 JSON。

接下来在 YApi 中建好分组和项目,让后将上面的 json 导入系统中。可以将上图中的 json 保存成本地文件,拖入数据管理导入窗口中即可。YApi 还提供了 cli 的数据导入方式,可点击数据导入窗口中的 通过命令行导入接口数据 查看具体操作方式。

数据导入成功后,就可以在接口 tab 页看到所有的接口:

点击具体接口后,可以看到详细的请求和响应参数和说明。

下面是借书接口的请求和响应数据字段的详细说明。还可以在编辑 tab 对接口的信息进行维护。

对于实际业务系统,微服务会很多,可以在 YApi 下通过分组和项目进行分类管理。分组可以对应一个领域或平台,项目可以对应领域或平台下的微服务,比如下图 Demo 中的图书管理系统下面有 4 个微服务,每个微服务又提供了很多对外的 API,那么可以通过项目分组进行管理。通过这种树桩分类管理可以很清晰的进行文档管理和查看,为应用开发之间的对接带来便利。

写在最后

上面介绍的两款工具都是开源的,swagger 和 YApi 可以相互辅助,利用 swagger 生成API文档元数据,再通过 YApi 进行集中管理。如果觉得不够方便,还可以自己打造一个中央文档系统,笔者的前东家唯品会就是这么做的,开发使用一条命令就可以生成API元数据并上传到中央文档系统中,使用更便捷。

0 人点赞