在线教育平台项目总结

一、业务流程

1.企业(B)

内容管理、媒资、缓存、消息、任务调度、搜索

2.个人(C)

搜索、缓存、订单、支付、学习

3.核心模块

包括了用户端、机构端、运营端

内容管理、媒资管理、课程搜索、订单支付、选课管理、认证授权等。

4.项目自述

专门针对职业技能教育的网络课堂系统,提供了职业技能培训的相关课程。项目基于B2B2C的业务模式,培训机构可以在平台入驻、发布课程,运营方由专门的人员对发布的课程进行审核,审核通过后课程才可以发布成功,课程包括免费和收费两种形式,对于免费课程普通用户可以直接选课学习,对于收费课程在选课后需要支付成功才可以继续学习。

采用前后端分离架构,后端采用SpringBoot、SpringCloud技术栈开发,数据库使用了MySQL,还使用的Redis、消息队列、分布式文件系统、Elasticsearch等中间件系统。

对平台上的课程进行管理,课程的相关信息比较多这里在数据库设计了课程基本信息表、课程营销表、课程计划、课程师资表进行存储 ,培训机构要发布一门课程需要填写课程基本信息、课程营销信息、课程计划信息、课程师资信息,填写完毕后需要提交审核,由运营人员进行课程信息的审核,整个审核过程是程序自动审核加人工确认的方式,通常24小时审核完成。课程审核通过即可发布课程,课程的相关信息会聚合到课程发布表中,这里不仅要将课程信息写到课程发布表还要将课程信息写到索引库、分布式文件系统中,所以这里存在分布式事务的问题,项目使用本地消息表加任务调度的方式去解决这里的分布式事务,保存数据的最终一致性。

二、关键技术

1.缓存三兄弟:

缓存穿透,高并发请求过来之后,查询数据库中存在数据,都发现缓存中没有,还是会去多次查数据库。如果查询数据库中不存在数据,高并发会一直查数据库,因为数据写入不到redis里。即请求穿透了缓存一直在查数据库。解决方案:1.参数校验,不符合规则直接返回2.把数据库中不存在数据也给缓存到redis,查缓存时也有该key,但是值为null。这种情况要加过期时间,防止数据库中什么时候真的有了该key。

缓存雪崩,缓存的大量key失效,因为他们设置了相同的缓存失效时间。解决方案:1.使用同步锁,只允许一个线程访问,性能不高。2.对同一类型的key设置不同的过期时间。

缓存击穿,大量并发访问同一个热点数据,热点数据失效后同时访问数据库。解决方案:1.同步锁,性能太低。不要直接在方法名上加synchronized,只锁查询数据库的代码,缓存查询可以高并发访问,锁进来后还要再查一次缓存。2.热点数据不过期。

2.JWT登录:

