Skip to content

[TOC]

三次握手和四次挥手(2020/11/29-2)

为什么tcp比udp稳定

手写socket通信示例(2021/4/6-2)

客户端和服务端实现。

python
# client
import socket
sock = socket.socket()
sock.settimeout(10)
sock.connect(('127.0.0.1', 8888))
sock.send('hello world'.encode())
res = sock.recv(1024)
print(res.decode())
sock.close()

# server
import socket
sock = socket.socket()
sock.bind(('127.0.0.1', 8888))
sock.listen(5)
conn, addr = sock.accept()
res = conn.recv(1024)
print(res.decode())
conn.send('hello world'.encode())
conn.close()
sock.close()

怎么实现强行关闭客户端和服务器之间的连接?

在 socket 通信过程中不断循环检测一个全局变量(开关标记变量),一旦标记变量变为关闭,则 调用 socket 的 close 方法,循环结束,从而达到关闭连接的目的。

简单描述一下对多线程和多进程的理解(2021/3/29)

简述 TCP 和 UDP 的区别以及优缺点?(2)

UDP 是面向无连接的通讯协议,UDP 数据包括目的端口号和源端口号信息。

优点:UDP 速度快、操作简单、要求系统资源较少,由于通讯不需要连接,可以实现广播发送

缺点:UDP 传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数据是否会正确接收,也不重复发送,不可靠。

TCP 是面向连接的通讯协议,通过三次握手建立连接,通讯完成时四次挥手

优点:TCP 在数据传递时,有确认、窗口、重传、阻塞等控制机制,能保证数据正确性,较为可靠。

缺点:TCP 相对于 UDP 速度慢一点,要求系统资源较多。

描述用浏览器访问 www.baidu.com 的过程

先要解析出 baidu.com 对应的 ip 地址:

  • 要先使用 arp 获取默认网关的 mac 地址

  • 组织数据发送给默认网关(ip 还是 dns 服务器的 ip,但是 mac 地址是默认网关的 mac 地址)

  • 默认网关拥有转发数据的能力,把数据转发给路由器

  • 路由器根据自己的路由协议,来选择一个合适的较快的路径转发数据给目的网关

  • 目的网关(dns 服务器所在的网关),把数据转发给 dns 服务器

  • dns 服务器查询解析出 baidu.com 对应的 ip 地址,并原路返回请求这个域名的 client

得到了 baidu.com 对应的 ip 地址之后,会发送 tcp 的 3 次握手,进行连接:

  • 使用 http 协议发送请求数据给 web 服务器

  • web 服务器收到数据请求之后,通过查询自己的服务器得到相应的结果,原路返回给浏览器。

  • 浏览器接收到数据之后通过浏览器自己的渲染功能来显示这个网页。

  • 浏览器关闭 tcp 连接,即 4 次挥手结束,完成整个访问过程

HTTP 协议状态码有什么用,列出你知道的HTTP协议的状态码,然后讲出他们都表示什么意思?

通过状态码告诉客户端服务器的执行状态,以判断下一步该执行什么操作。

常见的状态机器码有:

  • 100-199:表示服务器成功接收部分请求,要求客户端继续提交其余请求才能完成整个处理过程。

  • 200-299:表示服务器成功接收请求并已完成处理过程,常用 200(OK 请求成功)。

  • 300-399:为完成请求,客户需要进一步细化请求。302(所有请求页面已经临时转移到新的 url)。

  • 304、307(使用缓存资源)。

  • 400-499:客户端请求有错误,常用 404(服务器无法找到被请求页面),403(服务器拒绝访问,权限不够)。

  • 500-599:服务器端出现错误,常用 500(请求未完成,服务器遇到不可预知的情况)。

python的底层网络交互模块有哪些?

socket, urllib,urllib3 , requests, grab, pycurl

简述 OSI 七层协议(2)

什么是C/S和B/S架构?

三次握手过程:

1 首先客户端向服务端发送一个带有 SYN 标志,以及随机生成的序号 100(0 字节)的报文

2 服务端收到报文后返回一个报文(SYN200(0 字节),ACk1001(字节+1))给客户端

3 客户端再次发送带有 ACk 标志 201(字节+)序号的报文给服务端

至此三次握手过程结束,客户端开始向服务端发送数据。

四次挥手过程:

由于 TCP 连接是可以双向通信的(全双工),因此每个方向都必须单独进行关闭

四次挥手过程,客户端和服务端都可以先开始断开连接

1 客户端发送带有 fin 标识的报文给服务端,请求通信关闭

2 服务端收到信息后,回复 ACK 答应关闭客户端通信(连接)请求

