【面试系列】面试中项目如何准备?

本人在准备面试时做的项目是“在线教育系统”,现在把它的项目笔记发出来,供各位小伙伴参考,也祝各位小伙伴们能找到自己心仪的工作。

零、介绍项目

先引用一段牛客上一位伙伴的心得,我感觉说的很不错,有喜欢的小伙伴可以参考:“我之前都是先介绍项目总体有哪些功能,再挑一些自己做的功能的说一下大致流程,但是在面百度的时候,就感觉这样介绍面试官不满意。需要详细的介绍,实现某个功能的时候,走了哪些接口、方法,用了哪些技术,解决了什么问题。百度的面试官就告诉我介绍项目的时候说的不够详细,最好说到代码层面。”

接下来是我介绍项目时的一套说辞,我个人比较倾向三段式介绍:
Dream在线教育系统是我和我的几个对编程比较感兴趣的同学一起开发的,去年因为疫情,都上起了网课,所以我们就萌生出一个想法,开发一个在线教育系统来玩一玩。

这个系统有如下功能:

前台:主要针对学生用户,包括登录(微信登录)、注册,可以对录播视频购买后进行学习,学习过程中,系统自动记录它的学习进度。

后台:登录、权限管理模块、讲师管理模块、课程分类管理、课程管理、统计分析、Banner管理、订单管理等功能。

我们的技术选型:

  • 前后端分离 + 微服务 (利用nginx做了一个反向代理,来接受客户端发来的请求,转发到服务端响应的端口);

  • 后端:SpringBoot分模块开发、持久层框架MyBatis-Plus、Maven进行依赖包&项目打包、EasyExcel进行excel数据的批量导入;

  • 前端:VUE.js+Element-ui+ECharts(图表显示)

  • 中间件:Redis(进行当前用户权限列表存储、热门课程存储和课程进度的存储)、阿里云OSS(头像存储、视频存储)。

我个人负责的模块:

  • 登录以及权限管理

  • 讲师管理模块

  • 课程分类管理模块

  • 课程管理模块

    可以问一下用不用一一介绍!!

一、登录以及权限管理

1、功能介绍:

这一部分的功能主要是利用SpringScurity来实现的。

因为之前都是使用的session对象来实现的登录功能,因为考虑到之后可能会升级本项目的架构,把分布式集群加进来,再使用原来session登陆的方式就不可行了,所以这一次是使用的token的方式来实现的,首先用户输入账号和密码,然后通过用户名去数据库中查询用户信息,通过用户信息中的密码和用户输入的密码进行匹配,如果相同则登陆成功。

登陆成功后,先获取用户的权限列表,以用户名为key,以用户的权限列表为value,放入到redis缓存中。然后根据用户相关信息,使用JWT工具类,生成token,返回给客户端,客户端将其放入到localstore(可以长期存储,使用cookie容易被用户禁止)中。

这个时候直接用户读取所有的模块内容肯定是不合适的,所以我们利用了SpringScurity的授权功能。

之后每次访问其他功能模块(包括登陆后访问)时,将token携带到请求头中一起发送到服务端(利用请求拦截器),然后SpringScurity解析请求头获取token信息,根据token解析出用户信息,根据用户信息中的用户名从redis中取出权限列表,判断当前用户有没有权限去访问该模块,进一步生成菜单。(每次访问其他模块,先判断请求头中有无token,如果有,则进入认证中心进行认证,认证成功继续访问,进一步进行权限判断;认证失败,提示登陆。如果没有,则提示登陆。)

为了方便我们的权限判断,加了一个权限管理的功能。权限管理包括以下三个子功能:菜单管理、角色管理、用户管理。菜单管理就是对菜单的增删改查(菜单的显示和删除都使用了递归的操作);角色管理除了对角色的增删改查外,还包括为角色分配菜单;用户管理除了对用户的增删改查外,还包括为用户分配角色;

并且还加入了一个记住我的功能,七天自动登录。

2、注意点
1)选择实现登录方式时的考虑?

如果升级了我们的架构,使用分布式集群来部署,如果再使用session的方式,因为每一个模块,比如教师管理模块和课程管理模块是两个不同的项目,可能在不同的不同的服务器中,所以如果利用这种方式每个模块都需要进行登录,而这几个模块都属于一个项目,显然就不符合我们的要求了。

