当前位置: 首页 > news >正文

【redis】redis的多线程和IO多路复用

【redis】redis的多线程和IO多路复用

  • 【一】前言
  • 【二】Redis单线程和多线程问题的背景
    • 【1】Redis的单线程
    • 【2】Redis为什么选择单线程?
    • 【3】Redis为什么开始利用多核?
    • 【4】Redis当前的性能瓶颈
    • 【5】Redis的主线程如何和IO线程协同
  • 【三】IO多路复用的理解
    • 【1】什么是IO多路复用
    • 【2】深入理解Redis的IO多路复用
      • (1)I/O多路复用机制
      • (2)为什么从bio,nio到io多路复用
      • (3)如何解决服务器主线程阻塞的问题?
      • (4)如何解决多线程资源浪费的问题
      • (5)如何解决NIO模型cpu不友好的问题?
      • (6)IO多路复用的具体实现
        • 1-》select方法
        • 2-》poll方法
        • 3-》epoll方法
        • 4-》三个方法的对比

【一】前言

Redis的单线程主要处理键值对读写,但其他功能如持久化、异步删除由额外线程执行。Redis选择单线程避免上下文切换和线程竞争,利用多路复用技术提高处理多个客户端请求的能力。Redis4.0引入异步删除以减少主线程阻塞。Redis6/7开始使用多IO线程处理网络请求,主线程仍单线程处理命令操作以保证原子性。IO多路复用(如epoll)配合主线程和IO线程协同,提升了Redis的性能。

【二】Redis单线程和多线程问题的背景

Redis版本迭代历史
在这里插入图片描述

【1】Redis的单线程

(1)主要是指Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取(socket读)、解析、执行、内容返回(socket写)等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。这也是Redis对外提供键值存储服务的主要流程。

(2)但Redis的其他功能,比如持久化RDB、AOF、异步删除、集群数据同步等等,其实是由额外的线程执行的。

(3)Redis命令工作线程是单线程的,但是整个Redis来说是多线程的
在这里插入图片描述

【2】Redis为什么选择单线程?

(1)基于内存操作:Redis的所有数据都存在内存中,因此所有的运算都是内存级别的,性能比较高;
(2)数据结构简单:Rdis的数据结构是专门设计的,而这些简单的数据结构的查找和操作的时间大部分复杂度都是O(1),因此性能比较高
(3)多路复用和非阻塞I/O:Redis使用I/O多路复用功能来监听多个socket连接客户端,这样就可以使用一个线程连接来处理多个请求,减少线程切换带来的开销,避免了IO阻塞操作
(4)避免上下文功换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的消耗

Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库,由C语言编写,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。

这个数据不比采用单进程多线程的同样基于内存的 KV 数据库 Memcached 差!
在这里插入图片描述
上图是Redis官方的测试数据,横轴表示连接数,纵轴表示QPS。

如果Redis采用的是多线程,那么CPU会在多个线程之间来回切换,在如此高的QPS下,多个线程的切换时特别频繁的,十分消耗CPU。

假设我们有三个连接同时执行下面的操作:

incr count

由于Redis使用单线程来处理命令的,所以一条命令来了之后不会立即执行,而是将命令加到一个队列中,然后逐个被执行,这样就能保证在不加锁的情况下保证不会产生并发问题。

如果Redis采用多线程执行命令,那么对于三个连接同时执行incr操作的时候,就需要添加锁来保证线程的安全性了,会大大降低执行效率。

Redis采用非阻塞IO,使用epoll作为IO多路复用技术的实现,再加上Redis自身的树碱处理模型将epoll中的连接、读写、关闭都转为事件,不在网络IO上浪费过多的事件。

当然了,单线程机制也不是万能的,也会存在一个的问题:对于每个命令的执行事件是有要求的。如果某个命令执行事件过长,会导致其他命令被阻塞,对于Redis这种高性能的服务来说是致命的。

所以即使Redis性能很高,我们也要合理的去使用Redis,不合理的使用也会导致很多问题。

【3】Redis为什么开始利用多核?

使用多核的必要性

(1)Redis4.0引入异步删除,在删除大key时,单线程会出现阻塞(类似synchronized锁)

unlink key
flushdb async
flushall async