3 服务端发送带有 fin 标识的报文给客户端,也请求关闭通信

4 客户端回应 ack 给服务端,答应关闭服务端的通信(连接)请求

说一下什么是TCP 的 2MSL?

主动发送 fin 关闭的一方,在 4 次挥手最后一次要等待一段时间我们称这段时间为 2MSL

TIME_WAIT 状态的存在有两个理由:

  1. 让 4 次挥手关闭流程更加可靠

  2. 防止丢包后对后续新建的正常连接的传输造成破坏

为什么客户端在 TIME-WAIT 状态必须等待 2MSL 的时间?

  1. 为了保证客户端发送的最后一个 ACK 报文段能够达到服务器。 这个 ACK 报文段可能丢失,因而使处在 LAST-ACK 状态的服务器收不到确认。服务器会超时重传 FIN+ACK 报文段,客户端就能在 2MSL 时间内收到这个重传的 FIN+ACK 报文段,接着客户端重传一次确认,重启计时器。最好,客户端和服务器都正常进入到 CLOSED 状态。如果客户端在 TIME-WAIT 状态不等待一段时间,而是再发送完 ACK 报文后立即释放连接,那么就无法收到服务器重传的 FIN+ACK 报文段,因而也不会再发送一次确认报文。这样,服务器就无法按照正常步骤进入 CLOSED 状态。

  2. 防止已失效的连接请求报文段出现在本连接中。客户端在发送完最后一个 ACK 确认报文段后,再经过时间 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。

说说 HTTP 和 HTTPS 区别?

HTTP 协议传输的数据都是未加密的,也就是明文的,因此使用 HTTP 协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了 SSL(Secure Sockets Layer)协议用于对 HTTP 协议传输的数据进行加密,从而就诞生了 HTTPS。简单来说,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 http 协议安全。

HTTPS 和 HTTP 的区别主要如下:

  1. https 协议需要到 ca 申请证书,一般免费证书较少,因而需要一定费用。

  2. http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议。

  3. http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。

  4. http 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。

谈一下 HTTP 协议以及协议头部中表示数据类型的字段?

HTTP 协议是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网