而我们想要的效果是不管是哪个模块,只要这个模块是我们项目中的模块,我只需要在一个模块下登录,其他模块都不需要再登陆。也就是单点登录(SSO)。

于是我在网上查阅了相关资料,有这么几种解决方案:

第一种方式:利用session的广播机制来实现单点登录。

这种方式虽然可以实现我们的需求,但是如果我们之后添加更多的模块,这种方式就会浪费我们的资源,给服务器造成压力。

第二种方式:使用cookie + redis实现单点登录。

在任意一个模块中登录之后,在redis的key中存放生成的令牌【唯一的值(ip、用户id、UUID)】,来标识该用户,在value中存放用户数据;然后把生成的key值放入cookie中。

再去访问项目中的其他模块,因为在发送请求的过程中会带着cookie一起发送,所以我们可以在服务端获取cookie值,在redis中进行查询,如果可以查询到数据就可以顺利登陆。

第三种方式:使用token(按照一定规则生成一段字符串,字符串中可以包含用户信息)实现单点登录。

第一次访问时,用户未登录,用户输入账号密码验证成功后,利用生成的私钥生成一个包含用户信息的token,返回给客户端,客户端将这个token存在localStore中;

再一次访问该系统时,客户端在请求头中携带该token发送到服务端,服务端利用其持有的公钥解析该token,如果验证失败会报异常,我们可以抓住相应的异常进行特定处理;如果验证成功,我们可以解析出payload中的用户信息,利用用户信息做进一步的权限验证或其他处理。

再一次访问该系统时,如果没有解析出token,代表该用户没有登陆,则让该用户进行登录认证。

2)项目中是如何生成token的?

利用JWT生成token。

(用户登录–>服务的认证,通过后根据secret(私钥)生成token–>将生成的token返回给浏览器–>用户每次请求携带token–>服务端利用公钥解读jwt签名,判断签名有效后,从Payload中获取用户信息==>处理请求,返回响应结果.)

利用RSA非对称加密,生成私钥和公钥,私钥负责生成token,公钥负责解析token。(利用JDK自带的keytool生成)

JWT有三部分:第一部分是JWT头,里面包含签名使用的算法和令牌类型;第二部分是有效载荷,存放用户信息;第三部分是签名哈希,通过JWT头和有效载荷的信息通过指定的加密算法,生成签名。

好处:因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样就服务端就无需保存用户信息,减小服务端存储压力,甚至无需去数据库查询,完全符合Rest的无状态规范。

3)SpringScurity代码执行过程?

用户输入账号和密码后,先进入认证过滤器获取用户输入的用户名和密码;

然后进入实现UserDetailService接口的类中的loadByUsername方法,根据用户输入的用户名查询用户信息,如果查询到用户信息,根据用户信息中的密码和用户输入的密码作比较,如果相同,将用户信息封装到SpringScurity提供的一个User类的实例中返回。

然后进入认证过滤器成功的方法,先获取返回的User对象,根据对象中的用户信息生成token,再把用户名和权限信息放到redis缓存中,最后返回token,将其写入客户端的cookie中。

最后,进入授权过滤器,先从请求头中获取token,根据token获取用户名,根据用户名从redis中查询用户列表,进一步给该用户授权。


在这里插入图片描述

4)记住我的实现?

用户选择了“记住我”成功登录后,认证成功后,将会把username、随机产生的序列号、生成的token存入一个数据库表中,同时将它们的组合生成一个cookie发送给客户端浏览器。

当下一次没有登录的用户访问系统时,首先检查cookie,如果对应cookie中包含的username、序列号和token与数据库中保存的一致,则表示其通过验证,系统将重新生成一个新的token替换数据库中对应组合的旧token,序列号保持不变,同时删除旧的cookie,重新生成包含新生成的token,旧的序列号和username的cookie发送给客户端。

如果检查cookie时,cookie中包含的username和序列号跟数据库中保存的匹配,但是token不匹配。这种情况极有可能是因为你的cookie被人盗用了,由于盗用者使用你原本通过认证的cookie进行登录了导致旧的token失效, 而产生了新的token。这个时候Spring Security就可以发现cookie被盗用的情况,它将删除数据库中与当前用户相关的所有token记录, 这样盗用者使用原有的cookie将不能再登录,同时提醒用户其帐号有被盗用的可能性。