(2)利用惰性删除避免阻塞。因为Redis是单个主线程处理,而lazy free的本质就是把某些cost(主要时间复制度,占用主线程cpu时间片)较高删除操作,从redis主线程剥离让bio子线程来处理,极大地减少主线阻塞时间。从而减少删除导致性能和稳定性问题。

【4】Redis当前的性能瓶颈

(1)redis主要的性能瓶颈是内存或者网络IO,而不太可能是cpu
(2)虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF重写)。但是从网络IO处理到实际的读写命令处理,都是由单个线程完成的。
(3)单个主线程处理网络请求的速度跟不上底层网络硬件的速度,为了应对这个问题采用多个IO线程来处理网络请求,提高网络请求处理的并行度,Rdis6/7就是采用的这种方法。

但是,Redis的多IO线程只是用来处理网络请求的,对于读写操作命令Redis仍然使用单线程来处理。这是因为Redis处理请求时,网络处理经常是瓶颈,通过多个IO线程并行处理网络操作,可以提升实例的整体处理性能。而继续使用单线程执行命令操作,就不用为了保证Lua脚本、事务的原子性,额外开发多线程互斥加锁机制,这样一来Redis线程模型实现就简单了

【5】Redis的主线程如何和IO线程协同

既然瓶颈是网络IO,Redis的主线程如何和IO线程协同?

(1)服务端和客户端建立Socket连接,并分配处理线程。
首先,主线程负责接收建立连接请求。当有客户端请求和实例建立Socket连接时,主线程会创健和客户的连接,并把Socket放入全局等待队列中。紧接看主线程通过轮询方法把Socket连接分配给IO线程
(2)IO线程读取并解折请求。
主线程一旦把Socket分配给IO线程,就会进入阻塞状态,等待IO线程完成客户端请求读取和解析。因为有多个IO线程在并行处理,所似这个过程很快完成
(3)主线程执行请求操作。
等IO线程解析完请求,主线程以单线程的方式执行这些命令操作。
(4)IO线程回写Socket和主当主线程清空全局队列。
主线程执行完请求操作后,会把需要返回的结果写入缓冲区,然后主线程会阻塞等待IO线程,把这些结果回写到Socket中(多个线程在并发执行),并返回给客户端。等到lO线程回写Socket完毕,主线程会清空全局队列,等待客户端的后续请求。

在这里插入图片描述

【三】IO多路复用的理解

【1】什么是IO多路复用

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

在这里插入图片描述
IO多路复用的解释,即使用一个或一组线程处理多个tcp连接
(1)IO,指代网络IO,需要进行模态转换的读写操作
(2)多路,多个客户端连接(socket)
(3)复用,复用一个或多个线程

一个服务端进程可以同时处理多个socket,IO多路复用模型包括select,poll,epoll

在单个线程通过记录跟踪每一个Sockek(I/O流)的状态来同时管理多个I/O流。一个服务端进程可以同时处理多个套接字描述符。目的是尽量多的提高服务器的吞吐能力。

将用户socket对应的文件描述符(FileDescriptor)注册进epoll,然后epoll监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。这样整个过程只在调用select.、poll、epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的reactor模式。

因此IO多路复用+epoll也是Redis速度快的根本保证。redis的多线程即
(1)单个工作线程保证线程安全和高性能
(2)多个IO线程解决网络IO的问题
在这里插入图片描述

【2】深入理解Redis的IO多路复用

(1)I/O多路复用机制

Redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
在这里插入图片描述

Rdis是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以I/O操作在一般情况下往往不能直接返回,这会导致某一文件的I/O阻塞导致整个进程无法对其它客户提供服务,而I/O多路复用就是为了解决这个问题而出现

所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要select、pol川、epol来配合。多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

Redis服务采用Reactor的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符

Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:
(1)多个套接字
(2)IO多路复用程序
(3)文件事件分派器
(4)事件处理器。
因为文件事件分派器队列的消费是单线程的,所汉Redis才叫单线程模型

(2)为什么从bio,nio到io多路复用

在bio场景下,当client1请求并和server建立连接后,server在accept和read处发生阻塞,此时新的client2建立连接的请求无法被server处理(处于阻塞中)

注意:这里的应用程序指代的是server端

(3)如何解决服务器主线程阻塞的问题?

考虑使用子线程来处理新的客户端连接和请求。在连接socket后创建线程来处理read操作

client代码

