目次
回顾一下xml文件怎么写
哪个地方利用了哪个技术
MyBatis-Plus-oj的表结构设计,
管理员登录功能
Swagger
Apifox编辑
BCrypt
日志 框架引入(slf4j+logback)
nacos
Swagger无法被所有微服务获取到修改的原因
身份认证三种方式:
JWT(Json Web Json,一种基于JSON的安全跨平台信息传输格式,JWT(界说了一种紧凑且自包含的方式)用于各方间安全传输信息,此信息可以用于验证和相互信任。
缺点:
改良
Redis
RedisTemplate
Entity
DTO
VO
RouterLink组件
RouterView组件
VUE规则
Axios(代替ajax)
B端功能(界说接口哀求方式Get(查询),Post(新增),Put(修改),Delete(删除)
PageHelper
Vue声明周期函数
C端功能流程
阿里云密钥(阿里云短信登录)
焦点是利用redis来模仿
Jmeter
XXL-job
竞赛报名功能
TransmittableThreadLocal
我的竞赛功能
标题详情缓存 (引入ES)
ES是什么
ES写入进步服从
ES的全文检索
代码沙箱(判题功能)
因此引入MQ
用户拉黑功能
引入注解
引入切面类
我的消息功能
消息发送实现:
竞赛排名功能
暂时记录
Nginx
正向代理
反向代理
负载均衡
动静分离
标题标顺序列表(用户端的前一题和后一题,怎么做)
2025年5月13日 面试,某某某某达
ES怎么做添加
回顾一下xml文件怎么写
namespace=xxx映射定名空间,xml对应的java(Mapper)接口
<mapper namespace="com.bite.system.mapper.exam.ExamMapper">
resultType指,映射到ExamVO这个类中
利用#{}接收传递过来的参数
<select id="selectExamList" resultType="com.bite.system.domain.exam.vo.ExamVO">
- select
- te.exam_id,
- te.title,
- te.start_time,
- te.end_time,
- te.create_time,
- ts.nick_name as create_name,
- te.status
- from
- tb_exam te
- left join
- tb_sys_user ts
- on
- te.create_by = ts.user_id
- <where>
- //假如有一个有时候存在,有时候不存在则使用这个if,在where里面使用
- <if test="title!=null and title!=''">
- AND title like CONCAT('%',#{title},%) 进行一个模糊查询
- </><img alt="" src="https://i-blog.csdnimg.cn/direct/59b067bead6145499f56783f5140e7bc.png" />
- <if test="title !=null and title !=''">
- AND title LIKE CONCAT('%',#{title},'%')
- </if>
- <if test="startTime !=null and startTime !='' ">
- AND te.start_time >= #{startTime}
- </if>
- <if test="endTime !=null and endTime !='' ">
- AND te.end_time <= CONCAT(#{endTime},'23:59:59.999')
- </if>
- </where>
- ORDER BY
- te.start_time DESC
- </select>
复制代码
接口文档,接口的说明文档
作用:简化前端开发,易于错误处理,代码可维护性,文档化
Authorization(奥丝ruai贼神)
接口概述,接口地点,哀求方法,哀求参数,相应数据,哀求和相应示例
HTTP协议:1xx信息.表现暂时相应并且需要哀求者继续执行操作
2XX 成功。操作被成功接受并处理
3xx重定向,表现客户端必须执行一些其他操作才气完成其哀求
4xx客户端错误,哀求包含语法错误大概无法完成哀求
5xx服务器错误,这些错误可能是服务器本身的错误,而不是哀求出错
this(这个/自身)
+this,调用自身的属性和方法
什么时候利用this,当传入的参数名字和你的属性名字一样时候,就利用this。
this(),调用自己的构造方法,必须要放到首行
MyBatis-Plus-oj的表结构设计,
之间无脑快速安装版本简单易操作
数据库连接池:借助mybatis-plus操作数据库固然给我们很大便捷,但是这样方式操作会导致一些问题
频仍的创建来哪和销毁连接:包括TCP层的握手和Mysql协议的握手,会消耗大量时间
连接数不受控制:在业务流量高峰期,大量应用服务器可能同时哀求数据库连接,而数据库可以或许承载的连接数目有限,这可能导致数据库性能低落
连接管理困难:开发者需自行管理数据库连接的创建,利用和关闭,这增长了开发的复杂性和出错的可能性。
数据库连接池用来解决这些问题:
提供统一管理:数据库连接池对数据库连接创建等操作的统一管理
进步体系性能:由于创建和销毁数据库连接需要消耗时间和体系资源,如果每次进行数据库操作都不停销毁,创建会影响性能利用连接池可以复用已经创建好了连接,大大进步体系的性能
进步资源利用率:连接池通过复用已有连接,制止了频仍创建和销毁连接带来的资源浪费,进步了体系资源利用率
进步体系稳固性:数据库连接池可以有效控制体系中并发访问数据库的连接数量,制止因并发连接数过多导致数据库瓦解,同时连接池还会定时查抄连接的有效性,自动更换掉无效连接,包管了体系的稳固性
常见的:C3P0,Druid,HikariCP
为什么利用HikariCP(controation pool)
高性能:HikariCP是一个高性能的数据库连接池,他提供了快速,低耽误的连接获取和开释机制,在SpringBoot项目中,这可以显着进步应用步伐的相应速度和吞吐量,特别是在高并发场景
资源优化:HikariCP对资源的利用进行风雅的管理和优化,他采用一种内存服从极高的数据结构来存储和管理连接,减少了内存占用和垃圾回收的压力,还减少了不必要的线程和锁竞争
设置灵活:HikariCP提供了丰富的设置选项,允许开发者根据项目的详细需求进行微调
与SpringBoot集成良好:SpringBoot对HikariCp提供了良好的支持,可以轻松过集成
线程如果满了,超时就抛弃,如果空闲时间,没有利用谁人暂时线程,那么就会销毁这个线程
管理员登录功能
表结构设计:满足需求,制止冗余设计,考虑今后发展
1.导入依赖,mapper去继承这个类,
2.可以理解为接受的对象,就是和数据库表对应的,然后我们填入谁人TableId的名字
3.对应的Service,找到你想要的方法,去实现,如果不是谁人查找全部列表就lambda
好比这种,利用lambda表达式,传入的参数填入lambda,需要xx利用xx来查
Swagger
swagger是否是一个公共的呢,没啥难度,引入就直接操作啦
Apifox
BCrypt
哈希加密算法,被广泛应用于存储暗码和进行身份验证,并且BCrypt算法每次计算都会先天生一个随机盐值,与用户暗码一起到场计算最终得到一个加密的字符串,由于天生盐值随机的,以是每次利用相同的暗码得到结果也不相同,这样有效防止攻击者破解暗码
日志 框架引入(slf4j+logback)
1.重要性
故障的排查和问题定位
体系监控
数据收罗
日志 审计
2.留意事项
留意日志级别
留意日志内容,日志格式和可读性
留意日志的滚动和归档
为什么选择slf4j+logback
易于切换,设置灵活
logback性能更好,集成更方便,功能更强盛
SpringBoot默认的日志框架
每次修改一个设置都需要重新打包上线,团队的协作比力困难,环境隔离不足,开发,测试,生产
nacos
设置MYSQL数据库
- 数据持久性:利用MYSQL作为外置数据库可以确保数据被持久化存储,这对于确保服务的稳固性和数据的可靠性非常重要
- 高可用性:NACOS支持集群部署,利用MYSQL作为共享的数据存储可用确保集群中各个节点的数据一致性,此外MYSQL自身也支持高可用和故障转移,利用主从复制大概集群解决方案,从而进一步进步体系可用性。
- 性能优化:利用nacos内置的数据库,固然能简化部署,但是性能收到限制,外置的MYSQL可用根据需要进行优化和扩展,满足更高的性能需求
- 易于管理和维护:我们体系本身用MYSQL,nacos外置数据库和我们体系采用同样的数据库库,可以包管技术利用的统一,简化了数据库的管理和维护工作,低落运维成本。
这里我们项目分为三类
1000 SUCCESS 操作成功
2000 ERROR 服务器繁忙请稍后重试(前端根据错误码表现,服务器繁忙,请稍后重试)
3000 操作失败,但是服务器不存在异常
3001 未授权
3002 参数校验失败
3003资源不存在
3004资源已经存在
3101用户已经存在
3102用户不存在
3103用户大概暗码错误
3104 你已被列入黑名单,请联系管理员
Swagger无法被所有微服务获取到修改的原因
我们想把Swagger进行一些修改,可是发现为什么改不了呢
(org.springframework.boot.autoconfigure.AutoConfiguration.imports,明明这个已经写了,还是在谁人目次内里)
这个是由于你的META-INF.spring,是一步创建的,正常来说这个应该是先创建INF,再去创建spring,由IDEA来合并,可不是你自己就去合并了,这就是导致不能被访问的原因。
身份认证三种方式:
- 基于Session的身份认证:这个是最常见的身份认证方式,当用户首次登录的时候,服务器会将用户信息存入session并生产一个唯一的SessionID,然后返回给客户端,此后的哀求客户端会要携带这个Session ID,服务器通过Session ID有效性来判定用户身份
- 基于OAuth身份认证:OAuth认证机制是一种安全,开发且简易的授权尺度,他允许用户授权第三方应用访问其账户资源,而无需向这些应用提供用户名和暗码,如微信,QQ登录其他网站
- 基于Token的身份认证,这种方式,服务器在用户登录事后,会返回一个Token给客户端,客户端每次哀求资源,都在哀求头上携带Token,服务器通过验证Token的有效性来判定用户的身份,这种方式常见于前后端分离的架构中,如JWT进行身份验证。
JWT(Json Web Json,一种基于JSON的安全跨平台信息传输格式,JWT(界说了一种紧凑且自包含的方式)用于各方间安全传输信息,此信息可以用于验证和相互信任。
由三部门组成:头部,载荷,署名
头部(header):包含令牌类型和利用方法
载荷(payload):包含用户信息和其他元数据(利用base编码)
署名(signatiure):用于查验令牌的完备性和真实性
署名算法{
base64(header+base64编码(payload)
}
客户端利用用户名跟暗码哀求登录
服务端收到哀求,去验证用户名和暗码
验证成功后,服务端签发一个Token,再把这个Token发送客户端(token上述的jwt串)
客户端收到Token,会把他存储起来
每次客户向服务端哀求资源时候,带着Token
服务端收到哀求,验证客户端内里带着的Token,如果验证成功,向客户端返回哀求的数据。
缺点:
JWT的payload没有加密,如果token泄漏,那么用户的信息就完全暴露了,以是完全不能存敏感数据。(用户手机号,银行卡号啥的)
jwt,无状态,如果想要修改此中的内容,就要重新签发一个新jwt
无法延伸jwt的逾期时间。
改良(JWT+Redis)
1.payload不存敏感信息,解决:仅仅存储用户的唯一标识信息。
第三方存储敏感信息(根据唯一标识信息,从第三方机制查处相应敏感信息
存储的查询性能要高,并且不需要长期存储,
2.用户修改了个人信息之后,jwt不变。
满足第一点后,相称于就是两个分开了,我只是改了第三方敏感信息
3.控制jwt的逾期时间
不能再利用JWT提供的逾期时间的参数。
通过第三方记录jwt的逾期时间,并且支持逾期时间的修改,最好还有第三方工具提供存储功能
- Redis
当账号和暗码匹配成功之后,天生JWT
redis(表明用户身份字段,加上前缀就是)
logintoken+雪花id/大概(如果我们不消雪花id,没这个条件,只可以自增ID,B,C表全是自增的,肯定会重复,引入hutool工具包的uuid,),存储的对象是用的内里fastjson序列化器,
把一个对象存入redis(设置逾期时间哈要)
如果720分钟之后,用户拿着逾期的token,我怎么查找出来呢?,我直接通过redis值判定
token存的是自增主键和uuid
y用户携带我们给他返回的token,向我们服务器再次发送哀求的时候,我们调用parse方法解析(传入token和secret盐值),解析后得到这两个值,我去拿到redis中,去进行一个查询,如果查询到我们之前存的数据key是下面的,然后value有东西就可以
- key //LoginToken+userId (因为UserId本身就是唯一的,通过雪花算法)
复制代码 value:用户身份(管理员/用户) nickName昵称,headImage头像
Redis
jwt+Redis实现身份认证机制。
为什么要封装Service
- package com.bite.common.redis.config;
- import org.springframework.boot.autoconfigure.AutoConfiguration;
- import org.springframework.boot.autoconfigure.AutoConfigureBefore;
- import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
- import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
- import org.springframework.cache.annotation.CachingConfigurerSupport;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
- @Configuration
- @AutoConfigureBefore(RedisAutoConfiguration.class)
- public class RedisConfig extends CachingConfigurerSupport {
- @Bean
- // @ConditionalOnSingleCandidate 初始化这个Bean对象
- public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory
- connectionFactory) {
- //new对象
- RedisTemplate<Object, Object> template = new RedisTemplate<>();
- //对属性进行设置,和redis简历连接
- template.setConnectionFactory(connectionFactory);
- //new的一个自定义序列化器
- JsonRedisSerializer serializer = new JsonRedisSerializer(Object.class);
- // 使⽤StringRedisSerializer来序列化和反序列化redis的key值 key和value的序列化器是不同的
- template.setKeySerializer(new StringRedisSerializer());
- //我们一般对于key,还是哈希的key,都是采用String 类型,所以说,用String类型序列化器完全搞定
- //但是我们的value可能是存放具体的对象,对象的需要序列化,把序列化结果存起来,使用fastjson
- template.setValueSerializer(serializer);
- // Hash的key也采⽤StringRedisSerializer的序列化⽅式
- template.setHashKeySerializer(new StringRedisSerializer());
- template.setHashValueSerializer(serializer);
- //完成设置后,对于后续的初始化操作
- template.afterPropertiesSet();
- return template;
- }
- }
复制代码 直接用redisTemple,他的内部有很多bean对象,通过bean去调用方法进行操作,利用一个redisService(进行一个封装),使代码和详细第三方实现解耦
抽象于解耦合:封装第三方组建可以提供一个更加高级的抽象层,使得你的代码于详细的第三方实现解耦合,如果未来更换第三方组件,大概调解其设置,你只需要对封装的Service层,而不消修改整个应用中的大量代码
统一接口:多个三方提供相似功能,api与用法各自差别,通过封装提供统一接口,使开发者不消关注底层工具的详细差别
扩展性:更轻易为第三方工具添加额外的功能和逻辑
错误处理和异常处理:可以统一封装第三方的特定异常大概错误
代码可读性和可维护性:利用封装的service可以使代码更加清晰,轻易理解
RedisTemplate
Boolean haskey(String key)判定key是否存在
unit是时间单位
boolean expire(final String key ,final long timeout,finalTimeUnit unit ){} 设置有效时间
getExpire(final String key,final TimeUnit unit){}
deleteObject(String key)删除单个key
//缓存基本的对象,Integer,String 实体类等。
setCacheObject(final String key,final T value 缓存基本的对象,Integer,String,实体类等)
getCacheObject(final String key,Class<T>clazz)得到缓存基本对象
getListSize(final String key)得到List中存储数据数量
Entity
与数据库表中的字段--对应的实体类
他与数据库表一一对应,用于持久化数据
DTO
接受前端的传递的数据
DTO(Data Transfer Object,数据传输对象),通常是轻量级的,只包含需要传输的数据(只传递需要的数据,好比登录人,登录暗码(对应的数据库的ID,我们不去传递)
VO
VO(View Object 视图对象),用于在展示层表现数据,通常是将表现数据的实体对象中的一部门属性进行选择性的组合形成一个新的对象,目的是为了满足展示层数据要求的特定命据结构。 (好比新增大概删除数据,我们不会把此中的更新时间,更新人放出来)
区分的目的:
进步代码可读性和可维护性:每个对象都有特定的职责,使得代码结构更加清晰,每个对象统一定名,项目变得更加一致和可推测
解耦:Entity,DTO,VO的划分低落了各个部门的耦合度,修改某一层逻辑或数据时减少对其他层的影响
优化性能:差别的对象,好比DTO和VO可以根据自身功能和当前需求,进行裁剪和拼装,合理利用网络传输资源,进步性能
但是如果我们划分过细的话
增长复杂性:过多的概念划分可能导致开发者需要花费更多时间去理解每个对象的作用和用途,增长了学习和理解的难度,导致开发的服从低落,甚至引入不必要的错误。
过分设计/影响性能:过分设计意味着简单功能时引入了过多抽象和复杂性,增长开发成本,和对象转化与数据处理直接有性能开销(好比DTO需要转化成实体)
维护成本上升:对象划分的增多,对象数量增长,导致代码库的膨胀
耦合度增长:对象划分为了低落耦合度,过细划分导致耦合上升,由于创建过多的中间层大概转化层来处理对象之间差别的交互,导致体系各个部门之间的依赖关系
RouterLink组件
天生可点击的连接,用于页面之间的跳转,RouterLink通过其to属性指定链接的目的地点,当用户点击这些链接时候,路由会自动切换对应的页面
RouterView组件
用于根据当前路由状态动态渲染匹配的组件,在单页应用中,URL发生变化时候,RouterView会根据当前路由状态自动渲染对应组件,这意味着无论导航到哪里,RouterView都会表现与当前路由相匹配的组件内容
VUE规则
assets目次结构:用于存储项目中需要导入的静态资源:如图片
views:views目次通常存放安歇路由直接相干的页面级别的组件
components:components目次通常用于存放那些可复用的,小巧的,功能单一的组件,组件通常不会被直接渲染到路由对应页面上
router:存放Vue Router的设置文件
项目工程名:全部小写方式
目次和文件夹:全部小写,短横线分隔
js文件:全部小写,优先选择单个单词定名
1.前端接受用户输入信息(账号,暗码)
2.携带用户输入的信息,向后端发起登录哀求
3.后端接收到前端的哀求,执行登录相干的业务逻辑
4.后端执行完相干的逻辑之后,将结果返回前端
5.前端接受到后端返回的结果之后,如果登录成功,则跳转到背景管理页面,并且前端需要将后端返回到token存储起来,如果登录失败,前端将后端返回错误提示,展示给用户。
v-model是Vue中用于输入元素和Vue实例数据直接创建双向数据绑定的指令,他使得数据和DOM之间可以或许相互同步,当输入框的内容发生变化时,Vue实例中的数据也会自动更新,反之,当Vue实例中的数据发生变化时,输入框内容也会相应更新。
Axios(代替ajax)
1.前端携带token向后端发起哀求
2.收到后端相应后,前端应该根据后端返回的结果进行一个判定,如果成功,从相应结果中获取用户昵称并且展示,如果失败,将失败的原因展示给用户
相应式数据:当数据变化时候,可以自动更新和通知与之相干的视图和组件
前端发送哀求时候,需要携带token.,退出登录时候,怎么让token认证失败呢?我可以删除token,大概加加减减,反正只要不相同就行(把redis用户睡觉删除掉就行,让token认证失败)(失败就保持登录状态,停留当前页面,如果成功,就去执行后续流程)
前端打扫存储的token,返回登录页面
1.点击退出登录,弹出确认框
2.携带者token(哀求头中)向后端发起哀求
3.前端接受到后端的相应结果之后,如果成功,打扫掉cookie存储token,并且跳转回登录页面,如果失败,将失败原因提示给用户,停留给当前页面
1.非登录状态,访问除了登录页以外的页面,自动跳转回登录页
2.已经登录过,并且token没有逾期,此时访问登录页面应该自动跳转到背景管理页面
B端功能(界说接口哀求方式Get(查询),Post(新增),Put(修改),Delete(删除)
登录模块
题库管理-难度,标题搜索,支持分页等 -考虑表结构等设计(
标题添加
1.我们登录后需要点击标题管理
2.点击添加标题之后,弹出抽屉
3.添加一些选项,好比难度,标题,内容,默认代码块,main函数,输入相干标题信息,点击发布
4.向后端发起添加标题标哀求(将用户携带的标题信息作为参数发送添加标题标哀求)
5.后端收到哀求之后,对哀求参数进行处理
6.对数据存储起来,往mysql数据库内里存储等
7.后端需要将标题添加的结果返回给前端
8.前端收到后端的相应(如果成功,提示用户添加成功,并且在列表中,将新增的标题展示在第一个,如果失败,把失败原因提示给用户)
竞赛管理 (携带token向后端发送哀求,发送后哀求之后,等待后端处理,前端等待处理,直到前端接受到数据,根据相应结果,如果成功,将标题列表数据展示在页面中,如果失败,向用户提示错误信息,页面保持原样即可
标题编辑功能:
1.找到要编辑的标题,找到之后,点击编辑按钮,
2.携带着标题id向后端发起标题详情的哀求
3.后端接受到标题详情的哀求之后,根据标题查询出标题详情(从数据库中查询出来),并且将查询结果返回给前端展示(如果查询失败,编辑标题流程结束)
4.根据展示出来的标题详情和对于标题标修改需要,对标题标详细修改,点击发布操作
5.前端携带着标题id,和修改后的内容,向后端发起编辑标题标哀求。
6.后端收到编辑哀求之后,根据标题id查到对应的标题,在根据修改后的标题内容对标题进行修改。并且将修改后的结果返回给前端.
7.前端接收到后端返回的相应结果之后,成功->提示用户编辑成功,并且再去查看信息时候应该是修改之后的,失败则返回失败原因.
标题删除:
1.找到要删除的标题,并且点击删除按钮
2.前端携带者标题id向后端发起删除标题标哀求
3.后端接收到哀求之后,根据标题id删除标题,并将删除的结果返回给前端(成功大概失败)
4.前端接受后端相应之后,如果成功,提示用户删除成功,将删除的标题从标题列表删除 如果失败,提示用户失败原因
竞赛列表功能:
1.用户切换到竞赛管理
2.前端携带着相干参数发起获取竞赛列表的哀求
3.后端收到哀求之后,根据哀求参数,从数据库中查询符合条件的竞赛.并将查询结果返回给前端
4.前端接收到后端返回的相应之后,如果成功-把查询到的列表数据展示到页面当中
如果失败,提示用户失败的原因
标题新增:
一.不包含标题标新增,我可以先不去包含标题,先去考虑别的,那么此时我们不予许他发布就行,就像是这个csdn一样(不允许发布)
1.前端带着用户输入的竞赛基本信息,向后端发起添加竞赛哀求,
2.后端接收到哀求之后,对相干参数进行处理,将竞赛信息存入数据库,并且将添加结果返回前端(成功/失败)
3.前端接收到后端的相应之后,如果成功,提示用户添加成功,并且竞赛列表中能看到新增的竞赛,如果失败,提示用户失败的原因。
二.包含标题新增 =新增不包含标题都竞赛+为这个竞赛提供标题(如果我们不生存竞赛,就可以添加标题(如果用户添加了100+标题,然后一个革新(此时全毁了就,以是不能让他直接点击添加按钮
1.先走上面那三步(不包含标题标新增),
2.点击添加标题按钮,获取标题列表的哀求。(哀求之前实现的标题列表接口即可)
3.从标题列表中,选择标题,点击提交按钮,携带着竞赛id和标题id发起为竞赛添加标题标哀求
4.后端收到哀求后,处理相干参数
竞赛编辑功能:竞赛详情功能+
竞赛详情功能:
1.携带着竞赛id向后端发起获取竞赛详情的哀求的
2.后端接收到哀求之后,根据竞赛id从数据库中查询竞赛详情信息,并将查询结果返回给前端
3.前端接收到后端相应之后,如果成功,页面展示竞赛详情信息,如果失败,提示用户失败原因。
竞赛编辑功能:竞赛详情功能+竞赛基本信息进行编辑(可以不去编辑)+竞赛标题信息进行编辑(又可能不去编辑)
竞赛基本信息编辑:(针对输入框大概选择框进行编辑)
1.输入大概选择更新后端的数据,点击生存,前端携带着更新后的数据向后端发起编辑竞赛的基本信息的哀求,
2.后端接收到哀求之后,根据竞赛id找到要编辑的竞赛,根据哀求参数对竞赛数据进行更新,并且将更新结果要同步到数据库中,并且将编辑结果返回前端。
3.前端接收到后端相应之后,
如果成功,展示竞赛编辑后到信息,并且返回标题列表是看到的标题信息也是更新后得。如果失败,则提示用户失败原因,比拿给所展示的信息还是之前标题标信息。
竞赛中标题信息的编辑功能:竞赛中标题添加功能+竞赛中标题删除功能
竞赛中标题删除功能:
1.找到竞赛中要删除的标题,点击删除按钮,前端向后端发起删除竞赛中标题标哀求,携带(参数-看后端要什么你传什么,删除的话应该是id)
2.后端接收到哀求之后,查出要删除的竞赛,并且判定是否可以或许进行操作,如果不能操作,返回前端不能操作的原因,如果能操作,开始进行竞赛中的标题删除操作。根据标题id找到要被删除的标题进行删除操作。删除数据库中对应的竞赛中的标题数据。
3.前端接收相应,如果成功,竞赛标题列表中对应的标题将消失
如果失败,提示失败原因。
竞赛删除=删除竞赛的基本信息(tb_exam)+删除竞赛对应的标题信息(tb_exam_question)
1.找到要删除的竞赛,点击删除按钮,前端向后端id发起删除竞赛哀求,
2.后端接收到哀求之后,根据竞赛id(先去判定竞赛是否存在。竞赛是否开赛),删除竞赛基本信心(tb_exam)和竞赛标题信息(tb_exam_question)并且将删除结果返回给前端
3.前端接收到相应之后,如果成功竞赛列表中不在返回该竞赛,如果失败返回失败原因。
发布竞赛:(条件,竞赛是存在的,竞赛中存在标题)(对于竞赛发布的话,后端怎么做?)
1.添加竞赛页面(基本信息生存好,在满足发布竞赛条件之后,点击发布竞赛,前端向后端发起哀求(携带竞赛id))
2.后端接收到哀求之后,根据竞赛id判定条件是否建立,如果不建立将不建立的原因发布给前端,如果建立,竞赛的状态字段从0改为1,并且同步到数据库,再将修改结果返回给前端
3.前端接收到后端相应之后,如果失败,展示失败原因,如果成功,挑战会列表页,当前的状态变为已发布,C端竞赛要能找到已发布的竞赛
竞赛撤销发布 竞赛撤销条件,1.竞赛存在,2.竞赛还没有开始)
1.找到要撤销发布的竞赛,点击撤销发布按钮,前端携带着竞赛id向后端发起哀求。
2.后端接收到哀求之后,根据竞赛id撤销对应的竞赛是否建立,如果不建立,返回前端不建立的原因。如果建立,将竞赛状态字段再从0变成1,再将结果返回给前端,前端接收到相应之后,如果失败,表现失败原因,如果成功,B端竞赛列表中当前竞赛状态,变为未发布,当前竞赛C端竞赛列表中消失。
)
C端用户管理(
1.前端携带相应的参数向后端发起获取用户列表的哀求。
2.后端接收前端的哀求之后,将参数转换为查询条件,从数据中查出符合查询条件的数据,并且将查询结果返回给前端(成功/失败)
修改用户状态(拉黑/解禁)
1.前端携带参数(userId,status)向后端发起修改用户状态的哀求。
2.后端接收到哀求之后,从数据库中查询出要修改状态的用户,然后将其状态进行修改,并且将更新后的状态同步的到数据库中,再向前端返回更新结果(成功/失败)
3.前端接收到后端的相应之后,如果失败,用户保持原状态不变,将失败原因给用户
如果成功,用户状态变为修改之后的状态,如果是将用户拉黑,要限制用户的操作
如果解禁,那么就去放开之前限制用户操作的功能的利用权限。
)
定时任务管理
PageHelper
PageHelper会自动解析你执行的SQL语句,并根据你提供的页码和每页表现的记录数,自动添加分页相干的SQL片段,从而返回正确的分页结果,无需在SQL语句中手动编写复杂的分页逻辑
设置简单:在SpringBoot项目中国添加依赖,简单设置即可利用
易于集成:无论你是注解方式还是XML映射方式,都可以方便的利用PageHelper
性能优化:利用物理分页,比起内存分页,可以减少内存消耗进步查询性能
Vue声明周期函数
每个VUE组件实例创建时候,都需要经历一系列初始化步骤,如设置好数据侦听,编译模版,挂载实例到DOM,以及数据改变时,更新DOM等。(让开发者有特定机会执行自己的代码),在这个过程中他也会不停运行:被称为生命周期的函数
在此过程中他会运行被称为生命周期钩子的函数
创建阶段:
setup()组件初始化阶段的焦点函数,用于设置组件的初始状态,计算属性等
挂载阶段:
onBeforeMount:在组件挂载之前调用,这个钩子可以做一些准备工作
onMounted:在组件挂载到DOM后调用,在这个钩子里,你可以访问和操作DOM元素,如初始化第三方库,添加事件监听器等
更新阶段
onBeforeUpdate:Vue更新DOM之前调用,可以在这个钩子里执行一些结算大概逻辑判定
onUpdated:在组件DOM更新之后调用,可以在钩子里执行基于更新DOM的操作,如重新绑定事件监听器
卸载阶段
onBeforeUnmount:在组件即将卸载之前调用,用来打扫定时器,取消异步哀求,移除事件监听器等。
C端功能流程
C端登录/注册功能,分环境讨论,新用户/老用户,
老用户:
1.正常输入手机号,点击获取验证码,前端向后端发起验证哀求,
2.后端接收到哀求之后,随机天生一个验证码,并且将验证码发送到用户的手机上,
3.用户收到验证码之后,在验证码的输入框中输入收到的验证码,点击登录/注册按钮,发起登录/注册哀求,搜索一下库内里,根据手机号看是新还是老用户,如果是老用户,执行登录逻辑,并且将登录是否成功返回前端
4.后端接收到哀求之后,根据用户的手机号判定用户是新用户还是老用户,如果是老用户的话,直接执行登录逻辑,并将登录是否成功返回给前端。
5.前端接收到相应,如果成功,跳转到体系首页(体系右上角会获取他的昵称头像啥的)
如果失败,停留当前登录页
验证码是否能放到数据库内里呢?如果很多人登录会很多次都注册内里,导致服务器压力很大,验证码一样平常有有效时间
C端标题列表功能(
竞赛列表:引入redis-(如果redis+数据库都没有数据,如果redis没有,数据库有,那么把数据库同步到redis内里
数据结构:list key value
- //新用户,执行注册逻辑,
- //先完成验证码的比对,通过手机号拿到key,然后比对验证码,如果比对成功,往系统中新增一个用户(向数据库的用户表插入一条数据即可)
复制代码 设计一个开关:
如果是投入生产利用的话,我们把开关打开,打开后的逻辑,就是天生随机验证码,并且将验证码发送给用户手机上,如果测试,把开关关闭,天生一个固定的验证码123456,不把验证码发送出去
阿里云密钥(阿里云短信登录)
AccessKey和Access Secret是我们访问阿里云API的密钥,具有该账户的权限,需要我们妥善保管。
焦点是利用redis来模仿
我们会先对用户,设置一个验证码的接口,我们在redis中统计这个设置验证码的次数,然后对你的要求进行比力,看多还是少(第一个多少次20次,第二天计数清0,(code,time)c:t:手机号来存储(次数),你登录之后,我就利用(phone,code)p:c:存code,第一次就设置有效时间,需要去动态计算)
C端功能和B端功能利用的群体也差别,C端是给详细的用户,
sudo chmod -R 777 /文件名/oj-fe-c/src
需要两个页面:一个登录注册页,一个是首页
router设置文件中,增长新页面的路由设置信息
view目次下,创建两个页面级.vue (Login.vue \Home.vue)
获取竞赛列表差别:
展示差别(前端处理)
每个竞赛展示的数据差别,C端的更少一些,(只需要调解查询sql)
搜索条件差别+默认加上是否发布的搜索条件(只需要调解查询sql)
C端支持游客利用,B端必须先完成登录(网关设置跳过身份认证)
Jmeter
Apache Jmeter是Apache组织开发的基于Java的压力测试工具,对于软件做压力测试,
可以测试静态文件,Java小步伐,CGI脚本,Java对象
100万个数据
线程数1000,examList(服务器iP,服务器ip),对数据库接口的地点,和参数
测试结果说明:
Summary:表现该时间点新增啦多少个哀求
in:该时间点内发生的哀求数
如:summary +1000 in 00:00:30表现已往30秒内新增了100个哀求。
5.9/s每秒处理5.9个哀求,就是qps(当前性能特别差啦)Error:错误哀求占用总哀求的概率,由于每次压测的结果差别,受限于场外原因,所有就要考虑一下改啥的(从数据库内里拿)
加了redis之后,到了300qps,提拔差不多60倍
什么时候将C端竞赛数据存储到redis中,特点都是已经发布的竞赛,在发布竞赛的时候,将C端端竞赛数据存储到redis里,取消发布还需要将C端端竞赛数据从redis当中移除,
选择怎样的数据结构存储C端的页面。Redis的list可以维持他的插入顺序,还可以进行分页。
选择list:利用两个list 一个利用未完赛,一个利用汗青比赛,选择两个list结构,分别存储未完赛的竞赛列表和汗青竞赛列表。
会重复存储数据,以是不能这么存储(如果存储基本信息的话)
key是什么,value是什么
key : 竞赛:汗青:列表 e:h:l: value:竞赛:未完赛:列表 e:t:l (t:表现还偶然间)
value: 竞赛基本信息(对象存储->json情势) ->根据value的数据可以或许查到下面的信息是最好的(改变方式,存储examId,我只需要根据这个查找下面的examId )
怎样改变
将竞赛的基本信息只存储一份-数据结构
String key: value
key尽可能知道我们存的是什么 e:d:examId(详情描述detail,区分开差别竞赛)
value(存储竞赛的基本信息)
什么时候将C端竞赛数据存储到redis中,
都是已经发布的竞赛,在发布竞赛的时候,应该将C端端竞赛数据存储到redis里,取消发布时候,还需要将C端端竞赛数据从redis中移除
如果当前竞赛已经结束,就不能再发布了
未完赛列表的竞赛如果结束了,怎么从未完赛列表移入到汗青竞赛列表。
1.竞赛要先结束,当前时间如果小于结束时间。写一个移入方法,要去长期的反复的去执行竞赛移动的方法,(定时任务->按照肯定频率去反复的操作这个方法,怎样界说呢?多少频率:给每个竞赛去搞一个定时任务,(那能否一个定时任务解决呢?)每天凌晨1:00去执行一次汗青任务,把未完赛的移入到完赛的(但是过细一想还会有一点点问题:今天结束的竞赛:下午4:00就结束了,但是细想一下,今天的任务,也不能叫汗青任务)大概每12h执行一次,但是貌似你的竞赛中午去清也不是很好(12-1h 12-1)两个这样貌似还可以)
因此引入xxl-job
XXL-job
是一个分布式任务调度平台,焦点设计目的是开发敏捷,学习简单,轻量级
appName长度有限制,执行器在xxl-job内里的唯一标识,要和背景对应一点,名称没要求,就是自己知道是干嘛的就行
竞赛报名功能
比赛:已经报名,已经开赛,哪些用户报名需要记录下来,报名功能最相干的就是答题页面,竞赛排名功能:比赛已经结束,比赛统计时候只统计对象,是报名到场这个竞赛的用户,
我的竞赛功能:当前用户已经报名的竞赛,哪些用户报名我们需要记录,
竞赛列表功能:竞赛可不可以报名的按钮标签啥的,需要从是否报名来判定。
用户的竞赛报名信息,需要存储到数据库中,为了查询到性能,还需要放到redis,这个是我们竞赛列表
- redis存储结构
- 存储信息 数据结构 key value
- 未完赛竞赛列表 List e:t:l examId
- 历史竞赛列表 List e:h:l examId
- 竞赛详情信息 List e:d:examId JSON存储竞赛详情
复制代码 但是我的竞赛列表的缓存,应该不止一个,每个用户都有一个,以是应该
key: u:e:l:用户id (user exam list 用户的竞赛列表,)
- 用户竞赛列表 List u:e:l:用户id examId
- 竞赛详情信息 String e:d:examID JSON结构存储竞赛详情
复制代码 先登录,登录之后在竞赛列表找到要报名的竞赛,点击报名竞赛按钮,前端携带竞赛id和token想后端发送哀求
后端接收哀求,需要先判定是否符合报名条件 如果满足条件,将用户的报名信息存储到数据库和redis,
报名条件:1.用户处于登录状态,2.不能报名不存在的比赛,3.不能重复报名,4.已经开赛的竞赛不能再进行报名
前端接收到后端相应之后,根据返回结果提示用户报名成功大概失败
TransmittableThreadLocal
竞赛报名后端开发:由于我在开始的时候,我就以及把token解析后,已经获取来他的用户ID,此时就给他存起来,那么背面我就不消往返的去解析啥的了,我们要存一个地方,首选redis,但是我的userID属于哪个用户(他是作为一个value),ThreadLocal线程当地变量,为每个线程变量,拥有一个独立的副本,对于其他线程不受影响(useId 用userId存储时候,我既要存他,还要去区分差别的用户),相称于每个线程内里存储的都是一个用户id,就会解决掉了,把解析的id放到threadLocal,但是原生的ThreadLocal,处理异步任务,异步任务在主线程中,又可能开启一些子线程,这样会变成异步的,以是解决的话,利用阿里给的一个TransmittableThreadLocal.导入对应的依赖
我的竞赛功能
已开赛(标签) 竞赛开始时间<当前时间 前端+后端(返回当前用户是否报名这个竞赛)
当前用户未报名此竞赛
已报名(标签):竞赛开始时间>档期啊时间,用户之前报名了这个竞赛
竞赛练习和查看排名:应该是在汗青的竞赛内里才气看到
开始答题:竞赛开始时间<当前时间,竞赛结束时间>当前时间,用户已经报名到场了这个竞赛
报名参赛:竞赛开始时间>当前时间(未开赛)用户之前未报名这个竞赛
标题详情缓存 (引入ES)
string 类型 key:q:d:questionId value:JSON标题详情
搜索方式十分难受啊 好比合并两个有序数组,我搜索这道标题,有多种组合,这样就很麻烦,我查找后,进行一个过滤,利用java,原生方式,通过for循环从整个列表都过滤一遍,然后返回
2.redis把对应的关键字存起来,但是关键字的组合过多,而且关键字又可能会重复,你如果添加一个竞赛,关键字对应的内里有什么呢?
value:questionId.(而且拿中文当key有点抽象)
一样平常利用ElasticSearch,这个用来去解决含糊查询,(开源分布式搜索引擎,独特本领,近乎实时的数据搜索本领,全文检索,含糊查询,数据分析)
基本利用:
正排索引:
一种索引机制,通常按照文档的ID大概其他唯一标识符进行排序,正排索引中,索引的键是文档标识符,索引的值是文档的详细信息,当你知道一个文档ID时候,可以通过正排索引敏捷找到该文档的全部信息,
主键 数据
1 白蛇
2 白蛇喜好
3 白蛇吃白狐
4 白蛇猴利谢
5 白蛇大舌头
倒排索引:按照文档的(关键词)来组织,倒排索引中,索引的键是文档集合中出现的每个独特词汇和关键词,索引的值包含该关键词的所有文档标识符,以及可选的额外信息。(索引结构匹配含糊搜索)
索引关键字 对应电影序号
白蛇 12345
喜好 2
吃 3
大舌头 5
基本概念
- MYSQL ElasticSearch
- 数据库: DataBase index(索引)
- 表 : Table Type(类型)
- 数据行: row Document(文档)
- 数据列:column Field(字段)
- 模式:Schema Mapping(映射)
复制代码 index:具有相同结构的文档集合,犹如数据库一样,一个集群可以创建多个索引,如客户信息索引等,可以给每个索引创建数据结构,索引定名要责备部利用小写(创建索引,搜索,更新,删除操作)都需要用到索引名称(6.0之后降落到相称于数据库表的级别)
type:在索引内的逻辑细分,但是6.0后版本废弃。
Document:文档,相称于每个数据.可以包含多个字段,每个字段包含多个字段,可以是文本,数字,日期等类型。
Field:文档中的一个元素大概属性,每个字段都有一个数据类型,如字符串,整数,日期等
Mapping:ES中很重要的内容,类似传统关系型数据中的table的schema(界说数据库的数据怎样组织的,包括表的结构,字段的数据类型,键的设置(如外键,主键等),用于界说一个索引等数据结构,在es中,我们手动创建mapping,也采用默认创建方式,默认设置下,ES可以根据插入的数据自动的创建mapping
好比这个下令,他的要求不是十分严格,以是employee就相称于是数据库(自动帮你创建了,由于你写的很清楚,在xx目次啥的,doc就是document)
修改
- 1 POST /employee/_update/3
- 2 { 3 "doc" : {
- 4 "last_name" : "mark"
- 5 }
- 6 }
复制代码
标题列表:
先查es:如果能查到数据->前端,如果查不到,再查mysql,数据同步给es,并且将查到的数据返回前端,如果数据库查不到,返回给前端空。
IK分词器,分为两块,一个是ik_smart 和ik_slow
smart只管保持长词,好比我想吃肉夹馍
我,想吃,肉夹馍 花合斗
ES是什么
搜索,存储引擎,非关系型文档数据库
特点天生分布式,高性能,高可用,易扩展,易维护
ES写入进步服从
减少负分片,对Mapping属性进行暂时去除,同时可以批量写入
ES的全文检索
扫描文本中每个单词,对单词创建索引,生存该单词在文本的位置,以及出现的次数,如果是你的含糊匹配,必须要准确出现谁人词才行,但是这个就是他进行一个分词之后,你想要什么都可以,好比说es快速入门,es,快速入门,这两个作为两个关键词搜索,好比的,呢的过滤
撤销发布:为啥一样平常都是发布先变成撤销发布才可以修改(编辑啥的),由于我们搜索啥的涉及es,但是如果每个都去处理es和redis,是不是不是非常好处理,但如果我们撤销发布,他会自动把缓存打扫了,然后再去编辑啥的,就又省事了
代码沙箱(判题功能)
判题功能:后端收到前端的哀求,获取参数,我需要知道用户的语言选择是啥,根据语言进行差别处理,
根据questionId从es中查询出对应的main函数和测试用例(入参),将代码判定完备。查询的时候,还需要查询标题标时间,空间限制,难易程度
javac:将java代码进行编译(成功/失败)
成功:继续执行后续逻辑 (执行java)
失败:停止逻辑,失败原因返回前端.
java:
成功:继续执行后续逻辑
失败:停止逻辑,失败原因返回前端。
标题答案的比对:上一步代码实际输出的结果,和测试用例中盼望结果进行比对。
如果比对一致:标题作答正确,如果不一致,标题作答错误。返回错误原因给前端。
参数从main函数内里获取
public static void main(String[]var0){
String var1=var0[0]; //一样平常来说下令行操作,Java下令行背面跟一些参数,可以从这块获取
sout(isValid(var1));
}
这个传递的参数是啥呢?,岂非什么都行吗,我们是需要插入测试用例的。
利用一个json类型的数组。输入输出
input:参数(从这块拿你的参数)
output:输入结果(从这里查抄,预期输出结果)
时间限制和空间限制的比对:好比代码执行的所利用的实际的实际和空间和盼望的时间和空间进行对比,如果<=盼望值,说吗符合要求,判定标题作答正确,并且结果返回前端,否则则不符合要求,判定标题作答错误,并且将错误原因返回前端
对于用户答题结果,无论失败还是成功,我们都应该存储到mysql数据库中,以供后续利用,答题结果在计算的时候,需要留意对于分值的处理(分值的计算和标题难度相干)
对了
用户的代码可能有点抽象
资源滥用,攻击:好比死循环啥的,cpu,网络,把资源都占完了,那么体系就很难包管运行的稳固性和性能
-分配有限资源,限制cpu,内存资源等资源利用
-运行数据限制,不予许用户长时间占用资源
安全漏洞;代码中如果存在病毒会导致体系瘫痪
-限制用户代码执行权限,限制文件等读写,限制网络等访问。
数据异常:用户提交的代码有很高权限,可以访问我们体系内里的文件,如果敏感信息文件让别人获取就完了,大概往文件中写非法的数据
-和体系运行环境隔离开,差别用户代码执行环境也隔离开 相互干扰:多个用户同时提交代码,同个环境下可能互相影响
-如果发现恶意攻击的用户,随时将用户拉黑处理
利用docker,创建隔离的容器去实现代码,包括一些权限啥的,而且宿主机隔离的很好。
通过java操作docker,引入什么服务呢?
判题的逻辑很耗时,以是我们之前专门划分的judge服务,专门去处理判题,能进步体系的性能,可以会合资源去处理这个服务
由于friend内里有什么提交啥的好多功能,如果分开,就好操作了,而且还要维护什么es,啥的,因此,尽可能使judge性能更高,会合精力做一件事。
friend操作号所有数据,然后服务间调用judge服务就好
容器池(开始我们是一个创建容器,然后启动容器,再去删除容器,就像是多线程变成线程池)
来个哀求,都是创建一下(如果哀求大爆发,我们很有可能资源被吃满,好比竞赛,搞一个容器池)
创建一个方法,把用户提交的代码放到一个目次,多个容器,不能用同一个目次,怎么天生挂在目次,公共的加容器名
怎样存储容器,存储到哪里呢? 存到集合内里放到壅闭队列BlockingQueue<String>containerQueue 初始化就ArrayBlockingQueue<>(限制数量)
为什么引入消息队列:面对报名一个竞赛的时候,报名点击数目陡增
因此引入MQ
生产者把用户的信息啥的传递给MQ MQ给消费者,让他去拿,
用户拉黑功能
B端把C端用户的所有基本信息都列出来,可以进行拉黑操作,拉黑用户的目的是限制C端用户的行为,好比C端用户有违法操作,恶意攻击用户进行拉黑
status=0 拉黑
status=1 正常
限制他报名竞赛就好
checkUserStatus这个注解
这里是用了一个自界说注解结合切面类(通过ThreadLocal获取id,然后我看你的status状态码),就类似于谁人校验登录似的,如果你非法操作,我限制你报名竞赛(在你报名点击竞赛之前),用户被拉黑,我就不予许你被
由于我们要加入注解,给报名方法加上一个Before()前置通知类型的方法,然后我们会在目的方法之前执行。
引入注解
- package com.bite.friend.aspect;
- import java.lang.annotation.*;
- @Target({ElementType.TYPE, ElementType.METHOD}) // 可标注在类和方法上
- @Retention(RetentionPolicy.RUNTIME) // 运行时保留 //因此可以通过反射机制来读取。
- @Documented // 包含在Javadoc中
- public @interface CheckUserStatus {
- // 这是一个标记注解,没有定义属性
- }
复制代码 引入切面类
- 当调用带有@CheckUserStatus注解的方法时
- AOP拦截器会先执行UserStatusCheckAspect中的before方法
- 在before方法中去判定是否被拉黑
- package com.bite.friend.aspect;
- import com.bite.common.core.constants.Constants;
- import com.bite.common.core.enums.ResultCode;
- import com.bite.common.core.utils.ThreadLocalUtil;
- import com.bite.common.security.exception.ServiceException;
- import com.bite.friend.domain.user.vo.UserVO;
- import com.bite.friend.manager.UserCacheManager;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- import java.util.Objects;
- @Aspect
- @Component
- public class UserStatusCheckAspect {
- @Autowired
- private UserCacheManager userCacheManager;
- //应该是操作某些功能之前,进行一个判断,前置刚好合适。在报名之前去检查,注解加到哪里,就是哪个操作之前检查
- @Before(value = " @annotation(com.bite.friend.aspect.CheckUserStatus)")
- public void before(JoinPoint joinPoint) {
- //获取当前用户信息 status
- Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
- UserVO user = userCacheManager.getUserById(userId);
- if (user.getStatus() == 0) {
- throw new ServiceException(ResultCode.FAILED_USER_BANNED);
- }
- if (Objects.equals(user.getStatus(), Constants.FALSE)) {
- throw new ServiceException(ResultCode.FAILED_USER_BANNED);
- }
- }
- }
复制代码 我的消息功能
站内信:网站内部的一种通信方式
1.用户和用户之间的通信(点对点)
2.管理员/体系和某个用户之间的通信(点对点)
3.管理员/体系和某个用户群(满足某一条件的用户群体)之间的通信.(点对面)
竞赛结果通知消息-点对点(由于差别用户,你当前是多少多少名)
数据库表设计:
包含消息发送人,接收人,内容
主键id, 消息标题,消息接收人,发送人 ,消息内容 (长远来看,如果给用户和用户,用户和用户群加上,以免由于结构不富足,造成大规模调解,第一种支持,但是用户群就会冗余了,发送给一个用户群,我发的消息唯一,那么我要重复好多次)
主键 消息标题 内容 接收人 发送人
1. 福利信息 内容 1 0
1 福利信息 内容 1 0 这种
分表-
消息发送
消息怎样产生:竞赛结果通知消息(凌晨的xxljob 通知)
每天凌晨,会对当天(前一天,一个意思)结束的竞赛进行用户排名的统计,消息在统计的过程中,随即产生了。(为啥凌晨,肯定是要当天所有的竞赛,都统计一次,又可能早上一个,中午一个,下午一个,有可能一些人,写了还没提交,大部门竞赛的结束时间都是晚上xx点,好比10点,11点)
xxl-job凌晨1点执行,统计前一天结束的所有竞赛
消息怎样发送:
最终效果是用户能在消息页面中找到消息,换句话说,只要存在数据库内里就好,设计redis缓存
存储信息。 redis数据结构 key value
用户消息列表 List u:m:l:用户ID textId
消息详情信息 String m:d:textId JSON结构存储消息详情
消息发送实现:
通过定时任务天生消息->将消息存储到数据库和缓存当中
竞赛排名功能
只有汗青竞赛才气查看排名,未完赛你看不了,把得分排名啥的存储起来,开发和设计我的竞赛时候,这么存储
缓存结构:
数据结构list
key:竞赛排名列表 e:r:l:+examId
value:直接存详情名次, json :examRank userId(通过userId查
昵称会改,但是我的id要包管,不能说你id改完,找不到了,除非你缓存也要改
nickname score)
为什么不是存id呢(如果我们之间是存详情信息,那我我们会存很多份,由于首先他的竞赛就有可能你的题库和竞赛列表有重复的数据,如果有详情信息, 但是竞赛排名会有这个环境吗,但是竞赛的排名数据是基本不会重复)
暂时记录
我在做项目的时候,想到一个问题,我们微服务,为什么要把一个类,粘贴到另一个类
就好比我的job模块有User类,我的friend服务也需要这个类,为啥我不能导入job这个依赖,然后我去利用User类呢,这么粘贴不麻烦吗?
如果你引入啦,不就是变成单体架构啦吗,那么这里我还有感悟,你的引入依赖,引入的不应该是启动类的微服务,换句话你应该引入的全部都是工具类。
Nginx
Nginx是一个高性能的HTTP和反向代理Web服务器,功能有反向代理,负载均衡和动静分离
正向代理
客户端通过代理服务器来访问互联网上的其他服务器,这种环境,客户端设置了代理设置(代理是在客户端设置的)所有对外的哀求都会先发送到代理服务器上,再由代理服务器转发到真正的目的服务器,
(为什么不直接发目的服务器)
好处:
隐私保护:客户端的真实IP地点不会直接暴露给外部服务器
缓存:代理服务器具有缓存功能,对于频仍哀求的数据,可以直接从缓存中返回结果,进步相应速度
内容过滤:组织大概企业可以利用正向代理对员工的上网行为进行监控 ,大概过滤不符合的网站内容。
反向代理
客户端直接访问的服务器实际是代理服务器(代理服务器在服务器端设置)这个代理服务器接收客户端哀求后,会把哀求转发给真正的后端服务器,客户端本身是不知道这个代理服务器的存在的 (好处)
负载均衡:通过反向代理,可以多个后端服务器直接分配哀求,分散单个服务器压力
安全性:隐藏真正的后端服务器地点,增长安全防护
缓存与速度:类似于正向代理,反向代理也可以实现缓存功能,减少负载加速相应速度
负载均衡
为了制止单点故障大概现有的哀求使服务器压力太大无法承受,以是我们搭建一个服务器集群,将原先哀求会合到单个服务器上的环境改为将哀求分发到多个服务器上,将负载均衡的分发到差别服务器,也就是负载均衡
如果ABCD ,四个服务器,性能不均,怎么做到均衡呢?
Nginx提供了算法
1.轮询法
每个哀求按照时间顺序逐一分配(要求所有服务器性能大差不差)
2.权重模式(加权轮询,weight)
轮询,weight和访问比率成正比,用于后端服务器性能不均环境,这种方式比力灵活,当后端服务器性能存在差别的时候,通过设置权重,可以让服务器性能充分发挥,有效利用资源,weight和访问比率成正比,权重越高,被访问的概率越大(本领越大,干的越多)
ip_hash
哀求通过哈希算法,自动定位到该服务器。每个哀求按照ip的hash结果分配,这样每个访客固定访问一个后端服务器
最少连接
nginx统计每个后端当前活动连接数,分配活动连接数最少的服务器上,根据服务器的实际负载环境动态分配哀求,将新哀求分配到当前连接数最少的服务器上,制止某些服务器负载过高而其他服务器负载过低的环境。
动静分离
动:动态资源,动态资源指通过哀求服务器获取到的资源
静:静态资源,不需要哀求服务器就可以得到的资源如CSS,html,jpg,js)
这些静态资源既然无需服务器处理,则可以由nginx直接来处理无需后端服务器介入,这样可以减轻后端服务器的负载,使其专注于处理动态内容,从而进步团体性能,并且可以根据静态资源的特点做缓存操作
标题标顺序列表(用户端的前一题和后一题,怎么做)
redis的list数据类型,(包管顺序,只要差人的顺序不变就好,我们只要存入谁人标题标id存进去就好) list key:q:l : value:id
查询一下标题列表,@Component
@redisService
把顺序列表查出来
redis应该现在没数据,我们都是先查redis->没数据->mysql,
mp的语句
List<Question>questionList=questionMapper.selectList(new LambdaQueryWrapper<Question> //要返回的类型 ().select(Question::getQuestionId).orderByDesc(Question::getCreateTime)
然后流的利用,集合.steam().map(Question::getQuestionId).collect(Collectors.toList())
尾插,即右插入。
}
2025年5月13日 面试,某某某某达
抽象问题给我打懵逼了
前端通过ref获取到即时的相应式变量
怎么获取前端传过来的数据
el-input 绑定 这个ref,如果你输入框输入内容,userAccount自动更新
@PostMapping("/login")接收前端传递过来的数据
@RequestBody:接收前端传递过来的Body。 我们通常利用一个对象,来包装这个东西
@ApiResponse:这是Swagger注解,用于界说API的可能相应及其描述。
我们采用一个范型R,来包装
int code;好比成功的统一码(好比返回0失败,1成功,3....)
String msg; //通常作为code的辅助说明,一个code对应一个msg.
private T data; //哀求某个接口返回的数据 list SysUser范型。
利用一个范型
- export function loginService(userAccount,password){
- return service({
- url: "/sysUser/login",
- method: "post",
- data:{
- userAccount,
- password
- } //接口地址
- })
- }
复制代码
在您到场的定时任务中,是怎样处理竞赛信息的发布和用户排名的计算的,有没有效到一些特定的框架或技术
利用了xxl-job技术,用户竞赛信息的发布,首先要对时间进行一个处理,好比查看一下,当前时间和竞赛的开始结束时间比力,
竞赛排名信息的发布,由于我们每道题的竞赛都有分数,我们用sql都存了。
通过竞赛来找分(通过SQL计算时间范围内,所有竞赛id的组合)
ES怎么做添加
ES的结构:
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|