项目大纲
前端
- React 18
- Ant Design Pro 5.x 脚手架
- Ant Design & Procomponents 组件库
- Umi 4 前端框架
- OpenAPI 前端代码生成
后端
- Java Spring Boot
- MySQL 数据库
- MyBatis-Plus 及 MyBatis X 自动生成
- API 签名认证(Http 调用)
- Spring Boot Starter(SDK 开发)
- Dubbo 分布式(RPC、Nacos)
- Swagger + Knife4j 接口文档生成
- Spring Cloud Gateway 微服务网关
- Hutool、Apache Common Utils、Gson 等工具库
- 项目介绍和计划
- 需求分析
- 业务流程和子系统介绍
- 技术选型(各技术作用讲解)
- 前后端项目初始化
- 前端 Ant Design Pro 框架最新版本教程
- 后端 Spring Boot 万用模板使用
- 数据库表设计
- 前后端代码自动生成(强烈推荐,大幅提高开发效率!)
- 登录页开发
- 接口管理功能开发(Ant Design 高级组件使用)
- 模拟 API 接口项目开发
- HTTP 接口调用
- API 签名认证详解及开发
- 客户端 SDK 开发(Spring Boot Starter)
- 管理员接口发布 / 下线功能开发
- 接口列表页开发
- 在线调试接口功能开发
- 接口调用统计开发
- 后端开发
- 优化方案分析及对比
- API 网关详解
- 网关介绍及优点
- 10 种网关应用场景
- 网关分类及技术选型
- Spring Cloud Gateway 网关实现
- 详细带读官方文档
- 统一业务处理:用户鉴权及接口调用次数统计
- 项目分布式改造
- 公共模块抽象
- RPC 和 HTTP 调用详解及对比
- Dubbo 框架讲解及示例项目开发
- Dubbo 业务实战
- 管理员统计分析功能
- 前端 2 种可视化库的使用
- 后端聚合查询接口开发
- 项目扩展点及上线分析
一、初始化前后端框架
项目前端初始化---脚手架安装
直接使用开始使用 - Ant Design Pro,按照他的步骤安装即可。
1、安装库(电脑安装一次即可)
(base) wangjia@wangbangjia ~ % sudo npm i @ant-design/pro-cli -g
2、在指定目录创建项目框架
(base) wangjia@wangbangjia 星球项目 % pro create yuapi-frontend
? 🐂 使用 umi@4 还是 umi@3 ? umi@4
🧎🏻全量区块暂时不支持 umi@4
3、安装依赖:yarn install安装package.json对应的依赖-----node_modules时,一定要在项目根目录!!!!
即可得到前端框架了
注意版本号控制
切换nodeMac下的nodeJs版本切换和升级_mac npm版本切换_-HaiXin的博客-CSDN博客
yarn版本控制yarn卸载与版本降级升级_yarn降级_votaries的博客-CSDN博客
注意还要使用yarn安装所有的依赖--缺少啥依赖就会报错--再安装也行
直接敲 yarn 安装依赖
这个依赖还是很重要的!
sudo yarn add eslint-plugin-unicorn --dev
前端与后台服务交互
大致流程:
1.UI 组件交互操作;
2.调用 model 的 effect;
3.调用统一管理的 service 请求函数;
4.使用封装的 request.js 发送请求;
5.获取服务端返回;
6.然后调用 reducer 改变 state;
7.更新 model。
2、项目后端初始化--
使用springboot-init框架就好了。
启动之前要application.yml配置Redis、数据库的连接信息。之后MySQL连接IDEA,
二、正式业务
- 第一期 -- 初始化和展示
- 项目设计、技术选型
- 基础项目的搭建
- 接口管理
- 用户查看接口
1、数据库表设计
--- 建立接口信息表
使用http://sqlfather.yupi.icu/ 根据字段生成sql语句
*后端生成SQL表的 CRUD
---MybatisX-generator
MyBatisX Generator 可以根据数据库表结构信息快速生成与数据库交互所需的
实体类、Mapper 接口、XML 映射文件等代码
第三幅图本地还会有module也要勾选
最终生成的generator目录结构
将每个文件粘贴复制到对应的目录中 删掉generator即可
后端增删改查 -- 调用现有代码完成。
*前端生成后端接口CRUD
前端:实现登录功能 + 接口信息增删改查
怎么让前端调用后台的接口,去实现接口信息的增删改查管理?
前端接口调用:oneapi插件自动生成 -- 使用openapi规范
已经有了后端,想要前端直接生成调用后端的代码,只需要将后端基于openapi的文档提供给前端即可
获取后端openapi的JSON文档:后端主页的分组URL连在ip:port/工程名/后面即可
例如后端主页地址http://localhost:7529/api/doc.html#/home
查看到分组URL为/v3/api-docs 所以
最终后端基于openapi的文档地址为http://localhost:7529/api/v3/api-docs
将config/config.ts中openAPI 对应的path改为http://localhost:7529/api/v3/api-docs并设置项目名
然后package.json运行openapi即可生成前端增删改查 调用后端的接口 在service/yuapi-backend中6个文件
看不懂,先熟悉一下。
然后跟着做完成测试。
测试:使用真实的后端接口,就要使用dev方式运行。
用户中心项目copy以下注册页面代码
成功使用后端数据生成接口信息,让前端调用 以用户身份登录成功。
第一节即将结束。先步道乐跑打卡吃饭,最近一段时间,又开始起飞。
** 问题解决
为啥刷新页面,就需要重新登陆?
--- 1、前端没有种上Cookie
---2、前端发请求没有带上Cookie
在requestConfig.ts 发送请求配置中添加withCredentials: true
(用于指定跨域请求时是否发送身份凭证(如 cookies、HTTP 认证等))
权限管理:
只有管理员可以操作的接口信息管理
service/access.ts为权限管理判断
还可以直接对数据库操作,设置其userRole 是user还是admin
管理员页面填充接口信息
TableList的index.ts的columns变量中改为自己需要展示的列,然后修改request的方法
无登录2;39;40
由于之前没有刚开始就删除国际化配置,之后再执行的,导致出现故障,现在登录界面无法加载,也就是权限校验没法执行,技术解决不了,趁着还没有进行太多,直接重新,从头来过。
- 第二期 -- 接口调用
- 继续开发接口管理前端页面
- 开发模拟api接口
- 开发调用这个接口的代码
- 保证调用的安全性 (api签名认证)
- 客户端SDK开发
- 管理员接口 发布 与调用
- 接口文档展示、接口在线调用
注意:Ant-design Pro给出的框架,什么增删改查就是个模板,根本上还是要自己调用,或者进行修改。
1、完成了第二期接口管理前端页面 -- pages/InterfaceInfo
对于表单的处理,最重要的就是定义好columns,其余要考虑的就是调用组件完成添加、删除、修改即可。
2、开发模拟API接口 -- yuapi-interface
也就是写一个能发送请求并响应的springboot小项目
3、调用接口
几种http调用方式
HttpClient、RestTemplate、第三方库(Hutool,OKHTTP)
使用Hutool
4、API签名认证
本质: 1、签发签名 2、使用签名(校验签名)
Cookie是第一次使用要权限校验,之后会保存在浏览器中,每次发请求登录就会将用户信息加在请求报文中,实现“无校验登录”
但是由于调用API用户资格无法得知,每一次调用API都要进行一次权限认证。
通过Http request Header传递参数
参数1、accessKey:调用的表示 userA、userB
参数2、secretKey:秘钥 --- 该参数不放到请求头中传递,用于生成签名
类似用户名和密码(区别:ak、sk无状态的)
不可将秘钥直接在服务器间传递,有可能被拦截。
加密方式:对称加密、非对称加密、MD5签名(不可解密)
参数3、用户请求参数
MD5加密:
参数4、sign参数
用户参数 + 秘钥 => 签名生成算法得到值(不可解密) (比如:yupi + abcdefgh = diaehufishroser)
服务器校验方法:
服务器用相同的参数和算法来生成签名,再与用户传的进行校验。
怎么放重放? (例如配置了代理,代理拦截获取签名,直接重放后发送)
参数5、加nonce随机数,只能用一次(服务端要保存已使用过的随机数)
参数6、加timestamp时间戳,检验时间戳是否过期
现在正在做的就是 按照6个参数 完成API签名认证 (客户端制作签名、服务端认证签名)
客户端咋做? :根据参数 + 加密算法 ==》 生成签名 在发送请求时,将签名放在请求头中。
使用Hutool工具发送http请求,获取相应数据、打印。
服务端咋做? :根据发送请求的URL映射到具体的API(方法),完成签名校验,返回响应数据。
API签名认证很灵活,采用什么签名算法只是一个选择问题。最重要的就是选择哪些参数用来生成最终的API签名要根据具体场景来选择。(比如userId、appid、固定值等)
思考:难道开发者每次调用接口都要写一遍签名算法?
开发一个简单易用的SDK
理想情况:开发者只需要关注调用哪些接口,传递什么参数,就像调用自己的代码一样。
开发starter好处:开发者引入之后,可以在applocation.yml中写配置,自动创建客户端。
spring-boot-configuration-processor作用是自动生成配置的代码提示
建立加分项 --- 写starter:
首先就是确定包含那些依赖;其次在Resources中写文件夹META-INF的spring.factories中指定配置的注册类xxxClientConfig,在注册类中通过注解@ConfigurationProperties("yuapi.client")读取到配置类信息
使用sdk,咋就创建bean失败了,改了好久,最基本的spring创建bean到地了。先不改了,之后还会有延伸,到时再改
第三期
- 开发接口发布/下线的功能(管理员)
- 前端去浏览接口、查看接口文档、申请签名(注册)
- 在线调试(用户)
- 统计用户调用接口的次数
- 优化系统-API网关
1、开发接口发布/下线的功能(管理员)
发布接口(仅管理员): 1、校验该接口是否存在 2、判断该接口是否可调用 3、修改接口数据库中的状态字段为1
todo 判断该接口是否可以调用时 由固定方法名 改为 根据测试地址来调用
下线接口(仅管理员): 1、校验该接口是否存在 2、修改接口数据库中的状态字段为0
在controller层 写发布与下线接口
controller 层写上线,下线接口就还可以,但是引入YuApiClient就不行了,显示是无法生产bean,刚写的是没问题的,直接用yupi的也是出现这个问题,继续找找吧,肯定是个小问题,找不到,就继续看视频,晚上再找。
依然还是没有找到,只用不用注入的bean就行,这个问题很奇怪,我在注入的时候,都还有提示,自动导入jdk,也都不报错,但是要用到实例的bean就报错了。
晚上准备API签名认证 API网关的代码讲解录制发布到B站上
一来是再次梳理一遍、二来就是以便后来项目复习快速回忆
刚刚在InterfaceInfoController中补充写了接口的操作函数--后端发布 & 下线的接口,但是前端仅仅是显示,咋进行调用?
前端不用写任何关于接口调用的方法,直接使用openapi生成
后端改了,想要前端进行调用,直接使用openapi生成一下
1、做了主页展示接口并实现调用 --- pages/Index 加载展示接口列表(√)
2、查看文档接口页面 --- pages/InterfaceInfo中展示 单个接口信息(√)
3、实现用户注册时生成签名 在UserServiceImpl在插入数据前 生成ak、sk 然后插入。(√)
由于后端初始化项目User内没有ak、sk字段,还要在实体以及对应的mapper中添加字段
现在出现的最大的问题就是 使用openapi根据后端代码自动生成前端调用后端的接口 生成的不全!!到底是后端缺少了啥,还是openapi生成出现了问题?
不是少一俩个函数,实体也都少了,不解决没法进行。有些函数之前写也还有啊,怎么就没了?
标准json数据格式:
[
{
"name": "username", "type": "string"
}
]
注意调用逻辑:前端 -> 后端 -> 接口服务
然后 接口服务 -> 后端 -> 前端
第四期
1、开发接口调用次数的统计 2、优化整个系统的架构(API网关)网关是啥?啥作用?应用场景及实现?结合业务去应用网关。
todo 调用接口测试失败,显示是json格式不匹配 下午解决---原来是自己id赋值没有把对象中的id数值取出来,导致直接把对象赋值给了id。
1、用户接口信息表 建表,使用MybatisX-Generator生成对应的三层代码文件,分别移动后,并对dao层实体-mapper、Service层方法、controller层增删改查 所有代码基本处理(√)
2、
统计每个接口的调用次数,是针对每个接口都写一下统计的方法?大量重复工作! 使用AOP优点:独立于接口,在每个接口调用之后统计次数+1 缺点:只存在于单个项目中,如果每个团队都要开发自己的模拟接口,那么都要写一个切面,仍然大量重复工作。终极解决方案:网关文档:Spring Cloud Gateway 中文文档
*API网关:
关键点:根据断言的路由及局部的过滤器yml配置;全局过滤器鉴权、签名认证、调用次数校验及+1等功能实现。
定义 :API网关是一种集成了配置发布、环境管理、接入认证,用户鉴权、访问控制等功能的 API管理和服务治理的工具。
使用API网关托管API,即可高效,安全、低成本的管理服务。API网关作为请求的单一入口点,将请求分配给相应的服务,然后收集结果并将其传递给请求者。而不是让客户单独请求访问每项微服务。
- 核心功能:路由、鉴权
- 管理上处理:负载均衡、跨域、业务处理、访问控制、发布控制、流量染色、接口保护
- 日志上处理:监控日志、报表
路由:起到转发的作用。比如有接口A和接口B,网关会记录这些信息,根据用户访问的地址和参数,转发请求到对应的接口。(服务器/集群)/a -> 接口A/b -> 接口B 负载均衡:路由的基础上/c -> 服务A / 集群A(随机转发到其中的某一个机器) 统一鉴权:判断用户是否具有权限进行操作。无论访问什么接口,都统一去判断权限,不用重复写。 统一处理跨域:网关统一处理跨域,不在每个项目单独处理 统一业务处理:把一些每个项目中都要做的通用的逻辑放到上层(网关),统一处理,比如本项目的次数统计 访问控制:黑白名单,比如限制DDOS ip 发布控制:灰度发布,比如上线新接口,先给新接口分配20%流量,老接口80%,再慢慢调整比重 流量染色:给请求(流量)添加一些标识,一般是设置请求头中,添加新的请求头统一接口保护:限制请求、信息脱敏、降级(熔断)、限流、超时时间统一日志:统一的请求、相应信息记录 统一文档:将下游项目的文档进行整合,在一个页面统一查看网关的分类:全局网关(接入层网关):作用是负载均衡业务网关(微服务网关):会有一些业务逻辑,作用是将请求转发到不同的业务、项目、接口、服务。实现1、Nginx(全局网关)、Kong网关(API网关)编程成本高2、Spring Cloud Gateway 性能高、可以用java代码写逻辑
建议开启日志 logging:level:org:springframework:cloud:gateway: trace
核心概念路由:根据什么条件断言:一组规则、条件,用来确定如何转发路由过滤器:对请求进行一系列的处理,比如添加请求头、添加请求参数请求流程:1、客户端发起请求2、Handle Mapping:根据断言,去将请求转发到对应的路由3、Web Handle:处理请求(一层层经过过滤器)4、实际调用服务俩种配置方式1、配置式(方便、规范)2、编程式(灵活)断言(作用:根据断言转发路由)1、After在XX时间之后2、Before在XX时间之前3、Between在XX时间之间4、请求类别5、请求头(包含Cookie)6、权重7、查询参数8、客户端地址过滤器(基本功能:对请求头、请求参数、响应头的增删改查)1、添加请求头2、添加请求参数3、添加响应头4、降级(设置备份地址)
鱼皮经验:
基础核心知识掌握了,其他的应用类的知识,晓得是什么,应用场景就行了,不会就直接查文档。
第五期1、实现统一的用户鉴权、统一的接口调用次数统计(把API网关应用到项目中)2、完善功能
业务逻辑
1、用户发送请求到API网关2、请求日志3、(黑白名单)4、用户鉴权(判断ak、sk是否合法)5、请求的模拟接口是否存在6、请求转发,调用模拟接口7、响应日志8、调用成功,接口调用次数 + 19、调用失败,返回一个规范的错误码
* 具体实现
* 1、请求转发
* 使用前缀匹配断言:所有路径为/api/**的请求进行转发,转发到http://localhost:8123/api/**
* 比如请求网关:http://localhost:8090/api/name/get?name=yupi
* 转发到: http://localhost:8123/api/name/get?name=yupi
* spring:
* cloud:
* gateway:
* routes:
* - id: yuapi_route
* uri: http://localhost:8123
* predicates:
* - Path=/api/** 现在是分布式项目,但是没有引入微服务,微服务啥子?
*签名认证ak-sk:
本质:1、签发签名 2、校验签名
关键点:生成签名工具
后台生成签名工具 & 网关重新生成进行比对校验
ak-sk 机制基础:
1、ak-sk 由用户注册时提供的用户名产生,并作为用户信息的一部分存储在用户信息表中;
2、accessKey被视为公钥,用于标识用户的身份;secretKey 则被视为私钥,用于加密签名;
accessKey是身份标识,根据请求者accessKey在数据库中拿到对应的有资格用户User,并拿到对应的secretKey,和请求者的body现场生成serverSign请求者在发送请求时就会调用签名生成工具生成签名sign,存储在请求头中,拿出来。将sign与serverSign对比。
ak-sk机制:请求者根据ak-sk已经生成了签名sign(请求体+秘钥),在发送请求时,将ak和sign一起放在请求头中,API网关拿到请求头中的ak与sign,根据ak去数据库确定是哪个用户,拿到对应的sk,重新现场生成serverSign,再完成签名校验。
后台签名工具
存储生成密钥参数的hashmap
// 存储秘钥,放在请求头中private Map<String,String> getHeaderMap(String body){Map<String, String> hashMap = new HashMap<>();hashMap.put("accessKey",accessKey); // 秘钥一定不能发送给后端hashMap.put("nonce", RandomUtil.randomNumbers(4));hashMap.put("body",body);hashMap.put("timestamp",String.valueOf(System.currentTimeMillis() / 1000));//使用签名工具生成 签名hashMap.put("sign",genSign(body,secretKey));return hashMap;}
生成签名工具:
public class SignUtils {public static String genSign(String body, String secretKey){// 创建 SHA256算法 的消息摘要器Digester md5 = new Digester(DigestAlgorithm.SHA256);// 将请求体和密钥拼接起来以生成待签名的内容String content = body + "." + secretKey;// 使用消息摘要器计算内容的哈希值,并以十六进制形式返回return md5.digestHex(content);} }
将签名封装到请求头中完成发送请求
public String getUserNameByPost(User user){String json = JSONUtil.toJsonStr(user);HttpResponse httpResponse = HttpRequest.post("http://localhost:8123/api/name/user/").addHeaders(getHeaderMap(json)).body(json).execute();System.out.println(httpResponse.getStatus());String result = httpResponse.body();System.out.println(result);return result;} }
API网关用户鉴权
1、获取请求头中的accessKeyHttpHeaders headers = request.getHeaders();String accessKey = headers.getFirst("accessKey"); 2、根据accessKey获取到对应的调用者User invokeUser = innerUserService.getInvokeUser(accessKey); 3、获取到接口调用者的secretKey String secretKey = invokeUser.getAccessKey(); 4、根据body内参数与secretKey生成服务端的签名String serverSign = SignUtils.genSign(body,secretKey); 5、将服务端现场生成的签名和请求头中的签名进行对比if(sign == null || !sign.equals(serverSign)){ return handleNoAuth(response); }
现在是API网关内部的签名认证错误!!!就是sign与serversign不同!!!真是出鬼了!!!到底是哪里出了问题??? --原来是写错了,手欠,排查了好久.
*项目架构 --调用接口流程:
- 平台管理员
- 上线接口:管理员可以将开发完成的接口上线,使其可供接口使用者调用。
- 下线接口:管理员可以将某个接口下线,即停止该接口的调用。
- 管理接口:
- 增:管理员可以添加新的接口到平台,包括接口名称、接口描述、接口URL等信息。
- 删:管理员可以删除平台上的某个接口。
- 改:管理员可以修改接口的信息,如接口名称、接口描述、接口URL等。
- 查:管理员可以查看平台上所有接口的信息,包括接口名称、接口描述、接口URL等。
- 发布接口:管理员可以发布开发完成的接口,以供接口使用者调用。
- 测试接口:管理员可以对接口进行测试,以确保其功能的正确性和稳定性。
- 接口使用者
- 浏览接口:使用者可以浏览平台上的所有接口,包括接口名称、接口描述、接口URL等信息。
- 开通接口:使用者可以开通某个接口,使其可供自己调用。
- 调用接口:使用者可以通过接口URL调用已开通的接口,向其发送请求并获取响应。
信息管理请求
接口调用请求
调用接口流程:
1、前端发送请求:在InterfaceInfo下(也就是接口详细信息)首先是点击《调用》按钮触发点击提交事件,本质上就是提交表单,所以在onfinish函数提交表单,调用invokeInterfaceInfoUsingPOST
函数发送请求(这个是根据后端invokeInterfaceInfo函数使用openapi生成)到
后端/api/interfaceInfo/invoke 。
2、后端接收请求:在controller层接收到,对请求的接口进行基本的有无/合理判断,获取当前登录用户的ak、sk,并根据yuapi-client-sdk封装好的客户端,根据登录用户的ak、sk进行
向模拟接口yuapi-interface发送请求
3、在yuapi-interface内根据ak、sk进行权限等鉴定,最后返回结果。
现在问题是:
前端发送到的地址是/api/interfaceInfo/invoke(后端backend)、但是会被API网关拦截,
目的路由是http://localhost:8123/api/name/user,后端backend也是会调用hutool发送http请求到模拟接口,,所以调用过程到底是谁调的?
--- 点击按钮请求的是前端页面,页面显示的端口是前端端口。请求到了前端之后,前端在逻辑处理里面调用后端,如果接入了网关,前端调用后端的端口应该是网关端口,从网关的策略中转发到对应的后端端口
五个模块:API网关-Web系统-模拟接口、公共模块、客户端SDK。
第六期
1、在API网关项目中,没有Mybatis,在获取用户的ak、sk需要读取数据库,想要实现在API网关项目中 快速调用其他项目中的代码,即复用其他项目代码 -- RPC 2、开发监控统计功能
怎么调用其他项目的方法? 1、复制代码和依赖、环境 2、HTTP请求(提供一个接口controller,供其他项目调用) 3、RPC 4、把公共的代码打个jar包,其他项目去引用(客户端SDK)
HTTP请求怎么调用? 1、提供方开发一个接口(地址、请求方法、参数、返回值) 2、调用方使用HTTP Client之类的代码包去发送HTTP请求RPC 作用:像调用本地方法一样调用远程方法 1、对开发者更透明,减少了很多的沟通成本 2、RPC向远程服务器发送请求时,未必要使用HTTP协议,也可以是TCP/IP
《使用dubbo框架,运用RPC快速远程调用方法》
过程分析:
整合运用zookeeper注册中心:通过内嵌的方式运行,更方便。最先启动注册中心,先启动provider,再启动consumer。1、backend项目作为服务提供者,提供三个方法:a.实际情况是应该去数据库中查是否已分配给用户b.从数据库中查询模拟接口是否存在,以及请求方法是否匹配(还可以校验请求参数)c.调用成功,接口调用次数 + 1 invokeCount2、gateway项目作为服务调用者,调用这3个方法
在分布式系统中,在微服务架构常见问题:
- 服务和服务地址如何进行映射和管理
- 服务注册后,如何被及时发现
- 服务异常时,如何进行降级
- 服务宕机后,如何及时下线
- 服务如何有效的水平扩展
- 服务发现时,如何进行路由
- 注册中心如何实现自身的高可用
一个下午和一个晚上,就干了dubbo框架下RPC服务。晚上总算是跑通了。
明天继续干。争取明天,第一个项目,终结。
*使用dubbo框架实现RPC:
Dubbo是一个高性能、轻量级的开源Java RPC框架(RPC意思是远程调用)
关键点三大类:依赖配置、启动类服务类引用类注解、注册中心nacos安装启动。
1、消费者与提供者安装依赖(dubbo框架 & nacos注册中心);配置yml自动完成注册<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.9</version></dependency><dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId><version>2.1.0</version> </dependency>
dubbo:application:name: dubbo-springboot-demo-providerprotocol:name: dubboport: -1registry:id: nacos-registryaddress: nacos://localhost:8848
2、springboot项目启动类写@Enabledubbo;提供服务的类写@DubboService;消费服务@DubboReference3、映射接口要是相同的路径、内部声明相同的方法提供者:映射接口com/yupi/project/provider/DemoService.java
消费者:映射接口com/yupi/project/provider/DemoService.java
内部声明相同的方法,实现快速调用
4、实现调用
注入:
@DubboReference(check=false) private DemoService demoService;
*抽象公共服务
抽象公共服务
1、项目名:yuapi-common
2、目的:让方法、实体类在多个项目间复用,减少重复编写。
3、服务抽取:数据库中查是否已分配给用户秘钥(根据 accessKey 拿到用户信息,返回用户信息,为空表示不存在)
4、从数据库中查询模拟接口是否存在(请求路径、请求方法、请求参数,返回接口信息,为空表示不存在)
5、接口调用次数 + 1 invokeCount(accessKey、secretKey(标识用户),请求接口路径)
6、步骤:新建干净的 maven 项目,只保留必要的公共依赖抽取 service 和实体类install 本地 maven 包让服务提供者引入 common 包,测试是否正常运行让服务消费者引入 common 包
@Resource 与 @DubboReference区别:
1、@Resource是Spring中注入bean ,对象存储在容器中 (同一个项目中的引用)
2、@DubboReference是Dubbo框架下RPC引用 (不同项目中的引用)
正在写获取统计数据的操作,使用的很多新东西,没必要敲,直接看,看分析结果。
* 客户端SDK
主要是封装了获取请求拿到ak,sk与请求体,生成签名sign的功能。这样对于以后的项目可以直接调用。
*统计分析:
最开始通过MySQL统计,每次调用后,网关发起RPC调用,调用次数+1;
但是这样子在使用Jmeter压测工具分析时会出现调用次数统计不准确,我分析出来是由于没有在事务层面加锁,导致数据库出现并发写的问题。
想过直接在事务层面加锁,但是会大大降低效率。最后想到可以使用Redis的Zset来实现排序,其中权重参数score用来存储调用次数。
总结复习: 1、查看接口统计信息饼图还没处理好2、扩展的点自己试着做,找大佬项目学习3、首先是宏观:掌握 各个流程。再细节上:从头到尾把每个细节处理点掌握;代码掌握;业务逻辑!技术:RPC、签名认证啥的宏观上你得明确每个技术点;细节上代码 看看参考项目要掌握哪些东西。 第一个项目,不要搞成夹生饭!!!
先业务-三层架构逻辑 -》》 再具体方法代码逻辑
*面经:
1、项目是你自己做的吗?你为什么做这样的一个项目?你做这个项目的背景(初衷)是什么?(几乎每次都被问到)
答:
我的初衷是尽可能地帮助和服务更多的用户和开发者,让他们更加方便快捷地获取他们想要的信息和功能。接口开放平台它可以帮助开发者快速接入一些常用的服务,从而提高他们的开发效率,比如天气服务、随机头像和短网址等服务,它们是一些应用或者小程序中常见的功能,所以提供这些接口可以帮助开发者更加方便地实现这些功能。这些接口也可以让用户在使用应用时获得更加全面的功能和服务,从而提高他们的用户体验。所以我认为接口开放平台是一个有意义的项目,可以为用户和开发者带来更多的便利和价值。
(注:因为我个人已经将项目上线,并能够提供一些真实的接口服务。有条件的同学尽量将项目上线。此外有两场的面试官想要查看数据库,我开了屏幕共享给他们看,所以要对数据库的表结构和设计有一定的了解。)
2、项目的架构你是怎么设计的?
答:Nginx部署 + 分布式架构
我采用前后端分离的架构,前端使用 Nginx 部署,通过 Nginx 反向代理将请求转发到 web 项目,因为项目刚刚上线,所以这里暂时采用了单机部署的模式,未来可能采取水平扩容的方式,增加多台节点,通过 Nginx 的负载均衡,将请求平均的分发到我的每个节点上,以支撑更高的并发。我的 web 项使用 Spring Boot 开发,并连接到了数据库和 Redis ,数据库使用的是 Mysql ,主要用来存储用户的信息和接口的信息;通过 redis 实现了分布式 session ,因为考虑到未来要使用分布式架构,为了避免使用 tomcat 保存 session 有用户登录失效的问题。
(注:这里我说出了反向代理,水平扩容,负载均衡等技术名词,很多面试官会根据这些名词进行延伸提问(引导面试官往自己熟悉的东西上提问)比如:说说什么是正向代理/反向代理?什么是水平扩容?什么是负载均衡?你了解哪些负载均衡的算法?提前准备好这些知识之后,就可以跟面试官一顿输出了。)
3、你怎么做的技术选型?为什么要用这些技术?
答:springboot自动装配+ MySQL存表 + Redis分布式session锁缓存
使用 SpringBoot 是因为通过自动装配能够提高项目的开发效率,还能够很好的整合其他服务。使用 Mysql 的原因是因为考虑到未来有用户充值交易,限制调用次数等场景需要用事务保证数据的完整性和一致性。使用 Redis 的原因是因为可以用来实现分布式 session ,锁,缓存等功能。因为 Redis 是一个单独的中间件,不同客户端可以往同一个 Redis 或者集群中存放 session /加锁,这样就能保证资源能够在分布式服务下都可见。并且由于 Redis 也是单线程的,同时也支持 lua 脚本,可以保证并发安全的问题,所以可以很简单的实现分布式锁的功能。
注:被面试官追问自动装配的原理你了解过吗?自动装配是怎么实现的?分布式 session 的原理?
4、你的开发流程是什么?先实现还是先技术选型?
答:参考现有结合自己 + 先产品后技术。技术为业务服务!
我先参考了一些已有的产品,根据这些产品,总结出来比较好的功能点,再结合自己想要实现的一些功能特色,去做了一个项目整体设计,有了产品原型后再进行技术选型。使用什么样的技术去解决什么样的业务问题。
5、为什么你要使用网关?
答:隐藏真实接口地址 + 全局管理鉴权路由访控流控等功能
我这个平台的关键点就在于提供接口服务,要保证接口的可用性和稳定性,所以将接口服务独立部署在另一台机器上,隐藏真实的接口地址及端口,调用接口服务的请求都必须经过网关流量染色之后..(这里细节太多,比如 rpc 调用获取用户 sk ,重新生成签名认证等等)之后,将请求转发到真实的接口地址,防止接口被恶意调用、盗刷。
(注:这个问题要对网关做了什么事情非常非常熟悉,建议反复观看鱼皮大佬的直播回放。)
6、为什么使用 RPC 调用?有了解过其他的方式吗?
答:调用操作数据库服务 + Open Feign
网关内签名认证,验证接口等功能需要操作数据库,因为如果在网关引入数据库的操作的话,不仅会增加项目体积,而且违背了设计原则的单一职责原则,所以我考虑通过服务间调用的方式,常见有两种方式,第一种是 Open feign ,原理是构造了一个 HTTP 请求,并会添加很多的请求头, body 是使用 json 字符串传输,所以调用效率会比较低,更加适合外部服务间的调用。然后我了解到 RPC 是可以基于 TCP 协议,避免了无用的请求头,以及可以通过将数据序列化为二进制流的形式传输,效率更加高效,更加安全,所以更适用于我这个场景。最终我选择了 Dubbo RPC 框架来实现这个功能。
7、你的接口调用次数统计以及排行是怎么实现的?
答:
通过 Mysql 统计,每次调用结束后,网关都会发起一个 rpc 请求,调用次数+1。
注:这里我会抛出一个设计缺陷,在实际测试过程中,通过 jmeter 压测工具,会出现调用次数不准的情况,原因是因为没有在业务层面加锁,导致数据库出现并发写的问题。并且并发量大的话,对数据库造成很大的压力。引导面试官问出,那你有什么更好的解决方案吗?
答:如果在业务层面加一个写锁的话,会影响业务的执行效率,所以我想使用 Redis 去解决, Redis 有一个数据结构 Zset 支持排序, score 可以用来存储调用次数,并且 Redis 是单线程,可以解决并发问题。
注:这里被追问过 Zset 的底层实现,以及如何将这些数据进行持久化保存,防止 Redis 宕机导致数据丢失,可以从 AOF , RDB 展开来讲,或者在后台开启一个定时任务,定时将这些数据进行落库。
8、你做过什么优化吗?你接口的性能怎么样?
答:修改存储引擎
我有一个接口是随机返回土味情话,我在数据库中插入了几千条土味情话,当调用接口时随机返回一条。在还没有优化前,接口的 qps 在300左右,但是考虑到这个接口只有读操作,没有增删改操作,所以我将这张表的存储引擎从 Innodb 改为了 MyISAM ,接口的 qps 提升到了1500。
(注:被面试官追问为什么改为 MyISAM 有这么大的性能提升? Innodb 和 MyISAM 有什么区别?这个问题一定要根据自己实际情况来答,根据自己擅长的方面,比如对查询语句做了索引优化,提升了接口的性能。)
总而言之,面试官会从各个角度去深挖项目的细节,考量你是不是真的自己做的,是不是真的理解?所以要做到对项目的所有细节都非常的熟悉。当完全理解项目之后,就能够提前预测到面试官会怎么问,并在面试过程中说出一些技术名词引导面试官,然后对这些问题,和延伸的知识点能够完全掌握后,相信一定可以征服面试官。