后端日志-week2
day01
Nacos的原理和使用
四大功能:
- 服务发现和服务健康监测:(使服务更容易注册,并通过DNS或HTTP接口发现其他服务,还提供服务的实时健康检查,以防止向不健康的主机或服务实例发送请求。)
- 动态配置服务:(即Nacos即是服务发现中心,又是配置中心,当然配置写在服务本地也可,写在Nacos里可以利用公用配置简化服务配置过程)
- 动态DNS服务:动态 DNS 服务支持权重路由,更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。
- 服务及其元数据管理:管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。
服务发现过程:
在每个服务启动时会向服务发现中心上报自己的网络位置。在服务发现中心内部会形成一个服务注册表,服务注册表是服务发现的核心部分,是包含所有服务实例的网络地址的数据库。
服务发现客户端(即每个服务本身)会定期从服务发现中心同步服务注册表 ,并缓存在客户端。
当需要对某服务进行请求时,服务实例通过该注册表,定位目标服务网络地址。若目标服务存在多个网络地址,则使用负载均衡算法从多个服务实例中选择出一个,然后发出请求。
如何实现一个需求(以查询课程为例)
- 需求分析
分析该模块业务流程(以查询课程为例):
- 在课程进行列表查询页面输入查询条件查询课程信息
- 当不输入查询条件时输入全部课程信息。
- 输入查询条件查询符合条件的课程信息。
- 约束:本教学机构查询本机构的课程信息。
- 分析数据模型
可以找大模型帮忙分析一下
查询条件:
包括:课程名称、课程审核状态、课程发布状态
课程名称:可以模糊搜索
课程审核状态:未提交、已提交、审核通过、审核未通过
课程发布状态:未发布、已发布、已下线
因为是分页查询所以查询条件中还要包括当前页码、每页显示记录数。查询结果
包括:课程id、课程名称、任务数、创建时间、是否付费、审核状态、类型,操作
任务数:该课程所包含的课程计划数,即课程章节数。
是否付费:课程包括免费、收费两种。
类型:录播、直播。
因为是分页查询所以查询结果中还要包括总记录数、当前页、每页显示记录数。
根据以上分析内容创建PO(与数据库字段对等,使用代码生成器,比如Mybatis-plus的生成器)
3.接口设计分析
- 确定协议: 通常协议采用HTTP,查询类接口通常为get或post,查询条件较少的使用get,较多的使用post,其他的还有(Put,Delete等,采用RestFul风格)。确定content-type:参数以什么数据格式提交,结果以什么数据格式响应。(一般以JSON格式响应)。
Get和Post异同?
同:
GET/POST都是Http请求,都走TCP链接。GET和POST能做的事情是一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的(取决于服务器响应策略)。
异:
- 直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。
- Get参数显示在Url中,安全性较差。Post不显示,安全性较好。
- GET产生一个TCP数据包;POST产生两个TCP数据包。
分析请求参数:(DTO(公司脚手架里的ReqVO),前端给后端的数据) 主要根据对上述数据模型的分析,请求参数为:课程名称、课程审核状态、当前页码、每页显示记录数。根据分析的请求参数定义模型类。
分析响应结果:(RespVO,后端向前端的响应数据) 根据上述对数据模型的分析,响应结果为数据列表加一些分页信息(总记录数、当前页、每页显示记录数)。数据列表中数据的属性包括:课程id、课程名称、任务数、创建时间、审核状态、类型。
关于为什么需要VO、DTO、PO:
- 接口中调用Service方法完成业务处理。
Swagger文档的一些常用注解,仿照项目中脚手架也可以。
day02
Mybatis-plus的使用
Mybatis Plus 对 Mapper 层和 Service 层都将常见的增删改查操作都封装好了,只需简单继承即可,使用时只要注入接口就好
BaseMapper层
1 |
|
在公司脚手架的下段代码中,操作数据库的逻辑都在这个接口里用default方法写。(BaseMapperX是公司封装的MyBatis-plus包,没有封装的情况继承的是BaseMapper),继承于BaseMapper的方法有:userMapper.insert(),select(),delete(),upadate等变体,其中查询语句多与QueryWrapper相关。
1 | public interface AdminUserMapper extends BaseMapperX<AdminUserDO> { |
BaseMapper相关查询方法
1 | // 根据 ID 查询 |
参数说明:Collection<? extends Serializable> :
主键ID列表(不能为Null)
Service层
定义UserService 接口 ,让其继承自IService
1 | public interface UserService extends IService<User> { |
再定义实现类UserServiceImpl,让其继承自 ServiceImpl, 同时实现 UserService接口,这样就可以让UserService拥有了基础的CRUD功能,当然,实际开发中,业务会更加复杂,就需要向IService接口自定义方法并实现:
在公司脚手架中未见这种实现方式,相关数据库操作似乎只在BaseMapper方式中实现,另外这一类的Service是否能写业务相关的逻辑呢,在xuecheng项目里这一类的Service里都只写CRUD操作。
1 |
|
Service层CRUD方法:
saveBatch操作都是循环插入,但是MyBatis-plus帮你优化插入性能,内部会将每次的插入语句缓存起来,等到达到 1000 条的时候,才会统一推给数据库。
增加数据:save开头,删除remove开头,更新update开头,查询下面详细说明
1 | sava(T) : boolean |
注入UserService进行使用:
1 |
|
Service相关查询方法
- getXXX : get 开头的方法,用于查询一条数据。
- listXXX : list 开头的方法,用于查询多条数据;
- pageXXX : page 开头的方法,用于分页查询;
- count : 用于查询总记录数;
分页查询:(进行分页查询之前需要在MybatisPlusConfig配置类中,添加分页插件 PaginationInnerInterceptor),记得在MyBatisPlusConfig中添加@MapperScan("com.xxx.xxx.mapper")
注解,告诉MyBatisPlus应该扫描哪里。
BaseMapper
提供的分页查询相关的方法如下:
1 | // 分页查询,page 用于设置需要查询的页数,以及每页展示数据量,wrapper 用于组装查询条件 |
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper |
queryWrapper | 实体对象封装操作类,查询条件 |
IPage |
page | 分页查询条件,page的相关属性设置条件,如size和Current |
示例代码
1 | // 组装查询条件,当我想无条件查询的时候,queryWrapper为Null或者不设置条件都可以 |
Service层
封装的相关分页方法(其实与BaseMapper类似,BaseMapper是selectPage(),Service是page(),二者入参类似):
1 | IPage<T> page(IPage<T> page); |
示例代码:
1 | // 组装查询条件 |
多表联查(join):
UserMapper.xml
中编写关联语句,以及需要映射的对象,内容如下(注:多表联查涉及到xml,所以只用BaseMapper层):
1 |
|
- 关于xml文件的创建:
使用MyBatis-Plus的Generator开发持久层,每张表对应的PO类、Mapper接口、Mapper的xml文件。PO类对应数据库的每张表,每张表需要创建一个Mapper接口和Mapper的xml映射文件 。
- 关于xml文件的属性:
namespace标签内容=“前文BaseMapper接口的包路径名”
resultMap、type 标签定义查询语句返回结果集的类型,将实体类字段与数据库表中字段进行关联映射
select标签的id为下文使用的方法名,resultMap为上文定义的查询结果类型
- 创建完了
UserMapper.xml
,还要在applicatoin.yml
中添加如下配置,告诉 Mybatis-Plus 框架去扫描这些xml
文件:
1 | mybatis-plus: |
联表查询代码示例:
1 | List<OrderVO> orderVOS = userMapper.selectOrders(); //Mapperxml的selectId名 |
分页联表查询(分页+联表+条件查询):
实际开发场景中,很多关联查询都需要结合分页一起使用,假设上面展示的数据需要分页展示,且需要支持条件查询,要怎么做呢?
- 定义关联查询分页方法(同样基于UserMapper层):
1 | public interface UserMapper extends BaseMapper<User> { |
Tips: 入参:Mybatis-plus提供的分页类IPage与QueryWrapper (用于组装 where 条件)。
- 在
UserMapper.xml
中创建该方法对应的关联查询:
1 |
|
示例代码:
1 |
|
批量插入(SQL注入器实现):
MySQL 支持一条 SQL 语句可以批量插入多条记录,格式如下:
1 | INSERT INTO `t_user` (`name`, `age`, `gender`) VALUES ('ldp01', 0, 1), ('ldp02', 0, 1), ('ldp03', 0, 1); |
Mybatis的伪批量插入方法saveBatch(),源码实例如下:
1 | public boolean saveBatch(Collection<T> entityList, int batchSize) { |
[SQL注入器方法](https://www.quanxiaoha.com/mybatis-plus/mybatisplus-batch-insert.html)
QueryWrapper构造:
QueryWrapper
常用注解:
- @TableName:作用:表名注解,标识实体类(PO)对应的表。
- @TableId: 作用:主键注解,
@TableId(type = IdType.AUTO)
- @TableField:作用:指定数据库字段注解(非主键)。
- @TableLogic:作用:逻辑删除注解。
- @Version:作用:乐观锁注解。
day03
Git工作流
在工作中推荐使用GitHub flow协作方式,即不在主仓库的分支上开发,而是fork
到自己workspace
下。每次开发,需要先checkout
一个新的分支,commit
之后推送到自己的仓库,再向主仓库提Merge Request
。所以开发过程中对于大多数项目来说,都会经过如下步骤:
- Fork
- Create a branch
- Add Commits
- Open a Merge Request
- Code review
- Merge
- Deploy
流程详解:
- Fork主仓
项目组长建立代码的主仓库,剩余小组开发人员首先要对其仓库进行Fork。Fork之后,从自己的仓库进行clone,一般来说一个仓库会有几个分支,比如:
master
分支为主分支(保护分支),禁止直接在master上进行修改代码和提交,此分支的代码可以随时被发布到线上。develop/dev
分支为测试分支或者叫做合并分支,所有开发完成需要提交测试的功能合并到该分支,该分支包含最新的更改。feature
分支为开发分支,大家根据不同需求创建独立的功能分支,开发完成后合并到develop分支;fix
分支为bug修复分支,需要根据实际情况对已发布的版本进行漏洞修复;
一般而言,有master分支和dev分支便可。
- 添加远程仓库
首先我们需要把远程主仓库给添加进来,方便以后push和pull,例如将项目组长所建立的主仓库添加进来:(upstream是远程仓库的代称,可随意命名)
1 | $ git remote add upstream ssh://主仓库地址 |
之后可以通过以下方式push和pull代码,切记不要push主仓库。
1 | git push origin HEAD:分支 |
- 查看分支和状态
1 | git branch #查看本地分支 |
- 开始工作新建分支
不要一股脑的在develop分支上直接开发功能,先打开git bash,先pull一下主仓库保证代码最新,之后先新建一个分支。
1 | git checkout -b 分支名 //新建分支名并进入该分支 |
- 提交代码
主仓库的更新速度比你写代码快,也就是说很多情况主仓库的代码都是要比你的新,所以你要先pull
主仓库的代码进行更新,但是直接Pull的话也许会产生冲突,需要你将代码放入暂存区Stash进行保管
1 | git stash save "msg" //保存你的代码 |
更新完代码之后,你需要将之前存储在暂存区的代码进行恢复。
1 | git stash list #查看stash了哪些存储 |
- commit并且push
1 | git diff //用于查看修改的文件 |
- 提PR(Pull Request)
到自己的远程仓库里,向主仓库提交分支合并请求,一般是自己这次工作创建的分支合并到主仓库的develop分支,然后选择代码审查人员进行CodeReview。
day04
Docker基本命令
docker命令
1 | docker images #查看镜像 |
为了将容器内数据与容器解耦合,需要数据卷:
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录
- 可供容器使用的特殊目录,可以在容器之间共享和重用
- 对数据卷的修改会立即生效,对数据卷的更新 不会影响镜像
- 卷会一直存在,直到没有容器使用
在创建容器时,可以通过 -v 参数来挂载一个数据卷到某个容器内目录,命令格式如下:
1 | docker run -d -v /data/webserver:/usr/share/nginx/html nginx #将主机的/data/webserver挂载到容器的/usr/share/nginx/html目录 |
docker volume [COMMAND]命令用于管理Docker数据卷:
- create volumeName 创建一个volume,一般关联宿主机/var/lib/docker/volumes/目录下
- inspect volumeName 显示一个或多个volume的信息
- ls 列出所有volume
- prune 删除未使用的volume
- rm volumeName 移除一个volume
示例,创建并运行一个MySQL容器,将宿主机目录直接挂载到容器:
- 通过docker pull mysql:5.7.25 拉取mysql镜像
- 创建目录/tmp/mysql/data
- 创建目录/tmp/mysql/conf,并在该目录下新建文件hmy.cnf,写入如下内容,或者(docker volume create html,比如挂载nginx)
- -v 宿主机目录:容器内目录,
- -v 宿主机文件:容器内文件,
- -v volume名称:容器内目录
1 | [mysqld] |
- 将上述创建的目录挂载到mysql容器内部
1 | docker run --name mysql |
ApiFox的使用
看ApiFox的官方文档即可。
day05
tips:@ApiModelProperty不生效时,将POJO中属性字段名改为小写的就好。