# openapi-project **Repository Path**: liu_qinglong/openapi-project ## Basic Information - **Project Name**: openapi-project - **Description**: API接口调用平台,帮助企业、个人统一开放接口,减少沟通成本,避免重复造轮子,为业务高效赋能。 普通用户:注册登录,开通接口调用权限,使用接口。 后台:调用统计和可视化分析接口调用情况,管理员发布接口、下线接口、新增接口。 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 7 - **Forks**: 2 - **Created**: 2023-07-06 - **Last Updated**: 2025-01-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # openapi-project ### 介绍 API接口调用平台,帮助企业、个人统一开放接口,减少沟通成本,避免重复造轮子,为业务高效赋能。 普通用户:注册登录,开通接口调用权限,使用接口。 后台:调用统计和可视化分析接口调用情况,管理员发布接口、下线接口、新增接口。 ### 技术选型 #### 前端 - Ant Design Pro - React - Ant Design Procomponents - Umi - Umi Requset (Axios 的封装) #### 后端 - Spring Boot - Spring Boot Starter(SDK开发) - 自定义RPC框架 - Nacos(注册中心) - Spring Cloud Gateway(网关、限流、日志实现) ### 业务流程 ![输入图片说明](openapi-backend/doc/%E4%B8%9A%E5%8A%A1%E6%B5%81%E7%A8%8B%E5%9B%BE.png) ## 项目特点 - 根据业务流程,将整个项目后端划分为 Web 系统,接口服务,公共模块,客户端 SDK,API 网关五个子项目,使用 Maven 聚合项目,进行多模块依赖管理和打包。 - 后端使用 Swagger + Knife4j 自动生成 OpenAPI 规范的接口文档,前端在此基础上使用插件自动生成接口请求代码,降 低前后端协作成本 - 为防止接口被恶意调用,设计 API 签名认证算法,为用户分配唯一 ak / sk 以鉴权,保障调用的安全性、可溯源性(指便 于统计接口调用次数)。 - 为解决开发者调用成本过高的问题(须自己使用 HTTP + 封装签名去调用接口,平均 20 行左右代码),基于 Spring Boot Starter 开发了客户端 SDK ,一行代码 即可调用接口,提高开发体验。 - 为解决多个子系统内代码大量重复的问题,抽象模型层和业务层代码为公共模块,并使用自定义的RPC 框架实现子系统间 的高性能接口调用,大幅减少重复代码。 - 选用 Spring Cloud Gateway 作为 API 网关,实现了路由转发、访问控制、流量染色,并集中处理签名校验、请求参数 校验、接口调用统计等业务逻辑,提高安全性的同时、便于系统开发维护。 - 前端:参考 Swagger2+knif4j等 API 管理产品实现了 API 文档浏览及在线调用功能,提供 Json 编辑器与显示器来提升 用户输入请求参数 json 的体验。 - 客户端 SDK 尽量用最少的依赖,基于 Spring Boot Starter 自主设计 SDK ,保证 SDK 的精简、避免依赖冲突。 - 为解决开发者调用成本过高的问题(须自己使用 HTTP + 封装签名去调用接口,平均 20 行左右代码),基于 Spring Boot Starter 开发了客户端 SDK ,一行代码 即可调用接口,提高开发体验。 SDK 已经上传至 Maven 中央仓库 ![输入图片说明](openapi-backend/image.png) ## 项目截图 ![输入图片说明](openapi-backend/doc/image1.png) ![输入图片说明](openapi-backend/doc/image2.png) ![输入图片说明](openapi-backend/doc/image3.png) ![输入图片说明](openapi-backend/doc/image4.png) ## 自定义 RPC 框架 ### 架构图: ![输入图片说明](openapi-backend/doc/rpc-architure.png) 服务提供端 Server 向注册中心注册服务,服务消费者 Client 通过注册中心拿到服务相关信息,然后再通过网络请求服务提供端 Server。 1. 注册中心 :使用 Zookeeper。注册中心负责服务地址的注册与查找,相当于目录服务。服务端启动的时候将服务名称及其对应的地址(ip+port)注册到注册中心,服务消费端根据服务名称找到对应的服务地址。有了服务地址之后,服务消费端就可以通过网络请求服务端了。 2. 网络传输 :既然要调用远程的方法就要发请求,请求中至少要包含你调用的类名、方法名以及相关参数,使用基于 NIO 的 Netty 框架。 3. 序列化 :Kryo 是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积。 4. 动态代理 : 另外,动态代理也是需要的。因为 RPC 的主要目的就是让我们调用远程方法像调用本地方法一样简单,使用动态代理可以屏蔽远程方法调用的细节比如网络传输。也就是说当你调用远程方法的时候,实际会通过代理对象来传输网络请求。 5. 负载均衡 :在客户端实现了基于一致性哈希算法的负载均衡。 6. 借用Spring容器相关功能,通过注解注册和消费。 ## 使用 ### 服务提供端 实现接口: ```java @Slf4j @RpcService(group = "test1", version = "version1") public class HelloServiceImpl implements HelloService { static { System.out.println("HelloServiceImpl被创建"); } @Override public String hello(Hello hello) { log.info("HelloServiceImpl收到: {}.", hello.getMessage()); String result = "Hello description is " + hello.getDescription(); log.info("HelloServiceImpl返回: {}.", result); return result; } } @Slf4j public class HelloServiceImpl2 implements HelloService { static { System.out.println("HelloServiceImpl2被创建"); } @Override public String hello(Hello hello) { log.info("HelloServiceImpl2收到: {}.", hello.getMessage()); String result = "Hello description is " + hello.getDescription(); log.info("HelloServiceImpl2返回: {}.", result); return result; } } ``` 发布服务(使用 Netty 进行传输): ```java /** * Server: Automatic registration service via @RpcService annotation * * @author shuang.kou * @createTime 2020年05月10日 07:25:00 */ @RpcScan(basePackage = {"github.javaguide.serviceimpl"}) public class NettyServerMain { public static void main(String[] args) { // Register service via annotation new AnnotationConfigApplicationContext(NettyServerMain.class); NettyServer nettyServer = new NettyServer(); // Register service manually HelloService helloService2 = new HelloServiceImpl2(); RpcServiceProperties rpcServiceConfig = RpcServiceProperties.builder() .group("test2").version("version2").build(); nettyServer.registerService(helloService2, rpcServiceConfig); nettyServer.start(); } } ``` ### 服务消费端 ```java @Component public class HelloController { @RpcReference(version = "version1", group = "test1") private HelloService helloService; public void test() throws InterruptedException { String hello = this.helloService.hello(new Hello("111", "222")); //如需使用 assert 断言,需要在 VM options 添加参数:-ea assert "Hello description is 222".equals(hello); Thread.sleep(12000); for (int i = 0; i < 10; i++) { System.out.println(helloService.hello(new Hello("111", "222"))); } } } ``` ```java ClientTransport rpcRequestTransport = new SocketRpcClient(); RpcServiceProperties rpcServiceConfig = RpcServiceProperties.builder() .group("test2").version("version2").build(); RpcClientProxy rpcClientProxy = new RpcClientProxy(rpcRequestTransport, rpcServiceConfig); HelloService helloService = rpcClientProxy.getProxy(HelloService.class); String hello = helloService.hello(new Hello("111", "222")); System.out.println(hello); ```