Socket socket = new Scoket("127.0.0.1",6379);
OutputStream outputStream == socket.getOUtputStream();
// 获取标准输入到msg
outputStream.write(msg.getBytes());

server代码

ServerSocket serverSocket = new ServerSocket(6379);
while(true){Socket socket = serverSocket.accept(); // 阻塞等待连接new Thread(() -> {try{InputStream inputStream = socket.getInputStream();// 从input中获取输入,阻塞在读请求socket.close();}},Thread.currentThread().getName()).start();
}

(4)如何解决多线程资源浪费的问题

多线程模型要求每到来一个请求,就切换到内核态创建对应的的进程。新的进程和上下文切换都会产生性能的损耗

(1)池化技术,但是实际的线程数量未知
(2)NIO,使read方法不阻塞

NIO的特点是使用轮询替代阻塞
在这里插入图片描述
在客户端,如果kernal中的数据没有准备好,则会返回error

在NIO中一切都是非阻塞的
(1)accept方法非阻塞,如果没有客户端连接则返回无连接标识
(2)read方法非阻塞,如果read读不到数据返回空间中标识,有数据则只阻塞read读取数据的时间
(3)nio中服务端只有一个线程,对socker数组进行遍历

客户端代码和bio下相同

Socket socket = new Scoket("127.0.0.1",6379);
OutputStream outputStream == socket.getOUtputStream();
// 获取标准输入到msg
outputStream.write(msg.getBytes());

服务端代码
(1)通过ServerSocketChannel的configureBlocking(false)方法将获得连接设置为非阻塞的。此时若没有连接,accept会返回null
(2)通过SocketChannel的configureBlocking(false)方法将从通道中读取数据设置为非阻塞的。若此时通道中没有数据可读,read会返回-1

static ArrayList<SocketChannel> socketList = new ArrayList<>(); // 容纳socket数组
static ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 输入缓冲ServerSocketChannel serverSocket = new ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(127.0.0.1,6379));
serverSocket.configureBlocking(false); // jdk1.4开始兼容阻塞模式,此处设置为非阻塞while(true){for (SocketChannel element : socketList){int read  = element.read(byteBuffer); //不会阻塞if(read > 0){byteBuffer.flip();byte[] bytes = new byte[read];byteBuffer.get(byte);byteBuffer.clear();}}SocketChannel socketChannel = serverSocket.accpet(); // 不会阻塞if(socketChannel != null){sockerChannel.configureBlocking(false);socketList.add(socketChannel) // 连接成功}
}

(5)如何解决NIO模型cpu不友好的问题?

NIO模型存在的问题
(1)socketlist遍历存在性能瓶颈(空socket仍旧会轮询),cpu不友好
(2)对socket状态的判断仍旧需要切换模态(查询socket仍旧是通过read syscall),开销较大、
(3)轮询会不断询问内核,占用了大量cpu时间,系统资源利用率较低

如果通过linux内核完成上述工作(遍历socket),不需要模态切换,内核本身是非阻塞的。

多路复用就是使用一个信道传输多个信号,多个网络连接使用一个线程,多个socket使用一个通道是在内核和驱动层实现的

IO多路复用模型
在这里插入图片描述

基于I/O复用模型:多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

Reactor模式,是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor模式也叫Dispatcher模式。即I/O多了复用统一监听事件,收到事件后分发(Dispatch给某进程),是编写高性能网络服务器的必备技术。Reactor模式中有2个关键组成:

(1)Reactor,Reactor在一个单独的线程中运行,负贵监听和分发事件,分发给适当的处理程序来对IO事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人
(2)Handlers,处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际办理人。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作
(3)
在这里插入图片描述

(6)IO多路复用的具体实现

1-》select方法

在这里插入图片描述
select函数监视的文件描述符分3类,分别是readfds、writefds和exceptfds,将用户传入的数组拷贝到内核空间

(1)调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、或者有except)或超时(timeout指定等待时间,如果立即返回设为null即可),函数返回
(2)当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。

分析select函数的执行流程:
(1)select是一个阻塞函数,当没有数据时,会一直阻塞在select调用
(2)当有数据时会将rset中对应的位置为1
(3)selecti函数返回,不再阻塞
(4)遍历文件描述符数组,判断哪个fd被置位
(5)读取数据,然后处理

下面的示例代码
(1)实际是将之前java示例的代码从用户态搬到内核态运行
(2)将fd拷贝到内核态,避免模态切换

