webserver
项目流程
首先是服务器启动时的初始化,包括创建套接字,设置服务器的监听地址和端口,创建线程池和连接池等资源的初始化。
然后是服务器监听套接字等待客户端的连接请求,接受连接后生成新的连接套接字,用于与客户端进行通信。
第三步是对HTTP请求的解析与处理,然后生成HTTP响应及发送HTTP响应。
最后就是断开连接,等待下一个客户端连接请求。
项目中可能问到的问题
一、线程池相关
1.手写线程池
2.线程的同步机制有哪些?
互斥锁
信号量
条件变量
读写锁
原子操作
3.线程池中的工作线程是一直等待吗?
是的,等待新任务的唤醒。
4.你的线程池工作线程处理完一个任务后的状态是什么?
线程池中的工作线程在处理完一个任务后,会检查任务队列中是否还有待执行的任务。如果任务队列中有新的任务,工作线程会立即从任务队列中获取新的任务并开始执行。如果任务队列为空,工作线程会继续等待,直到有新的任务提交到任务队列中或者线程池被关闭。
5.如果同时1000个客户端进行访问请求,线程数不多,怎么能及时响应处理每一个呢?
这个项目是基于IO复用的并发模式,并非是一个客户连接就对应一个线程,当客户连接有事件需要处理时,epoll会进行事件提醒,然后将对应的任务加入请求队列,等待工作线程的竞争。如果处理的速度还是很慢,可以考虑扩大线程池的容量,或者是集群分布式的做法。
6.如果一个客户请求需要占用线程很久的时间,会不会影响接下来的客户请求呢,有什么好的策略呢?
会,因为线程的资源是有限的,如果一个客户连接长时间占用一个线程,肯定会影响服务器的整体响应速度。可以设置一个定时器,如果一个客户连接超过设定的时间阈值,就将其置于请求队列队尾或者是断开连接。
二、并发模型
1.简单说一下服务器使用的并发模型?
这个项目使用的是半同步半反应堆的并发模型。
以proactor模式为例的工作流程是:主线程使用异步线程,负责监听所有socket上的事件,若有新请求到来,主线程接受请求以得到新的socket连接,并在epoll内核事件表上注册该socket的读写事件。若在该socket上发生读写事件,主线程接受该读写数据,并将读写数据封装成请求对象插入到请求队列中。所有工作线程睡眠在请求队列中,当有任务到来时,通过竞争(互斥锁)的方式获得任务。
2.reactor、proactor、主从reactor模型的区别?
reactor模型要求主线程只负责监听文件描述符上是否有事件发生,若有,则立刻通知工作线程,并将该读写事件放入请求队列,读写数据、接受新连接以及处理客户请求均在工作线程中完成。
proactor模型是主线程和内核负责处理读写事件及接受新连接等IO操作。工作线程只负责处理业务逻辑,如处理客户请求。
主从reactor模型中,有一个主reactor和多个从reactor,主reactor负责监听和接受客户端连接,从reactor负责处理客户端请求。
3.你用了epoll,说一下为什么用epoll,还有其他复用方式吗?区别是什么?
还有select和poll。
对于select和poll来说,所有文件描述符都是在用户态被加入文件描述符集合的,每次调用都需要拷贝到内核态。而epoll将文件描述符集合维护在内核态,每次添加文件描述符时需要进行系统调用,减少了拷贝次数,也减少了系统调用的次数。因此epoll在处理大规模并发连接时会更加高效。
select使用线性表来存储文件描述符集合,文件描述符有上限。poll使用链表来存储文件描述符集合。epoll使用红黑树来存储文件描述符集合,并维护一个readylist,将就绪的事件添加到这里,当调用epoll_wait时,只需要观察list是否有数据即可。
select和poll需要遍历整个文件描述符集合来判断是否有活动产生,而epoll是在有活动发生时,通过回调函数通知文件描述符,然后将就绪的文件描述符放入ready list等待epoll_wait调用时被处理。
select和poll只能工作在低效的LT模式,而epoll可以工作在ET模式。
三、HTTP报文解析
1.用了状态机啊,为什么要用状态机?
有限状态机是一种抽象的理论模型,可以把有限个变量描述的状态变化过程,以可构造可验证的方式呈现出来。例如服务器在处理客户端请求时,需要跟踪每个连接的状态,如建立连接,请求处理,请求解析,响应生成,响应发送等。有限状态机可以方便的管理这些状态,并在不同的状态之间进行状态转移。
2.状态机的转移图画一下
3.https协议为什么安全?
混合加密的方式实现信息的机密性,解决了窃听的风险。
摘要算法的方式来实现完整性,它能够为数据生成独一无二的哈希值,哈希值用于校验数据的完整性,解决了篡改的风险。
将服务器公钥放入到数字证书中,解决了冒充的风险。
4.https的ssl连接过程
客户端发起连接请求->服务器发送公钥证书->客户端验证证书->客户端生成会话密钥并用服务器公钥加密会话密钥->服务器解锁密钥->客户端与服务器建立加密连接->进行加密通信
https://pic2.zhimg.com/v2-e03691ec1b4cec38f18360dcc4b2e7ad_b.jpg
5.GET和POST的区别
参数传递方式:GET请求将参数附加在URL的查询字符串中,而POST请求将参数包含在请求体中。在GET请求中,参数在URL中是可见的,而在POST请求中,参数在请求体中是不可见的。也因此POST请求相对更安全。
数据传输量:GET请求对传输的数据量有限制,通常限制在URL的最大长度,而POST请求没有固定的数据传输量限制。
幂等性:GET请求是幂等的,即多次请求同一URL,不会对服务器端产生副作用。而POST请求在多次请求同一URL时,可能会对服务器端产生副作用,例如重复提交表单等。
缓存:GET请求可以被缓存,可以被浏览器缓存、代理服务器缓存等,从而提高性能。而POST请求默认不会被缓存。
GET和POST本质上就是TCP连接,并无差别。在传输过程中,GET产生一个TCP数据包;POST产生两个TCP数据包。即对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
四、数据库登录注册相关
1.登录说一下?
将数据库中的数据载入到服务器中,然后对报文进行解析,提取用户名和密码,接着是对服务器进行注册和登录校验的流程,然后页面跳转。
2.你这个保存状态了吗?如果要保存,你会怎么做?(cookie和session)
利用cookie和session的方式对状态进行保存。
在服务器端,将需要保存的状态信息写入Cookie中。客户端在接收到Cookie后,会将Cookie保存在浏览器中,每次向服务器发送请求时都会携带相应的Cookie信息。服务器可以通过读取Cookie来获取保存在其中的状态信息,从而实现状态的保持。
Session是一种在服务器端保存用户状态信息的方式。当用户首次访问网站时,服务器会为用户创建一个唯一的Session ID,并将该Session ID发送给客户端。客户端在后续的请求中会自动携带Session ID,服务器通过Session ID来识别用户,并获取保存在Session中的状态信息。
3.登录中的用户名和密码你是load到本地,然后使用map匹配的,如果有10亿数据,即使load到本地后hash,也是很耗时的,你要怎么优化?
进行hash,利用hash建立多级索引的方式来加快用户验证。
首先,将10亿的用户信息,利用大致缩小1000倍的hash算法进行hash,这时就获得了100万的hash数据,每一个hash数据代表着一个用户信息块(一级);而后,再分别对这100万的hash数据再进行hash,例如最终剩下1000个hash数据(二级)。在这种方式下,服务器只需要保存1000个二级hash数据,当用户请求登录的时候,先对用户信息进行一次hash,找到对应信息块(二级),在读取其对应的一级信息块,最终找到对应的用户数据,
4.用的mysql啊,redis了解吗?用过吗?
五、定时器相关
1.为什么要用定时器?
处理非活跃连接,节约系统资源。
2.说一下定时器的工作原理
服务器为每个事件分配一个定时器。这个项目使用SIGALRM信号来实现定时器,每一个定时事件都处于一个升序链表上,通过alarm()函数周期性触发SIGALRM信号,而后信号回调函数利用管道通知主循环,主循环接收到信号之后对升序链表上的定时器进行处理:若一定时间内无数据交换则关闭连接。
3.双向链表啊,删除和添加的时间复杂度说一下?还可以优化吗?
添加一般情况下都是O(N),删除只需要O(1)。在双向链表中,节点的查找和插入操作可能会比较耗时,特别是在链表较长时。可以考虑使用索引、哈希等数据结构来加速节点的查找操作,或者采用其他高效的插入算法(如跳表、红黑树等)来优化节点的插入操作。
4.最小堆优化?说一下时间复杂度和工作原理
最小堆以每个定时器的过期时间进行排序,最小的定时器位于堆顶,当SIGALRM信号触发tick()函数时执行过期定时器清除,如果堆顶的定时器时间过期,则删除,并重新建堆,再判定是否过期,如此循环直到未过期为止。
插入,O(logn);
删除,O(logN);
六、日志相关
1.说下你的日志系统的运行机制?
初始化服务器时,利用单例模式初始化日志系统,根据配置文件确认是同步还是异步写入的方式。
2.为什么要异步?和同步的区别是什么?
同步方式写入日志时会产生比较多的系统调用,若是某条日志信息过大,会阻塞日志系统,造成系统瓶颈。异步方式采用生产者-消费者模型,具有较高的并发能力。
3.现在你要监控一台服务器的状态,输出监控日志,请问如何将该日志分发到不同的机器上?(消息队列)
可以通过使用消息队列作为中间件来实现。消息队列是一种常见的分布式系统间通信的方式,可以实现异步、解耦、可靠的消息传递。
在监控服务器上,将生成的监控日志写入消息队列中,如RabbitMQ、ActiveMQ等。在接收日志消息的机器上,部署相应的消息队列消费者,用于从消息队列中消费日志消息。消费者从消息队列中取出日志消息,并将其输出到不同的机器上。
七、压测相关
1.服务器并发量测试过吗?怎么测试的?
测试过,利用webbench,至少满足万余的并发量。
2.webbench是什么?介绍一下原理
Webbench是一款简单的HTTP性能测试工具,通过模拟多个并发连接向目标Web服务器发送HTTP请求,并记录性能指标,如响应时间、错误码等,从而评估目标Web服务器的性能和稳定性。

浙公网安备 33010602011771号