0、python的底层网络交互模块有哪些
socket request urllib urllib3 grab pycurl
1、 什么是socket?为甚么用socket?
a. socket是 应用层与传输层(TCP/IP协议通信)的中间软件抽象层,就是一组接口.在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在socket接口后面,对用户来说,一组简单的接口就是全部
b. 用socket是为了完成C/S架构
2、简述基于tcp协议的套接字通信流程(实例化socket对象)
- 服务端: 创建socket对象, 绑定ip端口 bind(), 设置最大链接数 listen(), accept()与客户端的connect()创建双向管道, send(发送消息) recv(接收消息), close(关闭套接字)
- 客户端: 创建socket对象, connect()与服务端accept()创建双向管道, send() recv() close()
注意, 三次握手和四次挥手是在两端的send() recv() 之间完成的
3、osi七层模型及列举各层协议
DNS服务器是将域名解析成IP ,ARP协议是将IP转化成MAC地址 RARP协议是反向解析地址,将MAC地址解析成IP
- 应用层
- 应用层 'http FTP NFS'
- 表示层 'Telnet SNMP'
- 会话层 'SMTP DNS'
- 传输层 'TCP UDP'
- 网络层 'IP ICMP ARP'
- 网络接口层
- 数据链路层 'Ethernet PPP PDN SLIP FDDI'
- 物理层 'IEEE 802.1A, IEEE 802.11'
4、什么是C/S架构,什么是B/S架构
C/S 架构: 客户端-服务端架构 例如:QQ,微信
B/S 架构: 浏览器-服务端架构 例如:12306网站;淘宝等购物网站
'区别:'
'C/S 优点:' 交互性好,对服务器压力小,安全; '缺点:' 服务器更新时需要同步更新客户端
'B/S 优点:' 不需要更新客户端 '缺点:' 交互性差,安全性低
5、简述TCP协议的三次握手、四次挥手
C(客户端) S(服务端)
- 三次握手的目的是为了'建立一个双向连接'
C--请求-->S--回应/请求-->C--回应-->S 建立连接
- 传输数据
- 四次挥手的目的是为了'断开连接'
C--数据传完,请求断连接-->S--回应/断开连接
S--数据传完,请求断连接-->C--回应/断开连接
- 为什么断连接需要四次:客户端与服务端都是在传完数据后才会断开连接
- TCP为什么是可靠传输呢?
'面向连接'
6、什么是arp协议
全称是
Address Resolution Protocol
,中文名是地址解析协议,使用ARP协议可实现通过IP地址获得对应主机的物理地址(MAC地址)
7、TCP/UDP之间有什么区别
- TCP面向连接,发送数据前建立连接; UDP是无连接的,数据发送不需要建立连接
- TCP提供可靠的服务.也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复且按序到达;UDP尽最大努力交付,但不保证可靠,也不保证交付
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP面向报文
- 连接方式: 每一条TCP连接只能是点到点;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节,UDP的首部开销小,只有8字节
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
8、tcp协议的通信为什么比udp协议的通信更可靠
tcp: 可靠, 对方接收消息后,返回确认消息,才发送下一条消息;没有返回确认消息就重发上一条消息
udp: 不可靠, 收到数据后直接发送,不需要对方回应
9、什么是局域网和广域网
- 局域网
局域网(Local Area Network) 简称LAN , 是指在某一区域内由多台计算机互联成的计算机组。某一区域指的是统一办公室、同一建筑物、同一公司和同一学校等,一般是方圆几千米以内。
局域网可以实现文件管理,应用软件共享,打印机共享,扫描仪共享,工作组内的日程安排,电子邮件和传真通信服务等功能。
局域网是封闭型的,可以由办公室内两台计算机组成,也可以由一个公司内的上千台计算机组成
- 广域网
广域网(Wide Area Network) 简称WAN,是一种跨域大的,地域性的计算机网络的集合.通常是跨越省,市,甚至一个国家.
广域网包括大大小小不同的子网,子网可以是局域网,也可以是小型的广域网
- 他们的区别
从简单意义上理解就是覆盖范围大小的区别
10、粘包现象
什么是粘包?
多个数据包储存在一个缓存空间内,接收方在读取的时候不确定发送方的发送边界,只能用一个预估值来接收,若双方的发送数据和接收数据不一致,就会出现粘包现象,(从发送方来看,发送的多个数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。)
发生粘包的两种情况:
a. 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会当做一个包发出去,产生粘包)
b. 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
注意:只有TCP有粘包现象,UDP永远不会粘包
TCP. 是流式协议,不知道啥时候开始,啥时候结束.
UDP. 是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据。
粘包现象的主要原因:所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。(接收端不知道发送端将要传送的字节流的长度)
解决粘包的思路:围绕如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据
实际方案:使用struct模块将数据转成固定长度的bytes类型,服务端使用struct模块接收
粘包的处理方法
(1)当时短连接的情况下,不用考虑粘包的情况
(2)如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包
(3)如果双方建立长连接,需要在连接后一段时间内发送不同结构数据
接收方创建预处理线程,对接收到的数据包进行预处理,将粘连的包分开;
分包是指在出现粘包的时候我们的接收方要进行分包处理。(在长连接中都会出现) 数据包的边界发生错位,导致读出错误的数据分包,进而曲解原始数据含义。
11.什么是防火墙,有什么用
防火墙就是一个位于计算机和它所连接的网络之间的软件.该计算机流入流出的所有网络通信均要经过此防火墙
防火墙的功能
防火墙对流经它的网络通信进行扫描,这样就能过滤掉一些攻击,以免其在目标计算机上被执行.防火墙还可以关闭不使用的端口.而且他还能禁止特定端口的流出通信,封锁特洛伊木马.最后,他可以禁止来自特殊站点的访问,从而防止来自不明入侵者的所有通信
为什么使用防火墙
防火墙具有很好的保护作用.入侵者必须首先穿过防火墙的安全防线,才能接触目标计算机.你可以将防火墙配置成许多不同保护级别.高级别的保护可能会禁止一些服务,如视频流等.
防火墙的类型
防火墙可以是硬件自身的一部分,你可以将因特网链接和计算机都插入其中.防火墙也可以在一个独立的机器上,运行该机器作为它背后网络中所有的代理和防火墙.最后,直接连载因特网的机器可以使用个人防火墙
12、select poll epoll 模型的区别
I/O多路复用的本质就是: select/poll/epoll, 去监听多个socket对象,如果其中的socket对象变化,只要有变化,用户进程就知道了,
windows系统下的: select是不断轮询去监听socket,socket个数有限制,一般为1024个;
Linux(max)系统下的: poll还是采用轮询方式监听,只不过没有个数限制
Linux(max)系统下的: epoll并不是采用轮询方式去监听,而是采用回调函数的形式,当socket有变化时通过回调的方式主动告知用户进程
13 什么是GIL锁
python全局解释锁(GIL) 简单来说就是一个互斥体(锁),这样的机制只允许一个线程来控制python解释器
这就意味着任何一个时间点只有一个线程处于执行状态.GIL对指向单线程任务的程序员们来说没有影响,但是它成为了计算机密集型和多线程任务的性能瓶颈
也正是这样,即使拥有多个CPU核的多线程框架下都只允许一次运行一个线程,所以在python功能中其名声可谓是"臭名昭著"
GIL解决了python的什么问题
python利用引用计数来进行内存管理,这就意味着在python中创建的对象都有一个引用计数变量来追踪指向该对象的引用数量,当数量为0时,该对象占用的内存被释放
回到GIL上: python的问题在于这个引用计数变量需要在两个线程上同时增加或者减少时从竞争条件中无法得到保护,如果发生了这种情况,可能会导致泄露的内存永远不会被释放,异或更严重的是当一个对象的引用仍然存在的情况下错误的释放内存.这可能会导致Python程序崩溃或者带来各种诡异的bug
通过对跨线程分享的数据结构添加锁, 以保证数据不会不一致的被修改,这样做可以很好的保证引用计数变量的安全但是对每一个对象或者对象组添加锁意味着会存在多个锁,这就导致两个问题:
1. 死锁(只有当存在多个锁时才会发生)
2. 由于重复获取和释放锁而导致的性能下降
CIL是解释器本身的一个单一锁,它增加的一条规则表名任何python字节码的指向都需要获取解释锁,这有效的防止了死锁(仅存在一个锁)并且不会带来太多的性能开销,但是这使一个多线程任务(计算密集型任务)变成了一个单线程
对多线程的影响
GIL对I/O密集型任务多线程程序的性能没有太大的影响,因为在等待I/O时锁可以在多线程之间共享。
但是对于一个线程是完全计算密集型的任务来说(例如,利用线程进行部分图像处理)不仅会由于锁而变成单线程任务而且还会明显的增加执行时间。这种执行时间的增加是由于锁带来的获取和释放开销。
如何处理python中的GIL
最流行的方法是由多线程改为多进程,在这个方法中你使用的是多个进程而不是线程,每个python进程都有自己的python解释器和内存空间,隐藏GIL不会成为问题,
python拥有一个multiprocessing模块可以帮助我们轻松创建多进程
相对于多线程性能并没有提升太多,甚至还有些下降,这是因为进程管理有自己的开销,进程比线程"重",因此,这可能成为规模的瓶颈
Python GIL经常被认为是一个神秘而困难的话题。但是请记住作为一名Python支持者,只有当您正在编写C扩展或者您的程序中有计算密集型的多线程任务时才会被GIL影响
参考:
什么是python的全局解释锁(GIL)
14、python中如何使用线程池和进程池
常用的方法
apply 同步提交任务(一个一个提交任务) 没有多进程的优势,且比不开启进程池的时间还慢(还有启动和关闭,切换进程的时间)
apply_async 异步提交任务,获得任务函数的返回值 常用
close 关闭 阻止往池中添加新的任务
join 等待所有工作进程(子进程)退出 join依赖close 一个进程池必须先close再join 好基友
map 接受一个任务函数,和一个 iterable(可迭代的) 节省了 for 循环和 close、join 是一种简便的写法
利用get()方法获取返回值,最长用的是利用列表保存起来,最后用for循环再get()
import time
from multiprocessing import Pool,Process
def func(i):
time.sleep(1) #在子进程处停顿一下
i**i # 只是求 计算 不用打印
if __name__ == '__main__':
qishi = time.time() '进程池部分'
p = Pool(5)
for i in range(100):
p.apply_async(func=func,args=(i,)) # 异步(互不干涉)提交了一个任务
p.close()
p.join()
print(time.time() - qishi) # 0.3305974006652832
qishi = time.time() '多进程部分'
li = [Process(target=func,args=(i,)).start() for i in range(100)]
print(time.time() -qishi) # 3.382025718688965
'对于高IO(输入输出)的代码 使用多进程更好 (相对的)'
小结:
对于纯计算型的代码 使用进程池更好
进程池比起多进程来说 节省了开启进程回收进程资源的时间 。给操作系统调度进程降低了难度
对于高I/O(输入输出)的代码,使用多进程更好一些(相对于进程池)
高IO操作(输入输出)
写数据库(从内存到数据库)——往文件里写(从内存到文件)——上传网络(内存到网络)——输出
读数据库(从数据库到内存)——从文件里读(从文件到内存)——下载网络(网络到内存)——输入
import time
import threadpool
def sayhello(str):
print ("Hello ",str)
time.sleep(2)
return f'Hello,{str}'
def hello(m, n, o):
"""传递多个参数"""
print("m = %s, n = %s, o = %s" % (m, n, o))
if __name__ == '__main__':
name_list = ['xiaozi', 'aa', 'bb', 'cc']
start_time = time.time()
# 进程: 耗时8s
# for i in map(sayhello,name_list):
# print(i)
# 线程: 耗时2s
pool = threadpool.ThreadPool(len(name_list)) # 创建 len(name_list) 个线程,不浪费线程
requests = threadpool.makeRequests(sayhello, name_list) # 开启线程
[pool.putRequest(req) for req in requests] # 将所有要运行多线程的请求扔进线程池
pool.wait() # 等待所有线程完成工作后退出
print('%d 时间' % (time.time() - start_time))
----传递多个参数-----
# 方法1
lst_vars_1 = ['1', '2', '3']
lst_vars_2 = ['4', '5', '6']
func_var = [(lst_vars_1, None), (lst_vars_2, None)]
# func_var = [lst_vars_1, lst_vars_2] # 报错,缺少必要的参数 n 和 o
# 方法2
dict_vars_1 = {'m': '1', 'n': '2', 'o': '3'}
dict_vars_2 = {'m': '4', 'n': '5', 'o': '6'}
# func_var = [(None, dict_vars_1), (None, dict_vars_2)]
# func_var = [dict_vars_1, dict_vars_2] # 报错,缺少必要的参数 n 和 o
print(func_var)
pool = threadpool.ThreadPool(2)
requests = threadpool.makeRequests(hello, func_var)
[pool.putRequest(req) for req in requests]
pool.wait()
15、threading.local 的作用
python3中有两个常用的线程模块:
_thread
threading(推荐使用)
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
run(): 用以表示线程活动的方法。
start():启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
示例:
import threading
from threading import currentThread 获取线程信息
import time
class myThread (threading.Thread): # 创建线程类 继承 threading.Thread
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self) # 调用 父类的方法
self.threadID = threadID
self.name = name
self.counter = counter
def run(self): # 线程活动的方法。
print ("开启线程: " + self.name)
print ('仅获取线程ID',threading.get_ident())
print ('线程信息', currentThread())
'调用的哪个类(线程名, 启动/终止 ,线程id)'
'os.getpid = 取进程id'
'os.getppid = 取父进程id'
# 获取锁,用于线程同步
threadLock.acquire()
hanshu(self.name, self.counter, 3) # 调用 hanshu 函数
# 释放锁,开启下一个线程
threadLock.release()
def hanshu(threadName, delay, counter):
while counter:
time.sleep(delay) # 延时
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock() # 获取锁,用于线程同步
threads = [] # 线程列表
# 创建新线程
thread1 = myThread(5, "Thread-1", 1) # 参数: id name 延时时间
thread2 = myThread(6, "Thread-2", 2)
# 开启新线程
thread1.start()
thread2.start()
# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)
# 等待所有线程完成
for t in threads:
t.join()
print ("退出主线程")
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
如果不存在多个线程修改同一数据,则不用加锁
重点
自定义Local对象为每个线程(协程)开辟一块空间进行数据储存
threading.local()这个方法的特点用来保存一个全局变量,但是这个全局变量只有在当前线程才能访问,如果你在开发多线程应用的时候 需要每个线程保存一个单独的数据供当前线程操作,可以考虑使用这个方法,简单有效。
为什么要做数据隔离?
每个人请求需要做数据隔离,不然会出现数据混乱。每个请求数据不同,需要放在不同的空间,根据线程不同,取不同的值。
真正请求来了之后,会通过local对象为他创造一块空间,每个请求来都会做个数据隔离
16、什么是操作系统
操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序
它位于计算机硬件和用户软件之间的内置软件
操作系统应该分成两部分功能:
1.隐藏了硬件接口,为应用程序员提供调用硬件资源更好更方便,更清晰的模型(系统调用接口).程序员可以不用在考虑操作硬件上的细节,专心开发自己的程序即可
2.将程序对硬件资源的竞态请求变得有序化
例如:很多应用软件其实是共享一套计算机硬件,比方说有可能有三个应用程序同时需要申请打印机来输出内容,那么a程序竞争到了打印机资源就打印,然后可能是b竞争到打印机资源,也可能是c,这就导致了无序,打印机可能打印一段a的内容然后又去打印c...,操作系统的一个功能就是将这种无序变得有序。
17、进程间如何通信
进程间通信有多种方法
包括信号,信号量,共享内存,socket,管道,消息队列
管道
低级的通信机制,消息队列比管道高级多了,管道分PIPE和FIFO,PIPE是无名的,所以只能在进程内或父子进程间通信,FIFO可任何两个进程间通信了。不过这两个依然比较低级,完成高级的应用服务器还需要消息队列等。
示例
重点
关注管道端点的管理问题
注意
管道在数据管理上是不安全的
消息队列
用于消息,不是简单的数据信息传递,消息队列还包括消息有优先级、消息到达通知等丰富内容。
实现机制
管道 + 锁
作用
维护了先进先出的秩序,还保证了数据在进程之间的安全
方法:
Queue( maxsize ) 创建共享进程队列, maxsize是队列中允许的最大项数,如果省略此参数,则无大小限制
q.put( x ) 向队列中存放 x ,如果队列满了,程序就会挂起,直到数据被取走
q.full() 校验队列是否满值,返回True,False
q.get() 从队列中获取一个值(队列按照先进先出的顺序),如果队列为空,则程序挂起,直到从队列中获取一个值
q.empty() 校验队列是否为空,返回True,False
q.put_nowait( x ) 向队列中储存值,但队列满后,程序不会挂起,而是直接报错
q.get_nowait() 向队列中取值,但队列空后,程序不会挂起,而是直接报错
q.close() 关闭队列
try:
q.get_nowait(3) # 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
print('队列已经空了')
展望未来,基于消息传递的并发编程是大势所趋
即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合,通过消息队列交换数据。这样极大地减少了对使用锁定和其他同步手段的需求,还可以扩展到分布式系统中。
但进程间应该尽量避免通信,即便需要通信,也应该选择进程安全的工具来避免加锁带来的问题。
以后我们会尝试使用数据库来解决现在进程之间的数据共享问题。
了解 rpc 远程过程调用
18、什么是并发和并行
两者区别: 一个交替执行,一个同时执行
什么是同步和异步
异步: 多任务,多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线(两个程序互不干涉,没有相同的步骤)
同步:多任务,多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线(一个程序完成后,才调用下一个程序,有相同的步骤)
什么是堵塞和非堵塞
堵塞:从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞,典型示例:IO操作
非堵塞:从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞
19、生产者消费者模型应用场景
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
基于队列实现生产者消费者模型:有一个问题,进程永远不会结束,因为生产者在生产完就结束了,而消费者则挂起,等待生产者的产品
所以,我们要让生产者在生产完后,网队列中发一个结束信后,或主进程发一个结束信号,等消费者接收信号后结束进程
让生产者发送结束信号None
from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
while True:
res=q.get()
if res is None:break #收到结束信号则结束
time.sleep(random.randint(1,3))
print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
def producer(q):
for i in range(10):
time.sleep(random.randint(1,3))
res='包子%s' %i
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
q.put(None) #发送结束信号
if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer,args=(q,))
#消费者们:即吃货们
c1=Process(target=consumer,args=(q,))
#开始
p1.start()
c1.start()
print('主')
主进程在生产者生产完毕后发送结束信号None
from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
while True:
res=q.get()
if res is None:break #收到结束信号则结束
time.sleep(random.randint(1,3))
print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
def producer(q):
for i in range(2):
time.sleep(random.randint(1,3))
res='包子%s' %i
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer,args=(q,))
#消费者们:即吃货们
c1=Process(target=consumer,args=(q,))
#开始
p1.start()
c1.start()
p1.join()
q.put(None) #发送结束信号
print('主')
当有多个生产者和消费者时,我们需要用一个很low的办法解决问题:有几个消费者就发几次结束信号
from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
while True:
res=q.get()
if res is None:break #收到结束信号则结束
time.sleep(random.randint(1,3))
print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
def producer(name,q):
for i in range(2):
time.sleep(random.randint(1,3))
res='%s%s' %(name,i)
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer,args=('包子',q))
p2=Process(target=producer,args=('骨头',q))
p3=Process(target=producer,args=('泔水',q))
#消费者们:即吃货们
c1=Process(target=consumer,args=(q,))
c2=Process(target=consumer,args=(q,))
#开始
p1.start()
p2.start()
p3.start()
c1.start()
p1.join() #必须保证生产者全部生产完毕,才应该发送结束信号
p2.join()
p3.join()
q.put(None) #有几个消费者就应该发送几次结束信号None
q.put(None) #发送结束信号
print('主')
20、什么是异步非堵塞
在银行柜台办理业务的时候,通常会遇到4种情况
同步阻塞:在柜台前排队(同步),在排队过程中不做任何其他事情(阻塞)
同步非阻塞:在柜台前排队(同步),在排队过程中做其他事情,比如玩手机,注意,玩手机的时候需要不时地去看是否已经排队排到了(非阻塞)
异步阻塞:叫号排队,不用一直等在柜台前(异步),在排到前不做任何其他事情(阻塞)
异步非阻塞:叫号排队,不用一直等在柜台前(异步),在排到前做其他事情,比如玩手机,玩手机的时候不用关心是否排队排到了,因为会叫号通知(非阻塞)
总结:
同步和异步仅仅关注的是消息如何通知的机制
阻塞和非阻塞关注的是等待消息通知时的状态
最后,需要注意理解的是“消息通知机制”和“等待消息通知时的状态”这两个概念,这是理解同步/异步、阻塞/非阻塞四个概念的关键所在。还有我们在讨论这四个概念的时候一定要放在同一个层级,比如操作系统级别,框架级别,业务代码级别等,因为一个事件在不同层级所属的性质不一定一样,只有在同一个层级,才能去讨论它的性质是同步/异步还是阻塞/非阻塞。
21、路由器和交换机的区别
路由器
1.在网络层,可以处理"TCP/IP"协议, 根据IP地址寻址
2.可以为局域网自动分配IP和虚拟拨号,连接不同的网络
3.把一个IP分给多个主机来使用,对外IP是相同的
4.可以提供防火墙
交换机
1.在数据链路层,根据MAC地址寻址
2.可以扩大局域网接入点,让更多的电脑接入局域网
3.把多个主机连接起来,对外IP不同
4.用来分配网络数据
22、什么是域名解析
域名解析是指将域名解析为IP。域名是IP的代名词,没有用户会去记忆你的IP,为了主机的安全你也不想把IP肆意透漏给别人。用户访问网站都是访问域名。正常的使用域名,需要用到域名解析,否则直接访问域名是无法打开网站的。
域名的解析工作由DNS服务器完成。DNS服务器会把域名解析到一个IP地址,然后在此IP地址的主机上将一个子目录与域名绑定。域名解析时会添加解析记录,这些记录有:A记录、AAAA记录、CNAME记录、MX记录、NS记录、TXT记录、SRV记录、URL转发。
A记录: 将域名指向一个IPv4地址(例如:100.100.100.100),需要增加A记录
AAAA记录: 将主机名(或域名)指向一个IPv6地址(例如:ff03:0:0:0:0:0:0:c1),需要添加AAAA记录
CNAME记录: 如果将域名指向一个域名,实现与被指向域名相同的访问效果,需要增加CNAME记录。这个域名一般是主机服务商提供的一个域名
MX记录: 建立电子邮箱服务,将指向邮件服务器地址,需要设置MX记录。建立邮箱时,一般会根据邮箱服务商提供的MX记录填写此记录
NS记录: 域名解析服务器记录,如果要将子域名指定某个域名服务器来解析,需要设置NS记录
XT记录: 可任意填写,可为空。一般做一些验证记录时会使用此项,如:做SPF(反垃圾邮件)记录
SRV记录: 添加服务记录服务器服务记录时会添加此项,SRV记录了哪台计算机提供了哪个服务。格式为:服务的名字.协议的类型(例如:_example-server._tcp)。
显性URL转发记录: 将域名指向一个http(s)协议地址,访问域名时,自动跳转至目标地址。例如:将www.liuht.cn显性转发到www.itbilu.com后,访问www.liuht.cn时,地址栏显示的地址为:www.itbilu.com。
隐性UR转发记录L: 将域名指向一个http(s)协议地址,访问域名时,自动跳转至目标地址,隐性转发会隐藏真实的目标地址。例如:将www.liuht.cn显性转发到www.itbilu.com后,访问www.liuht.cn时,地址栏显示的地址仍然是:www.liuht.cn。
23、如何修改本地的hosts文件
hosts文件时一个没有扩展名的系统文件,他的主要作用是加快域名解析,屏蔽网站
Hosts文件定义了IP地址和主机名的映射关系,是一个映射IP地址和主机名的规定。可以用文本文件打开!当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,浏览器会立即打开对应网页,如果没有找到,则浏览器会将网址提交DNS服务器进行IP地址解析。这也是提高快速打开网页的方法!
hosts文件通常是在"c:\windows\system32\drivers\etc"
修改hosts文件:
先将hosts文件复制到桌面上,修改hosts文件,最后替换C盘的hosts文件
24、什么是CDN
CDN全称是Content Delivery Network,即内容分发网络.CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或者网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,有缓存服务器直接响应用户请求
通俗的来说:当一个新的且有CDN服务的网站上线后,第一个访问此网站的人会访问的原网站,并且将该网站缓存在就近的CDN服务器中,等该区域第二个人访问的时候会就近从的从CDN缓存服务器上获取该网站
CDN服务主要应用于证券、金融保险、ISP、ICP、网上交易、门户网站、大中型公司、网络教学等领域。另外在行业专网、互联网中都可以用到,甚至可以对局域网进行网络优化。
什么是HTTP
HTTP(超文本传输协议)是一个应用层协议,有请求和响应构成,是一个标
准的客户端服务器模型
HTTP是一个短连接,无状态的协议
默认端口为80 HTTPS默认端口号443
工作流程
客户端请求建立连接
三次握手
客户端发送请求 格式: 请求类型/请求的资源/版本号 请求头部
/r/n 请求数据(如果是GET请求,则为空)
服务端返回响应 格式:版本号/状态码/状态消息/ 示例:HTTP/1.1 200 OK 响应头 /r/n 响应正文
客户端将响应信息显示在网页上,
四次挥手,然后跟服务器断开连接