一、业务流程
1.企业(B)
内容管理、媒资、缓存、消息、任务调度、搜索
2.个人(C)
搜索、缓存、订单、支付、学习
二、关键技术
1.缓存三兄弟:
缓存穿透,高并发请求过来之后,查询数据库中存在数据,都发现缓存中没有,还是会去多次查数据库。如果查询数据库中不存在数据,高并发会一直查数据库,因为数据写入不到redis里。即请求穿透了缓存一直在查数据库。解决方案:1.参数校验,不符合规则直接返回2.把数据库中不存在数据也给缓存到redis,查缓存时也有该key,但是值为null。这种情况要加过期时间,防止数据库中什么时候真的有了该key。
缓存雪崩,缓存的大量key失效,因为他们设置了相同的缓存失效时间。解决方案:1.使用同步锁,只允许一个线程访问,性能不高。2.对同一类型的key设置不同的过期时间。3.缓存预热,取出来加随机的过期时间。
缓存击穿,大量并发访问同一个热点数据,热点数据失效后同时访问数据库。解决方案:1.同步锁,性能太低。不要直接在方法名上加synchronized,只锁查询数据库的代码,缓存查询可以高并发访问,锁进来后还要再查一次缓存。2.热点数据不过期。
2.JWT登录:
使用jwt(json web token令牌可以自己校验,无状态认证,不存储用户身份。有三部分,header(头部),内容(用户的信息),签名(前两部分想加再加密),资源认证服务有密钥.密钥分为对称性加密和非对称性加密。对称性能高(即认证服务的和资源服务的密钥一样,非对称加密认证自己保留私钥,公钥下发给客户端和资源服务。内容可以根据token还原,放一些公开信息(比如头像).管控起来后http接口测试就不能正常进行,要带token去访问。在SecurityContextHolder中可以拿到用户信息(存储在jwt的token中)
要自定义UserDetailService类连接用户数据库,传给username,连同用户名、密码、权限一起返回给spring security框架。密码不是明文存储,采用bcrypt加密,每次生成的密码都不一样。将用户信息转成json传回,否则没有头像等信息,这个过程中要将密码等敏感信息置空。获取的时候可以写一个工具类json转对象
由于有很多可能的提交信息方式,比如:账号密码,手机验证码,微信扫码,因此需要统一认证,新建一个DTO类。userdetailservice原本传入的是username,现在要传入的是DTO类的json串。使用fastjson
重写DAOAuthenticationProvider(DaoAuthenticationProvider通过UserDetailsService以及PasswordEncoder,将用户名密码替换成UserDetails和Authorities),authentication(认证)authorization(授权)不止使用密码校验,所以要重写方法。一个接口有很多个实现类,在service中增加名字(password_authservice,wx_authservice)
3.幂等性、索引同步:
本地消息表+任务调度的机制来完成分布式事务的最终数据一致性的控制。建的本地消息表和课程发布表在同一个数据库,使用数据库事务控制,保证发布了就一定会在消息表中,然后任务调度定时调度消息表存储给redis,es,minIO。若当中有失败的,则下次定时会继续发,已经成功的根据幂等性,会跳过。
4.分布式锁
本地锁每个JVM都会查一次数据库。实现方案:1.数据库乐观锁(谁update成功)2.基于redis的set nx(不存在才可以在redis里面存,看是否可以set成功),set lock01 01 NX EX 30要记得设置过期时间,否则其他拿不到锁,锁过期时间很难设置,可以手动删除锁,为了防止删除别人的锁,可以判断锁的值是不是自己设置的。代码不具有原子性,要借助lua脚本,让cpu一次性执行完毕,可能有断电等问题,过期时间的值还是不精确。3.zookeeper4.redisson,自旋锁(非阻塞锁),另起线程看门狗,看线程是否干完活,可以续过期时间。
悲观锁synchronized总认为有线程抢锁,拿到锁后再执行。乐观锁认为没有线程抢锁,尽管执行,如果没有执行成功就重试。
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.权限控制:
分两种方式,基于角色的访问控制和基于资源的访问控制。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.JVM的GC机制:
(1)哪些内存需要回收?
堆和方法区,程序计数器、虚拟机栈、本地方法栈3个区域是随线程而生,随线程而灭的
(2)什么时候回收?
判断对象是否可以回收,有两种比较经典的判断策略。
- 引用计数算法,count+1-1,=0时失效
- 可达性分析算法,为起点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots 没有任何引用链相连时。
(3)如何回收?
- 标记-清除(Mark-Sweep)算法,大量不连续的内存碎片
- 复制(Copying)算法,(新生代)当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
- 标记-整理(Mark-Compact)算法,(老生代)让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
12.输入网址到显示:
1、输入网址
2、DNS解析获取域名对应的IP地址
3、建立TCP连接
4、web浏览器向web服务器发送HTTP请求
5、服务器的永久重定向响应
6、浏览器跟踪重定向地址
7、web服务器做出应答
8、浏览器显示 HTML
9、浏览器发送请求获取其他嵌入在 HTML 中的资源
10、web服务器关闭TCP连接
三、组件
1.Nacos:
服务注册中心和配置中心
namespace、group、dataid。
namespace(开发环境,测试环境,上线环境)和group(学成在线,瑞吉外卖)。
Dataid: 包括三部分:服务名、环境名、扩展名,
content-service-dev.yaml配置文件 由(content-service)-(dev). (yaml)三部分组成
server:servlet:context-path: /contentport: 63040test_config:a: 3ab: 3b#配置本地优先
spring:cloud:config:override-none: true
2.Gateway:
负载均衡、路由转发、统一鉴权
前端请求到Nginx,通过负载均衡到Gateway网关,通过网关将请求转发至各个微服务。网关从nacos读取服务地址
3.Redis
白名单、热点数据
实现分布式锁,避免课程查询等公开接口出现缓存击穿问题
4.RabbitMQ
将支付结果发通过给fanout类型的交换机,由交换机将消息广播发送至每个接收支付结果的微服务。
5.Elasticsearch
倒排索引,对课程发布信息进行索引和搜索。
布尔查询、聚合搜索、过滤器、高亮显示等功能
6.XXL-Job
由调用中心和执行器组成,调用中心负责按任务调度策略向执行器下发任务,执行器负责接收任务执行任务。主要用于索引同步
配置调度过期策略和阻塞处理策略,避免同一个执行器多次重复执行同一个任务
调度过期策略:忽略:调度过期后,忽略过期的任务,从当前时间开始重新计算下次触发时间;
阻塞处理策略:丢弃后续调度。