sockfd = socket(AF_INET,SOCK_STREAM,0);
memse(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(2000);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd,(struct sockaddr*)&addr, sizeof(addr));
listen (sockfd, 5);
for(i=0;i<5;i+){// 模拟5个客户端连接memset(&client,0,sizeof (client)); addrlen = sizeof(client);fds[i] = accept(sockfd, (struct sockaddr*)&client, &addrlen);if(fds[i]>max)max = fds[i]; //找到一个最大的文件描述符,在下面select中限制范围
}while(1){FD_ZERO(&rset); // 重置rset为0for(i=0;i<5;i++){FD_SET(fds[i],&rset); // &rset是个bitmap.如果5个文件描述符分别是1,2,4,5,7,那么这个bitmap为01101101}puts("round again");  select(max+1,&rset,NULL,NULL,NULL); // select是一个系统调用,它会堵塞直到有数据发送到socket,select会把&rset相应的位置置位,但并不会返回哪个socket有数据for(i=0;i<5;i++){// 用户态只要遍历&rst,看哪一位被置位了,不需要每次调用系统调用来判断了,效率有很大提升,遍历到被置位的文件描述符就进行读取if (FD_ISSET(fds[i],&rset)){memset(buffer, 0, MAXBUF);read(fds[i], buffer, MAXBUF);puts(buffer);}
}

select函数的缺点
(1)bitmap默认大小为1024(代码实现较早1983年),虽然可以调整但还是有限度的
(2)rset每次循环都必须重新置位为0,不可重复使用
(3)尽管将rset从用户态拷贝到内核态由内核态判断是否有数据,但是还是有拷贝的开销
(4)当有数据时select就会返回,但是selecti函数并不知道哪个文件描述符有数据了(仅仅置位)。后面还需要再次对文件描述符数组进行遍历,效率比较低

2-》poll方法

函数原型

int poll(struct pollfd *fds,nfds_t nfds,int timeout);
struct pollfdint fd; //file descriptor short events; // 读或者写事件short revents; // 文件描述符有事件则置1 
};

poll的执行流程:
(1)将五个fd从用户态拷贝到内核态
(2)poll为阻塞方法,执行poll方法,如果有数据会将fd对应的revents置为POLLIN
(3)poll方法返回
(4)循环遍历,查找哪个fd被置位为POLLIN了
(5)将revents重置为0便于复用
(6)对置位的fd进行读取和处理

解决的问题
(1)不在使用bitmap,消除了client的大小限制(1024)
(2)rset不可复用的问题(每次置0)

代码实现

for(1=0;1<5;1++) //模拟5个客户端连接
{memset(&client,0,sizeof (client));addrlen = sizeof(client);pollfds[i].fd = accept(sockfd,(struct sockaddr)&client,&addr);pollfds[i].events = POLLIN; //这5个socket只读事件
}c
sleep(1);
while(1){puts("round again");poll(po11fds,5,50000); // poll中传入pollfds数组,交给内核判断是否有事件发生,如果哪个发生事件则revents置1for(1=0; 1<5; 1++){ //遍历数组,找到哪个pollfd有事件发生if (pollfds[i].revents & POLLIN){pollfds[i].revents=O; // 找到后revents置0memset(buffer,0,MAXBUF);read(pollfds[i].fd,buffer,MAXBUF); //读取数据puts(buffer);}
}

缺点
(1)本质上还是select方法
(2)拷贝fd到内核态仍旧存在开销
(3)仍旧需要遍历socket(O(n)复杂度)

3-》epoll方法

函数原型

// 创建一个epoll的句柄,size表示内核监听的数目。并非最大限制,而是内核初始分配数据结构的建议值
int epoll_create(int size); // 向内核添加,修改或删除要监控的fd
int  epoll_ctl(int epfd, // epfd是epoll_create的返回值1nt op, // 表示op操作,用三个宏来表示:添加EPOLL.CTL_ADD,除EPOLL.CTL_DEL,修改EPOLL.CTL_MODint fd, //fd:是需要监听的fd(文件描述符)struct epoll_event *event // 告诉内核要监听什么事
); 
struct epoll_event// events可以是以下几个宏的熊合:// EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭);// EPOLLOUT:表示对应的文件描述符可以写;__uint32_t events;epoll_data_t data; // User data variable 
}// 类似select调用
int epoll_wait(int epfd,struct epoll event *events,int maxevents, // 最多返回的事件数int timeout
);