如果对应cookie不存在,或者包含的username和序列号与数据库中保存的不一致,那么将会引导用户到登录页面。

https://blog.csdn.net/weixin_30700977/article/details/98129893
在这里插入图片描述

5)为什么将认证成功后将其权限列表放入到redis中呢?

授权会在以下两种情况下出现:一是用户输入正确的账号和密码,已经通过认证了,这时候需要该用户的权限列表进行认证;二是当用户每次访问其他功能模块的时候,除了需要看是否认证,认证通过后还需要看一下有没有权限去访问这个模块,这时候也需要读取权限列表。

这样就看出来了,读取权限列表的行为是非常频繁的,并且生成的权限列表只存在一个会话周期就失效了,所以说我们选择redis进行权限列表的存储,非常适合这种读多而且短期存储的情况。并且redis是在内存中进行读写的,比起关系型数据库的读写相对来说快一些,所以说我们使用redis还减少了系统的响应时间,极大的增加了用户的体验感。

6)如何实现的单点登录?

单点登录(Single Sign On, SSO)是指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的应用系统。

用户统一在认证中心进行登录,登录成功后,认证中心记录用户的登录状态,并将 Token 写入 Cookie。

应用系统检查当前请求有没有 Token,如果没有,说明用户在当前系统中尚未登录,那么就将页面跳转至认证中心。由于这个操作会将认证中心的 Cookie 自动带过去,因此,认证中心能够根据 Cookie 知道用户是否已经登录过了。如果认证中心发现用户尚未登录,则返回登录页面,等待用户登录,如果发现用户已经登录过了,就不会让用户再次登录了,而是会跳转回目标 URL ,并在跳转前生成一个 Token,拼接在目标 URL 的后面,回传给目标应用系统。

应用系统拿到 Token 之后,还需要向认证中心确认下 Token 的合法性,防止用户伪造。确认无误后,应用系统记录用户的登录状态,并将 Token 写入 Cookie,然后给本次访问放行。(注意这个 Cookie 是当前应用系统的,其他应用系统是访问不到的。)当用户再次访问当前应用系统时,就会自动带上这个 Token,应用系统验证 Token 发现用户已登录,于是就不会有认证中心什么事了。

二、讲师管理模块

1、功能介绍

这一个模块主要功能有对讲师的批量增加(利用easyexcel实现)、删除(利用mybatisplus中的逻辑删除插件,需要注入到Spring的bean容器中)、分页条件查询(mybatisplus中有一个封装类)、更新讲师信息;

(增加、删除时进行updateTime更新,增加时进行createTime更新)

2、注意点
1)分页条件查询如何实现?

首先对分页插件进行配置,将前端传入的当前页和每页大小封装到mp的Page对象中,进一步使用mp的querywapper对象,封装条件查询参数,将page对象和querywapper对象传入mp的分页查询方法中,由这个查询方法自动给我们去进行分页处理,分页成功后,会将分页数据封装到page对象中,我们再提供page对象就可以取到分页后的数据了。

2)批量增加如何实现?(EasyExcel的使用)

我们之前都是使用的poi进行excel的读写操作,但是这个开源框架是一次性把数据读入内存,这种操作是非常耗内存的,可能会造成内存溢出。本项目中,我们采用了easyexcel进行excel的读写操作,这个开源框架正好解决了poi耗内存的缺点,而且操作也比较简单,它是将文件的数据一行一行的读取,逐个进行解析。

我们之前都是使用的poi进行excel的读写操作,但是这个开源框架是一次性把数据读入内存,这种操作是非常耗内存的,可能会造成内存溢出。本项目中,我们采用了easyexcel进行excel的读写操作,这个开源框架正好解决了poi耗内存的缺点,而且操作也比较简单,它是将文件的数据一行一行的读取,逐个进行解析。

批量增加这里主要是利用了easyexcel的读功能,这里需要定义一个类去继承AnalysisEventListener(excel监听器),这个监听器的作用就是当一行数据读取完成之后通知其再读下一行数据,这里是利用了观察者模式。

