Redis篇-2--原理篇1--I/O多路复用机制(5种I/O模型,I/O多路复用)
I/O多路复用机制:
Redis 是通过I/O多路复用机制来管理大量客户端连接。这使得redis可以实现通过单线程来处理多个客户端连接的请求,避免了为每个客户端创建独立的线程,从而减少了上下文切换的开销,提高了系统的并发性和性能。
理解:redis是通过一个线程来处理多个客户端的请求(通过 I/O 多路复用实现),意在减少CPU上下文的切换,提升系统的并发性和性能。
一、重点概念理解
1、什么是文件描述符?
文件描述符: 是操作系统为每个打开的文件、套接字、管道等 I/O 资源分配的一个整数标识符。它用于唯一标识进程与某个 I/O 资源之间的连接。
文件描述符是操作系统内核管理 I/O 资源的方式,应用程序通过文件描述符与这些资源进行交互。
简单理解下:
一个进程可以访问多个文件(或其他网络终端或通道连接等)资源,这些访问都需要进程与文件建立连接后才能进行数据传输。操作系统为了能清晰区分这些连接,不导致数据传输错乱,会给每一个的这种连接(进程和文件通信)都添加一个唯一整数标识,就是文件描述符。
如:一个快递站可以给人发快递(快递站如:操作系统),每个人通过快递站给别人寄快递(寄快递如:建立传输连接),快递站需要对这次寄件添加唯一的快递单号(快递单号如:添加文件描述符),这样才能保证寄件的安全对吧。
2、有哪些类型的文件描述符?
(1)、文件:普通文件(如文本文件、图片文件等)。
(2)、套接字:网络连接(如 TCP/UDP 连接)。
(3)、管道:进程间通信的通道。
(4)、设备:如终端、打印机等硬件设备。
上面介绍的I/O 资源,大概就是如上的4种类型。在简化下理解,I/O资源就是数据文件(你在电脑上看到的图片或文本等文件),这样就好理解吧?但实际上应该至少为上面的4种类型啊。
当你打开一个文件或创建一个网络连接时,操作系统会返回一个文件描述符,用于唯一区分下进程与文件之间的访问关系。
3、什么是“数据尚未准备好”?
“数据尚未准备好”是指当应用程序尝试从某个 I/O 资源(如文件、套接字等)读取或写入数据时,操作系统暂时无法提供所需的数据或无法立即处理写入请求。具体来说,这可以分为两种情况:
**(1)、读操作时数据尚未准备好:**当应用程序尝试从某个 I/O 资源读取数据时,操作系统还没有接收到任何数据,或者数据还没有完全到达。此时,操作系统无法立即提供数据,导致读操作暂时无法完成。
**(2)、写操作时数据尚未准备好:**当应用程序尝试向某个 I/O 资源写入数据时,操作系统可能暂时无法处理写入请求,例如网络连接中的缓冲区已满,或者磁盘写入速度较慢。此时,写操作也无法立即完成。
简单理解:
不考虑其他因素,简单理解为读和写实际都是耗时操作即可,这段读取和写入需要等待的时间就是数据尚未准备好的时间。
4、为什么会出现“数据尚未准备好”的情况?
出现这种情况的原因取决于 I/O 资源的类型和当前的状态:
(1)、网络套接字:在网络通信中,数据传输是有延迟的。当客户端发送数据到服务器时,服务器可能还没有接收到完整的数据包。同样,当服务器尝试向客户端发送数据时,网络可能暂时不可用,或者客户端的接收缓冲区已满,导致服务器无法立即发送数据。
(2)、文件 I/O:在读取文件时,如果文件位于磁盘上,操作系统需要从磁盘加载数据到内存中。如果文件较大或磁盘性能较差,数据加载可能需要一段时间,导致读操作无法立即完成。
(3)、管道或设备:在进程间通信或与硬件设备交互时,数据的产生和消费可能是异步的。例如,生产者进程可能还没有生成足够的数据(线程同步被阻塞等),或者消费者进程的缓冲区已满,导致读写操作无法立即完成。
二、5种I/O模型介绍
在计算机系统中,I/O 操作(如读取文件、网络通信等)通常是阻塞的,即当程序发起 I/O 请求(如:读取指定文件的内容)时,它会等待直到操作完成。
为了提高系统的并发性和性能,现代操作系统提供了多种 I/O 模型来处理 I/O 操作。
以下是五种常见的 I/O 模型:
1、阻塞IO(Blocking IO)
(1)、工作原理:
- 在阻塞 I/O 模型中,当应用程序发起 I/O 请求(如 read 或 write)时,进程会被阻塞,直到 I/O 操作完成。
- 如果数据尚未准备好(例如,网络连接中没有接收到数据),进程将一直处于等待状态,无法执行其他任务。
- 一旦 I/O 操作完成,操作系统会唤醒进程,继续执行后续代码。
即:当线程遇到阻塞任务(如InputStream.read())时,会一直卡在这里,直到处理完成后,才会继续向下执行代码
(2)、优点:
- 实现简单,编程模型直观,易于理解和使用。
(3)、缺点:
- 阻塞 I/O 会导致进程在等待 I/O 完成时无法做其他事情,浪费 CPU 资源,尤其是在高并发场景下,可能会导致大量进程处于等待状态,影响系统性能。
(4)、适用场景:
- 适用于单线程或低并发的应用程序,或者 I/O 操作非常少的场景。
代码示例:
如: InputStream.read(), OutputStream.write()或 serverSocket.accept()等方法都会阻塞,直到处理完成或接收到响应后才会放行
import java.io.*;
import java.net.*;public class BlockingIOServer {public static void main(String[] args) throws IOException {// 创建一个阻塞的 ServerSocket,监听 8080 端口ServerSocket serverSocket = new ServerSocket(8080);System.out.println("服务器启动,等待客户端连接...");while (true) {// 阻塞等待客户端连接Socket clientSocket = serverSocket.accept(); // serverSocket.accept()方法就是阻塞操作,当没有客户端连接,程序会一直卡在这里,直到有客户端连接后,才会放行并继续向下执行代码System.out.println("客户端已连接: " + clientSocket.getInetAddress());// 启动一个新线程处理每个客户端连接new Thread(new ClientHandler(clientSocket)).start();}}// 处理客户端连接的线程类static class ClientHandler implements Runnable {private final Socket clientSocket;public ClientHandler(Socket socket) {this.clientSocket = socket;}@Overridepublic void run() {try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {String inputLine;while ((inputLine = in.readLine()) != null) { // InputStream.read()方法都是阻塞的,只有数据完全返回后才会继续向下执行代码System.out.println("收到客户端消息: " + inputLine);out.println("服务器已收到: " + inputLine);}} catch (IOException e) {System.err.println("客户端连接异常: " + e.getMessage());} finally {try {clientSocket.close();} catch (IOException e) {System.err.println("关闭客户端连接时出错: " + e.getMessage());}}}}
}
2、非阻塞IO(Nonblocking IO)
(1)、工作原理:
- 在非阻塞 I/O 模型中,当应用程序发起 I/O 请求时,如果数据尚未准备好,操作系统不会阻塞进程,而是立即返回一个错误码(如
EAGAIN
或EWOULDBLOCK
)。 - 应用程序可以继续执行其他任务,直到数据准备好后再重新发起 I/O 请求。
- 这意味着应用程序需要不断地轮询(polling)检查 I/O 是否就绪,直到数据准备好为止。
理解:当读/写或连接请求发起后,会立即返回结果,不阻塞当前线程,继续向下执行代码。对于一些需要返回值的方法通常返回null或false等,不会造成主线程阻塞等待。但一般为了防止数据丢失,都会使用循环机制优化重试,如果不重试的话,那么任务可能就真的不做了。
(2)、优点:
- 进程不会被阻塞,可以在等待 I/O 的同时执行其他任务,避免了资源浪费。
(3)、缺点:
- 轮询机制会导致 CPU 频繁地检查 I/O 状态,增加了 CPU 开销,尤其是在 I/O 未准备好时,频繁的轮询会浪费大量的 CPU 时间。
- 编程复杂度增加,开发者需要手动管理 I/O 状态的检查和重试逻辑。
(4)、适用场景:
- 适用于对实时性要求较高的场景,或者 I/O 操作非常频繁但每次 I/O 量较小的场景。
代码示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;public class NonblockingIOServer {public static void main(String[] args) throws IOException {// 创建一个非阻塞的 ServerSocketChannel,监听 8080 端口ServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.bind(new InetSocketAddress(8080));serverSocket.configureBlocking(false); // 设置为非阻塞模式System.out.println("服务器启动,等待客户端连接...");while (true) {// 尝试接受客户端连接,不会阻塞SocketChannel clientSocket = serverSocket.accept(); // 因为上面设置了非阻塞模式,这里不会阻塞,如果没有连接产生,这里会立即返回null并向下执行代码if (clientSocket != null) {System.out.println("客户端已连接: " + clientSocket.getRemoteAddress());// 处理客户端连接handleClient(clientSocket);} else {// 如果没有客户端连接,继续循环,不会阻塞System.out.println("没有新的客户端连接...");}// 模拟其他任务try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}private static void handleClient(SocketChannel clientSocket) throws IOException {// 非阻塞读取数据clientSocket.configureBlocking(false);ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = clientSocket.read(buffer);if (bytesRead > 0) {buffer.flip(); // 切换到读模式byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data).trim();System.out.println("收到客户端消息: " + message);// 回复客户端ByteBuffer response = ByteBuffer.wrap(("服务器已收到: " + message).getBytes());clientSocket.write(response);}clientSocket.close();}
}
3、IO多路复用(IO Multiplexing)
(1)、工作原理:
- I/O 多路复用允许一个进程同时监视多个 I/O 文件描述符(如套接字),并等待其中任何一个文件描述符变为可读或可写。
- 当某个文件描述符就绪时,操作系统会通知应用程序,应用程序可以针对该文件描述符进行相应的 I/O 操作。
- 常见的 I/O 多路复用 API 包括 select、poll 和 epoll(Linux 特有)。
理解:一个线程同时监听多个客户端的连接,并对每一个连接的请求都能做出正确的回应。
(2)、优点:
- 单个线程可以同时处理多个 I/O 操作,避免了为每个 I/O 操作创建独立的线程或进程,减少了上下文切换的开销。
- 相比于非阻塞 I/O 的轮询机制,I/O 多路复用只需要在 I/O 就绪时才进行处理,避免了频繁的 CPU 检查,提高了效率。
(3)、缺点:
- 编程复杂度较高,开发者需要管理多个文件描述符的状态,并且需要处理 I/O 就绪的通知。
- select 和 poll 的性能随着文件描述符数量的增加而下降,因为它们需要遍历所有文件描述符来检查状态。epoll 在这方面做了优化,适合处理大量文件描述符。
(4)、适用场景:
- 适用于高并发场景,尤其是需要同时处理大量 I/O 操作的服务器应用程序,如 Web 服务器、数据库服务器等。
了解一下:
为了看懂之后的代码,这里需要先了解下几个概念。
Selector事件多路复用器:Selector 是 Java NIO 中的一个核心类,用于管理多个通道(Channel),并监听这些通道上的 I/O 事件(如连接、读、写等),当有事件发生时通知应用程序。它允许一个线程同时处理多个通道的 I/O 操作,而不需要为每个通道创建独立的线程。
ServerSocketChannel服务器端套接字通道:ServerSocketChannel是 Java NIO 中的一种特殊的通道,用于监听传入的客户端连接请求。它可以绑定到一个特定的端口,等待客户端发起连接请求。
Selector和ServerSocketChannel两者结合:
可以将 ServerSocketChannel 注册到 Selector 上,监听 OP_ACCEPT 事件。当有新的客户端连接请求到达时,Selector 会通知应用程序,应用程序可以调用 ServerSocketChannel.accept() 来接受这个连接,并将其转换为 SocketChannel。
SocketChannel和ServerSocketChannel的区别:
1、ServerSocketChannel:用于监听传入的客户端连接请求(OP_ACCEPT事件)。它绑定到一个特定的端口,等待客户端发起连接。当有新的客户端连接时,ServerSocketChannel会返回一个新的 SocketChannel,用于与该客户端进行数据通信。
2、SocketChannel:用于与客户端进行实际的数据传输。每个 SocketChannel 对应一个具体的客户端连接,负责读取和写入数据。你可以将 SocketChannel 注册到 Selector 上,监听 OP_READ或 OP_WRITE 事件,以便在数据准备好时进行处理。
具体使用流程如下:
1、创建一个 Selector实例。
2、创建一个 ServerSocketChannel,并将其绑定到某个端口。
3、将 ServerSocketChannel 注册到 Selector 上,监听 OP_ACCEPT事件。
4、使用 Selector.select() 阻塞线程,等待注册通道中发出 I/O 事件请求。
5、当有新的客户端连接请求时,Selector 会通知应用程序,应用程序可以通过 ServerSocketChannel.accept() 接受连接,并将新连接的 SocketChannel 注册到 Selector上,监听 OP_READ 或 OP_WRITE 事件。
6、继续处理其他 I/O 事件,直到服务器关闭。
如下图:
前半部分就描述了I/O多路复用的过程。
I/O多路复用程序就类似Selector的作用。
套接字S1,S2等就是发起请求的客户端。
S1,S2等客户端与I/O多路复用程序建立连接就相当于分别创建ServerSocketChannel且注册到Selector上。
建立完连接后,I/O多路复用程序会对每一个客户算S1,S2建立SocketChannel 通道,用于数据的传输。
I/O多路复用程序与客户端完成通道建立后,就会处于监听阻塞状态,等到管理的SocketChannel通道中存在I/O请求时,就会调用文件事务分派器去处理请求,并通过连接的通道将结果返回给客户端。
代码示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;public class NIOServer {public static void main(String[] args) throws IOException {// 创建一个 Selector,用于管理多个ServerSocketChannel Selector selector = Selector.open();// 创建一个 ServerSocketChannel,监听 8080 端口ServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.bind(new InetSocketAddress(8080));serverSocket.configureBlocking(false); // 设置为非阻塞模式// 注册 ServerSocketChannel 到 Selector,监听 OP_ACCEPT 事件serverSocket.register(selector, SelectionKey.OP_ACCEPT);// 可以再次创建第2,3个ServerSocketChannel并注册到Selector,这里占时忽略System.out.println("服务器启动,等待客户端连接...");while (true) {// 阻塞等待 I/O 事件selector.select(); // 这里会阻塞当前线程,直到管理的通道中有 I/O 事件就绪// 获取所有就绪的 SelectionKeySet<SelectionKey> selectedKeys = selector.selectedKeys(); // 获取I/O请求的具体信息,如:是哪一个通道的请求,是什么类型请求(连接还是读还是写)等,然后在根据请求做出回应Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove(); // 仅处理一个请求,避免重复处理if (key.isAcceptable()) {// 处理新的客户端连接ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel clientSocket = server.accept(); // 获取数据通道clientSocket.configureBlocking(false);// 注册 SocketChannel 到 Selector,监听 OP_READ 事件clientSocket.register(selector, SelectionKey.OP_READ);System.out.println("客户端已连接: " + clientSocket.getRemoteAddress());} else if (key.isReadable()) {// 处理客户端读事件SocketChannel clientSocket = (SocketChannel) key.channel();handleClient(clientSocket, key);}}}}private static void handleClient(SocketChannel clientSocket, SelectionKey key) throws IOException {ByteBuffer buffer = ByteBuffer.allocate(1024);// 读取客户端数据int bytesRead = clientSocket.read(buffer);if (bytesRead > 0) {buffer.flip(); // 切换到读模式byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data).trim();System.out.println("收到客户端消息: " + message);// 回复客户端ByteBuffer response = ByteBuffer.wrap(("服务器已收到: " + message).getBytes());clientSocket.write(response);// 继续监听该客户端的读事件key.interestOps(SelectionKey.OP_READ);} else if (bytesRead == -1) {// 客户端关闭连接System.out.println("客户端断开连接");clientSocket.close();key.cancel();}}
}
4、信号驱动IO(Signal Driven IO)
(1)、工作原理:
- 信号驱动 I/O 使用信号机制来通知应用程序 I/O 操作是否就绪。
- 应用程序通过调用 sigaction 函数注册一个信号处理函数(如
SIGIO
信号),当 I/O 操作就绪时,操作系统会发送信号给应用程序,触发信号处理函数。 - 信号处理函数可以在后台异步处理 I/O 操作,而主线程可以继续执行其他任务。
(2)、优点:
- 信号驱动 I/O 允许应用程序在 I/O 就绪时立即得到通知,避免了阻塞和轮询,提高了响应速度。
- 信号处理函数可以在后台异步执行,不会阻塞主线程。
(3)、缺点:
- 信号处理函数的执行环境较为特殊,不能进行复杂的操作(如分配内存、锁操作等),限制了其应用场景。
- 信号驱动 I/O 的实现较为复杂,调试和维护难度较大。
- 信号的传递是异步的,可能会导致信号丢失或合并,影响可靠性。
Java 标准库中并没有直接支持信号驱动 I/O 的 API,一般在java中不推荐使用。
5、异步IO(Asynchronous IO)
(1)、工作原理:
- 异步 I/O 是一种真正的异步 I/O 模型,应用程序发起 I/O 请求后,操作系统会在后台异步执行 I/O 操作,而应用程序可以继续执行其他任务。
- 当 I/O 操作完成后,操作系统会通知应用程序,应用程序可以选择在回调函数中处理结果,或者通过轮询的方式检查 I/O 是否完成。
- 异步 I/O 的典型实现包括 POSIX AIO(Linux/Unix)和 Windows 的 I/O Completion Port(IOCP)。
理解:当发起这种IO请求后,会在后台创建新的线程去执行任务,主线程不会阻塞。如果是需要返回值的场景调用Future.get()方法时会阻塞。(任务会在后台完成)
(2)、优点:
- 应用程序可以在发起 I/O 请求后立即返回,继续执行其他任务,不会被阻塞,真正实现了 I/O 操作的异步化。
- 异步 I/O 可以显著提高系统的并发性和响应速度,尤其是在高并发场景下,能够有效减少 I/O 等待时间。
(3)、缺点:
- 异步 I/O 的实现较为复杂,编程模型不同于传统的同步 I/O,开发者需要处理异步回调和状态管理。
- 不同操作系统对异步 I/O 的支持不同,跨平台兼容性较差。
- 异步 I/O 的性能并不总是优于 I/O 多路复用,具体取决于操作系统的实现和应用场景。
(4)、适用场景:
- 适用于高并发、高性能的网络应用,尤其是需要处理大量 I/O 操作的场景,如 Web 服务器、数据库服务器等。
6、5种I/O模型对比
(1)、阻塞 I/O:该任务会阻塞线程执行,直到任务完成放行,适合低并发场景。
(2)、非阻塞 I/O:该任务会立即返回结果,不会阻塞主线程。如果返回的是异常结果,如:false或null时,表示该任务并没有完成,主线程如果不采取合适机制去处理的话,就代表放弃了这个请求任务。一般可以轮询检查 I/O 状态,通过重试的机制去完成这样的请求处理,但这样可能会浪费 CPU 资源。
(3)、I/O 多路复用:通过 Selector监听多个文件描述符的 I/O 事件,避免了轮询,适合高并发场景。
(4)、信号驱动 I/O:通过信号通知 I/O 事件的发生,适合对实时性要求较高的场景,但在 Java 中不常用。
(5)、异步 I/O:真正的异步 I/O 模型,允许应用程序在发起 I/O 请求后立即返回,任务会在后台创建新的线程去执行,适合高并发、高性能的应用场景。
三、Redis实现I/O多路复用的原理
Redis 实现了不同的 I/O 多路复用机制,具体选择取决于操作系统的支持。
Redis 内部实现了一个抽象层,称为 ae(async event)库,它封装了不同操作系统的 I/O 多路复用 API,使得 Redis 可以在不同的平台上保持一致的行为。
1、支持的主要 I/O 多路复用机制
(1)、epoll机制(Linux 特有)
- epoll 是 Linux 提供的一种高效的 I/O 多路复用机制,特别适合处理大量文件描述符。epoll的优势在于它只监听就绪的文件描述符,而不是像 select和poll 那样需要遍历所有文件描述符,因此性能更高。
- Redis 在 Linux 系统上默认使用epoll,因为它提供了更好的性能和扩展性。
(2)、kqueue机制(FreeBSD、macOS)
- kqueue 是 FreeBSD 和 macOS 提供的一种 I/O 多路复用机制,类似于 epoll,也支持高效的事件通知和文件描述符管理。
- Redis 在 FreeBSD 和 macOS 系统上使用 kqueue 来处理 I/O 事件。
(3)、select 和 poll
- select 和 poll 是较早的 I/O 多路复用机制,适用于大多数 Unix 系统。它们的性能随着文件描述符数量的增加而下降,因为它们需要遍历所有文件描述符来检查状态。
- Redis 在不支持 epoll 或 kqueue 的系统上会退回到使用 select 或 poll,但这通常不是首选,因为它们的性能不如 epoll 和 kqueue。
2、I/O多路复用流程
(1)、初始化事件循环:Redis 启动时,会初始化一个事件循环(event loop),该循环负责监听所有客户端连接的 I/O 事件(如读、写、关闭等)。
(2)、注册文件描述符:每当有新的客户端连接时,Redis 会将该连接的文件描述符注册到 I/O 多路复用器(如 epoll 或 kqueue)中,以便监听该连接的 I/O 事件。
(3)、等待事件:事件循环会进入等待状态,等待 I/O 多路复用器通知有文件描述符就绪。在这个过程中,Redis 不会被阻塞,可以继续处理其他任务。
(4)、处理事件:当某个文件描述符就绪时,I/O 多路复用器会通知 Redis,Redis 会根据事件类型(如读、写、关闭等)进行相应的处理。例如,如果是一个读事件,Redis 会从该连接中读取数据;如果是一个写事件,Redis 会将数据写入该连接。
(5)、继续循环:处理完当前事件后,事件循环会继续等待下一个事件,重复上述过程。
3、为什么Redis选择I/O多路复用?
(1)、高并发处理能力:Redis 采用 I/O 多路复用可以同时处理大量的客户端连接,而不需要为每个连接创建独立的线程或进程。这大大减少了上下文切换的开销,提高了系统的并发性和性能。
(2)、高效利用 CPU:I/O 多路复用避免了阻塞和轮询,只有在 I/O 就绪时才会进行处理,因此可以更高效地利用 CPU 资源。
(3)、简化编程模型:相比多线程或进程模型,I/O 多路复用的编程模型更加简单,开发者不需要处理复杂的线程同步和锁问题。
(4)、跨平台支持:Redis 通过 ae 库封装了不同操作系统的 I/O 多路复用 API,确保了 Redis 在不同平台上的一致行为。
学海无涯苦作舟!!!
相关文章:
Redis篇-2--原理篇1--I/O多路复用机制(5种I/O模型,I/O多路复用)
I/O多路复用机制: Redis 是通过I/O多路复用机制来管理大量客户端连接。这使得redis可以实现通过单线程来处理多个客户端连接的请求,避免了为每个客户端创建独立的线程,从而减少了上下文切换的开销,提高了系统的并发性和性能。 理解…...
Knowledge Graph Studio:让知识图谱构建更简单、更智能
一、前言 上周和研究院的同事讨论 2025 年大模型产品规划时,让我产生了一些疑惑和不解,因为从大家交流的规划方向来看,更多的还是集中在Prompt提示词工程(包括提示词的管理、测试、评估、调优)这一块规划的确实挺细&a…...
vue 中实现音视频播放进度条(可拖拽,满足常见开发需求)
由于开发需要,作者封装了一个音视频播放进度条的组件,支持 vue2 及 vue3 ,有需要的朋友后台私信作者获取组件源码哦(工作日每天都在线),下面是对该款组件的介绍。 组件默认样式👇(组…...
[免费]SpringBoot+Vue企业OA自动化办公管理系统【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的SpringBootVue企业OA自动化办公管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue企业OA自动化办公管理系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 随着信息技术在管理上越来越深入…...
笔记:在WPF中BitmapSource都有哪些派生类,他们主要功能,使用方法,使用场景
一、目的:在WPF中BitmapSource都有哪些派生类,他们主要功能,使用方法,使用场景 BitmapSource 是 WPF 中图像处理的基类,提供了许多派生类来处理不同类型的图像源。以下是一些常见的 BitmapSource 派生类、它们的主要功…...
JAVA基础学习笔记_多线程
文章目录 多线程并发和并行多线程的实现方式Thread类实现Runnable接口方式实现callable接口和Future接口实现 常用的成员方法线程的生命周期线程的安全问题同步代码块同步方法lock锁死锁 生产者和消费者(等待唤醒机制)阻塞队列实现等待唤醒机制线程的6种状态线程池自定义线程池…...
什么是自动化办公
自动化办公是指使用技术工具或软件,通过预设流程或脚本,自动执行日常办公任务,从而提升效率、减少错误、节约时间的办公模式。它适用于需要重复性、规则明确的工作流程,让员工将精力集中在更具创造性和战略性的工作上。 自动化办公…...
数据库系统
数据库模式 3个阶段以及各自的产物: 1、需求分析(数据流图、数据字典、需求说明书); 2、概念结构设计(ER模型); 3、逻辑结构设计(关系模式)); 关…...
文件系统--底层架构(图文详解)
一、文件系统的底层存储与寻址 当我们谈到文件系统的底层结构时,最关键的问题是:文件的数据与元数据(属性)如何存储在磁盘上,以及系统是如何定位这些数据的?在谈及文件系统之前,我们要先对储存…...
【OCR】——端到端文字识别GOT-OCR2.0不香嘛?
代码:https://github.com/Ucas-HaoranWei/GOT-OCR2.0?tabreadme-ov-file 在线demo:https://huggingface.co/spaces/stepfun-ai/GOT_official_online_demo 0.前言 最早做ocr的时候,就在想如何能做一个端到端的模型,就不用先检测再…...
SkyWalking 和 ELK 链路追踪实战
一、背景 最近在给项目搭建日志平台的时候,采用的方案是 SkyWalking ELK 日志平台,但发现 ELK 日志平台中的日志没有 Trace ID,导致无法追踪代码报错的整体链路。 空哥提示:Trace ID 是分布式追踪中用来唯一标识一个服务请求或事…...
ETCD的封装和测试
etcd是存储键值数据的服务器 客户端通过长连接watch实时更新数据 场景: 当主机A给服务器存储 name: 小王 主机B从服务器中查name ,得到name-小王 当主机A更改name 小李 服务器实时通知主机B name 已经被更改成小李了。 应用:服务注册与发…...
基于大数据爬+数据可视化的民族服饰数据分析系统设计和实现(源码+论文+部署讲解等)
博主介绍:CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围…...
torch.optim.lr_scheduler.ReduceLROnPlateau
torch.optim.lr_scheduler.ReduceLROnPlateau 是 PyTorch 中的一种学习率调度器,主要用于在模型训练过程中根据某些指标(如验证损失)动态调整学习率。它是一种基于性能指标动态调整学习率的策略,而不是预定义的固定时间调整。 主要…...
Linux 搭建ftp服务
FTP是什么? FTP(文件传输协议,File Transfer Protocol)是一种用于在计算机之间传输文件的网络协议。它基于客户端-服务器模型,允许用户从远程服务器上传、下载和管理文件。 FTP的主要作用 文件传输:FTP最基…...
阳光电源嵌入式面试题及参考答案
讲一讲声明变量的时候应该注意哪些内容。 在声明变量时,首先要考虑变量的类型。不同的数据类型有不同的用途和占用的存储空间大小。例如,基本数据类型如整型(int)通常占用 4 个字节,用来存储整数;而浮点型(float)用于存储带有小数部分的数字,占用 4 个字节,双精度浮点…...
PS的功能学习(形状、文字、图层)
关于图层 如果是在一个已经有其他图层的文档界面下,拉一张新图进来,就会自动转换成智能对象 注意,放大之后再栅格化,是会根据原本的防矢量图规则放大之后,再变回像素图层,这个变回来的像素图层是“在原像素…...
项目实例_FashionMNIST_CNN
前言 提醒: 文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。 其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展…...
Ubuntu 安装 web 服务器
安装 apach sudo apt install apache2 -y 查看 apach2 版本号 apache2 -v 检查是否启动服务器 sudo service apache2 status 检查可用的 ufw 防火墙应用程序配置 sudo ufw app list 关闭防火墙 sudo ufw disable 更改允许通过端口流量 sudo ufw allow Apache Full 开启…...
burp的编解码,日志,比较器
声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...
2.1、模版语法
2.1.1、插值语法 1、代码示例 <body><!-- 准备容器 --><div id"app"><!-- 在data中声明的 --><!--1、 data中声明的变量 --><h1>{{msg}}</h1><h1>{{sayHello()}}</h1><!-- 不在data中的变量不可以 -->…...
最小二乘法拟合出二阶响应面近似模型
背景:根据样本试验数据拟合出二阶响应面近似模型(正交二次型),并使用决定系数R和调整的决定系数R_adj来判断二阶响应面模型的拟合精度。 1、样本数据(来源:硕士论文《航空发动机用W形金属密封环密封性能分析…...
【汽车】-- 常见的汽车悬挂系统
汽车悬挂系统是车辆的重要组成部分,其主要功能是连接车轮和车身,减缓路面颠簸对车身的影响,提高行驶的平顺性、舒适性和操控性。以下是常见的汽车悬挂系统类型及其特点: 1. 独立悬挂系统 每个车轮可以独立上下运动,不…...
VMware Workstation Pro 17 下载 以及 安装 Ubuntu 20.04.6 Ubuntu 启用 root 登录
1、个人免费版本 VMware Workstation Pro 17 下载链接怎么找?直接咕咕 VMware 找到如下链接。链接如下:Workstation 和 Fusion 对个人使用完全免费,企业许可转向订阅 - VMware 中文博客 点进去链接之后你会看到如下,注意安装之后仍…...
记录ubuntu22.04重启以后无法获取IP地址的问题处理方案
现象描述:我的虚拟机网络设置为桥接模式,输入ifconfig只显示127.0.0.1,不能连上外网。,且无法上网,用ifconfig只有如下显示: 1、sudo -i切换为root用户 2、输入dhclient -v 再输入ifconfig就可以看到多了…...
linux 删除系统特殊的的用户帐号
禁止所有默认的被操作系统本身启动的且不需要的帐号,当你第一次装上系统时就应该做此检查,Linux提供了各种帐号,你可能不需要,如果你不需要这个帐号,就移走它,你有的帐号越多,就越容易受到攻击。 1.为删除你系统上的用户,用下面的…...
core Webapi jwt 认证
core cookie 验证 Web API Jwt 》》》》用户信息 namespace WebAPI001.Coms {public class Account{public string UserName { get; set; }public string UserPassword { get; set; }public string UserRole { get; set; }} }》》》获取jwt类 using Microsoft.AspNetCore.Mvc…...
【Redis】Redis基础——Redis的安装及启动
一、初识Redis 1. 认识NoSQL 数据结构:对于SQL来说,表是有结构的,如字段约束、字段存储大小等。 关联性:SQL 的关联性体现在两张表之间可以通过外键,将两张表的数据关联查询出完整的数据。 查询方式: 2.…...
Oracle Recovery Tools工具一键解决ORA-00376 ORA-01110故障(文件offline)---惜分飞
客户在win上面迁移数据文件,由于原库非归档,结果导致有两个文件scn不一致,无法打开库,结果他们选择offline文件,然后打开数据库 Wed Dec 04 14:06:04 2024 alter database open Errors in file d:\app\administrator\diag\rdbms\orcl\orcl\trace\orcl_ora_6056.trc: ORA-01113:…...
常用环境部署(二十四)——Docker部署开源物联网平台Thingsboard
1、Docker和Docker-compose安装 参考网址如下: CENTOS8.0安装DOCKER&DOCKER-COMPOSE以及常见报错解决_centos8安装docker-compose-CSDN博客 2、 Thingsboard安装 (1)在/home目录下创建docker-compose.yml文件 vim /home/docker-com…...
自己怎样建网站做微商/河北百度seo关键词
在helm中安装nginx,前面试了很多次由于仓库失效等各种原因都没有安装成功,但是这个名字(testnginx)可能是已经存在了,所以出现下面的错误时换个名字即可。 [rootk8smaster bin]# helm install testnginx apphub/nginx Error: INSTALLATION F…...
seo企业网站优化/长沙优化网站厂家
1.某公司取得贷款2000万元,年利率12%,要求5年内每年年末等额偿还,则每年的偿还额大约为()。 A.555万元 B.545万元 C.520万元 D.400万元 2.资本资产定价模型把任何一种风险资产的价格都归纳为以下几个因素(…...
北京昌平网站建设/营销网站建设价格
好像这题二分也可以做…… 话说这年头写堆都不用Heapify 函数的? Program P2010; constmaxc100000;maxn19999;maxaid100000;maxf2000000000; typenoderecorda,b:longint;end;heaprecordsize:longint;d:array[1..maxc] of longint;end; varn,c,f,i,j:longint;a:arra…...
江西seo网站排名优化/免费浏览网站推广
sqlplus据说是不区分大小写的,但是我做了个实验感觉还是区分大小写啊?1)大写SQL> select count(*) from tab where tname like %BIN%;COUNT(*)----------370Elapsed: 00:00:00.07SQL> 2)小写SQL> select count(*) from tab where tname like %…...
绍兴做网站选哪家/建立网站的软件
shell字符串的截取的问题: 一、Linux shell 截取字符变量的前8位,有方法如下: 1.expr substr “$a” 1 8 2.echo $a|awk ‘{print substr(,1,8)}’ 3.echo $a|cut -c1-8 4.expr $a : ‘\(.\\).*’ 5.echo $a|dd bs1 count8 2>/dev/null 二…...
曹县住房和城乡建设局网站/百度学术论文查重官网
作者:Cary G.Gray and David R. Cheriton 1989 译者:phylipsbmy 2011-5-7 出处:http://duanple.blog.163.com/blog/static/70971767201141111440789/ [ 序:所谓租约(leases),其实就是一个合同,即服务端给予…...