Loading

【Java基础】day14

day14

一、什么是 RESTful 架构?

REST 全称是 Representational State Transfer,中文意思是表征性状态转移。它首次出现在 2000 年 Roy Fielding 的博士论文中,Roy Fielding 是 HTTP 规范的主要编写者之一。 他在论文中提到:我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST 指的是一组架构约束条件和原则。

如果一个架构符合 REST 的约束条件和原则,我们就称它为 RESTful 架构。

REST 本身并没有创造新的技术、组件或服务,而隐藏在 RESTful 背后的理念就是使用 Web 的现有特征和能力,更好地使用现有 Web 标准中的一些准则和约束。虽然 REST 本身受 Web 技术的影响很深,但是理论上 REST 架构风格并不是绑定在 HTTP 上,只不过目前 HTTP 是唯一与 REST 相关的实例。所以我们这里描述的 REST 也是通过 HTTP 实现的 REST。

Reference

https://www.runoob.com/w3cnote/restful-architecture.html

二、缓存和数据库一致性问题的解决方式?

为了提高数据响应速度,会将一些高频访问的数据保存在缓存中。这样就不用每次请求都去查询数据库,可以提高服务接口的响应速度。目前最常使用的缓存就是 Redis。缓存一般是用来对数据库中的高频数据做一个优化的,也仅存储一些数据库中被高频访问的数据的。

Cache-Aside

最经典的一个缓存 + 数据库的读写模式就是 Cache-Aside 模式了。

  • 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应
  • 更新的时候,先更新数据库,然后再删除缓存,这里可能会有其他问题

读取的时候一般没有问题,有问题的是数据更新。在这个模式中,更新数据采用的是先更新数据库再删除缓存的模式。问题主要存在两个:

① 为什么是删除缓存而不是更新缓存?

  • 复杂缓存场景下更新缓存困难。在一些复杂的缓存使用场景中,可能并不是直接将数据库字段的结果存储在缓存中,而是将数据库中的数据经过一系列复杂计算后的结果存储在缓存中。删除缓存而不是更新缓存,就是一个 lazy 计算的思想,不要每次都重新做复杂的计算,而是让它到需要被使用的时候再重新计算。数据使用少更新开销大,冷数据大量产生。
  • 写频繁时更新缓存会造成额外性能开销。在写场景比较多的情况下,如果多次更新缓存,但实际上只有很少次数使用到缓存,这样就会导致很多次的缓存无效,造成额外的性能开销。这样的情况下就不如直接删除缓存来的划算。
  • 并发场景下可能导致数据一致性问题。如果首先 A 线程更新了数据库,接下来 B 线程更新了数据库;由于网络等原因,B 线程先更新了缓存, A 线程更新了缓存。那么此时缓存中的数据是错误的,导致数据一致性出现问题。

Reference
https://blog.csdn.net/qq_38420688/article/details/108803569

② 为什么不删除旧缓存再更新数据库?

  • 并发场景下可能导致数据一致性问题。A 线程删除了缓存,而 B 线程查询了数据库之后进行了缓存写入,之后 A 线程再进行了数据库更新。此时就会出现数据一致性问题。而且在高并发场景下,可能导致数据库瞬时压力过大。

延迟双删

其实无论是先更新数据库再删除缓存,还是先删除缓存再更新数据库,在并发环境下都有可能存在问题。假设有 A、B 两个并发请求:

  • 先更新数据库再删除缓存:当请求 A 更新数据库之后,还未来得及进行缓存清除,此时请求 B 查询到并使用了 Cache 中的旧数据。
  • 先删除缓存再更新数据库:当请求 A 执行清除缓存后,还未进行数据库更新,此时请求 B 进行查询,查到了旧数据并写入了 Cache。

当然前面已经分析过了,尽量先操作数据库再操作缓存,但是即使这样也还是有可能存在问题,解决问题的办法就是延迟双删

延迟双删是先执行缓存清除操作,再执行数据库更新操作,延迟 N 秒之后再执行一次缓存清除操作,这样就不用担心缓存中的数据和数据库中的数据不一致。一般来说,N 要大于一次写操作的时间,如果延迟时间小于写入缓存的时间,会导致请求 A 已经延迟清除了缓存,但是此时请求 B 缓存还未写入。具体需要结合自己的业务来统计这个数值。