读操作的话大体思路是这样的,先读取每一行的数据,根据教师ID判断当前教师是否已经在数据库中存在,如果不存在则添加。

3)更新讲师信息如何实现?

更新操作可以利用mybatisplus的乐观锁实现线程安全的更新。

做法是:在表中添加一个version字段,更新时先读取version,在执行更新时先判断当前的version是否和更新时读取的version值一样,如果一样则更新,并将version值加1,如果不一样则不进行更新。

4)分布式主键生成的策略?
 * 数据库ID自增
 * 全局唯一ID (idWorker)

 * 全局唯一ID (UUID)

 * 字符串全局唯一ID (idWorker 的字符串表示)

 * 雪花算法:

 * snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。

 snowflake算法可以根据自身项目的需要进行一定的修改。比如估算未来的数据中心个数,每个数据中心的机器数以及统一毫秒可以能的并发数来调整在算法中所需要的bit数。

 优点:

 1)不依赖于数据库,灵活方便,且性能优于数据库。

 2)ID按照时间在单机上是递增的。

 缺点:

 1)在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。

三、课程分类的树形显示

说清楚之前使用的方法和递归方法的优缺点!

数据库的课程分类表中定义了id、parentid(其上一级的分类,如果为0,则代表为树根)

之前是查询了两次数据:第一次查询了所有parentid为0的数据,也就是根节点数据。第二次查询了parentid不为0的数据,也就是子节点。因为这里我就做到了二级分类,所以就封装了两个类来保存根节点和子节点。

然后通过遍历根节点数据来构建根节点,在构建根节点的过程中遍历子节点数据,通过id = parentid来构建子节点。

这样的缺点是查询了两次数据库,并且也就只能构建两层的树。

之后我利用递归实现,这样只需要查询一次数据库,并且不论多少层树,都能构建出来。

定义了一个SubjectNode,其中定义三个数据:分类id,title,children(这三个是前端所需要的数据),获取课程分类树的方法中,先构建一个List集合,相当于存放整个树的数据。从查询出来的数据中遍历,如果parentid则构建一个SubjectNode,将其id和title设置上,再通过递归的方式来添加子节点(子节点也是一样,再遍历一遍数据,判断一下有没有等于当前结点id的,如果等于再次封装),最后返回List集合就是一个菜单树。

四、课程管理

主要有两大部分:课程发布和其他功能。

1、课程发布的流程

课程发布需要两个角色进行两个操作:第一个是讲师进行课程基本信息、课程大纲信息的添加,添加完成后选择提交并审核课程,交由管理员角色进行审核;管理员角色进入课程列表界面,可以看到三个课程的状态:未提交审核、审核中、已发布、修改审核中、已下架

  • 未提交审核:讲师添加完课程基本信息后,或添加完课程大纲后,因为某些原因退出,没有选择提交并审核。这时讲师可以返回完善课程信息或大纲信息,进一步提交审核或者讲师也可以对这个课程进行删除。或者管理员未通过添加课程审核,交给讲师继续修改课程信息。
  • 审核中:讲师已经添加完课程基本信息和课程大纲信息,而且确认课程无误后,提交并审核。这时讲师可以修改课程基本信息和大纲信息或删除课程信息,管理员可以审核信息。
  • 已发布:管理员已经通过审核,这时管理员可以下架课程;讲师可以修改课程,但需要交给管理员审核。
  • 修改审核中:讲师修改课程后,提交给管理员审核,变为此状态,此状态下,学生用户只能看到修改之前的课程,课程如果通过管理员审核,才能真正看到修改后的课程。
  • 已下架:管理员将已发布的课程下架,此时课程无法被修改,无法显示到前台学生用户界面上。

讲师当点击课程添加的时候,先去填写课程的基本信息,点击下一步,去填写课程大纲信息(包括课程章节和课程小节部分),填写无误后,点击下一步进入课程预览页面,这个页面显示课程和大纲的一些基本信息交给用户去确认,确认无误后,点击提交并审核,课程状态变为审核中。

如果讲师在添加课程的途中退出,课程状态是未提交审核,讲师可以选择编辑课程or课程大纲进行课程信息的进一步添加。

管理员收到讲师提交的课程信息后,选择未审核课程进行审核,审核无误后,点击通过审核,课程状态变为已发布。课程未通过审核,课程重新变为未提交审核状态,让教师进一步修改。

