1.RabbitMQ延迟队列干什么用的,怎么实现的延迟队列
RabbitMQ延迟队列(Delayed Queue)是一种特殊类型的队列,它允许消息在指定的延迟时间后才被消费者接收到。
这种机制在许多应用场景中非常有用,例如:
- 任务调度:在特定时间执行某个任务。
- 重试机制:在任务失败后,延迟一段时间再重试。
- 限流控制:控制消息的处理速率,防止突发流量导致系统过载。
实现RabbitMQ延迟队列的两种主要方法
使用TTL(Time-To-Live)和死信队列(DLX)
- 创建一个普通队列,并设置TTL(消息的存活时间)。
- 配置死信交换器(DLX),当消息在普通队列中过期后,会被转发到死信交换器。
- 创建一个死信队列,绑定到死信交换器。
2.RabbitMQ是怎么构成的
1. Broker(代理)
RabbitMQ的核心是消息代理(Broker),负责接收、存储和转发消息。Broker由多个内部组件组成,包括连接管理、队列管理、交换器管理等。
2. Connection(连接)
连接是客户端与RabbitMQ Broker之间的TCP连接。每个连接可以包含多个信道(Channel),用于并发处理消息。
3. Channel(信道)
信道是建立在连接上的虚拟连接,用于执行消息的发送、接收和其他操作。信道是轻量级的,可以在一个连接中创建多个信道,以提高并发性和性能。
4. Exchange(交换器)
交换器是消息路由的核心组件,负责根据路由规则将消息分发到相应的队列。交换器有不同的类型,包括:
- Direct Exchange:根据完全匹配的路由键将消息路由到队列。
- Topic Exchange:根据模式匹配的路由键将消息路由到队列。
- Fanout Exchange:将消息广播到所有绑定的队列,不考虑路由键。
- Headers Exchange:根据消息头属性将消息路由到队列。
5. Queue(队列)
队列是消息的存储容器,消息被发送到队列中,等待消费者处理。队列有不同的属性,如持久性、自动删除、排他性等。
6. Binding(绑定)
绑定是交换器和队列之间的连接,定义了消息如何从交换器路由到队列。绑定可以包含路由键和其他参数,用于细化路由规则。
7. Message(消息)
消息是RabbitMQ中传递的数据单元,包含消息体(payload)和消息属性(如路由键、头部信息等)。消息可以是持久化的,也可以是非持久化的。
8. Virtual Host(虚拟主机)
虚拟主机是RabbitMQ中的多租户机制,用于隔离不同的应用和用户。每个虚拟主机拥有独立的交换器、队列、绑定和权限配置。
9. Plugin(插件)
RabbitMQ支持插件机制,可以通过插件扩展其功能。例如,管理插件(Management Plugin)提供了一个Web界面用于监控和管理RabbitMQ,延迟消息插件(rabbitmq_delayed_message_exchange)用于实现延迟队列等。
10. Clustering(集群)
RabbitMQ支持集群模式,可以将多个Broker节点组成一个集群,以提高可用性和扩展性。集群中的节点可以共享队列和交换器,实现负载均衡和故障转移。
11. Federation(联邦)和Shovel
RabbitMQ支持联邦和Shovel机制,用于跨数据中心或跨网络连接不同的RabbitMQ实例。联邦机制允许在不同的RabbitMQ实例之间转发消息,而Shovel机制则是一个更灵活的消息转发工具。
12. High Availability(高可用性)
RabbitMQ支持镜像队列(Mirrored Queue),可以在集群中的多个节点上复制队列,以实现高可用性和数据冗余。当主节点故障时,镜像队列可以自动切换到其他节点。
RabbitMQ的工作流程
- 客户端连接到Broker:客户端通过TCP连接到RabbitMQ Broker,并创建一个或多个信道。
- 声明交换器和队列:客户端声明交换器和队列,并设置相应的属性(如持久性、自动删除等)。
- 绑定队列到交换器:客户端创建绑定,将队列绑定到交换器,并指定路由键。
- 发送消息到交换器:客户端将消息发送到交换器,并指定路由键。交换器根据路由规则将消息路由到相应的队列。
- 队列存储消息:队列接收到消息并存储,等待消费者处理。
- 消费者接收消息:消费者从队列中接收消息,并执行相应的处理逻辑。
- 消息确认:消费者处理完消息后,向Broker发送确认(ACK)。如果消息处理失败,可以发送拒绝(NACK),消息可以重新入队或丢弃。
3.进程的调度算法有哪些
非抢占式调度算法
非抢占式调度算法中,进程一旦获得CPU控制权,就会一直运行到完成或主动放弃CPU。
1. 先来先服务(First-Come, First-Served, FCFS)
- 概念:按照进程到达的顺序分配CPU。
- 优点:实现简单,易于理解。
- 缺点:可能导致长时间等待,尤其是当一个长进程阻塞了后续短进程时(即“长进程问题”)。
2. 最短作业优先(Shortest Job First, SJF)
- 概念:选择预计执行时间最短的进程优先执行。
- 优点:可以最小化平均等待时间。
- 缺点:难以准确预测执行时间,可能导致“饥饿”现象(长进程可能永远得不到执行)。
抢占式调度算法
抢占式调度算法允许操作系统在某些条件下中断正在运行的进程,将CPU分配给其他进程。
3. 最短剩余时间优先(Shortest Remaining Time First, SRTF)
- 概念:在现有的进程中选择剩余执行时间最短的进程。如果新进程的预计执行时间比当前进程的剩余时间短,则抢占当前进程。
- 优点:进一步减少平均等待时间。
- 缺点:同样面临难以预测执行时间和“饥饿”问题。
4. 优先级调度(Priority Scheduling)
- 概念:根据进程的优先级分配CPU,优先级高的进程优先执行。
- 优点:可以根据进程的重要性进行调度。
- 缺点:可能导致低优先级进程“饥饿”。可以通过“老化”机制提高长期等待的低优先级进程的优先级来解决。
5. 轮转调度(Round Robin, RR)
- 概念:每个进程按顺序分配一个固定的时间片(Time Quantum),时间片结束后切换到下一个进程。
- 优点:公平,响应时间好。
- 缺点:时间片过长会退化为FCFS,时间片过短会导致频繁切换,增加开销。
6. 多级反馈队列(Multilevel Feedback Queue, MLFQ)
- 概念:使用多个队列,每个队列有不同的优先级和时间片。新进程进入最高优先级队列,随着时间片用完,逐渐降低优先级。
- 优点:灵活,适应不同类型的进程。
- 缺点:实现复杂,需要调参。
7. 最短剩余时间优先(Shortest Remaining Time Next, SRTN)
- 概念:类似于SRTF,但更注重剩余时间的动态调整。
- 优点:进一步减少平均等待时间。
- 缺点:复杂度较高,难以准确预测剩余时间。
实时调度算法
实时系统中,调度算法需要保证任务在规定的时间内完成。
8. 最早截止时间优先(Earliest Deadline First, EDF)
- 概念:根据任务的截止时间进行调度,截止时间最早的任务优先执行。
- 优点:可以动态适应任务的变化。
- 缺点:实现复杂,需要精确的时间管理。
9. 最低松弛度优先(Least Slack Time First, LSTF)
- 概念:根据任务的松弛度(截止时间减去剩余执行时间)进行调度,松弛度最小的任务优先执行。
- 优点:适用于硬实时系统。
- 缺点:实现复杂,计算开销大。
其他调度算法
10. 公平分享调度(Fair Share Scheduling)
- 概念:将CPU时间按用户或进程组分配,而不是按单个进程分配。
- 优点:保证不同用户或进程组的公平性。
- 缺点:实现复杂,需要管理用户或进程组。
4.内存管理方式有哪些
1. 单一连续分配
概念
在这种方式下,整个内存空间分为两部分:一部分用于操作系统,另一部分用于用户进程。每次只有一个用户进程运行,占用整个用户区内存。
优点
- 实现简单。
缺点
- 内存利用率低。
- 不能同时运行多个进程。
2. 固定分区分配
概念
内存被划分为若干固定大小的分区,每个分区可以分配给一个进程。分区的大小在系统启动时确定,不能动态调整。
优点
- 实现简单。
- 分区管理容易。
缺点
- 内存碎片问题(内部碎片)。
- 不灵活,分区大小固定,可能不适应实际需求。
3. 动态分区分配
概念
内存被动态划分为若干分区,每个分区的大小根据进程的需求动态分配。分区的大小和位置可以在运行时调整。
优点
- 内存利用率较高。
- 灵活,能够适应不同大小的进程。
缺点
- 内存碎片问题(外部碎片)。
- 分区管理复杂,需要合并和分割分区。
4. 分页(Paging)
概念
将内存和进程的地址空间均划分为固定大小的页(Page),内存中的页称为物理页(Frame),进程中的页称为逻辑页。通过页表(Page Table)将逻辑页映射到物理页。
优点
- 消除了外部碎片问题。
- 内存利用率高。
- 可以实现虚拟内存。
缺点
- 需要页表管理,增加了开销。
- 可能存在页表开销(多级页表)。
5. 分段(Segmentation)
概念
将进程的地址空间划分为若干段(Segment),每段表示一个逻辑单元(如代码段、数据段、堆栈段)。通过段表(Segment Table)将逻辑段映射到物理内存。
优点
- 逻辑上更接近程序的结构。
- 支持动态增长的段,如堆栈和堆。
缺点
- 可能存在外部碎片问题。
- 需要段表管理,增加了开销。
6. 分页与分段结合
概念
结合分页和分段的优点,将进程的地址空间先划分为段,每段再划分为页。通过段表和页表的组合实现地址映射。
优点
- 结合了分页和分段的优点。
- 提供了灵活的内存管理方式。
缺点
- 实现复杂。
- 需要管理段表和页表,增加了开销。
7. 虚拟内存(Virtual Memory)
概念
虚拟内存是一种技术,使得进程可以使用比实际物理内存更大的地址空间。通过将不常用的页面交换到磁盘上,实现虚拟内存。
优点
- 提高了内存利用率。
- 允许运行大于物理内存的程序。
- 提供了内存保护机制。
缺点
8. 内存映射文件(Memory-Mapped Files)
- 需要磁盘交换,可能导致性能下降(页面置换开销)。
概念
内存映射文件是一种将文件内容映射到进程地址空间的技术,使得文件操作可以像内存操作一样进行。
优点
- 提高了文件I/O的效率。
- 方便共享内存。
缺点
- 需要操作系统支持。
- 可能导致内存碎片问题。
5.僵尸进程 孤儿进程是什么
僵尸进程(Zombie Process)
概念
僵尸进程是指已经终止但其父进程尚未读取其退出状态的进程。僵尸进程仍然占据进程表中的一个条目,但不占用其他系统资源。
产生原因
当一个子进程终止时,它会向父进程发送一个SIGCHLD信号,通知父进程它已经结束。父进程需要调用
wait()或waitpid()函数来读取子进程的退出状态。如果父进程没有及时调用这些函数,子进程的状态信息会保留在进程表中,从而形成僵尸进程。影响
僵尸进程本身不占用大量资源,但如果进程表中的僵尸进程过多,会导致进程表项耗尽,影响系统的正常运行。
解决方法
- 父进程调用
wait()或waitpid():父进程可以定期调用这些函数来清理僵尸进程。- 忽略SIGCHLD信号:父进程可以通过设置SIGCHLD信号的处理方式为
SIG_IGN来自动清理僵尸进程。- 重新捕捉SIGCHLD信号:通过自定义信号处理函数来处理子进程的终止。
孤儿进程(Orphan Process)
概念
孤儿进程是指其父进程已经终止,但它仍在运行的进程。孤儿进程会被操作系统的init进程(PID为1的进程)收养,init进程会成为它们的新父进程。
产生原因
当一个父进程终止时,所有由该父进程生成的子进程会变成孤儿进程。操作系统会将这些孤儿进程的父进程ID(PPID)重置为init进程的PID。
影响
孤儿进程本身不会对系统产生负面影响,因为init进程会负责清理它们的资源。但是,如果孤儿进程数量过多,可能会增加init进程的负担。
解决方法
孤儿进程不需要特别处理,因为init进程会自动接管和管理它们。
6.HTTP常见状态码有哪些
1xx 信息性响应
这些状态码表示临时响应,客户端应继续请求或忽略响应。
100 Continue
- 含义:请求已接收,客户端应继续发送请求的其余部分(如请求体)。
101 Switching Protocols
- 含义:服务器同意客户端的协议切换请求。
102 Processing (WebDAV)
- 含义:服务器已接收到并正在处理请求,但尚未完成。
2xx 成功
这些状态码表示请求已成功接收、理解和处理。
200 OK
- 含义:请求成功,服务器返回请求的资源。
201 Created
- 含义:请求成功并且服务器创建了新的资源。
202 Accepted
- 含义:请求已接受,但尚未处理完成。
203 Non-Authoritative Information
- 含义:请求成功,但返回的元信息不是从原始服务器获取的,而是从本地或第三方副本获取的。
204 No Content
- 含义:请求成功,但没有内容返回。
205 Reset Content
- 含义:请求成功,客户端应重置视图。
206 Partial Content
- 含义:服务器成功处理了部分GET请求。
3xx 重定向
这些状态码表示客户端需要采取进一步的操作以完成请求。
300 Multiple Choices
- 含义:请求的资源有多种表示,客户端可以选择一个进行重定向。
301 Moved Permanently
- 含义:请求的资源已永久移动到新位置,客户端应使用新URL进行请求。
302 Found
- 含义:请求的资源临时移动到新位置,客户端应继续使用原URL进行请求。
303 See Other
- 含义:请求的资源可在另一个URL找到,客户端应使用GET方法获取资源。
304 Not Modified
- 含义:请求的资源未修改,客户端可以使用缓存的版本。
307 Temporary Redirect
- 含义:请求的资源临时移动到新位置,客户端应使用原请求方法和新URL进行请求。
308 Permanent Redirect
- 含义:请求的资源已永久移动到新位置,客户端应使用新URL进行请求,并使用原请求方法。
4xx 客户端错误
这些状态码表示客户端的请求包含错误,服务器无法处理。
400 Bad Request
- 含义:请求无效或格式错误,服务器无法理解。
401 Unauthorized
- 含义:请求未授权,客户端需要提供身份验证凭据。
402 Payment Required
- 含义:保留状态码,暂未使用。
403 Forbidden
- 含义:服务器拒绝请求,即使提供了有效的身份验证凭据。
404 Not Found
- 含义:请求的资源未找到,服务器无法找到请求的资源。
405 Method Not Allowed
- 含义:请求方法不被允许,服务器不支持请求方法。
406 Not Acceptable
- 含义:请求的资源无法满足客户端的内容协商条件。
407 Proxy Authentication Required
- 含义:客户端需要通过代理进行身份验证。
408 Request Timeout
- 含义:请求超时,客户端未在服务器期望的时间内发送请求。
409 Conflict
- 含义:请求与服务器的当前状态冲突。
410 Gone
- 含义:请求的资源已永久删除,不再可用。
411 Length Required
- 含义:服务器要求在请求中提供Content-Length头部。
412 Precondition Failed
- 含义:请求的前提条件未满足。
413 Payload Too Large
- 含义:请求实体过大,服务器无法处理。
414 URI Too Long
- 含义:请求的URI过长,服务器无法处理。
415 Unsupported Media Type
- 含义:请求的媒体类型不受支持。
416 Range Not Satisfiable
- 含义:请求的范围无效,服务器无法提供。
417 Expectation Failed
- 含义:服务器无法满足Expect请求头的要求。
418 I'm a teapot (RFC 2324)
- 含义:愚人节玩笑状态码,表示服务器是茶壶,无法冲泡咖啡。
421 Misdirected Request
- 含义:请求被定向到无法生成响应的服务器。
422 Unprocessable Entity (WebDAV)
- 含义:请求格式正确,但由于逻辑错误无法处理。
423 Locked (WebDAV)
- 含义:请求的资源被锁定。
424 Failed Dependency (WebDAV)
- 含义:由于前一个请求失败,当前请求失败。
425 Too Early
- 含义:服务器不愿意处理可能重播的请求。
426 Upgrade Required
- 含义:客户端应切换到TLS/1.0。
428 Precondition Required
- 含义:请求需要满足前提条件。
429 Too Many Requests
- 含义:客户端发送的请求过多,超出了服务器的处理能力。
431 Request Header Fields Too Large
- 含义:请求头字段过大,服务器无法处理。
451 Unavailable For Legal Reasons
- 含义:由于法律原因,服务器无法提供请求的资源。
5xx 服务器错误
这些状态码表示服务器在处理请求时发生错误。
500 Internal Server Error
- 含义:服务器内部错误,无法完成请求。
501 Not Implemented
- 含义:服务器不支持请求的方法。
502 Bad Gateway
- 含义:网关或代理服务器从上游服务器收到无效响应。
503 Service Unavailable
- 含义:服务器暂时无法处理请求,通常是由于过载或维护。
504 Gateway Timeout
- 含义:网关或代理服务器未能及时从上游服务器接收响应。
505 HTTP Version Not Supported
- 含义:服务器不支持请求的HTTP版本。
506 Variant Also Negotiates
- 含义:服务器存在内部配置错误,导致内容协商循环。
507 Insufficient Storage (WebDAV)
- 含义:服务器无法存储请求所需的内容。
508 Loop Detected (WebDAV)
- 含义:服务器检测到无限循环。
510 Not Extended
- 含义:服务器需要扩展请求才能处理。
511 Network Authentication Required
- 含义:客户端需要进行网络身份验证。
7.介绍一下HTTPS
8.布隆过滤器代替分布式锁,主要是为了什么,布隆过滤器有什么优点,占用内存具体是多少
使用布隆过滤器代替分布式锁的原因:
- 减少锁开销:分布式锁需要依赖外部存储或协调服务(如Redis、Zookeeper等)来保证锁的获取和释放,增加了额外的网络延迟和负载。布隆过滤器可以在本地内存中快速判断,避免频繁的分布式锁操作,减少系统的开销。
- 提高并发性能:在高并发场景下,分布式锁可能成为瓶颈,导致性能下降。布隆过滤器通过高效的查找机制大幅提高了并发性能,尤其是在需要判断数据是否存在时,能够迅速给出结果。
- 适用于不严格要求准确性的场景:布隆过滤器可以接受一定的误判(假阳性),在某些业务场景中,宁可做多一些冗余操作,也不希望增加分布式锁带来的复杂性和延迟。
布隆过滤器的优点:
- 内存占用少:相比直接存储所有元素,布隆过滤器通过位数组和多个哈希函数来存储数据,极大地减少了内存占用。即使存储大量数据,布隆过滤器也能保持较低的内存使用。
- 高效的查询速度:布隆过滤器的查询操作非常快,因为只需要计算多个哈希函数并检查相应的位,时间复杂度为O(k),其中k是哈希函数的数量,且通常很小。
- 容易扩展:布隆过滤器结构简单,可以轻松地水平扩展,适用于分布式系统。
- 不存在假阴性:布隆过滤器确保当某个元素不存在时,返回结果一定是正确的(不存在假阴性)。
布隆过滤器的内存占用计算:
布隆过滤器的内存占用取决于以下几个因素:
- n:需要存储的元素数量。
- p:期望的误判率(假阳性率)。
- m:布隆过滤器的位数组长度。
- k:使用的哈希函数个数。
布隆过滤器的位数组大小 m 可以通过以下公式近似计算:
9.如果我有一个项目,上线了一年多了,这个时候布隆过滤器误判率有点高了怎么办
1. 重建布隆过滤器
最直接的解决方案是重新构建布隆过滤器,调整位数组的大小或增加哈希函数的数量。通过减少误判率(例如,重新计算更合适的位数组大小和哈希函数数量),你可以降低误判率。
步骤:
- 重新评估当前需要存储的元素数量 nnn 和你希望的误判率 ppp。
- 根据公式重新计算位数组大小 mmm 和哈希函数个数 kkk,并用新的参数重建过滤器。
- 可以逐步替换旧的布隆过滤器,保证业务不中断。
2. 使用分层布隆过滤器
分层布隆过滤器是一种扩展方法,适用于长期运行的项目。你可以创建一个新的布隆过滤器用于存储新增的元素,而旧的过滤器保留。查询时,依次在新的和旧的布隆过滤器中查找。这种方法避免了单个布隆过滤器的过度饱和,并且容易扩展。
优点:
- 动态扩展,适合逐步增加的元素。
- 保持一定的内存利用率,而不需要一次性分配大量内存。
3. 结合其他缓存机制
在某些场景下,假阳性率可能导致不必要的额外查询或计算。如果布隆过滤器的误判率较高,导致不必要的操作量增加,可以结合其他缓存机制(如LRU缓存)来减少误判带来的影响。
4. 将布隆过滤器替换为更复杂的数据结构
如果系统允许使用更多内存,可以考虑使用计数布隆过滤器或Cuckoo Filter(布谷鸟过滤器)。这些数据结构在内存稍多的情况下能够提供更好的误判率控制,甚至允许删除元素。
5. 定期清理或压缩布隆过滤器
如果系统中的元素有生命周期,可以通过定期清理或压缩布隆过滤器来减少误判率。例如,可以定期移除过期的元素并使用较小的布隆过滤器重新加载活跃数据。
10.布谷鸟过滤器
布谷鸟过滤器(Cuckoo Filter)是一种基于布谷鸟哈希(Cuckoo Hashing)原理的数据结构,类似于布隆过滤器(Bloom Filter),但在某些情况下性能更好。它提供了快速的插入、查找和删除操作,且在相同的误判率条件下,布谷鸟过滤器的内存利用率通常比布隆过滤器更高。
布谷鸟过滤器的主要特点:
- 支持删除操作:与布隆过滤器不同,布谷鸟过滤器允许删除元素。这是因为布谷鸟过滤器不仅记录某个元素是否存在,还能在某些情况下准确地找出该元素的位置。
- 较低的误判率:布谷鸟过滤器的误判率通常与布隆过滤器相近甚至更低,且在相同的内存条件下,布谷鸟过滤器可以处理更多的元素。
- 高效的查找:布谷鸟过滤器在查找操作中非常高效,通常需要查询的哈希桶数量非常少(通常为两个),并且哈希冲突率较低。
- 空间效率高:布谷鸟过滤器在存储密度上通常比布隆过滤器更高,能以更少的内存存储相同数量的元素,并保持低误判率。
工作原理:
布谷鸟过滤器是基于布谷鸟哈希的,它通过以下方式工作:
- 两个哈希函数:每个元素通过两个哈希函数映射到两个可能的位置中的一个。每个位置只存储该元素的“fingerprint”(指纹,即元素的简短哈希值)。
- 插入操作:当插入一个元素时,首先尝试将其存储在其两个可能的桶中的一个。如果这两个桶已满,布谷鸟过滤器会使用布谷鸟哈希的方式,将其中一个桶中的元素“踢”出,重新插入到其另一个可能的位置。这一过程会持续直到某个元素找到可用的存储位置,或达到设定的最大尝试次数。
- 查找操作:要查询某个元素是否存在,只需要计算出它的两个哈希值并检查相应的桶是否包含该元素的指纹。如果两个桶中都没有该指纹,则可以断定该元素不存在。
- 删除操作:删除时,只需要从桶中删除元素的指纹即可。由于每个元素的指纹是唯一的,因此布谷鸟过滤器能够安全地删除元素。
布谷鸟过滤器与布隆过滤器的比较:
- 删除操作:布隆过滤器不支持删除,而布谷鸟过滤器支持删除元素。
- 空间利用率:布谷鸟过滤器在大部分场景下比布隆过滤器更节省空间,尤其是在误判率较低的情况下。
- 插入复杂度:由于布谷鸟过滤器可能需要进行“踢出”操作,插入元素的复杂度会比布隆过滤器稍高,特别是在过滤器接近满时。
- 查找效率:布谷鸟过滤器在查找时只需检查少量桶(通常是两个),而布隆过滤器则需要计算多个哈希值和检查相应的位。
内存占用:
布谷鸟过滤器的内存占用与指纹长度(通常为8到16位)、桶大小和过滤器的容量有关。指纹越短,内存占用越小,但误判率会更高。通常,布谷鸟过滤器在相同的误判率下,比布隆过滤器需要的内存更少。
使用场景:
布谷鸟过滤器适用于需要频繁插入、删除和查找的场景,尤其是:
- 缓存系统:用于检测缓存中是否存在某个对象,支持对象的添加和删除。
- 数据库查询:判断某个键是否存在于数据库中,避免不必要的磁盘I/O。
- 网络防火墙和反垃圾邮件:用于高效过滤数据包或邮件中的黑名单对象。
11.SpringCloud Alibaba那一套,主要用了哪些,请求到响应大概是怎么样一个过程
1. Nacos 服务发现与注册
- Nacos 是 Spring Cloud Alibaba 用于服务注册和发现的组件。
- 当微服务启动时,服务会自动向 Nacos 注册,告诉 Nacos 它的网络位置(IP地址、端口等)。
- 每当客户端(发起请求的服务)需要调用某个服务时,它首先会从 Nacos 获取目标服务的实例信息,以便后续的调用。
2. Gateway 作为API网关
- Spring Cloud Gateway 通常用作微服务架构中的网关层,处理外部请求并将其路由到相应的微服务。
- 网关首先拦截来自客户端的请求,可以做一些全局性的处理,比如鉴权、限流、负载均衡、跨域控制等。
- 然后,Gateway 会根据定义的路由规则,将请求转发到相应的服务。
3. Ribbon 负载均衡
- 当请求进入网关或者服务需要调用其他服务时,Ribbon 负责负载均衡。
- Ribbon 从 Nacos 获取到多个可用的服务实例,并根据配置的负载均衡策略(如轮询、随机等)选择一个实例进行调用。
- 这一部分是微服务之间的核心机制,确保流量合理分配给多个服务实例。
4. Feign 服务调用
- Feign 是 Spring Cloud Alibaba 中常用的声明式 HTTP 客户端,负责简化微服务之间的通信。
- Feign 客户端会通过 Nacos 获取到目标服务实例的地址,并根据业务逻辑构造 HTTP 请求,发送到相应的微服务。
- Feign 支持负载均衡,默认会结合 Ribbon 在多个实例中选择一个。
5. Sentinel 限流、熔断、降级
- Sentinel 提供流量控制、熔断、降级等功能,确保在高并发或服务故障情况下系统仍能稳定运行。
- 在服务调用的过程中,Sentinel 会监控请求的流量情况,自动进行限流或熔断。例如,如果某个服务的响应时间过长或请求量过大,Sentinel 可以立即触发熔断策略,停止向该服务发送请求,避免系统过载。
- 当服务降级后,可以配置降级策略,如返回默认值或执行降级逻辑。
6. 服务响应与处理
- 被请求的服务执行相应的业务逻辑并返回结果。
- 返回的响应数据首先经过 Feign、Ribbon 和 Gateway 层的处理,再返回到客户端。
7. 配置管理:Nacos 配置中心
- 服务启动时,业务配置可能会从 Nacos 配置中心 动态加载。Nacos 允许在服务运行过程中动态更新配置,而无需重新部署。
- 配置中心的作用是统一管理服务的配置,并在配置变化时自动推送到对应的服务。
典型的请求到响应的流程:
- 客户端发起请求,首先到达 Spring Cloud Gateway。
- Gateway 根据路由规则,查找目标微服务,通过 Ribbon 负载均衡选择一个实例,并转发请求。
- 如果需要调用其他微服务,服务通过 Feign 和 Nacos 实现服务发现和调用。
- 在调用过程中,Sentinel 监控请求,执行流量控制、熔断等措施。
- 服务处理完业务逻辑后返回响应,数据经过 Gateway 返回给客户端。
组件之间的关系:
- Nacos:负责服务注册与发现,以及动态配置管理。
- Ribbon:负责负载均衡,选择合适的服务实例。
- Feign:简化服务调用,作为声明式 HTTP 客户端。
- Gateway:API 网关,负责请求转发、负载均衡、鉴权等。
- Sentinel:提供限流、熔断、降级等保护机制,确保系统稳定性。
12.用OpenFeign进行微服务间的调用的话,为什么加一个注解他就可以调用了,其中的原理和调用过程
Feign 使用 Java 动态代理 创建
ServiceClient的实现类。Spring 在启动时,会扫描@FeignClient注解的接口,Feign 会为每个接口生成代理类。当你调用
ServiceClient.getData()时,这个调用实际上被代理类捕获,然后代理类会:
- 根据方法的注解(如
@GetMapping、@RequestMapping等)构建出一个 HTTP 请求。- 使用 Feign 生成的 HTTP 客户端执行这个请求,并将返回结果封装成调用的返回值。
13.数据库的隔离级别有哪些,有哪些数据库隔离的问题,可重复读解决了哪些,为什么能解决不可重复度问题
数据库的四种隔离级别:
SQL 标准定义了数据库的四种隔离级别,每种隔离级别能解决不同类型的并发问题。隔离级别越高,并发性越低,但数据的一致性越强。
- 读未提交(Read Uncommitted):
- 事务可以读取到其他事务尚未提交的数据,即脏读。
- 由于允许读取未提交的数据,隔离性最低,基本不保证一致性。
- 可能出现问题:脏读、不可重复读、幻读。
- 读已提交(Read Committed):
- 事务只能读取到其他事务已经提交的数据,避免了脏读。
- 但在同一个事务中,可能在不同时间读取到同一个数据的不同值,导致不可重复读问题。
- 可能出现问题:不可重复读、幻读。
- 这是大多数数据库(如 Oracle)的默认隔离级别。
- 可重复读(Repeatable Read):
- 在同一个事务中,多次读取同一数据时,结果始终相同,避免了不可重复读问题。
- 但仍可能遇到幻读问题。
- 这是 MySQL InnoDB 引擎的默认隔离级别。
- 串行化(Serializable):
- 最严格的隔离级别,事务完全串行执行,保证事务间完全隔离。
- 通过加锁或时间戳序列化执行,确保没有并发问题出现。
- 解决所有并发问题,包括脏读、不可重复读、幻读,但并发性最差,性能开销大。
隔离问题:
- 脏读(Dirty Read):
- 一个事务读取了另一个未提交事务的数据,如果那个事务回滚,读取到的就是脏数据。
- 解决方法:通过读已提交及更高级别的隔离级别可以解决。
- 不可重复读(Non-repeatable Read):
- 一个事务在多次读取同一行数据时,读到了不同的结果。原因是另一个事务在第一次读取之后对该数据进行了修改并提交。
- 解决方法:可重复读隔离级别可以解决。
- 幻读(Phantom Read):
- 一个事务在多次查询时,发现查询结果的条目数发生了变化(如有新插入或删除的行)。这是因为另一个事务插入或删除了数据。
- 解决方法:通过串行化隔离级别可以解决。
可重复读解决了哪些问题?
- 可重复读(Repeatable Read) 解决了不可重复读问题。
- 在可重复读隔离级别下,当一个事务读取数据时,数据库会确保在该事务期间,即使其他事务对同一数据进行了修改或删除,该事务仍然能够读到其初次读取的数据。
可重复读是如何解决不可重复读问题的?
在可重复读隔离级别下,数据库通常通过多版本并发控制(MVCC)来解决不可重复读问题。具体机制如下:
- 快照读(Snapshot Read):
- 当一个事务第一次读取某条数据时,它会获取该数据的一个快照,这个快照包含了在事务开始时该数据的状态。
- 在整个事务过程中,无论其他事务如何修改、删除这条数据,当前事务始终读取到这个快照中的数据,不受外界修改的影响。
- 版本控制:
- 在 MVCC 机制下,每次修改数据时,数据库并不会直接覆盖原数据,而是创建数据的一个新版本(即新的一条记录),并标记旧版本的有效时间。
- 这样,正在执行的事务始终能根据它开始时的时间戳来读取当时的数据版本,保证数据的一致性。
通过 MVCC 机制,事务可以避免不可重复读问题,因为每个事务只会看到自己第一次读取时的数据版本,即使其他事务修改了数据,当前事务也不会受到影响。
可重复读解决的不可重复读问题 vs 幻读:
- 不可重复读是针对同一条记录的多次读取结果不一致的情况,可重复读通过 MVCC 确保多次读取的结果一致。
- 幻读则是指查询的结果集本身发生了变化,例如同一查询返回的行数不同,虽然可重复读解决了单行记录的不可重复读问题,但对于幻读,需要更高的隔离级别(如串行化)来解决。
14.ACID,其中持久性是怎么体现的,mysql通过什么来解决持久性的
持久性在 ACID 中的体现:
- 当一个事务成功提交时,它所做的所有更改都必须被永久写入存储设备(如磁盘)。
- 即使发生系统崩溃或断电,已经提交的事务所产生的更改也必须是可以恢复的,不应被丢失。
MySQL 如何实现持久性:
1. 事务日志(Redo Log):
- Redo log 是 InnoDB 实现持久性的关键。它是一个物理日志,记录了事务对数据的修改操作,但并不是记录每一条数据的详细内容,而是记录如何修改数据的过程(如修改了哪些数据页、如何修改等)。
- 当事务开始执行时,MySQL 不会立刻将数据直接写入磁盘(因为磁盘 I/O 较慢),而是将数据的修改操作写入redo log(此过程是顺序写,性能更高)。
- 一旦 redo log 记录完成,事务就可以认为是提交成功的,即持久性得到了保障。
- 即使数据库崩溃或系统宕机,由于 redo log 已经记录了所有的修改操作,系统在恢复时可以根据 redo log 重做未完成的数据修改,从而确保事务的持久性。
2. 写前日志(Write-Ahead Logging,WAL):
- WAL 机制要求事务必须先将修改的日志写入日志文件(如 redo log),然后再将数据实际写入磁盘的数据页中。
- 这确保了即使在将数据写入磁盘之前发生了崩溃,InnoDB 仍可以通过 redo log 恢复数据。
- 具体流程是:先写 redo log,然后标记事务已提交,接着将修改后的数据页缓存在内存中,最后异步地将数据页刷新到磁盘(这一步称为 checkpoint)。
3. 缓冲池(Buffer Pool)和 Checkpoint 机制:
- 缓冲池 是 MySQL 用来缓存数据页的内存区域。数据库的读写操作首先在缓冲池中进行,数据修改并不会立即写入磁盘。
- Checkpoint 是一个定期将缓冲池中的数据页刷新到磁盘的过程,减少恢复时需要依赖 redo log 的内容量。当发生崩溃时,系统可以从最近的 checkpoint 开始,用 redo log 重做之后的操作,确保数据持久性。
4. 崩溃恢复:
- 当 MySQL 因为某种原因崩溃或异常关闭时,数据库重启时会通过 redo log 和 undo log 来进行恢复。
- redo log 负责确保已经提交的事务的持久性,系统会根据 redo log 重做那些已提交但尚未写入磁盘的数据修改。
- undo log 则用于回滚未提交的事务,确保数据一致性。
MySQL 的持久性保障流程:
- 当事务执行时,MySQL 首先将数据修改记录到 redo log 中,而不是直接写入磁盘中的数据文件。
- 当 redo log 写入成功后,事务可以被认为提交成功,持久性得到保障。
- 后续,MySQL 会将内存中的数据页(缓冲池)异步地写入到磁盘中,完成数据的物理持久化。
- 在发生故障时,系统可以通过 redo log 重做未完成的写操作,确保事务持久性。
15.redolog是把它写入buffer中才返回成功还是顺序写到磁盘中才返回成功
写入 redo log buffer(内存缓冲区):
- 当事务进行修改操作时,修改会先被记录在 redo log buffer 中,这个是位于内存中的缓冲区域。
- 这个过程是非常快速的,因为在内存中操作的速度比磁盘快得多。
写入磁盘:
- 在事务提交时,MySQL 需要确保对应的 redo log 被写入磁盘。这里涉及到两步:
- 写入操作系统缓冲区(通过
fsync()或fdatasync()):这是指将 redo log 从 MySQL 内存写入到操作系统的文件系统缓存。- 写入磁盘:实际的磁盘写操作可以通过
fsync()或者由操作系统在适当的时候将数据从文件系统缓存同步到磁盘。提交成功的条件:
- 在事务提交时,MySQL 会执行 fsync()(或类似系统调用),确保 redo log 从内存中的缓冲区真正写入磁盘。
- 一旦 fsync() 操作成功完成,MySQL 才认为该事务已经安全地提交,并向客户端返回提交成功的响应。这意味着即使数据库在之后崩溃,也可以通过 redo log 进行恢复,保证事务的持久性。
延迟写入磁盘(异步刷新数据页):
- 数据的实际修改(即数据页的更新)通常会先保存在 InnoDB 的缓冲池(Buffer Pool)中,并不会立即写入磁盘。
- 数据页的写入和持久化是通过 checkpoint 机制异步进行的,但这并不会影响事务提交的结果,因为持久性已经通过 redo log 保证了。












Comments NOTHING