具体实现
(1)socket生命周期只会从用户态到内核态拷贝一次
(2)使用event通知级值,每次socket有数据会主动通知内核加入就绪列表,不需要遍历所有socket

struct epoll_event events[5];
int epfd epoll_create(10); // epoll_create在内核开辟一块内存空间,用来存放epolle中fd的数据结构for(i=0;i<5;i+) { //模拟S个客户端连接static struct epoll_event ev; // epoll中fd的数据结构和polI中的差不多,只是没有了reventsmemset(&client,0,sizeof (client));addrlen sizeof(client); ev.data.fd = accept(sockfd,(struct sockaddr&client,&addrlen);ev.events = EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,ev.data.fd,&ev); // epoll ctl把每-个socket的fd数据结构放到epoll_create创建的内存空间中
}while(1){puts("round again");nfds = epoll_wait(epfd,events,5,1O0O0); // epoll_wait阻塞,只有当epoll_create中创建的内存空间中的fd有事件发生,才会把这些fd放入就绪链表中,返回就绪fd的个数for(i=0;i<nfds;i++){ //遍历就绪链表,读取数据memset(buffer,0,MAXBUF);read(events[i].data.fd, buffer, MAXBUF);puts(buffer);}
}
4-》三个方法的对比

不同操作系统选择的方式不同,windows没有epoll默认为select
在这里插入图片描述

相关文章:

【redis】redis的多线程和IO多路复用

【redis】redis的多线程和IO多路复用 【一】前言【二】Redis单线程和多线程问题的背景【1】Redis的单线程【2】Redis为什么选择单线程&#xff1f;【3】Redis为什么开始利用多核&#xff1f;【4】Redis当前的性能瓶颈【5】Redis的主线程如何和IO线程协同 【三】IO多路复用的理解…...

webstorm 编辑器配置及配置迁移

1.下载地址 WebStorm&#xff1a;JetBrains 出品的 JavaScript 和 TypeScript IDE 其他版本下载地址 2.安装 点击下一步安装&#xff0c;可根据需要是否删除已有版本 注意&#xff1a; 完成安装后需要激活 3.设置快捷键 以下为个人常用可跳过或根据需要设置 如&#xff1a…...

Oracle19.25发布,如何打补丁到19.25

一. 19.25发布 2024年10月16日 19c 19.25补丁发布 文档编号19202410.9&#xff0c;文档编码规则&#xff1a; 19&#xff08;版本号&#xff09;2024&#xff08;年份&#xff09;07&#xff08;当季的第一个月01/04/07/10&#xff09;.9 一般每个季度的首月中15号左右发布…...

vue3中,拦截双击事件的第一次点击,写一些逻辑

在 Vue 3 中&#xff0c;如果想要拦截双击事件的第一次点击并执行一些逻辑&#xff0c;你可以使用一个状态变量来跟踪第一次点击事件&#xff0c;并在第二次点击时阻止第一次点击逻辑的执行。以下是一个实现示例&#xff1a; <template><divmousedown"handleMou…...

落地 ZeroETL 轻量化架构,ByteHouse 推出“四个一体化”策略

在数字化转型的浪潮中&#xff0c;数据仓库作为企业的核心数据资产&#xff0c;其重要性日益凸显。随着业务范围扩大&#xff0c;企业也会使用不同的数据仓库来管理、维护相关数据。研发人员需要花费大量时间和精力&#xff0c;从中导出数据&#xff0c;然后进行手动整理、转换…...

如何提高LabVIEW编程效率

提高LabVIEW编程效率对开发者来说非常重要&#xff0c;尤其是在处理复杂项目或紧迫的开发周期时。以下是一些可以显著提升LabVIEW编程效率的技巧&#xff0c;从代码结构、工具使用到团队协作的多个角度进行详细分析&#xff1a; 1. 模块化设计 模块化设计 是提高代码可维护性和…...

Android 开发 TabLayout 自定义指示器长度

前言 原生 TabLayout 的指示器长度是充满整个屏幕的&#xff0c;但在实际开发中 UI 会设计成 指示器的长度等于或者小于标题字体长度&#xff0c;如图 如果设置成跟字体长度一样即使用 API: mTabLayout.setTabIndicatorFullWidth(false);或者在 xml 布局文件中的TabLayout标签…...