变为已发布状态后,如果课程下架,管理员点击列表中的课程下架按钮,将课程变为下架状态。如果需要修改课程信息,讲师进行修改,修改完成后,需要再次提交给管理员审核,此时状态变为修改审核中状态;如果管理员通过修改审核,课程状态再次变为已发布,更新课程;课程未通过审核,课程状态也变为已发布,课程信息不变。

只有已发布或修改审核中(显示的是没修改的课程信息)状态的课程可以给用户进行显示。

还有一个比较有特色的功能就是添加课程小节的信息时,有一部分是添加视频,利用了阿里云的vod视频点播服务。

2、课程管理的其他功能

需要结合不同的角色进行说明,具体见 注意点 的 4) 5)条。

3、注意点
1)课程发布过程中需不需要用事务?

我感觉不需要,因为用户有可能只对课程基本信息进行了添加后,网络不好,退出去了,这时候可以保存他添加的课程基本信息到数据库,用户再登录进来的时候,可以通过课程列表中的编辑课程信息的功能,对其做进一步的修改或添加大纲。

2)如何判断课程的状态?

数据库中有一个课程状态的字段,利用这个字段来判断课程的状态,从而进行进一步的操作。

3)简介表为什么在课程表中单独分离出来?

课程表中简介是单独分出了一个表出来,是因为简介中利用了富文本编辑器,所存储的数据量可能相对来说较大,而课程的数据是要经常查到并进行显示的,而课程简介不太需要显示,所以单独分出一个表,这样就会提高一点查询的效率。

4)讲师对课程具有哪些操作权限?

查看个人的课程信息:主要就是查看个人发布的课程,有未提交审核、审核中、已发布、修改审核中、已下架的课程。

添加课程:讲师可以对课程进行添加

编辑课程:

  • 未提交审核:可以对未提交审核的课程进行进一步的编辑操作,可以编辑课程基本信息,也可以编辑课程大纲信息,直到最后的预览,提交审核。
  • 审核中:编辑完课程信息后,课程状态还是审核中,管理员那边的课程信息也会同步改变。(如果此时管理员也恰巧也在审核信息应该怎么办呢?)
  • 已发布:编辑完课程信息后,课程状态变为修改审核中,交由管理员进行课程信息审核。管理员审核通过后课程状态变为已发布。管理员未通过审核,课程状态也是变为已发布,课程信息不变,通知讲师课程审核未通过。
  • 修改审核中:不能够修改课程信息(都已经修改提交审核了,这时候就没必要修改了)

删除课程:

  • 未提交审核/审核中:可以对课程进行彻底删除。
  • 已发布:不可以删除。(但是可以联系管理员将其下架)
  • 修改审核中:不可删除、不可下架(只有管理员审核完后,将相应状态变为已发布时,才可以下架,但这个操作也是管理员的)
5)管理员对课程有哪些操作权限?

查看所有讲师课程:主要就是查看所有讲师发布的课程,有未提交审核、审核中、已发布、修改审核中、已下架的课程。

审核课程:对审核中和修改审核中的课程进行审核。审核中的课程,审核通过后课程变为已发布状态。审核未通过课程变为未审核状态,交给用户继续修改。

下架课程:对已发布的课程进行下架处理(只有管理员审核完后,将相应状态变为已发布时,才可以下架)

6)修改审核的步骤

有两张表:课程表和修改课程表,两张表的表结构是一样的

讲师修改课程信息后,提交审核。此时,课程表的课程状态变为修改审核中,课程信息还是原来没修改的。同时向课程修改表中添加相应的修改后的课程数据(注意:这两个课程id一定是相同的)。

此时讲师、管理员、学生看到的课程信息是不一样的。

管理员会在课程列表中,看到的是课程信息修改后的信息,需要将课程表和课程修改表做一个union操作,再通过课程ID进行分组,再根据添加时间进行排序,选出添加时间靠后的课程。

讲师会在其课程列表中,看到课程修改前的信息,这样就可以只查找课程表,不查找课程修改表,提高了查询的效率。

学生是看到的所有讲师已发布和修改前的信息,这个操作和管理员的操作差不多,只不过最后是选出时间靠前的。

