目录
如果一个客户请求需要占用线程很久的时间,会不会影响接下来的客户请求呢,有什么好的策略呢?
如果同时1000个客户端进行访问请求,线程数不多,怎么能及时响应处理每一个呢?
线程池:
1.空间换时间,使用服务器的硬件资源,换取运行效率;
2.池是一组资源的集合,这组资源在服务器启动之初就被完全创建并初始化好,称为静态资源;
3.当服务器进入正式运行阶段,开始处理客户请求,如果他需要相关资源可以直接从池中获取,无需动态分配;
4.当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用;
为什么使用线程池?
1. 避免创建和销毁线程所产生的开销,避免活动线程消耗系统的资源;
2. 提高响应速度;
3. 提高线程的可管理性,使用线程池可以进行统一分配、调优和监控;
使用多线程充分利用多核CPU,并使用线程池避免频繁创建、销毁加大系统开销:
a)创建一个线程池来管理多线程,线程池中主要包含任务队列和工作线程集合,将任务添加到队列中,然后在创建线程后,自动启动这些任务。使用了一个固定线程数的工作线程,限制线程最大并发数;
b)多个线程共享任务队列,所以需要下线程间同步
怎么创建线程池?
该项目使用线程池((半同步半反应堆模式))并发处理用户请求,
主线程负责监听文件描述符,接受socket新的连接,若当前监听的socket发生了读写事件,将任务插到请求队列中。
工作线程(连接池中的线程)从请求队列中取出任务,完成数据的读写处理,逻辑处理(HTTP请求报文的解析等)。
半同步/半反应堆
半同步/半异步模式:
既包含同步操作,又包含异步操作,在这种模式下,同步操作和异步操作共同协作,处理并发任务提高效率和可扩展性。
实现半同步/半异步模式时,通常会使用一个线程池和一个消息队列。同步操作会在主线程中直接执行,而异步操作则会在单独的线程中执行,并将结果放入消息队列中。主线程会从消息队列中读取结果,并进行处理。这样,同步操作和异步操作可以同时进行,提高程序的处理效率。
工作流程:
同步线程用于处理客户逻辑
异步线程用于处理I/O事件
异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中
请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象
领导者/追随者模式:
领导者负责系统的状态管理、决策和其他核心任务,追随者节点则通过与领导者节点进行通信,以获得系统状态更新指令。正常情况下,领导者节点宕机或者失去连接,那么系统中的其他节点会自动选举新的领导节点,确保系统的正常运行。
半同步/半反应堆并发模式是半同步/半异步的变体,将半异步具体化为某种事件处理模式。
并发模式中的同步和异步:
· 同步指的是程序完全按照代码序列的顺序执行
· 异步指的是程序的执行需要等待特定的事件发生才会执行相应的代码
半同步/半反应堆工作流程(以Proactor模式为例)
主线程充当异步线程,负责监听所有socket上的事件
若有新请求到来,主线程接收之以得到新的连接socket,然后往epoll内核事件表中注册该socket上的读写事件
如果连接socket上有读写事件发生,主线程从socket上接收数据,并将数据封装成请求对象插入到请求队列中
所有工作线程睡眠在请求队列上,当有任务到来时,通过竞争(如互斥锁)获得任务的接管权
五种IO模型
阻塞IO:调用者调用了某个函数,必须等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作
非阻塞IO:非阻塞等待,每隔一段时间就去检测IO事件是否就绪,没有就绪就可以做其他事。非阻塞I/O执行系统调用总是立即返回,不管事件是否已经发生,若事件没有发生,则返回-1,此时可以根据errno区分这两种情况,对于accept,recv和send,事件未发生时,errno通常被设置成eagain(意味着一个操作暂时无法完成,因此需要等待一段时间后再尝试。)
信号驱动IO:linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号,然后处理IO事件。
IO复用:linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数
异步IO: linux中,可以调用