Reference

https://blog.csdn.net/zxd1435513775/article/details/123840314

三、过滤器和拦截器有什么区别?

Filter 过滤器, Interceptor 拦截器,两者区别:

  • Filter 是基于函数回调的,而 Interceptor 则是基于 Java 反射的。
  • Filter 依赖于 Servlet 容器,而 Interceptor 不依赖于 Servlet 容器。
  • Filter 对几乎所有的请求起作用,而 Interceptor 只能对 action 请求起作用。
  • Interceptor 可以访问 Action 的上下文,值栈里的对象,而 Filter 不能。
  • 在 action 的生命周期里,Interceptor 可以被多次调用,而 Filter 只能在容器初始化时调用一次。

两者的执行顺序如下:过滤前——拦截前——action 执行——拦截后——过滤后

四、类加载的过程?

加载

加载是指 JVM 查找 .class 文件,并且根据字节流创建 java.lang.Class 对象的过程。这个过程将类的 .class 文件中的二进制数据读入内存,放在运行时数据的方法区内。然后在堆中创建 java.lang.Class 对象,用来封装类在方法区的数据结构。

类加载阶段:

  1. Java 虚拟机将 .class 文件读入内存,并为之创建一个 Class 对象;
  2. 任何类被使用时系统都会为其创建一个且仅有一个 Class 对象;
  3. 这个 Class 对象描述了这个类创建出来的对象的所有信息,比如有哪些构造方法,都有哪些成员方法,都有哪些成员变量等。

验证

验证阶段是保证 .class 文件字节流包含的信息符合 JVM 规范,不会给 JVM 造成危害。如果验证失败,就会抛出一个 java.lang.VerifyError 异常或其子类异常。验证过程分为四个阶段:

  1. 文件格式验证:验证字节流文件是否符合 .class 文件格式的规范,并且能被当前虚拟机正确的处理。
  2. 元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合 Java 语言的规范要求。
  3. 字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。
  4. 符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。

准备

准备阶段为变量分配内存并设置类变量的初始化。在这个阶段分配的仅为 static 修饰的变量,而不包括类的实例变量。对于非 final 的变量,JVM 会将其设置成零值,而不是其赋值语句的值。例如 pirvate static int size = 12; 在这个阶段,size 的值为 0,而不是 12。但 final 修饰的 static 变量将会赋值成真实的值。

解析

解析过程是将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析:类或接口的解析、字段解析、方法解析、接口方法解析。

初始化

初始化是为标记为常量值的字段赋值的过程,即只对 static 修饰的变量或语句块进行初始化。如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

Reference

https://blog.csdn.net/weixin_30720461/article/details/121142420

五、Redis 单线程为什么这么快?

1.纯内存操作

Redis 是一个内存数据库,它的数据都存储在内存中,这意味着我们读写数据都是在内存中完成,这个速度是非常快的。

Redis 底层采用了高效的数据结构,例如哈希表和跳表,这是它实现高性能的一个重要原因。

2.采用 IO 多路复用机制

Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。

当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

虽然文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接,这保持了 Redis 内部单线程设计的简单性。

3.非 CPU 密集型任务

采用单线程的缺点很明显,无法使用多核 CPU。由于 Redis 的大部分操作并不是 CPU 密集型任务,而 Redis 的瓶颈在于内存和网络带宽

在高并发请求下,Redis 需要更多的内存和更高的网络带宽,否则瓶颈很容易出现在内存不够用和网络延迟等待的情况。如果单个 Redis 实例的性能不足以支撑业务,推荐部署多个 Redis 节点,组成集群的方式来利用多核 CPU 的能力,而不是在单实例上使用多线程。

4.单线程的优点

Redis 的单线程已经达到了足够高的性能,所以采用了单线程模型来完成请求的处理工作。另外,单线程模型还带了以下好处:

  • 避免多线程上下文切换导致的性能损耗
  • 避免多线程访问共享资源加锁导致的性能损耗

5.多线程优化

在进行一些 AOF 的刷盘工作时,是使用的多线程,特别是在 4.0 版本之后,有更多的优化措施。

posted @ 2023-04-13 20:26  雨下一整晚Real  阅读(10)  评论(0编辑  收藏  举报  来源