管理员对课程进行审核,如果审核通过,课程表中,将新的课程信息更新,同时把状态变为已发布;如果审核不通过,课程表中,只修改状态为已发布;无论审核是否通过,都删除课程修改表中的信息。

修改审核完成之后,通知讲师是否已经通过审核(未实现,可用websocket实现)

7)上传视频的功能是怎么实现的?

开通了阿里云的视频点播功能后,我参照其上传视频SDK的文档说明,了解到这个上传视频的服务其实是上传到阿里云OSS中,所以我又开通了阿里云OSS服务。从文档里得知,上传服务需要往上传视频的一个配置中加载OSS服务的accessKeyId和accessKeySecret,而且这两个还是要经常用到的。所以我就直接建立了一个配置类继承InitializingBean,在加载bean的时候,就加载这两个配置项。然后是通过它给的demo案例,有一个文件流的方式进行上传,我从controller中用MultipartFile类型的对象去接收从前端上传过来的文件,通过它的getInputStream方法获取文件流,创建一个他自定义的文件上传的request对象,将配置属性、文件流放到里面,最后上传成功后它会生成一个类似于response的对象,我从response对象中可以获取到视频ID,返回给前端。

通过视频ID,可以获取视频地址和视频播放凭证,方便我们之后完善视频展示的功能。

8)删除课程时,怎么判断课程下的大纲和小节都被删除?

第一种方法:删除前先根据课程id对该课程下的大纲和小节进行查询,看一下是否还有,如果有的话先去删除课程大纲和小节,如果没有的话,直接去删除课程。

第二种方法:课程表和大纲表中分别加入一个课程下的大纲和大纲下的小节字段,当新增时,加1;当删除时,减1。这样就只从一个表中判断,就可以知道该课程下有没有大纲和小节了。

五、项目中遇到的问题

1、Maven加载问题
  • Maven加载项目时,不会加载src-java文件夹里面的xml类型文件
  • 解决方案:
    • 1、复制xml文件到target目录
    • 2、在maven中配置,与properties配置文件中指定xml文件夹
2、跨域问题:
  • 访问协议+ip地址+端口号,三者有任何一个不一样,就会产生跨域问题
  • 解决方案:
    • 在controller添加注解@CrossOrigin
    • 也可以使用nginx反向代理、httpClient、网关(但项目中没有使用)
3、SpringScurity
  • 各种资源的放行问题(特别是静态资源)

    静态资源、登陆和注册页面、登陆和注册相关的处理器、登陆失败后跳转的页面也需要放行

    • 使用redis存储热门课程,显示到靠前的位置。
    • 如何对学生的学习进度进行记录(学习进度经常改变,可以利用redis存储学习进度,过一段时间往数据库中写)
    • 如何对视频进行存储,如何获取课程视频
4、调用某个接口太频繁,性能降低
1)问题

微服务架构的一个缺点是服务间接口调用太过频繁。特别是在获取一个数据集合,每条记录都需要去调用其他微服务的接口时,过多的服务间接口调用会导致速度慢,性能降低。

需要从一个业务模块中获取课程详情,课程详情中需要获取讲师姓名,但课程表中只有课程ID,这个时候如果采用微服务调用的方式,那么查询每条课程信息的时候都需要调用讲师模块接口,导致大量的系统间的通信。

2)解决办法

一种解决办法是在课程表中扩展讲师姓名字段,增加数据的冗余,修改课程列表的接口,这样的做法如果在上线之后改动的话付出的代价有点大。

第二种方式就是在调用接口的基础上,每次在将获取到的讲师ID和讲师姓名都存储在redis中,每次在课程列表中调用讲师模块的时候都查询一下redis中有没有该数据。这样可以减少模块间的通信和数据的冗余。

还有一种方式是利用表连接和一个视图类,将需要的数据一次性返回,封装到视图类中,将这个视图类一次性返回。避免多次请求,减少网络通信次数。

六、其他问题

