原主时间线:
3.20:熟悉产品产品流程管理
4.15:环境搭建、项目熟悉(阿里规范手册)
4.25:基础服务、功能分配
4.30:第一阶段需求开发、第一阶段技术需求
5.20:第二阶段需求开发、第二阶段技术需求
6.10:代码review、代码优化
6.12:技术博客、项目整体验收
6.23:项目结束总结
先给大家带来比较完整的软件开发流程
项目架构图
流程:
- 用户在客户端发起请求,进行DNS(Domain Name System域名服务器)解析,其中包括WAF(Web Application Firewall)、CDN(Content Distribution Network内容分发网络)、防火墙,
- 再通过Nginx集群反向代理到Spring5.0之后添加的Webflux网关,通过SpringCloud Gateway进行动态路由指定Predicate(断言)和Filter(过滤器)寻址与Sentinel+Shard进行认证授权和令牌限流,中间还可以整合Redis;
- 另一方面还可以同时与Nacos合作为它提供监控报警和日志,集成服务注册发现功能,Nacos集群在整个架构中都能提供服务注册发现与动态配置和配置管理功能,整合SpringBoot Admin进行服务监控
- 同时Gateway可以通过Ribbon进行负载均衡、Sentinel熔断降级和业务集群建立桥梁,形成联系,先是通过SpringSecurity框架与OAuth2认证中心整合JWT进行公钥私钥的颁发授权与相应验签认证功能。
- SpringBoot应用在业务集群中通过OpenFeign进行相互调用,中间还整合Redis、Mysql、MQ、ES、OSS、JOB等工具,其中Redis集群可以用来做分布式数据缓存,Mysql主从复制进行数据的持久化,RabbitMQ进行系统解耦削峰填谷异步调用,ES全文搜索引擎整合Kibana、Logstash进行近乎实时搜索、分析和可视化的全文检索,使用阿里云OSS云存储服务进行对象存储、企业数据管理,使用xxl-job进行分布式任务调度。
- 业务集群还与分布式相挂钩,有着分布式主键,分布式锁,分布式事务,其中有两个理论,一个是CAP(分别是Consistency一致性、Availability可用性、Partitiontolerance 分区容错性)理论(这三个要素最多只能同时实现两点,不可能三者兼顾),一个是BASE(Basically Available基本可用、Soft State软状态、Eventual Consistency最终一致性)理论(即使无法做到强一致性,CAP的核心就是强一致性,但应用可以采用适合的方式达到最终一致性)
- 日志收集通过Beats、ELK、和Kafka整和完成,具体过程是这样的:Beats用于日志数据采集使用,Logstash收集日志,发送给Kafka进行解耦、异步处理和流量削峰,然后通过Elasticsearch集群存储日志数据,索引日志数据,再通过Kibana视图形式展现日志信息,更加人性化地在客户端进行检索以及相关操作。
- 以Skywalking为核心的运维监控中心可用提供链路追踪和监控报警机制,与运维报警系统Prometheus结合Grafana最终和Alertmanager通过短信、微信或者邮件的形式给模块负责人发送警告通知。
- 最后Developer将代码文件上传到GitHub上通过docker容器部署,使用K8s AP对资源进行编排,管理应用的全生命周期,同时也提高发布与更新版本的效率,然后通过Jenkins Pipeline进行整个构建、测试、交付等持续集成,运维人员继续对这些进行维护。
API 文档规范
- 请求方式只用 GET/POST
- 请求路径按照任务分配表上的写
- GET 请求的参数在 Query 里面填写,POST 在 Body 里写。不要混用
- 不要使用 RESTFul 的路径传参
- 环境的使用:
- 不要使用环境变量,目前用不上
- 只使用一个环境 url 前缀即可(协议+ip+端口)
- 已有的环境可以直接用,但是不要改,因为这是公用的。如果有需求自己建一个环境即可。
- 请求参数规范:以下
- 参数名采用驼峰命名
- 数据库中存在的字段的参数,命名要保持一致
- 如果参数中用多个 id 参数,必须区分命名,且要与数据库一致
- 传参的实例值尽量真实一点
- 类型要选择正确,类型的选项很多自己看看。
- 参数描述简练准确
- 响应数据示例的格式必须的实际的格式
- 响应数据的类型和描述和请求参数的要求一致
- 金额数据:后端使用整数存,但是到了前端部分要转化为小数(类型为Number),这点也是师哥提过
推荐工具:CHINNER
、APIPOST
kkb
改成hotel 基础配置留着
最后要求各自的项目运行起来
数据库设计
下图就是我们队伍经过多次交流讨论,最后由队长总结出来的项目流程梳理
再就是初始数据库模型
我们队伍负责用户中心,我又和另一个同学负责用户签到积分以及钱包这部分模块,先来给大家展现部分数据库设计,后续我也还会依据阿里规范以及自己报的课学到的内容总结出一些我在项目中犯过错或者觉得大家容易在这方面出错的博客。
用户签到表 建议加一个签到日期的字段,不然不好获取用户签到的日期
接口设计
需求开发
签到里程和零钱积分模块
里程明细
签到
积分查询零钱查询
第一次改版
数据库方面
项目初步结构
中间遇到的问题及有关解决知识点
在设计签到功能的时候,因为可能涉及到很多用户的访问,然后自己也没实现过相应功能,自己没有太多的思路,在博客上逛了逛,发现可以使用Redis来实现功能。
发现原来可以使用Redis中的Bitmaps这一个数据集合
先来看看官方对Bitmaps的说明:
- Bitmaps并不是实际的数据类型,而是定义在String类型上的一个面向字节操作的集合。因为字符串是二进制安全的块,他们的最大长度是512M,最适合设置成2^32个不同字节。
- Bitmaps的位操作分为两类:
- 固定时间的单个位操作,比如把String的某个位设置为0或者1,或者获取某个位上的值
- 对于一组位的操作,对给定的bit范围内,统计设定值为1的数目(比如人口统计)
- Bitmaps最大的优势就是在存储数据时可以极大的节省空间,比如在一个项目中采用自增长的id来标识用户,就可以仅用512M的内存来记录40亿用户的信息(比如用户是否希望收到新的通知,用1和0标识)
简单来说bitmaps就是一个长度可变的bit数组。每个位只能存储0或1。我们先来看看bitmap的具体表示,当我们使用命令 setbit key (0,2,4,6) 1后,这个bit数组的具体表示为:
一天的1亿人的登录情况(登录、未登录)就可以使用一个长度为1亿的bit数组存储,数组的索引就是用户的userId(假设userId是自增的)。
重要知识点:
不要写两表联查,可以单表多查一次,调两个service就行,然后用Java的流拼起来,原因:
- 单表走索引好保证,很难保证联合查询走索引,所以不见得联查会有多快
- 系统拆分联合查询很难分出来
中间其实就数据库和代码都修改了多次,开始接触觉得没什么太多思路,后来组内交流多了,代码看多了,也就慢慢知道怎么改了,懂得抛全局异常,使用MybatisPlus的一些API,更加熟悉MVC三层架构以及代码的编写,代码整洁度和规范性大大加强,中间有很多优化我没有在这里展现出来,另一篇博客中的代码版本是整体第二版,算是初步优化之后的版本,后续有机会把最终版呈现给大家,在这次代码修改有比较多的注意点,这里先把一些大的点列了出来
注意事项
- 路径请求格式(业务/模块/方法) 方法名里面要加能表示动作的词,比如说 get
- 返回值使用 CommomResult 分页 CommomResult<CommonPage>, 返回码 使用 resultCode
- 配置 redisTemlate 解决 redis 乱码问题
- auth 认证:constant 涉及 JWT 一些通用常量
- log、mpaop 切面 :log 包提供 各个 controller 的切面处理,主要是记录请求信息。mpaop 提供 在portal 和 admin添加切入点,主要记录一些集合使用的时候出现异常的记录
- 调用Asserts.fail 会抛出 ApiException异常,被 GlobalExceptionHandler 全局异常处理器(处理GlobalExceptionHandler 里面对应的异常),@NotNull 注解也会抛出异常被 ControllerAdvice 捕获
- redisServiceImpl 对 redisTemlate 的方法进一步封装。提供redis 的各项基本操作。
- 常用的是 api 和 exception 包里面的内容。
- service 里面调用方法要加 this 来调用。
- 注释:参数介绍 和 返回值
- DAO、VO 大写
- service 需要继承 Iservice,ServiceImpl 需要继承 mp.ServiceImpl
这下面是一个不错的例子
git的简单使用
中间涉及git的使用,我是很久没有用了,有点生疏,多亏群里有大佬愿意在视频里手把手细节教学,感谢大佬。
第二次大的改版
数据库方面
用户信息总表
用户登录信息表
用户积分表
用户过期积分表(新增加的业务需求,我们考虑使用xxl-job实现)
用户积分流水表
用户签到表
用户钱包表
用户账单流水表
最终负责的接口
这里具体的和之前区别不大,主要是新增两个配合订单的接口,所以就不再一一展示了。
总结
现在经过这一个月忙碌的时间,这个项目自己写代码的时间可能不太长,但是花费的精力确实很大,从最开始什么都没有,什么都不知道的小白,慢慢了解软件开发的全过程,从需求设计,数据库设计,接口设计、熟悉版本控制工具的使用,代码编写阶段与代码review,期间的很多次小组会议,共同学习阿里开发规约等团队协作。对未来的工作也有了相对更为全面深刻的认知,期间我遇到了很多问题。下面列举总结一下
-
首先就应该是工具的熟悉与使用了,比如项目中使用到的git是我很久之前浅显地学习了一些,在实际操作中还闹出没有update直接pull到master分支的情况,再就是ApiPost和Chinner的使用,这些之前都没接触过,中间还出现为保存文件就直接退出的乌龙,所以以后一定要及时备份保存相关数据
-
再就是需求分析的时候有很大的问题,自己也第一次开始用ProgressOn这种在线画图工具去梳理酒店项目的一些具体流程,第一次以架构师的角度去考虑项目的问题,包括项目中的模块拆分,比如用户中心,商家中心,酒店中心,后台管理员中心。跟着现有的小程序界面去好好体验一下不同界面,梳理流程。从用户的视角来看可能是我们比较熟悉的,就主要是页面的浏览,针对地点星级价格评价等进行同一家商店的纵向对比和不同商家之间的横向对比。其中涉及到的es搜索筛选,商品的增删改查都是以后真正工作要经常考虑的东西。以前只是停留在使用产品的层次上,不知道这些底层是什么,当我真正看到我们齐心协力写出的小程序展示出的效果的时候感觉非常惊喜,以及说不出的成就感。这里想先展示一下自己开始的设计,现在第一次的小组会议,被小组选出来给大家讲解的场景也确实历历在目,还有一丢丢小自豪。当然现在看着这个流程确实是还有很多欠缺,以后在学习和工作中需要去不断完善细节,也想不断加强自己的架构能力。会以一个开发者的角度去思考问题,我想这就是我在这个项目中学到的最有价值的东西,具体的增删改查能力自己可以慢慢增强。
-
其次是数据库的设计,原来也听说过这些都是架构师帮你想好的,你只需要去具体的使用就行。当这次真的自己去设计的时候,有很多相关知识就得应用起来,比如数据库三大范式,每个表中涉及到的字段,存在的话都是有一定的原因的,有时候因为需要方便而设计冗余字段,有时候还需要设计中间表,这就需要站在整个系统业务这一宏观角度去思考这些问题,再就是约束和索引的创建,约束用来保证字段的唯一性、不为空或者方便查询的外键,从而保证数据的正确性、有效性、完整性;首先要明确索引的创建的目的,是为了提高查询速度,但是索引的创建本身就是比较消耗磁盘性能,需要减少磁盘IO次数,只有在数据量很大的时候,当创建索引之后的提升比索引本身的消耗高的时候才适合创建,明确适合创建索引的情况,比如频繁作为WHERE查询条件的字段,DISTINCT字段、经常GROUP BY和ORDER BY的字段等等。同时我们应该也要注意索引失效的情况,比如使用了select * 、索引列上有计算、用了函数、不满足最左匹配、like左边包含%、使用OR关键字、NOT IN和NOT EXISTS等等。这些都是我需要在面试和以后工作的时候十分清楚的。还有就是在数据库的设计中我们需要重点关注最新的阿里开发规约,命名规范和一些必需的字段以及某些字段用的数据类型,尤其是跟金额相关的数据要格外注意,等以后工作的话在金额方面造成精度丢失可就是大事了。数据库的设计是个大头,我们团队合作共同商讨,几经改版,最终终于有一个相对合适的版本,当然可能因为我们前期经验不足,在后续业务代码开发的时候还需要重新新增一些表或者字段。
-
再来谈接口设计这一块。我们用的ApiPost这个国产软件,团队开发人数在没付费之前是有15这样一个限制的,前期确实优点阻碍我们工作的进行,但整体使用类似Postman,还是中文,比较友好。具体设计是根据前端页面,确定接收的参数和传给前端的参数,有些参数在实体表中不存在的情况下很多时候还需要自己封装DTO来进行数据的传输,前后端的交互也是项目中很关键的一个部分,后期代码开发出来之后进行一系列的联调来确保程序可以正常进行。这就让我想起了自己原来做练手小项目的时候,就一边后端debug,一边在前端服务器查看请求传递的json数据,不然用Postman直接报个500或者404也没什么太多的头绪去修改。在设计接口的时候团队最好统一参数的传递格式,倒也不一定要用Restful风格,接口名称模块名+具体功能名,中间用 / 隔开,而且当功能名的词数过多时,应该用 - 来分开,而不是我们经常用的驼峰命名法。
-
这会到了具体代码的开发,因为报名了项目架构小组,这算是给自己额外的任务,因为之前看微服务的直播课的时候刘雪松老师经常讲Oauth2授权部分,所有我看到有这部分的时候毫不犹豫选择了这个模块,但确实这个模块比较晦涩,也是每个项目的核心部分,一般的底层程序员不太可能接触的到,我原以为看了好多遍其中的好几种授权模式就会比较好接受,当我看到原来那些auth模块的代码的时候还是很懵,又是一堆陌生的知识,其中关于公钥的颁发和验签流程是我在后续学习得继续巩固的,要求自己在面试如果问到这一块的时候必须得说清楚。前期搭建对我来说是再次熟悉一遍流程+抄代码,其中因为数据库中没有resource表以及相应字段就导致程序始终启动失败,好在是在于小伙伴交流之后解决了。我在代码开发前面一段时间都没有在做项目,而是在关注JUC和面试的一些知识点,对于MVC三层架构的具体应用也出了很多差错,只知道DAO层继承mp的BaseMapper,而service中接口和对应实现类并没有去继承,也给自己带来了很多的麻烦,这里有一些细节,就是继承mp那些之后就拥有了那些常用的增删改查方法,在每一层具体调用就行,能给我们减少很多代码的开发,当然,当我们需要扩展的方法的时候,就直接在service接口中扩展,尽量在service实现类中进行单表查询而不是在dao层自己加个方法写SQL,因为这在以后项目的拆分的时候非常不方便,这样写的话这些代码全部要改。这里也需要注意service实现类中、如果是涉及本张表的时候可以直接用this调用其中强大的方法,需要其他表的操作时候需要在前面自动注入其他表的相应service接口,其实最开始使用mp时,感觉比较陌生,其中的Lambda写法更是让我摸不着头脑,刚开始编码的好几天每天都是在看这些API的具体使用,期间去官网好好看了一些操作,也去阅读相应的代码,有一定的认知之后再去开发,这个过程其实还是蛮痛苦的,看着感觉啥也不会就要动手写代码的感觉还是特别慌,后来使用多了其实主要也就是那么些东西,主要就是一些条件条件构造器的使用,有基础的话最好结合Lambda表达式去写,这样写起来代码也会比较优雅,注意使用条件构造器的时候需要很清楚要用的SQL语句是什么,再就是其中使用stream流将实体类数据赋值,比如在传输中用的DTO赋值,就会经常用到Stream流,这些算是在公司中需要掌握的基础知识吧,如果连这种简单的CRUD都不太熟练,那可能真的在公司里也会过得很难受。这中间我还在另一个项目中犯了一个错,就是在controller层中注入的是service实现类,导致在新增某个方法时程序报动态代理异常,因为Spring是自动选择动态代理类型的,当我使用的不一致就会报错,要不就在配置文件中都强制使用同一个动态代理。在具体业务中暂时也没有太复杂的逻辑,就是尽量用卫语句尽量减少if-else的嵌套,多使用if就行,这样相应代码的可读性也会提高。当然在遇到复杂的业务不可避免使用if-else的情况下写一些也没事,这里其实就体验出程序员的水平了,如果算法功底比较好,对于这些的分类判断就是会很清晰的,这也坚定了我学好算法的决心,做好长期战斗的准备,因为是非科班,这方面基础欠缺,要想以后自己更上一层楼,这些也是必经之路,好在年轻,好在对这些有足够的热情,在面试前再集中分类刷题,才能真的进入比较好的公司。
-
最后需要扩展提升的就是对于微服务和中间件的熟悉,当前对于Redis、MQ、ES等的使用还是有很大的欠缺,在做积分模块的时候就涉及到过期积分的处理,我们还是因为经验不足,直接使用xxl-job来处理,这其中有很大的一个弊端,如果直接使用这个那定时任务设置的更新时间就有很大的问题,如果设计的是1s更新一次那就对系统是很大的性能消耗,设计1min那时效性又会比较差;过期积分放在Rocket延时队列或者Redis延迟队列中,比如优惠券过期时间,就是到了那个时间之后,才把消息发给你,这样就能保证数据的实时性。后面优先复习Redis和MQ的知识。
接下来要多学习的地方:先多看这个项目大家写的代码,并选择其中一些有代表的功能自己去手动实现,把基础再打扎实一些,多背面试题,刷算法题,巩固多线程这些东西,并且去系统学习计算机网络和操作系统,弥补非科班的短板。上文的Redis和MQ知识的学习是必不可少的,MySQL相关知识基础也要打牢再就是多去熟悉各种微服务理论知识以及实际的应用。再就是有时间的情况下对JVM有一个初步的系统的了解,在以后的工作中多关注一些深入的点和一些细节上的东西。
最后的话
师哥也在群里传授了很多经验,这些是我们在普通的课程中是学不到的,这里优先展示一下前段时间令我印象深刻的一句话:
工作之后一切从0开始,工作中自己达到80分要把60分的往上拉到80分,而不是自己80分冲到90分。不要过于看重自己的院校,要用实际能力说话。
过去的不能代表太多的东西,我们在技术面前要一直保持一颗童心般的好奇,这是团队协作的工作,前期先在团队协作之外疯狂学习,之后等能力提高之后一定要好好帮助团队,这样才不被局限,能走得更远。
最后我这些总结的逻辑可能还不够清晰,后续会不断按点好好整理,这是我入软件开发行业前重要的初始积累,可能很久以后都会记得现在做的这个项目,想起这样一群小伙伴和师哥的教导。时间不早了,先好好休息了,明天继续准备多线程的面试题,如果大家觉得在这个博客里有同感或者学到了一些东西的话还请麻烦一键三连,赠人玫瑰,手有余香。