构造mex(牛客周赛 Round 59)

题目链接&#xff1b; D-构造mex_牛客周赛 Round 59 (nowcoder.com) 题目描述&#xff1a; 输出和输出描述&#xff1a; 输入样例&#xff1a; 3 6 3 3 7 4 3 6 6 0 输出样例&#xff1a; NO YES 4 0 1 2 YES 1 1 1 1 1 1 分析&#xff1a; 数学思维题&#xff0c;赛后看了一…...

RabbitMQ 交换机的类型

在 RabbitMQ 中&#xff0c;交换机&#xff08;Exchange&#xff09;是一个核心组件&#xff0c;负责接收来自生产者的消息&#xff0c;并根据特定的路由规则将消息分发到相应的队列。交换机的存在改变了消息发送的模式&#xff0c;使得消息的路由更加灵活和高效。 交换机的类…...

机器人顶会参会经验——许华哲老师PRE-IROS 2024分享

摘要&#xff1a;清华大学交叉信息学院许华哲老师在PRE-IROS 2024上分享了机器人顶会参会技巧&#xff0c;包括社交和活动选择方面的实用建议等内容。本文整理了许老师在直播中分享的干货。 在刚刚过去的PRE-IROS 2024论文预分享会上&#xff0c;清华叉院许华哲老师全方位解析…...

计算机组成原理--一章二章

这里写目录标题 第一章&#xff1a;计算机系统概述计算机的发展计算机的组成计算机的性能指标 第二章&#xff1a;数据的表示和运算2.1进位十进制BCD码无符号整数的表示和运算带符号整数的表示和运算原反补码的特性对比移码定点小数 2.2奇偶校验码算数逻辑运算单元&#xff08;…...

zookeeper kafka集群配置

一.下载安装包 地址&#xff1a;https://download.csdn.net/download/cyw8998/16579797 二.配置文件 zookeeper.properties dataDir/data/kafka/zookeeper_data/zookeeper # the port at which the clients will connect clientPort2181 # disable the per-ip limit on the…...

Java IO 基础知识

IO 流简介 IO 即 Input/Output&#xff0c;输入和输出。数据输入到计算机内存的过程即输入&#xff0c;反之输出到外部存储&#xff08;比如数据库&#xff0c;文件&#xff0c;远程主机&#xff09;的过程即输出。数据传输过程类似于水流&#xff0c;因此称为 IO 流。IO 流在…...

【报错处理】MR/Spark 使用 BulkLoad 方式传输到 HBase 发生报错: NullPointerException

博主希望能够得到大家的点赞收藏支持&#xff01;非常感谢 点赞&#xff0c;收藏是情分&#xff0c;不点是本分。祝你身体健康&#xff0c;事事顺心&#xff01; Spark 通过 BulkLoad 方式传输到 HBase&#xff0c;我发现会出现空指针异常。简单写下如何解决的。 原理&#xf…...

域7:安全运营 第17章 事件的预防和响应

第七域包括 16、17、18、19 章。 事件的预防和响应是安全运营管理的核心环节&#xff0c;对于组织有效识别、评估、控制和减轻网络安全威胁至关重要。这一过程是循环往复的&#xff0c;要求组织不断总结经验&#xff0c;优化策略&#xff0c;提升整体防护能力。通过持续的监测、…...

Linux常见基本指令 +外壳shell + 权限的理解

下面这篇文章主要介绍了一些Linux的基本指令及其周边知识, 以及shell的简单理解和权限的理解. 目录 前言1.基本指令及其周边知识1.1 ADD类touch [file]文件的时间mkdir [directory]cp [file/directory]echo [file]输出重定向Linux中, 一切皆文件 1.2 DELETE类rmdirrm通配符关机…...

Android Framework AMS(07)service组件启动分析-1(APP到AMS流程解读)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读应用层service组件启动的2种方式startService和bindService&#xff0c;以及从APP层到AMS调用之间的打通。关注思维导图中左侧部分即…...

深度学习:领域适应(Domain Adaptation)详解

领域适应&#xff08;Domain Adaptation&#xff09;详解 领域适应是机器学习中的一个重要研究领域&#xff0c;它解决的问题是模型在一个领域&#xff08;源域&#xff09;上训练得到的知识如何迁移到另一个有所差异的领域&#xff08;目标域&#xff09;上。领域适应特别重要…...

