32. 一次网页的访问从URL开始,说一下整个访问的过程
客户端获取URL - > DNS解析 - > TCP连接 - >发送HTTP请求 - >服务器处理请求 - >返回报文 - >浏览器解析渲染页面 - > TCP断开连接
客户端: (应用层开始)获取URL,通过负责域名解析的域名服务获取网址的IP地址,根据HTTP协议生成HTTP请求报文(应用层结束)(传输层开始)根据TCP协议连接从客户端到服务端(通过三次握手)客户端给服务端发送一个带SYN(同步)标志的数据包给客户端,然后客户端接收到信息再给客户端回传一个带有SYN/ACK(确认)标志的致据包以示传达确认信息,客户端最后再传送一个带ACK标志的数据包,代表”握手”结束,连接成功.TCP协议在把请求报文按序号分割成多个报文段(传输层结束)(网络层开始)根据IP协议(传输数据),ARP协议(获取MAC地址),OSPF协议(选择最优路径),搜索服务器地址,一边中转一边传输数据(网络层结束)(数据链路层开始)到达后通过数据链路层,物理层负责0,1比特流与物理设备电压高低,光的闪灭之间的互换。数据链路层负责将0,1序列划分为致据帧从一个节点传输到临近的另一个节点,这些节点是通过MAC来唯一标识的(MAC,物理地址,一个中主机会有一个MAC地址)。(数据链路层结束) 服务端 通过数据链路层->通过网络层>再通过传输层(根据TCP协议接收请求报文并重组报文段)->再通过应用层(通过HTTP协议对请求的内容进行处理)->再通过应用层->传输层->网络层->数据链路层>到达客户端 客户端 通过数据链路层 - >网络层 - >传输层(根据TCP协议接收响应报文并重组) - >应用层(HTTP协议对响应进行处理) - >浏览器渲染页面 - >断开连接协议四次挥手)33. DNS
用户访问网页,DNS服务器(域名解析系统)会根据用户提供的域名查找对应的IP地址域名解析服务器是基于UDP协议实现的一个应用程序,通常通过监听53端口来获取客户端的域名解析请求DNS查找过过程如下:浏览器缓存、系统缓存、路由器缓存、DNs服务器解析
34. HTTP请求报文
是由三部分组成: 请求行, 请求头和请求体。
请求行: GET index.html HTTP/1.1请求⽅法、URL、协议版本常用的方法有: GET, POST, PUT, DELETE, OPTIONS, HEAD。常见的请求报头有: Accept, Accept-Charset, Accept-Encoding, Accept-Language, Content-Type, Authorization, Cookie, User-Agent等35. 响应报文
HTTP响应报文也是由三部分组成: 响应行, 响应头和响应体。
响应行:【协议版本 状态码 描述】 HTTP/1.1 200 ok常见的响应报头字段有: Server, Connection响应报文:服务器返回给浏览器的文本信息,通常HTML, CSS, JS, 图片等文件就放在这一部分。Web服务器有Tomcat, Jetty和Netty等等。HTTP状态码:
是由3位数组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx 【消息】服务器收到请求,需要请求者继续执行操作2xx 【成功】请求已成功被服务器接收、理解、并接受3xx 【重定向】客户端需要采取进一步的操作以完成请求4xx 【客户端请求错误】客户端错误,请求包含语法错误或无法完成请求5xx 【服务器错误】服务器在处理请求的过程中发生了错误平时遇到比较常见的状态码有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500等:
200 OK //客户端请求成功
304 Not Modified // 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized // 当前请求要求用户的身份认证
403 Forbidden // 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found //请求资源不存在,输入了错误的URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable // 由于超载或系统维护,服务器暂时的无法处理客户端的请求。一段时间后可能恢复正常
301和·302区别:301为永久重定向,302为临时重定向
301永久重定向指的是旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址。
302临时重定向指的是旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。
37. 视频⾯试⽤到哪些协议?UDP⽤在哪⾥?
用到的协议:DNS,HTTP,HTTPS,TCP,UDP
将TCP和UDP的特性互相结合起来,让这个协议既能够保证可靠性,又能够保证明实时性,这也就是咱们所说的RUDP((Reliable UDP),常见的RUDP协议有QUIC,WebRTC,Aeron等等
40. UDP如何实现可靠传输,上层应用怎么实现?
UDP不属于连接协议,具有资源消耗少,处理速度快的优点,传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层
最简单的方式是在应用层模仿传输层TCP的可靠性传输:
三种使用UDP进行可靠数据传输的协议:RUDP、RTP、UDT
41. 常见安全漏洞
代码注入、会话固定、路径访问、弱密码
42. 栈(Stack)溢出漏洞:
一方面因为程序员的疏忽,使用了 strcpy、sprintf 等不安全的函数,增加了栈溢出漏洞的可能。
另一方面,因为栈上保存了函数的返回地址等信息
43. xss攻击、cors攻击原理
跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。
CORS全名跨域资源共享(Cross-Origin-Resourece-sharing),该机制主要是解决浏览器同源策略所带来的不便,使不同域的应用能够无视同源策略,进行信息传递。
44. 安全+幂等:
安全:HTTP协议中,安全是指请求⽅法不会破坏服务器上的资源
幂等:多次执⾏相同的操作,结果都相同
45. RAII机制
RAII机制,通过在栈上创建临时变量,这样临时变量就接管了堆上内存的控制权,当该临时变量声明周期结束时,则对应的堆上内存自然就被释放了。
在资源管理方面,智能指针(std::shared_ptr和std::unique_ptr)是RAII最具代表性的实现,使用了智能指针,可以实现自动的内存管理,再也不用担心忘记delete造成内存泄漏了。在状态管理方面,线程同步中使用std::unique_lock或std::lock_guard对互斥量std::mutex进行状态管理也是RAII的典型实现,通过这种方式,我们再也不用担心互斥量之间的代码出现异常而造成线程死锁。总结起来说,RAII的核心思想是将资源或状态与类对象的生命周期绑定,通过C++语言机制,实现资源与状态的安全管理。资源在对象构造初始化 资源在对象析构时释放是一种实现管理资源、避免内存泄露的方法46. 负载均衡
什么是负载均衡?
当一台服务器无法支持大量的用户访问时,将用户分摊到两个或多个服务器上的方法叫负载均衡。负载均衡的方法很多,Nginx负载均衡、LVS-NAT、LVS-DR等
什么是Nginx?
Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。Nginx有4种类型的模块:core、handlers、filters、load-balancers。我们这里讨论其中的2种,分别是负责负载均衡的模块load-balancers和负责执行一系列过滤操作的filters模块。如果我们的平台配备了负载均衡的话,前一步DNS解析获得的IP地址应该是我们Nginx负载均衡服务器的IP地址。所以,我们的浏览器将我们的网页请求发送到了Nginx负载均衡服务器上。Nginx根据我们设定的分配算法和规则,选择一台后端的真实Web服务器,与之建立TCP连接、并转发我们浏览器发出去的网页请求。Web服务器收到请求,产生响应,并将网页发送给Nginx负载均衡服务器。Nginx负载均衡服务器将网页传递给filters链处理,之后发回给我们的浏览器
47. 浏览器渲染
浏览器根据页面内容,生成DOM Tree。根据CSS内容,生成CSS Rule Tree(规则树)。调用JS执行引擎执行JS代码。根据DOM Tree和CSS Rule Tree生成Render Tree(呈现树)根据Render Tree渲染网页 浏览器是一个边解析边渲染的过程。首先浏览器解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上48. CDN
CDN叫内容分发网络,是依靠部署在各地的边缘服务器,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度。
内容分发网络(Content Delivery Network或Content Distribution Network,简称CDN)是指一种通过互联网互相连接的电脑网络系统,利用最靠近每位用户的服务器,更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户,来提供高性能、可扩展性及低成本的网络内容传递给用户。
49. 线程池相关
线程池:(创建流程)
定义一个结构体(互斥锁、条件变量、任务队列queue)传入参数线程池数量,遍历创建线程,设置线程分离属性:如果任务队列不空,就取出任务加锁执行,为空,就阻塞等待;如果关闭就结束此线程添加任务:加锁,任务队列插入任务,唤醒一个等待线程结束线程池:回收线程池资源,把所有休眠线程唤醒,标志位设置为释放线程的同步机制有哪些?
临界区 互斥量 信号量 事件
临界区: 临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又具有无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用。通过对多线程的串行化来访问公共资源或者一段代码,速度快,适合控制数据访问互斥量: 为协调对一个共享资源的单独访问而设计,只有拥有互斥量的线程才有权限访问系统的公共资源,因为互斥量只有一个,所以能保证资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源的访问,还能实现不同应用程序公共资源的安全共享信号量: 为控制一个具有有限数量的用户资源而设计。它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目事件: 用来通知线程有一些事件已经发生,从而启动后续任务的开始线程池中的工作线程是一直等待吗?
当请求队列中有任务时,工作线程会被唤醒执行任务;当请求队列为空时,工作线程会阻塞等待新的任务到来。这种设计可以有效处理高并发情况下的任务调度。
你的线程池工作线程处理完一个任务后的状态是什么?
这里要分两种情况考虑
(1) 当处理完任务后如果请求队列为空时,则这个线程重新回到阻塞等待的状态
(2) 当处理完任务后如果请求队列不为空时,那么这个线程将处于与其他线程竞争资源的状态,谁获得锁谁就获得了处理事件的资格
如果同时1000个客户端进行访问请求,线程数不多,怎么能及时响应处理每一个呢?
(服务器如何处理高并发的问题 -->对子线程循环调用来解决高并发问题)
通过子线程的run调用函数进行while循环,让每一个线程池中的线程永远都不会终止,他处理完当前任务就去处理下一个,没有任务就一直阻塞在那里等待。这样就能达到服务器高并发的要求
如果一个客户请求需要占用线程很久的时间,会不会影响接下来的客户请求呢,有什么好的策略呢?
会影响接下来的客户请求,因为线程池内线程的数量是有限的,如果客户请求占用线程时间过久的话会影响到处理请求的效率,当请求处理过慢时会造成后续接受的请求只能在请求队列中等待被处理,从而影响接下来的客户请求
应对策略:我们可以为线程处理请求对象设置处理超时时间, 超过时间先发送信号告知线程处理超时,然后设定一个时间间隔再次检测,若此时这个请求还占用线程则直接将其断开连接。
ET模式下 一次性循环读
ssizze_t HttpConn::read(int saveErrno){//一次性读出所有数据 ssize_t len = -1; do { len = readBuff_.ReadFd(fd_, saveErrno); //把数据读到readBuff里 if(len <= 0) break; //Read独到的字节数小于等于0,就break } while (isET); return len;}HttpConn:: isET = (connEvent_& EPOLLET);
50. 缓冲区自动扩容
在对文件描述符的数据进行处理的时候,因为要先把数据读取到缓冲区中,再从缓冲区读取出来进行处理,所以缓冲区的大小影响着我们对数据的读取,下面是它的实现原理:
首先我们再内存中创建一个缓冲区,缓冲区的大小为1024字节,同时定义两个指针,一个读指针,表示的是可以读取的数据的开始位置,一个写指针表示的是可以写入数据的起始位置,开始的时候两根指针都是指向内存开始的地方,如下图所示:
当有文件描述符监听到事件的发生的时候,比如发生的是读事件,那么就要把文件描述符监听到的事件读取到缓冲区,加入读取的字节的长度为len,那么写指针的位置就要发生变化,如下图所示:
因为缓冲区的大小是固定的大小,而我们通常是一次性将数据全部读取到缓冲区,那么就有可能装不下数据,所以需要临时创建一个缓冲区来缓解,将存不下的放到临时缓冲区,这样就可以一次性将所有的数据读入,这里利用临时缓冲区的技术是一个分散读的技术,即将数据分散读取到内存中不同的位置,分散读的话需要定义一个结构体数组如下所示:
struct iovec{ voidiov_base; //表示的是缓冲的地址 size_t iov_len; //表示的是缓冲的大小}
生成的结构体数组如下图所示:
如上图所示,当固定的缓冲区想要继续写数据的时候,发现剩余的位置不够写的时候那么就可以先把数据写入到临时缓冲中,再将临时数组的数据读取到固定缓冲区来处理,比如上图所示读指针的位置表示前面的数据已经被读取了,写指针的位置表示如果有数据那么要从这里开始写。
那么如何实现动态扩容的呢?
因为数据的处理只能放到固定大小的缓冲中进行处理,即上述的1024字节的缓冲中,那么如果很多数据都读取到这块区域的话,那么肯定是放不下的,我们就可以利用一个临时的缓冲区,把放不下的数据先放到临时的缓冲的位置,等到1024字节大小的内存有剩余的空间的时候我再将临时缓冲区的数据放入到1024的位置进行处理,那么何时1024缓冲中有空闲的位置呢?原理及实现如下图所示:
可以从上图看出,如果我们想要写入数据,那么此时可以利用的空间就是最前面的部分和最后面的部分的位置,但是写入数据一定要连续,所以唯一的办法就是将中间的数据移动到最前面,这样就可以将空闲的区域连接在一起,方便后面的写数据。具体的实现就是将读指针到写指针之间的数据复制到最前面,再更改读指针和写指针的位置,这就是利用一个缓冲实现自动增长的原理,
分散读、集中写
因为读设置的是边沿触发,需要一次性读完所有数据。所以定义一个大小1024的容器,但是有可能放不下,所以在定义一个65535的备用容器,采用分散读的形式,读到这两个容器里。然后整合这两个容器里数据(因为后序要吧数据取出来进行解析请求,所以需要合到一起):如果第一个能放下,写指针向后移动;如果放不下,看看能否凑出来,能凑则凑,凑不出来,第一个容器自动扩容resize。这样所有数据都在第一个容器里了。没必要一开始就用大容器:占内存,影响性能。
解析http请求,生成http响应响应首行,响应头在buffer里,响应体在内存映射里
写数据:边沿触发,一次性把数据从缓冲区buffer、内存映射写到socket中,所以需要分散写。
51. 并发模型相关
采用什么网络模型?
采用单Reactor多线程模型。主线程是一个Reactor,进行IO多路复用实现连接事件和读写事件的监听,同时主线程负责新连接的建立,子线程则负责数据的读写还有业务逻辑处理。
服务器程序通常需要处理三类事件:I/O 事件、信号及定时事件。有两种高效的事件处理模式:Reactor和 Proactor,同步 I/O 模型通常用于实现 Reactor 模式,异步 I/O 模型通常用于实现 Proactor 模式。
Reactor模式
要求主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑单元),将 socket 可读可写事件放入请求队列,交给工作线程处理。除此之外,主线程不做任何其他实质性的工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。
Proactor模式
Proactor 模式将所有 I/O 操作都交给主线程和内核来处理(进行读、写),工作线程仅仅负责业务逻辑。
reactor、proactor模型的区别?
件)后,就需要应⽤进程主动调⽤ read ⽅法来完成数据的读取,也就是要应⽤进程主动将 socket 接收缓存
中的数据读到应⽤进程内存中,这个过程是同步的,读取完数据后应⽤进程才能处理数据。Proactor 是异步⽹络模式, 感知的是已完成的读写事件。在发起异步读写请求时,需要传⼊数据缓冲区的地
址(⽤来存放结果数据)等信息,这样系统内核才可以⾃动帮我们把数据的读写⼯作完成,这⾥的读写⼯作全
程由操作系统来做,并不需要像 Reactor 那样还需要应⽤进程主动发起 read/write 来读写数据,操作系统完
成读写⼯作后,就会通知应⽤进程直接处理数据。
Reactor模式中,各个模式的区别?
Reactor模型是⼀个针对同步I/O的⽹络模型,主要是使⽤⼀个reactor负责监听和分配事件,将I/O事件分派给对应的Handler。新的事件包含连接建⽴就绪、读就绪、写就绪等。reactor模型中⼜可以细分为单reactor单线程、单reactor多线程、以及主从reactor模式。单reactor单线程模型就是使⽤ I/O 多路复⽤技术,当其获取到活动的事件列表时,就在reactor中进⾏读取请求、业务处理、返回响应,这样的好处是整个模型都使⽤⼀个线程,不存在资源的争夺问题。但是如果⼀个事件的业务处理太过耗时,会导致后续所有的事件都得不到处理。单reactor多线程就是⽤于解决这个问题,这个模型中reactor中只负责数据的接收和发送,reactor将业务处理分给线程池中的线程进⾏处理,完成后将数据返回给reactor进⾏发送,避免了在reactor进⾏业务处理,但是 IO 操作都在reactor中进⾏,容易存在性能问题。⽽且因为是多线程,线程池中每个线程完成业务后都需要将结果传递给reactor进⾏发送,还会涉及到共享数据的互斥和保护机制。主从reactor就是将reactor分为主reactor和从reactor,主reactor中只负责连接的建⽴和分配,读取请求、业务处理、返回响应等耗时的操作均在从reactor中处理,能够有效地应对⾼并发的场合。
52. 什么是ET(边缘触发)、LT(水平触发)?ET、LT优缺点?
LT模式和ET模式的区别
LT模式下只要内核缓冲区还有数据可读便会提醒(哪怕已经提醒过,针对同一事件可以多次提醒)
ET模式下,每一次事件到来只通知一次(针对一个事件只提醒一次而不是提醒多次),没有及时读取完,该部分剩余数据直到下次触发,才能被读取(有可能永远也读不到,如果没有再次触发文件描述符上的该事件)
什么时候用ET,什么时候用LT?
LT适用于并发量小的情况,ET适用于并发量大的情况。 为什么?
ET在通知用户之后,就会将fd从就绪链表中删除,而LT不会,它会一直保留,这就会导致随着fd增多,就绪链表越大,每次都要从头开始遍历找到对应的fd,所以并发量越大效率越低。ET因为会删除所以效率比较高。怎么解决LT的缺点?
LT模式下,可写状态的fd会一直触发事件,该怎么处理这个问题 方法1:每次要写数据时,将fd绑定EPOLLOUT事件,写完后将fd同EPOLLOUT从epoll中移除。方法2:方法1中每次写数据都要操作epoll。如果数据量很少,socket很容易将数据发送出去。可以考虑改成:数据量很少时直接send,数据量很多时在采用方法1.
触发LT模式后,读一次还是循环读?
读一次。
为什么ET模式下一定要设置非阻塞?
ET需要一次读完–>多次重复读取–>阻塞读取时最后一次必定阻塞–>任务线程阻塞,后续新的事件任务没法及时处理–>多个文件描述符上的事件饿死
ET非阻塞时,(重复)最后一次的读写(没有数据)必定阻塞,因为需要把数据一次读完,而此时没有数据必定阻塞。阻塞会导致多个文件描述符上的任务得不到及时处理而被饿死(对应的读写、逻辑处理任务存放在生产者消费者队列里),此时任务线程都阻塞在调用中无法处理其他文件描述符上的事件
53. (I/O多路复用)Select、poll和epoll
I/O 多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能,Linux 下实现 I/O 多路复用的系统调用主要有 select、poll 和 epoll。
Select:
主旨思想:
a.这个函数是阻塞的
b.函数对文件描述符的检测的操作是由内核完成的在返回时,它会告诉进程有多少(哪些)描述符要进行I/O操作。
Poll优点:
解决了select的1024限制(定义一个结构体数组,可以自己设大小),可以无数个;解决了fd复用,因为结构体除了有events,还有revents;(
short events; /* 委托内核检测文件描述符的什么事件 /
short revents; / 文件描述符实际发生的事件 */
)
Epoll
Epoll 的工作模式:
假设委托内核检测读事件 -> 检测fd的读缓冲区
读缓冲区有数据 - > epoll检测到了会给用户通知
a. 用户不读数据,数据一直在缓冲区,epoll 会一直通知
b. 用户只读了一部分数据,epoll会通知
c. 缓冲区的数据读完了,不通知
LT(level - triggered)是缺省(默认)的工作方式,并且同时支持 block 和 no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的。ET 模式(边沿触发)
假设委托内核检测读事件 -> 检测fd的读缓冲区
读缓冲区有数据 - > epoll检测到了会给用户通知
a. 用户不读数据,数据一致在缓冲区中,epoll下次检测的时候就不通知了
b. 用户只读了一部分数据,epoll不通知
c. 缓冲区的数据读完了,不通知
ET(edge - triggered)是高速工作方式,只支持 no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个 fd 作 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
select、poll和epoll的区别?三者的应用场景
对于select和poll来说,所有文件描述符都是在用户态被加入其文件描述符集合的,每次调用都需要将整个集合拷贝到内核态;epoll则将整个文件描述符集合维护在内核态,每次添加文件描述符的时候都需要执行一个系统调用。
select使用线性表描述文件描述符集合,文件描述符有上限;poll使用链表来描述;epoll底层通过红黑树来描述,并且维护一个ready list,将事件表中已经就绪的事件添加到这里,在使用epoll_wait调用时,仅观察这个list中有没有数据即可。
select和poll的最大开销来自内核判断是否有文件描述符就绪这一过程:每次执行select或poll调用时,它们会采用遍历的方式,遍历整个文件描述符集合去判断各个文件描述符是否有活动;epoll则不需要去以这种方式检查,当有活动产生时,会自动触发epoll回调函数通知epoll文件描述符,然后内核将这些就绪的文件描述符放到之前提到的ready list中等待epoll_wait调用后被处理。
select和poll都只能工作在相对低效的LT模式下,而epoll同时支持LT和ET模式。
当监测的fd数量较小,且各个fd都很活跃的情况下,建议使用select和poll;当监听的fd数量较多,且单位时间仅部分fd活跃的情况下,使用epoll会明显提升性能。
当有事件触发时,select和poll需要用户自己去遍历文件描述符找出触发的事件,而epoll能够直接返回所有的触发事件;
每次调用select和poll都需要将文件描述符集合拷贝到内核空间,返回时再拷贝一次。而epoll只需要拷贝需要修改的文件描述符而不需要集体的拷贝;
select支持的文件描述符数量有上限,而poll和epoll没有此限制;
select可以跨平台, epoll不可以
54. HTTP报文解析相关
为什么要用状态机?
每个状态都有一系列的转移,每个转移与输入和另一状态相关。当输入进来,如果它与当前状态的某个转移相匹配,机器转换为所指的状态,然后执行相应的代码。
传统应用程序的控制流程基本是按顺序执行的:遵循事先设定的逻辑,从头到尾地执行。简单来说如果想在不同状态下实现代码跳转时,就需要破坏一些代码,这样就会造成代码逻辑混乱,代码显得十分复杂。
状态机的转移图
https协议为什么安全?
https=http+TLS/SSL
TLS/SSL协议位于应用层协议和TCP之间,构建在TCP之上,由TCP协议保证数据传输版的可靠性,任何数据到权达TCP之前,都经过TLS/SSL协议处理。
https是加密传输协议,可以保障客户端到服务器端的传输数据安全。用户通过http协议访问网站时,浏览器和服务器之间是明文传输,这就意味着用户填写的密码、帐号、交易记录等机密信息都是明文,随时可能被泄露、窃取、篡改,被第三者加以利用。安装SSL证书后,使用https加密协议访问网站,可激活客户端浏览器到网站服务器之间的"SSL加密通道"(SSL协议),实现高强度双向加密传输,防止传输数据被泄露或篡改。
https的ssl连接过程
GET和POST的区别
(https://www.cnblogs.com/logsharing/p/8448446.html)
GET和POST是什么?HTTP协议中的两种发送请求的方法。
HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。
HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。 GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
GET和POST还有一个重大区别,简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包。
长的说:
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?
GET
POST
一般用来请求获取数据。不对服务器产生影响
一般向服务器提交数据,影响服务器
也可以传参到后台,但是传递的参数则显示在地址栏,安全性低,且参数的长度也有限制(2048字符)
将传递的参数放在request body中,不会在地址栏显示,安全性比get请求高,参数没有长度限制
刷新浏览器或者回退没有影响
刷新浏览器会重新请求一遍
可以被缓存,也会保留在浏览器的历史记录中
不会被缓存,也不会保留在浏览器的历史记录中
通过url地址请求
通过form表单请求
安全幂等
不安全不幂等
只产⽣⼀个TCP数据包,浏览器会把请求头和请求数据⼀并发送出去,服务器响 应200 ok(返回数据)
产⽣两个TCP数据包,浏览器会先将请求头发送给服务器,待服务器响应100 continue,浏览器再发送请求数据,服务 器响应200 ok(返回数据)
55. 数据库登录注册相关
设置一个固定大小的数据库连接池,利用信号量和互斥锁控制放入的连接数量
登录说一下?
数据库登录分为:1.载入数据表 2.提取用户名和密码 3.注册和登录校验 4.页面跳转
载入数据表就是把数据库的数据通过通过map容器传到服务器上。当从浏览器上输入用户的用户名和密码后,浏览器会一个post请求报文,服务器通过解析请求报文的消息体,解析出账号密码。根据解析出的账号密码,与map容器中保存账号密码进行对比校验,相符则成功登陆,就将浏览器跳转到对应的界面。注册账号时,同样将输入的账号密码与数据库已经存储的账号名进行对比校验,防止出现相同的账号名。如果不相同就加入数据库。当输入的账号密码与数据库的数据成功匹配,就将浏览器跳转到对应的界面。数据库用单例模式设置一个队列queue存放数据库连接信息设置信号量代表队列里可用的连接数量设置互斥锁
初始化为信号量为最大连接数,用一个-1,释放一个+1
RAII机制:利用lock_guard实现自动的状态管理,构造加锁和析构释放锁
保存状态了吗?如果要保存,你会怎么做?
(cookie和session)Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了
登录中的用户名和密码你是load到本地,然后使用map匹配的,如果有10亿数据,即使load到本地后hash,也是很耗时的,你要怎么优化?
——这个问题的关键在于大数据量情况下的用户登录验证怎么进行?将所有的用户信息加载到内存中耗时耗利,对于大数据最遍历的方法就是进行hash,利用hash建立多级索引的方式来加快用户验证。具体操作如下:
首先,将10亿的用户信息,利用大致缩小1000倍的hash算法进行hash,这时就获得了100万的hash数据,每一个hash数据代表着一个用户信息块(一级);
而后,再分别对这100万的hash数据再进行hash,例如最终剩下1000个hash数据(二级)。
在这种方式下,服务器只需要保存1000个二级hash数据,当用户请求登录的时候,先对用户信息进行一次hash,找到对应信息块(二级),在读取其对应的一级信息块,最终找到对应的用户数据,
用的mysql啊,redis了解吗?用过吗?
mysql和redis的数据库类型mysql是关系型数据库,主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。redis是NOSQL,即非关系型数据库,也是缓存数据库,即将数据存储在缓存中,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限。mysql的运行机制mysql作为持久化存储的关系型数据库,相对薄弱的地方在于每次请求访问数据库时,都存在着I/O操作,如果反复频繁的访问数据库。第一:会在反复链接数据库上花费大量时间,从而导致运行效率过慢;第二:反复的访问数据库也会导致数据库的负载过高,那么此时缓存的概念就衍生了出来。缓存缓存就是数据交换的缓冲区(cache),当浏览器执行请求时,首先会对在缓存中进行查找,如果存在,就获取;否则就访问数据库。缓存的好处就是读取速度快redis数据库redis数据库就是一款缓存数据库,用于存储使用频繁的数据,这样减少访问数据库的次数,提高运行效率。redis和mysql的区别总结(1)类型上从类型上来说,mysql是关系型数据库,redis是缓存数据库(2)作用上mysql用于持久化的存储数据到硬盘,功能强大,但是速度较慢redis用于存储使用较为频繁的数据到缓存中,读取速度快(3)需求上mysql和redis因为需求的不同,一般都是配合使用56. 定时器相关
为什么要用定时器?
方便释放那些超时的非活动连接,关闭被占用的文件描述符,才使用定时器
说一下定时器的工作原理?
(小顶堆)将所有定时器中超时时间最小的一个定时器的超时值作为alarm函数的定时值。这样,一旦定时任务处理函数tick()被调用,超时时间最小的定时器必然到期,我们就可以在tick 函数中处理该定时器。然后,再次从剩余的定时器中找出超时时间最小的一个(堆),并将这段最小时间设置为下一次alarm函数的定时值。如此反复,就实现了较为精确的定时。
插入,O(logn);
删除,O(logN);
57. 日志相关
说下你的日志系统的运行机制?
——初始化服务器时,利用单例模式初始化日志系统,根据配置文件确认是同步还是异步写入的方式。
为什么要异步?和同步的区别是什么?
——同步方式写入日志时会产生比较多的系统调用,若是某条日志信息过大,会阻塞日志系统,造成系统瓶颈。异步方式采用生产者-消费者模型,具有较高的并发能力。
现在你要监控一台服务器的状态,输出监控日志,请问如何将该日志分发到不同的机器上?
——为了便于故障排查,或服务器状态分析,看是否需要维护;可以使用消息队列进行消息的分发,例如mqtt、rabitmq等等;
58. 压测相关
webbench是什么?介绍一下原理
是一款轻量级的网址压力测试工具,可以实现高达3万的并发测试。
原理:Webbench实现的核心原理是:父进程fork若干个子进程,每个子进程在用户要求时间或默认的时间内对目标web循环发出实际访问请求,父子进程通过管道进行通信,子进程通过管道写端向父进程传递在若干次请求访问完毕后记录到的总信息,父进程通过管道读端读取子进程发来的相关信息,子进程在时间到后结束,父进程在所有子进程退出后统计并给用户显示最后的测试结果,然后退出。