使用jwt(json web token令牌可以自己校验,无状态认证,不存储用户身份。有三部分,header(头部),内容(用户的信息),签名(前两部分想加再加密),

资源认证服务有密钥.密钥分为对称性加密和非对称性加密。对称性能高(即认证服务的和资源服务的密钥一样,非对称加密认证自己保留私钥,公钥下发给客户端和资源服务。

要自定义UserDetailService类连接用户数据库,传给username,连同用户名、密码、权限一起返回给spring security框架。密码不是明文存储。将用户信息转成json传回(有头像图片,所以转json),这个过程中要将密码等敏感信息置空。获取的时候可以写一个工具类json转对象

由于有很多可能的提交信息方式,比如:账号密码,手机验证码,微信扫码,因此需要统一认证,新建一个DTO类。userdetailservice原本传入的是username,现在要传入的是DTO类的json串。

重写DAOAuthenticationProvider(将用户名密码替换成UserDetails和Authorities),authentication(认证)authorization(授权)不止使用密码校验,所以要重写方法。一个接口有很多个实现类,在service中增加名字(password_authservice,wx_authservice)

3.幂等性、索引同步:

本地消息表+任务调度的机制来完成分布式事务的最终数据一致性的控制。建的本地消息表和课程发布表在同一个数据库,使用数据库事务控制,保证发布了就一定会在消息表中,然后任务调度定时调度消息表存储给redis,es,minIO。若当中有失败的,则下次定时会继续发,已经成功的根据幂等性,会跳过。

4.分布式锁

1.数据库锁(乐观)

谁update成功谁拿到锁

2.redis

(1)基于SETNX命令
  • 使用SETNX命令在Redis中设置一个键值对,如果该键不存在,则设置成功,表示获取到了锁;如果该键已经存在,则设置失败,表示锁已经被其他客户端持有。
  • 为了避免锁的过期导致死锁,可以为锁设置一个过期时间,使用EXPIRE命令或者SET命令的EX参数。
  • 释放锁时,可以使用DEL命令删除对应的键。
(2)基于SET命令和Lua脚本
  • 使用SET命令结合Lua脚本可以实现原子性的获取锁和设置过期时间操作,保证了在多个命令执行时的原子性。
  • Lua脚本可以保证多个Redis命令的原子性执行,避免了在执行多个命令期间锁被其他客户端获取。

if redis.call('set', KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2]) then
    return 1
else
    return 0
end

3.另起线程看门狗

看线程是否干完活,可以续过期时间。

5.数据库事务控制:

关于文件上传过程中的事务控制,抽取出只有数据库操作的方法来加。在类内curProxy通过代理对象调用。文件名为md5值+.jpg ,.mp4.content-type来判断是图片还是文档、视频

脏读:指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚

不可重复读(Update):指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的

幻读(Insert):假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用。

6.熔断降级:

Hystrix框架实现熔断、降级处理。开启feign熔断保护,定义降级逻fallbackFactory,降级处理逻辑(返回一个null对象,上游服务请求接口得到一个null说明执行了降级处理。)@feignClient,微服务雪崩A->B->C,Cdown了,会影响B进而影响A。下游服务异常熔断后,上游服务不再调用异常的微服务而是执行了降级处理逻辑(一种保护系统的手段),使用hystrix框架实现熔断、降级处理。解决:定义fallback类,重新处理,这种方法拿不到熔断异常信息。定义fallbackfactory类,出现熔断之后,上游调用create方法来进行降级处理(可以拿到熔断的异常信息)

7.异常处理:

抛给框架处理。运用AOP思想,@ControllerAdvice控制增强,通常和@ExceptionHandler结合使用。增强类处理。在base里写,和前端约定返回的异常信息模型,新建本项目自定义异常类型(继承runtimeexception),新建类写异常处理(可以区别捕获的异常类型)。

异常处理除了输出在日志中,还需要提示给用户。1.自定义项目异常类,使用该类的cast函数在业务代码中throw异常,在全局异常处理器(加了@RestControllerAdvice控制器增强和@ExceptionHandler)中处理该异常,定义restErrorResponse类实现序列化接口return给前端。

异常处理器中要解析jsr303的异常,否则会报unknow的统一异常信息,因为他不属于xuecheng的程序员自定义类型异常。前端后端都要校验,为了防止使用postman等访问后端。

多个接口共用模型类(新增id为空,但修改id不为空),使用分组校验。

8.权限控制(RBAC):

分两种方式,基于角色的访问控制和基于资源的访问控制。

角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等

if(主体.hasRole("总经理角色id")){
查询工资
}

资源(或权限)进行授权,比如:用户必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下:

if(主体.hasPermission("查询工资权限标识")){
    查询工资
}

if里面写的是角色还是是否有这个权限(一个属性字段)。基于资源的访问控制健壮性更好。使用@PreAuthorize("hasAuthority('权限标识符')")进行控制,标识符会在数据库中定义。在方法前面加该注解,拥有此权限才可以访问该方法。

细粒度授权(数据权限),对某一资源有访问权限,但是可以访问到的数据不一样,比如两个培训机构。course_base中有培训机构id。在service接口根据参数去做,security框架无法实现

9.跨域:

跨域是基于浏览器的同源策略,协议(https)、主机、端口三者,

1.通过script标签的src属性进行跨域请求

2.在响应头添加 Access-Control-Allow-Origin:*

3.通过nginx代理跨域,因为服务端之间没有跨域

10.页面静态化方案

课程信息页面的访问量较大,并且课程信息发布后在一段时间不会修改,这里使用Freemarker(thymeleaf)静态化技术将课程信息页面静态化,提前生成html页面并通过媒资管理模块的文件服务上传到文件系统,用户浏览课程信息页面通过分布文件系统浏览,提高了课程信息页面访问性能。

11.模块间通信:

  1. HTTP/REST API:使用HTTP协议和RESTful风格的API进行通信。每个微服务都可以提供一组API供其他服务调用。在nacos注册,请求来了之后到gateway负载均衡和请求转发,然后使用openfeign进行声明式调用。

  2. 消息队列:通过消息队列实现异步通信,使用RabbitMQ中间件。一个微服务可以将消息发送到队列,其他微服务可以订阅这些消息并进行处理。

三、组件

1.Nacos

服务注册中心和配置中心

namespace、group、dataid。

namespace(开发环境,测试环境,上线环境)和group(学成在线,瑞吉外卖)。

Dataid:  包括三部分:服务名、环境名、扩展名,

content-service-dev.yaml配置文件  由(content-service)-(dev). (yaml)三部分组成

server:
  servlet:
    context-path: /content
  port: 63040

test_config:
 a: 3a
 b: 3b

#配置本地优先
spring:
 cloud:
  config:
    override-none: true

2.Gateway

负载均衡、路由转发、统一鉴权

前端请求到Nginx,通过负载均衡到Gateway网关,通过网关将请求转发至各个微服务。网关从nacos读取服务地址

3.Redis(旁路缓存)

白名单、热点数据

实现分布式锁,避免课程查询等公开接口出现缓存击穿问题

(1)缓存原理

缓存的实现原理就是通过在内存中存储数据副本,以提高数据访问的速度和性能。

缓存命中:如果缓存中有数据副本,并且数据是最新的,则直接从缓存中获取数据。否则从原始数据源获取数据,并将其存储到缓存中,以备将来访问。

缓存更新:当原始数据源的数据发生变化时,需要更新缓存中的数据,以确保缓存中的数据与原始数据源中的数据保持同步。

缓存淘汰:由于缓存空间有限,当缓存空间不足时,需要根据一定的策略淘汰一些数据,以腾出空间存储新的数据。

缓存策略:缓存的效率和性能很大程度上取决于缓存策略,包括缓存的替换策略(如最近最少使用算法等)、缓存的过期策略(如基于时间的过期策略、基于访问次数的过期策略等)等。

(2)淘汰策略和删除策略

替换策略见操作系统页面置换,过期策略基于时间或者访问次数。

(3)如何限流:

  1. 令牌桶算法:令牌桶算法是一种常见的流量控制算法,它通过在令牌桶中存放一定数量的令牌,每个令牌代表一个可用的请求。当有请求到来时,需要先从令牌桶中获取一个令牌,如果令牌桶中没有足够的令牌,则拒绝请求或者将请求放入队列中等待。Redis可以使用有序集合(Sorted Set)来实现令牌桶算法,利用有序集合的分数(score)表示令牌的到期时间,以及集合操作来实现令牌的生成和获取。

  2. 基于计数器的限流:通过Redis的计数器功能,可以对请求的数量进行计数,并根据设定的阈值来限制请求的流量。

  3. 漏桶算法:漏桶算法是另一种常见的流量控制算法,它通过固定速率从漏桶中漏出请求,当有请求到来时,将请求放入漏桶中,然后以固定速率处理漏桶中的请求。如果漏桶已满,则拒绝新的请求。

(4)为什么快?

redis是单线程但速度依旧很快,基于内存的数据存储,使用了IO多路复用模型。

(5)数据类型

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

查询所有的key用:

KEYS *

SCAN 0 MATCH *

keys会阻塞一段时间,推荐用scan,因为是非阻塞的。

(6)布隆过滤器

可以用来判断一个元素一定不在集合中,或者可能在集合中,但不保证一定在集合中。

核心是一个位数组(Bit Array)和多个哈希函数

  1. 初始化:创建一个长度为m的位数组,所有位都初始化为0。

  2. 插入操作:将待插入的元素通过多个哈希函数计算出多个哈希值,然后将位数组中对应的位置置为1。

  3. 查询操作:将待查询的元素通过多个哈希函数计算出多个哈希值,然后检查位数组中对应的位置是否都为1。如果所有位置都为1,则说明该元素可能在集合中;如果存在任意一个位置不为1,则说明该元素一定不在集合中。

(7)Zset

与普通集合相比,在存储元素的同时,每个元素都关联了一个分数(score),这样就可以根据分数对元素进行排序。Zset的底层数据结构是跳跃表(Skip List)和哈希表(Hash Table)的结合。

  1. 跳跃表(Skip List)

    • 跳跃表是一种随机化的数据结构,由多个层级的有序链表组成。每个节点都包含一个指向下一个节点的指针数组,通过这些指针可以快速定位到目标节点。
    • 跳跃表中的每个节点都保存了一个元素和对应的分数,根据分数对元素进行排序。通过层级的指针数组,可以在跳跃表中快速查找、插入和删除元素,时间复杂度为O(log n)。
  2. 哈希表(Hash Table)

    • 在跳跃表的基础上,每个元素还会保存在一个哈希表中,哈希表中的键为元素,值为分数。哈希表用于快速定位到跳跃表中某个元素的位置,提高了元素查找的效率。
    • 哈希表的键值对数量通常会少于跳跃表的节点数量,因此哈希表的大小会比较小,降低了内存占用。

Zset怎么实现插入和删除?如何确定Zset新节点的层数?O(log(N))

  1. 插入操作

    • 首先,生成一个新的节点,其中包含了元素的值和分数。
    • 然后,为新节点确定一个随机的层数,通常采用的是随机函数生成层数,一般会限制最大层数,例如Redis中的默认最大层数为32。
    • 接下来,从Zset的头节点开始,逐层查找插入位置,确保元素按照分数的顺序插入,并且更新相邻节点的指针。
    • 最后,将新节点插入到对应的位置。
  2. 删除操作

    • 首先,根据元素的值,查找到待删除的节点。
    • 然后,逐层删除节点,并更新相邻节点的指针,确保删除后跳跃表的结构保持正确。
    • 最后,释放被删除节点的内存空间。

(8)哈希扩容过程

  1. 初始化新哈希表: 当发现当前哈希表的负载因子超过了设定的阈值(负载因子通常为0.75),Redis会初始化一个新的空哈希表,用于存储扩容后的数据。新哈希表的大小通常是当前哈希表大小的两倍。

  2. 迁移数据: 逐个遍历当前哈希表的桶,将当前哈希表中的键值对重新计算哈希值,并将其移动到新哈希表中对应的位置。 在数据迁移的过程中,客户端可能会同时发起读取操作,Redis会根据标志位判断,如果发现正在进行数据迁移,会在当前哈希表中进行数据查找。

  3. 切换指针: 当所有数据迁移完成后,Redis会将当前哈希表的指针指向新哈希表,完成扩容操作。同时,将标志位设置为已完成,允许客户端进行写操作。

  4. 释放旧哈希表: 最后,Redis会释放掉旧的哈希表所占用的内存空间,完成扩容过程。

(9)持久化

Redis提供了两种主要的持久化方式来保证数据在重启或宕机后不丢失:RDB(Redis DataBase)和AOF(Append Only File)

RDB:快照的持久化方式,它会在指定的时间间隔内将数据集的快照写入磁盘,fork()系统调用创建一个子进程来完成持久化过程。缺点是周期性,会丢失。

  • 适合用于备份数据和灾难恢复,快速且轻量级。
  • RDB文件通常较小,对于大型数据集来说,恢复速度也较快。

AOF:基于日志的持久化方式,它会将写命令追加到文件末尾,记录了对数据集进行的所有写操作。持久化文件通常比RDB大,慢

  • AOF持久化方式提供了更高的数据安全性,因为它记录了每个写操作。
  • AOF文件可以进行日志重放,即使文件损坏一部分也能够尽可能地恢复数据。

(10)事务

Redis事务是一种原子性操作的机制,允许将多个命令组合在一起作为一个事务单元进行执行。

  1. MULTI命令:事务的开始,使用MULTI命令表示开始一个事务块。

  2. EXEC命令:提交事务,将事务块中的所有命令一起执行。如果事务中的所有命令都成功执行,则返回执行结果;如果其中任何一个命令执行失败,则事务将回滚,返回一个错误。

  3. DISCARD命令:取消事务,放弃事务块中的所有命令,清空事务队列,使得事务中的命令不会被执行。

  4. WATCH命令:监视指定的键,当这些键被其他客户端修改时,事务将被中断,以保证在事务执行期间键的值保持不变。

  5. 事务队列:在执行事务期间,客户端可以将多个命令依次发送到Redis服务器,Redis服务器将这些命令缓存到一个事务队列中,待EXEC命令执行时一次性执行。

4.RabbitMQ

将支付结果发通过给fanout类型的交换机,由交换机将消息广播发送至每个接收支付结果的微服务。

(1)丢失

  1. 持久化消息:在发送消息时,设置消息的持久化属性,确保消息被持久化到磁盘中。这样即使RabbitMQ节点崩溃,消息也不会丢失。

  2. 持久化队列:将队列声明为持久化队列,在RabbitMQ重启后能够自动恢复队列和其中的消息。

  3. 生产者确认机制:在发送消息后,等待RabbitMQ返回确认响应。如果生产者未收到确认响应,可以选择重新发送消息或进行其他处理。

  4. 消费者确认机制:在消费者处理消息后,向RabbitMQ发送确认消息。只有当消费者成功处理了消息并发送了确认消息后,RabbitMQ才会将消息从队列中删除。

  5. 备份队列:配置备份队列,将消息发送到备份队列,确保消息在主队列丢失时可以从备份队列中恢复。

  6. 高可用集群:使用RabbitMQ的集群模式,将消息分发到多个节点,确保即使一个节点故障,其他节点仍然能够提供服务。

(2)重复

  1. 消息幂等性设计:确保消息的处理操作具有幂等性,即无论收到多少次相同的消息,最终的结果都是相同的。这样即使消息被重复消费,也不会对系统造成影响。

  2. 消息唯一标识:给每个消息分配一个唯一的标识符,可以是消息的 ID 或者是业务相关的唯一标识。消费者在处理消息时,根据消息的唯一标识进行去重,避免重复处理相同的消息。

  3. 消息去重存储:维护一个消息去重存储,用于记录已经处理过的消息的唯一标识。在消费者处理消息之前,先查询消息去重存储,判断该消息是否已经被处理过,如果已经处理过则不再处理。

  4. 消息 TTL 设置:设置消息的 TTL(Time To Live),即消息的生存时间。当消息在指定的时间内没有被消费者处理,则消息会被丢弃或者进入死信队列。通过设置合适的 TTL,可以确保消息在一定时间内只被消费一次。

(3)顺序

默认情况下不保证消息的顺序性,因为它是一个多生产者、多消费者的消息队列系统。

  1. 分区有序:将消息根据某种规则分区,然后将每个分区的消息发送到不同的队列中。消费者按照分区顺序处理消息,可以保证每个分区内的消息顺序。这种方法适用于需要并发处理多个分区的情况。

  2. 标识消息顺序:在消息的内容中包含序列号或者时间戳等标识,消费者在处理消息时,根据标识来排序消息。这种方法适用于无法保证消息发送顺序但能够保证消息内容顺序的场景。

(4)堆积

  1. 监控队列深度:定期监控队列的消息深度,超过一定阈值时,触发报警或者自动进行处理。

  2. 流量控制:通过设置队列的最大长度或者限制生产者的速率来控制消息的流量。当队列中的消息数量达到一定阈值时,停止接受新的消息或者限制消息的生产速率,以避免消息堆积。

  3. 消费者健康检查:定期检查消费者的健康状态,确保消费者能够及时处理消息。如果发现消费者出现异常或者无法处理消息,及时进行处理,避免消息在队列中堆积。

  4. 消息 TTL 设置:为队列设置消息的 TTL(Time To Live),即消息的生存时间。当消息在指定的时间内没有被消费者处理,则消息会被丢弃或者进入死信队列,避免消息在队列中长时间堆积。

  5. 队列分片:将队列按照某种规则分片,将消息分散到多个队列中,分散队列的负载,避免单个队列堆积过多消息。

5.Elasticsearch

倒排索引,对课程发布信息进行索引和搜索。

布尔查询、聚合搜索、过滤器、高亮显示等功能

倒排索引将文档的内容作为索引的键,将文档的编号或者标识作为索引的值,从而可以根据关键词快速定位到包含该关键词的文档。倒排索引由词项(Terms)和倒排列表(Posting List)组成。

假设有以下文档:

  • 文档1: "apple orange banana"
  • 文档2: "apple banana"
  • 文档3: "orange banana"

构建的倒排索引可能如下所示:

Term       Posting List
apple      1, 2
orange     1, 3
banana     1, 2, 3

当用户输入一个关键词进行查询时,系统会根据关键词在倒排索引中查找对应的倒排列表,从而得到包含该关键词的文档编号或者标识,进而检索出相应的文档。

6.XXL-Job

调用中心执行器组成,调用中心负责按任务调度策略向执行器下发任务,执行器负责接收任务执行任务。主要用于索引同步

配置调度过期策略阻塞处理策略,避免同一个执行器多次重复执行同一个任务

调度过期策略:调度过期后,忽略过期的任务,从当前时间开始重新计算下次触发时间;
阻塞处理策略:丢弃后续调度。

@Scheduled

优点:
不需要依赖外部框架。
简单快速实现任务。@EnableScheduling、@Scheduled 注解

缺点:
无法管理任务。要停止某个任务,必须重新发布。
不支持动态调整。修改任务参数需要重启项目。
不支持集群方式部署。集群模式下会出现任务多次被调度执行的情况,因为集群的节点之间是不会共享任务信息的,每个节点上的任务都会按时执行。

四、sql

1.给出部门表和员工薪水表,求每个部门薪水最高的员工姓名和部门名称

SELECT d.department_name, e.employee_name, e.salary
FROM Departments d
INNER JOIN (
    SELECT department_id,employee_name, MAX(salary) AS max_salary
    FROM Employee e
    GROUP BY department_id
) max_salaryON d.department_id = max_salary.department_id

首先利用子查询找出每个部门的最高薪水,然后将结果与部门表和员工薪水表进行连接,以获取最高薪水员工的姓名和对应部门名称。

2.找出所有影片描述为  boring (不无聊) 的并且 id 为奇数 的影片。返回结果按 rating 降序排列

表:cinema

+----------------+----------+
| Column Name    | Type     |
+----------------+----------+
| id             | int      |
| movie          | varchar  |
| description    | varchar  |
| rating         | float    |
+----------------+----------+
id 是该表的主键(具有唯一值的列)。
每行包含有关电影名称、类型和评级的信息。
评级为 [0,10] 范围内的小数点后 2 位浮点数。

select * from cinema where description!='boring' and mod(id,2)=1

order by rating DESC

五、场景

1.后端接口响应慢的排查方法

首先看是一个接口慢还是所有接口都慢

(1)一个接口

数据库耗时长:必要字段加索引,确定是否索引失效了

架构不合理

  1. 关联性很强的服务,尽量将它们放到同一个微服务。
    1. 比如:订单微服务和库存微服务。
  2. 每次访问都要用到的功能,尽量不要作为一个服务,这样每次都要有网络损耗
    1. 比如:验证用户的token是否有效。
      1. 这个功能基本每个接口都会用到,最好是将其写到公共代码中,直接作为依赖进行打包。
      2. 用户其他的功能,比如:注册、登录、用户管理等,可以单独写一个微服务。

业务逻辑:加强代码评审

死锁:判断有无

(2)所有接口

可能是服务器问题,需要排查网络、CPU使用率、内存使用率、磁盘使用率

2.海量数据的存储系统设计

(1)确定需求,明确系统的功能需求和性能要求

(2)对海量数据进行分析,了解数据的特点和结构。

(3)存储设计,在设计存储方案时,可以采用分布式文件系统(如HDFS)、分布式数据库(如HBase)等技术来实现数据的高可靠性和高性能

(4)数据处理,在实现海量数据的处理时,可以使用分布式计算框架(如Hadoop、Spark)进行数据的并行处理和计算。

3.出现 OOM 后怎么排查问题

  1. 查看错误日志:首先查看应用程序的日志,确定错误类型和发生位置。如果有堆栈信息,可以从中了解到发生错误的代码位置。

  2. 查看内存使用情况

  3. 检查代码:检查代码中是否存在内存泄漏的情况,比如未关闭的资源、循环引用等。可以通过代码审查和静态分析工具来帮助排查问题。

  4. 调整 JVM 参数:根据应用程序的实际情况调整 JVM 参数,如增大堆内存大小、调整垃圾回收器的参数等,以提高内存利用率和性能。

  5. 优化代码和数据结构:优化代码,减少不必要的内存占用,使用合适的数据结构和算法来降低内存消耗。

4.多个客户抢夺一个资源

只是单机,采用volatile关键字修饰该订单采用CAS操作对其进行乐观锁操作。

否则采用redis,分布式锁加锁。

5.分发

(1)1000个任务分给10个人做

(2)如何把一个文件快速下发到 100w 个服务器

网状形式 单个节点既可以从其他节点接收服务又可以向其他节点提供服务。用树要是down了一个结点,后果很严重。

hash

创建10个线程,然后用同步锁,Synchornized

6.top k类问题

(1)10 亿个数,找出最大的 10 个。

(2)有几台机器存储着几亿淘宝搜索日志,怎么选出搜索热度最高的十个

(3)有十万个单词,找出重复次数最高十个

直接在内存总使用Hash方法将数据划分成n个partition。

每个partition维护一个小根堆 每一次都与最小的数进行比较。如果某一后续元素比容器内最小数字大,则删掉容器内最小元素,并将该元素插入容器。

最后归并

7.三个线程交替打印

六、智力

1.毒药和老鼠的问题

8瓶水,3只老鼠

000=0
001=1
010=2
011=3
100=4
101=5
110=6
111=7

1,3位置的老鼠死了,则有毒的为101=5号水有毒

二分也可以,但是比这个慢。

2.颜色碰撞

有三个颜色,a红、b蓝、c黄、两两碰撞会变成第三种颜色,比如ab碰撞会变成c,问abc满足什么数量关系,才能只有一种颜色?

  1. 红、蓝、黄的数量都是偶数;
  2. 红、蓝、黄的数量都是奇数。

有偶数个红色、蓝色和黄色,每种颜色都有成对的,碰撞之后它们会互相抵消,最终不会剩下任何一种颜色。

如果我们有奇数个红色、蓝色和黄色,每种颜色都会有一个“孤立”的颜色,它们无法与任何其他颜色抵消,因此最终只剩下这种孤立的颜色。

3.老虎吃羊

100只老虎,吃了羊会变成羊,老虎害怕被吃又想吃羊,老虎会去吃羊吗

1老虎  1羊  吃

2老虎  1羊  吃了之后会变成情况一,所以不吃

3老虎  1羊  吃了之后会变成情况二,吃...

老虎数量为奇数,吃,偶数时,不吃。

4.拿苹果

有100个苹果,两个人,然后每次都能拿1-5个。问怎么样才能保证自己拿到最后一个苹果

只有剩下6个的时候,怎么拿都是自己赢,往上递归,要拿100-6=94,94-6=88...,100%6=4.

如果你先手,先拿四个,然后之后每次拿都保证你两的和是6,这样就能保证最后剩6个苹果。

  • 30
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个在线教育平台的开发说明书例子: 一、项目概述 本项目是一个在线教育平台,为广大学生和教育工作者提供优质的教育资源和服务。该平台将包括多个模块,如课程管理、学生管理、教师管理、支付管理、数据统计等。 二、技术选型 1. 前端技术:使用React框架和Ant Design组件库进行开发。 2. 后端技术:使用Java语言和Spring Boot框架进行开发,数据库采用MySQL。 3. 服务器环境:使用Linux系统,部署在阿里云服务器上。 4. 版本控制:使用Git进行版本控制。 三、模块介绍 1. 课程管理模块:包括课程信息的添加、修改、删除等功能,支持课程分类、搜索等功能。 2. 学生管理模块:包括学生信息的添加、修改、删除等功能,支持学生查询、筛选等功能。 3. 教师管理模块:包括教师信息的添加、修改、删除等功能,支持教师查询、筛选等功能。 4. 支付管理模块:支持学生缴费功能,支持多种支付方式,如支付宝、微信支付等。 5. 数据统计模块:对课程、学生、教师等数据进行统计分析,提供数据可视化功能。 四、开发计划 1. 第一阶段:需求分析和设计。预计耗时1周。 2. 第二阶段:前端和后端开发。预计耗时4周。 3. 第三阶段:测试和调试。预计耗时1周。 4. 第四阶段:上线和发布。预计耗时1周。 五、开发团队 本项目开发团队由以下人员组成: 1. 项目经理:负责项目的整体规划和管理。 2. 前端开发人员:负责前端界面的开发和优化。 3. 后端开发人员:负责后端接口的开发和优化。 4. 测试人员:负责项目的测试和质量保证。 5. 运维人员:负责项目的部署和维护。 六、总结项目将会提供优质的在线教育服务,为广大学生和教育工作者提供更好的学习和教学体验。我们将不断优化和完善本平台,为用户提供更好的服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值