1、怎么解决出现海量数据请求的情况,怎么保证服务器稳定性?
  • 应用程序和静态资源文件进行分离(两者放在不同的服务器上,减轻单个服务器压力):利用nginx
  • 页面缓存:将应用生成的很少发生数据变化的页面缓存起来,这样就不需要每次都重新生成页面了,从而节省大量CPU资源,如果将缓存的页面放到内存中速度就更快。
  • 搭建服务器集群,每个集群上放相同的代码,通过Nginx负载均衡:转发用户请求到不同服务器上,减轻单个服务器的压力
  • 分布式:每个服务器上放不同的服务,通过nginx进行反向代理。
  • CDN(听说过,不太可能会用,money很高)
2、项目中还可以进行怎样的优化?
  • 通知功能(socket实现)
  • 跨域可以用网关解决
  • 加入微信登陆
  • 适当尝试一下微服务
3、项目中体现的面向对象的思想,具体为类描述
  • MVC三层架构的目的就是解耦,把层次分离出来,以便以后代码的二次开发和维护。
  • 封装:在model层中,定义了一个vo的包,专门用来定义视图类,我们之前针对每个表已经定义过实体类,但是视图类中定义的属性包含了某个表全部的字段,有些情况下,用户并不像看到某些字段,或某些字段根本没用,也就是说我们把用户在界面上输入的或者用户想在界面上看到的一些字段,封装到一个类中,将它当作一个视图类来使用。
  • 继承:用户类,定义用户的公共属性;学生类、教师类、管理员类继承用户类,定义一些私有的属性。
  • 多态:定义一个用户(不管是学生、教师、管理员),只定义其学生接口,具体new的对象是学生、教师、管理员的类对象。
4、基于这个项目,在还没有开始进行之前,怎么向组员沟通,规划项目的内容和项目框架

可以往软工的方向答(可行性分析、需求分析、总体设计、详细设计、编码、测试、部署上线)

5、用Junit框架测试是怎样进行的,Junit框架主要流程包含了什么?

当写完每个功能接口的时候,针对于这个接口中的方法进行测试,看一下是不是返回我们想要的结果。

先Controller,如果Controller能跑通,那么service和mapper也是通的,不用测试。

如果Controller不同,再近一步测试service和mapper,看看是哪里出了问题。

首先是引入junit的依赖,其次针对每个类都建一个测试类,对类中的每个方法进行测试。

6、项目中遇到问题经常怎么解决的?

如果是功能性的问题,我会先和我的组员商量一下,大家讨论出解决方案。

如果是技术问题,我会先去B站或者找本书去学习,学习完自己去写一个小的功能demo,然后再继续把这个技术整合到项目中。

如果是程序上的错误,我会自己慢慢去排查,实在排查不出来会找组员或老师请教。

7、如果一个用户一直点击某个添加按钮,应该怎么去防止?

在前端解决,每次用户点击添加按钮后,就利用按钮的禁止,来防止用户重复点击。

8、JWT生成token及过期处理方案
后端
  1. 在登录接口中 如果校验账号密码成功 则根据用户id和用户类型创建jwt token(有效期设置为-1,即永不过期),得到A
  2. 更新登录日期(当前时间new Date()即可)(业务上可选),得到B
  3. 在redis中缓存key为ACCESS_TOKEN:userId:A(加上A是为了防止用户多个客户端登录 造成token覆盖),value为B的毫秒数(转换成字符串类型),过期时间为7天(7 * 24 * 60 * 60)
  4. 在登录结果中返回json格式为{“result”:“success”,“token”: A}
  5. 用户在接口请求header中携带token进行登录,后端在所有接口前置拦截器进行拦截,作用是解析token 拿到userId和用户类型(用户调用业务接口只需要传token即可), 如果解析失败(抛出SignatureException),则返回json(code = 0 ,info= Token验证不通过, errorCode = ‘1001’); 此外如果解析成功,验证redis中key为ACCESS_TOKEN:userId:A 是否存在 如果不存在 则返回json(code = 0 ,info= 会话过期请重新登录, errorCode = ‘1002’); 如果缓存key存在,则自动续7天超时时间(value不变),实现频繁登录用户免登陆。
  6. 把userId和用户类型放入request参数中 接口方法中可以直接拿到登录用户信息
  7. 如果是修改密码或退出登录 则废除access_tokens(删除key)
前端(VUE)
  1. 用户登录成功,则把username存入cookie中,key为loginUser;把token存入cookie中,key为accessToken 把token存入Vuex全局状态中
  2. 进入首页
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackson Xi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值