(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议。

HTTP 是一个基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

HTTP 是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体

信息系统。它于 1990 年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在 WWW 中使用的是 HTTP/1.0 的第六版,HTTP/1.1 的规范化工作正在进行之中,而且 HTTP-NG(NextGeneration of HTTP)的建议已经提出。

HTTP 协议工作于客户端-服务端架构为上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服

务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。表示数据类型字段: Content-Type

HTTP 请求方法都有什么?

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。

HTTP1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。

  1. GET请求指定的页面信息,并返回实体主体。

  2. HEAD类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头

  3. POST,向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。

  4. PUT从客户端向服务器传送的数据取代指定的文档的内容。

  5. DELETE 请求服务器删除指定的页面。

  6. CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。

  7. OPTIONS允许客户端查看服务器的性能。

  8. TRACE 回显服务器收到的请求,主要用于测试或诊断。

HTTP 常见请求头?

  1. Host (主机和端口号)

  2. Connection (链接类型)

  3. Upgrade-Insecure-Requests (升级为 HTTPS 请求)

  4. User-Agent (浏览器名称)

  5. Accept (传输文件类型)

  6. Referer (页面跳转处)

  7. Accept-Encoding(文件编解码格式)

  8. Cookie (Cookie)

  9. x-requested-with :XMLHttpRequest(是 Ajax 异步请求)

Reactor/Proactor的区别?

七层模型? IP ,TCP/UDP ,HTTP ,RTSP ,FTP 分别在哪层?

应用层:HTTP,FTP,NFS

表示层:Telnet,SNMP

会话层:SMTP,DNS

传输层:TCP,UDP

网络层:IP,ICMP,ARP,

数据链路层:Ethernet,PPP,PDN,SLIP,FDDI

物理层:IEEE 802.1A,IEEE 802.11

url 的形式?

形式: scheme://host[:port#]/path/…/[?query-string][#anchor]

scheme:协议(例如:http, https, ftp)

host:服务器的 IP 地址或者域名

port:服务器的端口(如果是走协议默认端口,80 or 443)

path:访问资源的路径

query-string:参数,发送给 http 服务器的数据

anchor:锚(跳转到网页的指定锚点位置)

如这样的url:https://www.cnblogs.com/Neeo/articles/13553582.html#foreign-key

什么是arp协议?

ARP协议,全称“Address Resolution Protocol”,中文名是地址解析协议,使用ARP协议可实现通过IP地址获得对应主机的物理地址(MAC地址)

TCP和UDP的区别?为何基于tcp协议的通信比基于udp协议的通信更可靠?

1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的

UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP首部开销20字节;UDP的首部开销小,只有8个字节

6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

tcp:可靠 对方给了确认收到信息,才发下一个,如果没收到确认信息就重发

udp:不可靠 一直发数据,不需要对方回应

什么是局域网和广域网?

局域网:(Local Area Network,LAN), 局域网是一个局部范围的计算计组,比如家庭网络就是一个小型的局域网,里面包含电脑、手机和平板等,他们共同连接到你家的路由器上。又比如学校的机房就是一个局域网,里面有几百几千台电脑,当机房无法上外网时,但是电脑之间仍可以通信,你们可以通过这个局域网来打CS 、玩红警。理论上,局域网是封闭的,并不可以上外网,可以只有两台电脑,也可以有上万台。

广域网:(WAN,Wide Area Network),广域网的范围就比较大了,可以把你家和别人家、各个省、各个国家连接起来相互通信。广域网和局域网都是从范围的角度来划分的,广域网也可以看成是很多个局域网通过路由器等相互连接起来。

再补充点:

以太网:(Ethernet),以太网可以看成是一种实现局域网通信的技术标准,是目前最广泛的局域网技术。以太网的运行速率有10Mbps,100Mbps,1Gbps,10Gbps的,它的传输介质有的是双绞线,有的是光纤。 简单的说,以太网就是在局域网内,把附近的设备连接起来,可以进行通讯。

互联网:(Internet),互联网可以看成是局域网、广域网等组成的一个最大的网络,它可以把世界上各个地方的网路都连接起来,个人、政府、学校、企业,只要你能想到的,都包含在内。互联网是一种宽泛的概念,是一个极其庞大的网络。

什么是socket?简述基于tcp协议的套接字通信流程。

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。

服务端:创建socket对象,绑定ip端口bind(), 设置最大链接数listen(), accept()与客户端的connect()创建双向管道, send(), recv(),close()

客户端:创建socket对象,connect()与服务端accept()创建双向管道 , send(), recv(),close()

什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象?

粘包:数据粘在一起,主要因为:接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的

数据量比较小,时间间隔比较短,就合并成了一个包,这是底层的一个优化算法(Nagle算法)

IO多路复用的作用?

I/O多路复用是用于提升效率,单个进程可以同时监听多个网络连接IO。

举例:通过一种机制,可以监视多个文件描述符,一旦描述符就绪(读就绪和写就绪),能通知程序进行相应的读写操作,I/O多路复用避免阻塞在io上,

原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个socket的状态后轮询处理

什么是防火墙以及作用?

在互联网上防火墙是一种非常有效的网络安全模型,通过它可以隔离风险区域(即Internet或有一定风险的网络)与安全区域(局域网)的连接,同时不会妨碍人们对风险区域的访问。所以它一般连接在核心交换机与外网之间。

  1. 过滤进出网络的数据

  2. 管理进出访问网络的行为

  3. 封堵某些禁止业务

  4. 记录通过防火墙信息内容和活动

  5. 对网络攻击检测和告警

select、poll、epoll 模型的区别?(2)

I/O多路复用的本质就是用select/poll/epoll,去监听多个socket对象,如果其中的socket对象有变化,只要有变化,用户进程就知道了。

  • select是不断轮询去监听的socket,socket个数有限制,一般为1024个;

  • poll还是采用轮询方式监听,只不过没有个数限制;

  • epoll并不是采用轮询方式去监听了,而是当socket有变化时通过回调的方式主动告知用户进程。

路由器和交换机的区别?

交换机通常端口比较多,路由器端口少体积小,(路由器一般都集成了交换机的功能,LAN口就是作为交换机的端口来使,

WAN用于连接外网的端口。

工作层次不同

交换机在数据链路层(实现数据帧的转发),而路由器在网络层(肩负着网络互连的作用)。

数据的转发对象不同

交换机是根据MAC地址转发数据帧,而路由器是根据IP地址来转发数据报。

IP地址决定最终数据要到达某一台主机,而MAC地址是决定下一跳将要交给哪一台设备(一般是交换机或者路由器),

IP地址是软件实现的,可以描述主机所在的网络,MAC地址是硬件实现的,

每一个网卡在出厂时都会将全世界唯一的MAC地址固化在ROM中,因此MAC不可改,IP可改

分工不同

交换机主要是用于组建局域网,而路由器则负责让主机连接外网,多台主机可以通过网线连接到交换机,

这时候就组建好了局域网,就可以通过网线连接到交换机,这时就组建好了局域网,

就可以将数据发给局域网中的其他主机,然而通过交换机组建的局域网是不能访问外网的,

这时就需要路由器为我们来打开外网大门,局域网的所有主机使用的都是私网的IP,所以必须通过,

路由器转换为公网IP之后才能访问外网。

冲突域和广播域

交换机分割冲突域,但不分割广播域,而路由器分割广播域。

由交换机连接的网段,仍属于同一个广播域,广播数据包会在交换机连接的所有网段上传播,

这时会导致广播风暴和安全漏洞。而连接在路由器上的网段会被分配到不同的广播域。

路由器不会转发广播数据。交换机会转发广播数据给局域网中的所有主机,

值得说明的是单播的数据包在局域网中会被交换机唯一的送到目标主机,

其他主机不会接受到数据,这是区别于原始的集线器的。

其他:

  • 路由器可以给局域网自动分配IP,虚拟拨号。交换机则只是用来分配网络数据的。

  • 路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。交换机可以把很多主机连起来,这些主机对外各有各的IP。

  • 交换机工作在中继层,根据MAC地址寻址,不能处理TCP/IP协议。路由器工作在网络层,根据IP地址寻址,可以处理TCP/IP协议。

  • 路由器提供防火墙服务,交换机不能提供该功能。

什么是域名解析?

域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务。IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址。域名解析就是域名到IP地址的转换过程。域名的解析工作由DNS服务器完成。

域名解析也叫域名指向、服务器设置、域名配置以及反向IP登记等等。说得简单点就是将好记的域名解析成IP,服务由DNS服务器完成,是把域名解析到一个IP地址,然后在此IP地址的主机上将一个子目录与域名绑定。

互联网中的地址是数字的IP地址,域名解析的作用主要就是为了便于记忆。

https://baike.baidu.com/item/域名解析/574285?fr=aladdin

如何修改本地hosts文件?

Windows:

以管理员的身份修改,C:\Windows\System32\drivers\etc\hosts文件。

C:\Windows\System32\drivers\etc目录是默认的hosts文件保存目录。

linux:

vim /etc/hosts

什么是cdn?

CDN的全称是Content Delivery Network,即内容分发网络。CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用bai户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。

纵观整个宽带服务的价值链,内容提供商和用户位于整个价值链的两端,中间依靠网络服务提供商将其串接起来。随着互联网工业的成熟和商业模式的变革,在这条价值链上的角色越来越多也越来越细分。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。

扩展资料:

目前的CDN服务主要应用于证券、金融保险、ISP、ICP、网上交易、门户网站、大中型公司、网络教学等领域。另外在行业专网、互联网中都可以用到,甚至可以对局域网进行网络优化。

利用CDN,这些网站无需投资昂贵的各类服务器、设立分站点,特别是流媒体信息的广泛应用、远程教学课件等消耗带宽资源多的媒体信息,应用CDN网络,把内容复制到网络的最边缘,使内容请求点和交付点之间的距离缩至最小,从而促进Web站点性能的提高,具有重要的意义。

CDN能几乎涵盖国内所有线路。而在可靠性上, CDN 在结构上实现了多点的冗余,即使某一个节点由于意外发生故障,对网站的访问能够被自动导向其他的健康节点进行响应。CDN能轻松实现网站的全国铺设,不必考虑服务器的投入与托管、不必考虑新增带宽的成本、不必考虑多台服务器的镜像同步、不必考虑更多的管理维护技术人员。

什么是LVS?

LVS的英文全称是Linux Virtual Server,即Linux虚拟服务器,它主要用于多服务器的负载均衡。它工作在网络层,可以实现高性能,高可用的服务器集群技术。它廉价,可把许多低性能的服务器组合在一起形成一个超级服务器。它易用,配置非常简单,且有多种负载均衡的方法。它稳定可靠,即使在集群的服务器中某台服务器无法正常工作,也不影响整体效果。另外可扩展性也非常好。

LVS可分为三部分:

  1. Load Balancer:这是LVS的核心部分,它好比我们网站MVC模型的Controller。它负责将客户的请求按照一定的算法分发到下一层不同的服务器进行处理,自己本身不做具体业务的处理。另外该层还可用监控下一层的状态,如果下一层的某台服务器不能正常工作了,它会自动把其剔除,恢复后又可用加上。该层由一台或者几台Director Server组成。

  2. Server Array:该层负责具体业务。可有WEB Server、mail Server、FTP Server、DNS Server等组成。注意,其实上层的Director Server也可以当Real server用的。

  3. Shared Storage:主要是提高上一层数据和为上一层保持数据一致。四.负载均衡机制前面我们说了LVS是工作在网络层。相对于其它负载均衡的解决办法,比如DNS域名轮流解析、应用层负载的调度、客户端的调度等,它的效率是非常高的。LVS的通过控制IP来实现负载均衡。IPVS是其具体的实现模块。IPVS的主要作用:安装在Director Server上面,在Director Server虚拟一个对外访问的IP(VIP)。用户访问VIP,到达Director Server,Director Server根据一定的规则选择一个Real Server,处理完成后然后返回给客户端数据。这些步骤产生了一些具体的问题,比如如何选择具体的Real Server,Real Server如果返回给客户端数据等等。

参考:https://www.jianshu.com/p/7a063123d1f1

什么是Nginx?

Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。

Nginx是一款轻量级的Web服务器/反向代理服务器以及电子邮件代理服务器,并在一个BSD-like协议下发行。由俄罗斯的程序设计师lgor Sysoev所开发,供俄国大型的入口网站及搜索引擎Rambler使用。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好。

Nginx相较于Apache\lighttpd具有占有内存少,稳定性高等优势,并且依靠并发能力强,丰富的模块库以及友好灵活的配置而闻名。在Linux操作系统下,nginx使用epoll事件模型,得益于此,nginx在Linux操作系统下效率相当高。同时Nginx在OpenBSD或FreeBSD操作系统上采用类似于Epoll的高效事件模型kqueue.

参考:https://blog.csdn.net/qq_15037231/article/details/80405294

什么是正向代理和反向代理?

代理服务器

一般是指局域网内部的机器通过代理服务器发送请求到互联网上的服务器,代理服务器一般作用在客户端。

反向代理

客户端(用户A)向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端。而客户端始终认为它访问的是原始服务器B而不是服务器Z。由于防火墙作用,只允许服务器Z进出,防火墙和反向代理共同作用保护了院子资源服务器B。

用途:将防火墙后面的服务器提供给Internet用户访问。反向代理还可以为后端的多台服务器提供负载平衡或为后端较慢的服务器提供缓冲服务。

正向代理

正向代理是一个位于客户端A和原始服务器(服务器B)之间的服务器(代理服务器Z),为了从原始服务器取得内容,用户A向代理服务器Z发送一个请求并指定目标(服务器B),然后代理服务器Z向服务器B转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。

用途:在防火墙内的局域网客户端提供访问Internet的途径。还可以使用缓冲特性减少网络使用率。

从安全性来讲:

​ 正向代理允许客户端通过它访问任意网站并且隐藏客户端自身,因此你必须采取安全措施以确保仅为经过授权的客户端提供服务。

​ 反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。

参考:https://blog.csdn.net/qq_15037231/article/details/80405294

什么是keepalived?

Keepalived是Linux下一个轻量级别的高可用解决方案。高可用(High Avalilability,HA)。

Keepalived起初是为LVS设计的,专门用来监控集群系统中各个服务节点的状态,它根据TCP/IP参考模型的第三、第四层、第五层交换机制检测每个服务节点的状态,如果某个服务器节点出现异常,或者工作出现故障,Keepalived将检测到,并将出现的故障的服务器节点从集群系统中剔除,这些工作全部是自动完成的,不需要人工干涉,需要人工完成的只是修复出现故障的服务节点。

后来Keepalived又加入了VRRP的功能,VRRP(Vritrual Router Redundancy Protocol,虚拟路由冗余协议)出现的目的是解决静态路由出现的单点故障问题,通过VRRP可以实现网络不间断稳定运行,因此Keepalvied 一方面具有服务器状态检测和故障隔离功能,另外一方面也有HA cluster功能。

参考:https://www.cnblogs.com/AbnerLc/p/11898289.html

什么是haproxy?

HAProxy 是一款提供高可用性、负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件,支持虚拟主机,它是免费、快速并且可靠的一种解决方案。

HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。

HAProxy运行在时下的硬件上,完全可以支持数以万计的 并发连接。

并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上。

作用: 高可用性,负载平衡和用于TCP和基于http的应用程序的代理

什么是负载均衡?

参考:https://zhuanlan.zhihu.com/p/32841479

什么是rpc及应用场景?

RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。

rpc可以基于tcp和http协议来实现。

rpc相对于http1.1,它的请求报文体积更小。

rpc相对于http传输效率更高。

一般的rpc框架都实现了负载均衡。

常见的rpc框架有:thrift、dubbo、spring cloud、grpc。

rpc适用于微服务、分布式系统架构和内部的服务调用,而http主要对外提供服务。

参考:

生产者消费者模型应用场景?

消息队列、异步场景、流量削峰的场景(秒杀)

描述多进程开发中join与deamon的区别

首先,一个普通的多进程是下面这样的执行效果:

python
import time
from multiprocessing import Process
def f1():
    print('f1....')
    time.sleep(3)
def f2():
    print('f2....')
    time.sleep(5)
if __name__ == '__main__':
    p1 = Process(target=f1, )
    p2 = Process(target=f2,)
    p1.start()
    p2.start()
    print('ending')    
    """
    ending
    f1....
    f2....
    """

主进程执行完代码后就结束了(但并不是程序结束),所以先打印了ending.....;然后主进程会等子进程p1p2执行完,然后回收其资源后再结束程序的运行。

而当有了守护进程后,守护进程随主进程结束而结束,如下效果:

python
import time
from multiprocessing import Process
def f1():
    print('f1....')
    time.sleep(3)
def f2():
    print('f2....')
    time.sleep(5)
if __name__ == '__main__':
    p1 = Process(target=f1, )
    p2 = Process(target=f2, )
    p1.daemon = True
    p1.start()
    p2.start()
    print('ending')
    """
    ending
    f2....  
    """

上例,p1随着主进程的结束而结束,所以它几乎不会执行(因为执行它的时间完全没有主进程启动p2然后打印的速度快),就伴随着主进程的结束而结束了。

join等待其子进程结束之后,主进程才往下执行,如下代码:

python
import time
from multiprocessing import Process
def f1():
    print('f1....')
    time.sleep(3)
def f2():
    print('f2....')
    time.sleep(5)
if __name__ == '__main__':
    p1 = Process(target=f1, )
    p2 = Process(target=f2, )
    p1.start()
    p1.join()
    p2.start()
    print('ending')
    """
    f1....
    ending
    f2....
    """

上例,执行到p1.join时,主线程就卡着了,等着p1进程执行结束,主进程再往下继续执行。

扩展:

守护线程会在主线程结束之后,等待其他子线程的结束才结束。

曾经在哪里使用过:线程、进程、协程?

爬虫,定时任务

Python协程与线程的区别?

请使用yield实现一个协程?

python
def producer():
    for i in range(100):  # 0 1 2 3 4 5 6 ....
        n = yield i
        print("结果:", n)
def consumer():
    # 初始化当前生成器函数
    g = producer()
    # send可以类比next,第一次调用时,必须发送None,send给yield发送数据(上一个yield)
    g.send(None)
    for i in range(10):  # 0 1 2 3 ... 9
        res = g.send(i)
        print(res)
consumer()

asynio是什么?

asyncio是python原生的底层协程模块。

参考:https://www.cnblogs.com/Neeo/articles/14142552.html

请使用python内置async语法实现一个协程?

python
import asyncio
async def func():    # 协程方法
    print('start...')    
    await asyncio.sleep(1)     # 阻塞
    print('end....')
loop = asyncio.get_event_loop()  # 开启事件循环
loop.run_until_complete(func())

简述线程死锁是如何造成的?如何避免?

现象:

python
import time
from threading import Thread, Lock
noodle_lock = Lock()
fork_lock = Lock()
def eat1(name, noodle_lock, fork_lock):
    noodle_lock.acquire()
    print('%s抢到面了' % name)
    fork_lock.acquire()
    print('%s抢到叉子了' % name)
    print('%s吃了一口面' % name)
    time.sleep(0.1)
    fork_lock.release()
    print('%s放下叉子了' % name)
    noodle_lock.release()
    print('%s吃完面了' % name)

def eat2(name, noodle_lock, fork_lock):
    fork_lock.acquire()
    print('%s抢到叉子了' % name)
    noodle_lock.acquire()
    print('%s抢到面了' % name)
    print('%s吃了一口面' % name)
    time.sleep(0.1)
    noodle_lock.release()
    print('%s吃完面了' % name)
    fork_lock.release()
    print('%s放下叉子了' % name)

lst = ['张开', '李开', '王开', '陈开']
Thread(target=eat1, args=(lst[0], noodle_lock, fork_lock)).start()
Thread(target=eat2, args=(lst[1], noodle_lock, fork_lock)).start()
Thread(target=eat1, args=(lst[2], noodle_lock, fork_lock)).start()
Thread(target=eat2, args=(lst[3], noodle_lock, fork_lock)).start()

死锁是如何造成的:

  1. 在多线程中出现了多把锁

  2. 且多把锁交替使用

如何解决:

  1. 递归锁

  2. 优化代码

现在来使用递归锁优化上面的示例:

python
import time
from threading import Thread, Lock, RLock


noodle_lock = fork_lock = RLock()


def eat1(name, noodle_lock, fork_lock):
    noodle_lock.acquire()
    print('%s抢到面了' % name)
    fork_lock.acquire()
    print('%s抢到叉子了' % name)
    print('%s吃了一口面' % name)
    time.sleep(0.1)
    fork_lock.release()
    print('%s放下叉子了' % name)
    noodle_lock.release()
    print('%s吃完面了' % name)


def eat2(name, noodle_lock, fork_lock):
    fork_lock.acquire()
    print('%s抢到叉子了' % name)
    noodle_lock.acquire()
    print('%s抢到面了' % name)
    print('%s吃了一口面' % name)
    time.sleep(0.1)
    noodle_lock.release()
    print('%s吃完面了' % name)
    fork_lock.release()
    print('%s放下叉子了' % name)


lst = ['张开', '李开', '王开', '陈开']
Thread(target=eat1, args=(lst[0], noodle_lock, fork_lock)).start()
Thread(target=eat2, args=(lst[1], noodle_lock, fork_lock)).start()
Thread(target=eat1, args=(lst[2], noodle_lock, fork_lock)).start()
Thread(target=eat2, args=(lst[3], noodle_lock, fork_lock)).start()

gevent模块是什么?

该模块主要工作:检测程序中的io,进而实现自动切换

什么是twisted框架?

wisted是用Python实现的基于事件驱动的网络引擎框架。Twisted诞生于2000年初,在当时的网络游戏开发者看来,无论他们使用哪种语言,手中都鲜有可兼顾扩展性及跨平台的网络库。Twisted的作者试图在当时现有的环境下开发游戏,这一步走的非常艰难,他们迫切地需要一个可扩展性高、基于事件驱动、跨平台的网络开发框架,为此他们决定自己实现一个,并从那些之前的游戏和网络应用程序的开发者中学习,汲取他们的经验教训。

Twisted支持许多常见的传输及应用层协议,包括TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC以及FTP。就像Python一样,Twisted也具有“内置电池”(batteries-included)的特点。Twisted对于其支持的所有协议都带有客户端和服务器实现,同时附带有基于命令行的工具,使得配置和部署产品级的Twisted应用变得非常方便。

参考:https://blog.csdn.net/hanhuili/article/details/9389433

多线程、多进程、协程的作用和应用场景(2020/11/29-4)

  1. 进程是计算器最小资源分配单位 .

  2. 线程是CPU调度的最小单位 .

  3. 进程切换要比线程切换资源消耗大

  4. 协程切换任务资源很小,效率高(协程本身并不存在,是我们自己用代码来实现的) .

  5. 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中进行切换的

进程:

一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。

线程:

调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。

协程:

协程的切换需要我们自己控制,开销小,用户操作可控,完全不会增加操作系统的压力。

Python 中的进程与线程的使用场景?

多进程适合在 CPU 密集型操作(cpu 操作指令比较多,如位数多的浮点运算)。

多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)

谈谈你对多进程,多线程,以及协程的理解,项目是否用?

这个问题被问的概率相当之大,其实多线程,多进程,在实际开发中用到的很少,除非是那些对项目性能要求特别高的,有的开发工作几年了,也确实没用过,你可以这么回答,给他扯扯什么是进程,线程(cpython 中是伪多线程)的概念就行,实在不行你就说你之前写过下载文件时,用过多线程技术,或者业余时间用过多线程写爬虫,提升效率。

进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。

线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。

协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

什么是多线程竞争?

线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态即:数据几乎同步会被多个线程占用,造成数据混乱 ,即所谓的线程不安全。

那么怎么解决多线程竞争问题?-- 锁。

锁的好处:

确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。

锁的坏处:阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了

锁的致命问题:死锁。

解释一下什么是锁,有哪几种锁?

锁(Lock)是 Python 提供的对线程控制的对象。有互斥锁、可重入锁、死锁。

什么是死锁?

若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。

GIL 锁(有时候,面试官不问,你自己要主动说,增加 b 格,尽量别一问一答的尬聊,不然最后等到的一句话就是:你还有什么想问的么?)

GIL 锁 全局解释器锁(只在 cpython 里才有),作用:限制多线程同时执行,保证同一时间只有一个线程执行,所以 cpython 里的多线程其实是伪多线程!

所以 Python 里常常使用协程技术来代替多线程,协程是一种更轻量级的线程,

进程和线程的切换时由系统决定,而协程由我们程序员自己决定,而模块 gevent 下切换是遇到了耗时操作才会切换。

三者的关系:进程里有线程,线程里有协程。

什么是线程安全,什么是互斥锁?

每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

同一个进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一个线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。

说说下面几个概念:同步,异步,阻塞,非阻塞?

同步:多个任务之间有先后顺序执行,一个执行完下个才能执行。

异步:多个任务之间没有先后顺序,可以同时执行有时候一个任务可能要在必要的时候获取另一个

同时执行的任务的结果,这个就叫回调!

阻塞:如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。

非阻塞:如果不会卡住,可以继续执行,就是说非阻塞的。

同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。

什么是僵尸进程和孤儿进程?怎么避免僵尸进程?

孤儿进程:父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。

僵尸进程:进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。

避免僵尸进程的方法:

1.fork 两次用孙子进程去完成子进程的任务;

2.用 wait()函数使父进程阻塞;

3.使用信号量,在 signal handler 中调用 waitpid,这样父进程不用阻塞。

并行(parallel)和并发(concurrency)?

并行:同一时刻多个任务同时在运行。

并发:在同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况。

实现并行的库有:multiprocessing

实现并发的库有:threading

程序需要执行较多的读写、请求和回复任务的需要大量的 IO 操作,IO 密集型操作使用并发更好。

CPU 运算量大的程序程序,使用并行会更好。

IO 密集型和 CPU 密集型区别?

IO 密集型:系统运作,大部分的状况是 CPU 在等 I/O (硬盘/内存)的读/写。

CPU 密集型:大部份时间用来做计算、逻辑判断等 CPU 动作的程序称之 CPU 密集型。

Python中如何使用线程池和进程池?

python
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ProcessPoolExecutor
def func(x1, x2):
    print(x1, x2)
if __name__ == '__main__':
    p = ProcessPoolExecutor(5)
    for i in range(10):
        p.submit(func, i, i)
    t = ThreadPoolExecutor(5)
    for i in range(10):
        t.submit(func, i, i)

threading.local的作用?

threading.local()的作用就是为每个线程开辟一个独立的空间进行数据存储。

请用 python 写出一个生产者和消费者的非阻塞程序

要求:

  • 生产者方法负责每三毫秒生产一个数字 n ,从 1 到 10000 ,第一次生产 1 , 第二次生产 2 ,以此类推,消费者方法负责每一毫秒消费一个生产出来的数字(输出至屏幕),消费者每消费一次在全局变量 g_count中计数+ 1
  • 要求使用多进程或多线程的方式,一个生产者和三个消费者,且必须为数据安全的模式 g_count = 0

线程池锁应用

问题:写一个函数,使用10个线程(0, 9)依次打印1~1000个数字,线程0只打印以0结尾的数字,线程1只打印以1结尾的数字,依次类推。

这个起线程池,加锁即可:

python
import time
import threading
from threading import Lock
from concurrent.futures import ThreadPoolExecutor


def task(n):
    l.acquire()
    time.sleep(0.001)
    # print(threading.current_thread().ident, n)  # 你自己测试的时候,可以放开这行代码
    if threading.current_thread().ident in d:
        d[threading.current_thread().ident].append(n)
    else:
        d[threading.current_thread().ident] = [n, ]
    l.release()


if __name__ == '__main__':
    d = {}
    l = Lock()
    t = ThreadPoolExecutor(10)
    for i in range(1, 31):  # 我为了结果展示方便,这里就循环到31就行了,你可以换成1000
        t.submit(task, i)
    t.shutdown()
    print(d)
    """
    {
        51748: [1, 11, 21], 
        4808: [2, 12, 22], 
        14676: [3, 13, 23], 
        45084: [4, 14, 24], 
        32020: [5, 15, 25], 
        50204: [6, 16, 26], 
        27292: [7, 17, 27], 
        34844: [8, 18, 28], 
        44876: [9, 19, 29], 
        50240: [10, 20, 30]
    }
    """

进程之间如何进行通信?

  1. 管道:速度慢,容量有限,只有父子进程能通讯

  2. FIFO:任何进程间都能通讯,但速度慢

  3. 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题

  4. 信号量:不能传递复杂消息,只能用来同步

  5. 共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

什么是并发和并行?

并行 : 并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核的CPU )

并发 : 并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率。

区别:

并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。

并发是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session。

解释什么是异步非阻塞?

同步阻塞形式

效率最低。拿排队的例子来说,就是你专心排队,什么别的事都不做。

异步阻塞形式

如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发(通知),也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;

异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。

同步非阻塞形式

实际上是效率低下的。

想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的。

异步非阻塞形式

效率更高,因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换。

比如说,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下,那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了。

很多人会把同步和阻塞混淆,是因为很多时候同步操作会以阻塞的形式表现出来,同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞。

参考:https://www.cnblogs.com/Eva-J/articles/8253549.html#_label5