华三服务器R4900 G5在图形界面使用PMC阵列卡(P460-B4)创建RAID,并安装系统(中文教程)

环境以用户需求安装Centos7.9&#xff0c;服务器使用9块900G硬盘&#xff0c;创建RAID1和RAID6&#xff0c;留一块作为热备盘。 使用笔记本通过HDM管理口&#xff08;&#xff09;登录 使用VGA&#xff08;&#xff09;线连接显示器和使用usb线连接键盘鼠标&#xff0c;进行窗…...

Linux实验三

Linux实验三 实验步骤&#xff1a; 一、登录进入 CentOS7 系统&#xff0c;打开并进入终端&#xff0c;使用 su root 切换到 root 用户 &#xff1b; ​​ 二、将主机名称修改为 个人学号&#xff0c;并完成以下操作&#xff1a; 1、使用 uname -a 查看系统内核信息&#x…...

Vue预渲染:深入探索prerender-spa-plugin与vue-meta-info的联合应用

在前端开发的浪潮中&#xff0c;Vue.js凭借其轻量级、易上手和高效的特点&#xff0c;赢得了广大开发者的青睐。然而&#xff0c;单页面应用&#xff08;SPA&#xff09;在SEO方面的短板一直是开发者们需要面对的挑战。为了优化SEO&#xff0c;预渲染技术应运而生&#xff0c;而…...

使用`ThreadLocal`来优化鉴权逻辑并不能直接解决Web应用中session共享的问题

使用ThreadLocal来优化鉴权逻辑并不能直接解决Web应用中session共享的问题。实际上,ThreadLocal和session共享是两个不同的概念,它们解决的问题也不同。 ThreadLocal的作用 ThreadLocal是Java中提供的一个线程局部变量类,它可以让每个线程都拥有一个独立的变量副本,这样线…...

Python implement for PID

Python&#xff0c;serves as language for calculation of any domain 待更 Reference PID pythonPID git...

C++中的initializer_list类

目录 initializer_list类 介绍 基本使用 常见函数 initializer_list类 介绍 initializer_list类是C11新增的类&#xff0c;其原型如下&#xff1a; template<class T> class initializer_list; 有了initializer_list&#xff0c;一些容器也可以实现列表初始化&am…...

持续科技创新 高德亮相2024中国测绘地理信息科技年会

图为博览会期间, 自然资源部党组成员、副部长刘国洪前往高德企业展台参观。 10月15日&#xff0c;2024中国测绘地理信息科学技术年会暨中国测绘地理信息技术装备博览会在郑州召开。作为国内领先的地图厂商&#xff0c;高德地图凭借高精度高动态导航地图技术应用受邀参会。 本…...

深入理解HTTP Cookie

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 HTTP Cookie定义工作原理分类安全性用途 认识 cookie基本格式实验测试 cookie 当我们登录了B站过后&#xff0c;为什么下次访问B站就…...

Python多进程编程:使用`multiprocessing.Queue`进行进程间通信

Python多进程编程&#xff1a;使用multiprocessing.Queue进行进程间通信 1. 什么是multiprocessing.Queue&#xff1f;2. 为什么需要multiprocessing.Queue&#xff1f;3. 如何使用multiprocessing.Queue&#xff1f;3.1 基本用法3.2 队列的其他操作3.3 队列的阻塞与超时 4. 适…...

Docker 常见命令

命令库&#xff1a;docker ps | Docker Docs 安装docker apt install docker.io docker ps -a 作用&#xff1a;显示所有容器 docker logs -f frps 作用&#xff1a;持续输出容器名称为frps的日志信息&#xff08;监控&#xff09; docker restart frps 作用&#xff1a;重…...

Map 双列集合根接口 HashMap TreeMap

Map接口是一种双列集合,它的每一个元素都包含一个键对象Key和值Value 键和值直接存在一种对应关系 称为映射 从Map集中中访问元素, 只要指定了Key 就是找到对应的Value 常用方法 HashMap实现类无重复键无序 它是Map 接口的一个实现类,用于存储键值映射关系,并且HashMap 集合没…...

Pip源设置(清华源)相关总结

1、临时使用 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package 2、永久更改pip源 升级 pip 到最新的版本 (>10.0.0) 后进行配置&#xff1a; pip install pip -U pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 如…...