网络编程(四)
1. 输入 URL 中间经历的过程
回答这个问题应该从以下几个方面入手:
- 中间设计到哪些过程
- 包含哪些网络协议
- 每个协议都干了什么?
DNS 查询(解析 IP) --> TCP 握手 --> HTTP 请求 --> 反向代理 Nginx --> uwsgi/gunicom(将请求转发到 web 框架层) --> Web APP 响应 --> TCP 挥手
三次握手和四次挥手
用 wireshark 找包工具可以查看
2. TCP 和 UDP 区别
- TCP :面向连接,可靠地,基于字节流, 发送后需要确认
- UDP:无连接,不可靠,面向报文,不需要确认
3. HTTP
HTTP 请求
HTTP 请求由:状态行、请求头、消息主体组成,可以使用 httpie 模块查看:
pip install httpie
http baidu.com # 发送 get 请求
http -f POST baidu.com hello=world -v # 发送 post 请求
[图片上传失败...(image-277c36-1593095791043)]
HTTP 响应组成
HTTP 响应组成:状态行、响应头、响应正文
3.1 HTTP 常见状态码
- 1xx:服务器收到请求,需要请求者继续执行操作
- 2xx:操作被成功接收并处理
- 3xx:重定向,需要进一步操作完成请求
- 4xx(客户端错误):请求有语法错误或无法完成请求
- 5xx(服务端错误):服务器处理请求过程中发生错误
3.2 GET 和 POST 区别
- RESTful 语义上一个是获取,一个是新建
- GET 是幂等的,POST 是非幂等的
- GET 请求参数放在 URL 明文,有长度限制;POST 请求放在请求体中,更安全
幂等性
所谓幂等就是无论调用多少次都得到相同结果的 HTTP 方法,如:a = 4
是幂等的,但 a += 4
就是非幂等的,幂等的方法客户端可以安全地重发请求。安全是指是否会修改数据。
HTTP 请求方法 | 是否幂等 | 是否安全 |
---|---|---|
options | yes | yes |
get | yes | yes |
head | yes | yes |
put | yes | no |
post | no | no |
delete | yes | no |
no | no |
3.3 什么是 HTTP 长连接
- 短连接:建立连接 -- 数据传输 -- 关闭连接(连接的建立和关闭开销大)
- 长连接:Connection:Keep-alive,保持 TCP 连接在一段时间内不会断开
- HTTP1.1 版本才有长连接,如果带有 Connection:Keep-alive 就表示是长连接
长连接可以保证 tcp 连接一段时间内不断开,那么它是如何区分不同的请求的呢?
它是通过请求的长度(Content-Length )和传输编码(Transfer-Encoding)来区分
image3.4 cookie 和 session 的区别
HTTP 是无状态的,即对于服务器来说,每一次请求都认为是一次新的请求(新人),那么如何识别用户呢?
解决:在服务端给用户生成一个标识,然后每次让客户端带过去给后端
- session:一般存储在服务器生成之后给客户端(通过 URL 参数或 cookie)
- cookie:一般存储在客户端,是实现 session 的一种机制,通过 HTTP cookie 字段实现
- session:通过服务器保存 sessionid 识别用户
4. TCP / UDP 和 socket 编程
-
了解 TCP / UDP 编程的原理
-
了解如何发送 HTTP 请求
-
了解如何使用 socket 模块,如何建立 TCP socket 客户端和服务端
-
客户端和服务端之间的通信
客户端:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 8080))
sk.sendall(b'Hello World!')
data = sk.recv(1024)
print(data.decode('utf-8'))
sk.close()
服务端:
import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.listen()
while True:
conn, addr = sk.accept()
data = conn.recv(1024)
print(data.decode('utf-8'))
conn.sendall('你好!'.encode('utf-8'))
conn.close()
sk.close()
socket 发送 HTTP 请求
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 80))
http = b'GET /HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n'
s.sendall(http)
buf = s.recv(1024)
print(buf)
s.close()
5. 五种IO 模型
Unix 网络编程中提到了 五种网络模型:
- Blocking IO:阻塞型
- Nonblocking IO:非阻塞型
- IO multiplexing:IO 多路复用
- Signal Driven IO:信号驱动(不常用)
- Asynchronous IO:异步 IO
5.1 如何提高并发能力
常见的提升并发能力的方式:
- 多线程模型,创建新的线程处理请求(不可能无限地增加线程)
- 多进程模型:创建新的进程处理请求(占用资源,难以同时创建太多)
- IO 多路复用:实现单线程同时处理多个 socket 请求
5.2 IO 多路复用
即操作系统提供同时监听多个 socket 的机制,Linux 常见的是:select/poll/epoll,可以用单进程处理多个 socket。
- 阻塞时 IO:遇到 IO 时会阻塞,直至 IO 阻塞完毕才继续处理
- IO 多路复用:也会阻塞(监听 socket 的文件描述符),因为是同时监听多个 socket,所以即使阻塞,它也会去处理别的 socket 请求。
while True:
envents = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
select/poll/epoll 的区别
imagePython 如何实现 IO 多路复用
- Python 的 IO 多路复用基于操作系统实现(select/poll/epoll)
- Python2 的 select 模块,Python3 的 selectors 模块
- select:采用轮询方式检测就绪事件,连接有限制,时间复杂度为 O(n)
- poll:采用轮询方式检测就绪事件,连接有限制,时间复杂度为 O(n)
- epoll:采用回调方式检测就绪事件,连接无限制,时间复杂度为 O(1)
具体可参考官网:https://docs.python.org/3/library/selectors.html
5.3 Python 并发网络库
- Tornado:并发网络库同时也是一个 Web 微框架
- Gevent 绿色线程(greenlet)实现并发,猴子补丁修改内置 socket
- Asyncio:Python3 内置的并发网络库,基于原生协程
Tornado 框架
Tornado 使用与微服务,实现 RESTful 接口:
- 底层基于 Linux 多路复用
- 可以通过协程或者回调实现异步编程
- 不过生态不完善,相应的异步框架不如 ORM 完善
import tornado.ioloop
import tornado.web
from tornado.httpclient import AsyncHTTPClient
class APIHandler(tornado.web.RequestHandler):
async def get(self):
url = 'http://httpbin.org/get'
http_client = AsyncHTTPClient()
resp = await http_client.fetch(url)
print(resp.body)
return resp.body
def make_app():
return tornado.web.Application([
(r'/api', APIHandler),
])
if __name__ == '__main__':
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
Gevent
Gevent 是一个高性能的并发网络库:
- 基于轻量级绿色线程 greenlet 实现并发
- 需要注意 monkey patch,修改了内置的 socket 改为非阻塞
- 配合 gunicorn 和 gevent 部署 作为 wsgi server
推荐学习书籍:《Gevent 程序员指南》
import genvent.monkey
genvent.monkey.patch_all()
import gevent
import requests
def fetch():
url = 'http://httpbin.org/get'
resp = requests.get(url)
print(len(resp.text), i)
def asynchronous():
threads = []
for i in range(1, 10):
threads.append(gevent.spwan(fetch, i))
gevent.joinall(threads)
print('Asynchronous:')
asynchronous()
Asyncio 模块
Asyncio 基于协程实现的内置并发网络库:
- Python3 引入到内置库,协程+事件循环
- 生态不够完善,没有大规模生产环境检验
- 目前应用不够广泛,基于 Aiothhp 可以实现一些小的服务
import asyncio
from aiohttp import ClientSession
async def fetch(url, session):
async with session.get(url) as response:
return await response.read()
async def run(r=10):
url = 'http://httpbin.org/get'
tasks = []
async with ClientSession() as session:
for i in range(10):
task = asyncio.ensure_future(fetch(url, session))
task.append(task)
responses = await asyncio.gather(*tasks)
for res_body in responses:
print(len(resp_body))
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run())
loop.run_until_comlete(future)
6. 总结
-
TCP 和 HTTP 是重点和常考点
-
TCP/UDP/HTTP 抓包工具:wireshark/cur/hhtpie 等
-
了解 socket 编程原理以及 IO 多路复用编程
-
熟悉常见并发网络库:Tornado、Gevent、Asyncio
思考
编写一个异步爬虫类,使用 gevent 或 asyncio
- 可以选择使用 gevent 或 asyncio(推荐),编写一个异步爬虫类
- 要求该类可以传入需要抓取的网址列表
- 要求该类可以通过继承方式提供一个处理 response 的方法
7. 面试题
1、TCP/IP分别在模型的哪一层
TCP 在传输层(运输层),IP 在网络层(互联网层)。
2、socket长连接是什么意思
TCP/IP
TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。
在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
在传输层中有TCP协议与UDP协议。;
在应用层有:通过TCP协议来通信的应用层协议包括FTP、HTTP、TELNET、SMTP等 ;
通过UDP协议来通信的应用层协议包括DNS、TFTP等;
短连接
连接->传输数据->关闭连接
HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。
长连接
连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。
http的长连接
HTTP也可以建立长连接的,使用Connection:keep-alive
,HTTP 1.1默认进行持久连接。HTTP1.1和HTTP1.0相比较而言,最大的区别就是增加了持久连接支持(貌似最新的 http1.0 可以显示的指定 keep-alive),但还是无状态的,或者说是不可以信任的。
什么时候用长连接,短连接?
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三次握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,再处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
socket解读,http和socket之长连接和短连接区别!
**3、select、poll 和 epoll 的区别
select,poll,epoll都是IO多路复用的机制。同时监听多个 socket 对象,当 socket 对象有变化时(有数据)就通知用户进程
- select:采用轮询的方式去监听 socket,最大连接数一般为 1024个,单进程下支持高并发,跨平台
- poll :也是采用的轮询方式监听,但没有了个数限制,与
select
原理相同,改进的地方是请求和返回分离,避免每次都要重设数组 - epoll:连接无限制,每个 socket 对象绑定一个回调函数,当 socket 对象活动则触发回调函数,并把事件活动存入事件列表中,epoll 直接调用事件列表即可。即有变化时主动告知用户进程。也就是事件驱动
- `select`:单进程支持高并发,跨平台,最大支持 1024 个文件描述符;缺点:多次从内核到应用,应用到内核的数组拷贝
- `poll`:与 `select` 原理相同,由打开文件的上限决定,请求和返回分离,避免每次毒药重设数组,突破 1024 的限制,不能跨平台
- `epoll`:不管是 `poll` 还是 `select` 都需要遍历数组轮询,突破 1024 限制,不能跨平台。无须遍历整个文件描述符,只需遍历被内核 IO 时间异步唤醒,而加入 `ready` 队列的文件描述符。
4、TCP UDP区别;三次握手四次挥手讲一下
- TCP:面向连接、无消息保护边界、数据不会丢失,没有接收完的包,下次继续接收(除非收到 ack 即断开连接时,才会清空缓冲区内容),数据可靠、粘包。
- UDP:面向消息,为每条消息装上消息头。有消息保护边界、数据会丢失,不可靠,不会粘包。
1、三次握手
image image2、四次挥手
image1、第一次
client 发送一个 FIN ,用来结束连接。client 进程发出连接释放报文,并停止发送数据。释放报文首部:`FIN=1`,序列号 `seq=i`。
此时 client 进入 `FIN_WAIT_1` (终止等待1)状态。
2、第二次
server 收到这个 FIN 后,返回一个 `ACK`(确认),确认序号:`ack=i+1`。同时携带自己的序列号 `seq=j`。
此时, server 进入 `CLOSED_WAIT`(关闭等待)状态。
并通知高层的应用进程,此时处于半关闭状态,client 没有数据发送了,但 server 若发送数据,client 依然会接收,这种状态还会持续一段时间。
3、第三次
server 将最后的数据发送完毕后,发送一个 `FIN`(结束),确认序号:`ack=i+1`,同时携带序号 `seq=w`,准备 关闭 client 的连接,等待 client 的最后确认。
此时,server 进入 `LAST_ACK`(最后确认)状态。
4、第四次
client 发送 `ACK` 确认,并将确认序号+1:`ack=w+1`,而自己序列号 `seq=i+1`。
此时,client 进入 `TIME_WAIT`(时间等待)状态。
> **Note:**此时 client 并没有释放,必须等待 2MSL(最长报文段寿命)使君子后,当 server 撤销相应 TCB 后,从进入 `CLOSED` 状态。server 只要收到了 client 发出的确认,立即进入`CLOSED`状态。同样,撤销TCB后,就结束了这次的TCP连接
**为什么会是四次挥手?**
三次挥手时没有数据传输,而四次挥手时涉及到有数据传输。client 发出关闭请求,表示已经数据传输完毕,但是 server 有可能数据还未传输完毕,这时就需要已 server 端数据是否传输完毕为标准,因此需要四次。
当高并发时,现实情况往往是 server 先断开 client 连接,因为多保存 client 一次连接,就会多占用一些资源。因此在短时间内再次向 server 发起连接,会提示 serve time_wait。
**客户端突然挂掉了怎么办?**
正常连接时,客户端突然挂掉了,如果没有措施处理这种情况,那么就会出现客户端和服务器端出现长时期的空闲。解决办法是在服务器端设置保活计时器,每当服务器收到
客户端的消息,就将计时器复位。超时时间通常设置为2小时。若服务器超过2小时没收到客户的信息,他就发送探测报文段。若发送了10个探测报文段,每一个相隔75秒,
还没有响应就认为客户端出了故障,因而终止该连接。
其他答案:
三次握手用于建立连接,四次挥手用于断开连接
**三次握手**
- 首先客户端向服务器发送 `SYN` 报文,请求建立新的连接
- 服务端接收到来自客户端的请求后,结束 `LISTEN` 阶段,回复客户端 `ACK` ,表示确认客户端的报文 `seq` 序号有效,服务端能正常接收客户端发送的数据,并同意创建新的连接
- 客户端接收到 服务端返回的 `ACK` 后,明确了从客户端到服务端的数据传输是正常的,结束 `SYN+SENT` 阶段,也向服务端回复 `ACK` 确认号,并分配资源,这样 `TCP` 连接就建立了
**四次挥手**
- 客户端发送 FIN 报文(终止连接),服务端接收到后,若还有数据未发送完毕,则不会关闭 `socket`,可以继续发送数据。服务端发送 `ACK` 报文(告诉客户端你的请求已收到,但是我还没准备好,请继续等我的消息)
- 此时客户端进入 `FIN_WAIT` 状态,继续等待服务端的 `FIN` 报文;当服务端接收完数据后,则向客户端发送 `FIN` 报文,告诉客户端已经接收完数据,准备好关闭连接
- 客户端接收到 `FIN` 报文后,就可以关闭连接了,但是它不相信网络,怕服务端不知道要关闭,所以又给服务端发送一个 `ACK` 的确认报文,进入了 `TIME_WAIT` 状态,若服务端没有收到 `ACK` 则可以重传
- 服务端接收到 `ACK` 后,就断开连接,客户端等待 `2MSL` 后依然没有收到回复,则证明服务端已关闭连接,那么客户端也断开连接。
**为什么是四次挥手**
关闭连接时,服务端收到 `FIN` 报文时,很可能并不会立即关闭 `socket`,所以只能先回复一个 `ACK` 报文,告诉客户端,你发送的 `FIN` 报文我已经收到,只有等服务端所有的报文都发送完后,才发送 `FIN` 报文,因此不能一起发送,故而需要四次挥手。
因为三次连接的双方的资源,四次的话是要双方都能够安全地确认,都不传输数据了,才释放资源。
**为什么是三次握手**
因为通信是双向的,要保证服务端、客户端的输入输出都是对的,且能够正常传输数据
参考文章:`https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc`
5、TIME_WAIT过多是因为什么
6、http一次连接的全过程:你来说下从用户发起request——到用户接收到response
客户端发起 http 请求,服务器接收请求,解析请求(请求方法、请求体。。。),服务器根据解析出来的结果,返回相应的值,再由浏览器解析出来,最终呈现给用户查看。
使用 TCP 传输服务:
- 服务器在 80 端口等待客户端的请求
- 浏览器发起服务器的 TCP 连接(socket 套接字)
- 服务器接收来自浏览器的 TCP 连接
- 浏览器(HTTP 客户端)与 Web 服务器(HTTP 服务器)交换 HTTP 信息
- 关闭 TCP 连接
7、http连接方式。get和post的区别,你还了解其他的方式么?
- get:获取数据
- post:新建数据
- put:更新数据
- delete:删除数据
8、restful你知道么
网站即软件,软件和网络是两个不同的领域,很少有交集。软件开发主要针对单机环境,网络则主要研究系统之间的通信。互联网的兴起,使得这两个领域开始融合,现在我们必须考虑,如何开发在互联网环境中使用的软件。
RESTful 架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
也可以成为表现层状态转化。
9、状态码你知道多少,比如200/403/404/504等等
- 200:请求成功
- 301:资源被永久转义到其他 URL
- 403:Forbidden
- 404:请求资源不存在
- 500:内部服务器错误
- 504:充当网关或代理的服务器,未及时从远程服务器获取请求
- 400 Bad Request: /客户端请求有语法错误,不能被服务器所理解
- 503 Server Unavailable: 服务器当前不能处理客户端的请求,一段时间后可能恢复正常
- 401 Unauthorized: 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
10、http、https 区别
-
https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
-
http是超文本传输协议,信息是明文传输,https则是具有安全性的 ssl 加密传输协议。
-
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
-
ssl(Secure Sockets Layer),中文叫做“安全套接层
C/S 架构即客户端/服务端架构,B/S 架构(浏览器与服务端)也是 C/S 架构的一种。
11、OSI 七层
- 应用层:用户进程
- 表示层
- 会话层
- 传输层(运输层):TCP、UDP
- 网络层:ICMP、IP、IGMP
- 数据链路层:ARP、硬件接口、RARP
- 物理层
socket 是应用层与传输层(TCP/UDP协议)通讯的中间软件抽象层。它封装了 TCP/UDP协议,并提供一组接口,供开发软件使用。
[图片上传失败...(image-23045c-1593095791043)]
12、手写一个简单的 socket
客户端:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 8080))
while True:
msg = input('>>>').strip()
sk.send(msg.encode('utf-8'))
data = sk.recv(1024)
print(data.decode('utf-8'))
sk.close()
服务端:
import socket
sk = socket.socket()
sk.bind(('127.0.0.1', 8080))
sk.listen(5)
while True:
conn, addr = sk.accept()
while True:
try:
data = conn.recv(1024)
print(data.decode('utf-8'))
msg = input('>>>').strip()
conn.send(msg.encode('utf-8'))
except Exception as e:
break
conn.close()
sk.close()
13、介绍下协程,为何比线程还快
进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态。
因为线程会等待,遇到 IO 会阻塞,而协程遇到 IO 阻塞可以去处理别的请求,等 IO 阻塞过去了,可以调用回调函数执行别的操作
14. 异步 IO 和 IO 多路复用
1、异步 IO
2、IO 多路复用
通过一种机制,可以监视多个文件描述符,一旦某个描述符就绪(一般为读或者写就绪),能够通知程序进行相应的读写操作。
Python
中有一个 select
模块,提供了:select、poll、epoll
三个方法,分别调用系统的 select、poll、epoll
,从而实现 IO
多路复用。
IO
多路复用用于提升效率,单个进程可以同时监听多个网络连接 IO
,可以避免阻塞在 IO
上。原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个 socket
的状态后轮询结果。
IO 模型
- 阻塞(blocking IO)
- IO和非阻塞(non-blocking IO)
- 多路复用(IO multiplexing)
- 异步 IO(asynchronous IO)
- 信号驱动 IO(signal driven IO):不常用
15、为何基于 tcp
协议的通信比基于 udp
协议的通信更可靠?
-
tcp
:面向连接,给对方发送消息,收到后才会发下一条,若没有收到就重发 -
udp
:面向消息,一直发送数据,不需要对方回应,类似于广播
16、什么是 GIL
锁?
全局解释锁,在同一时间内,Python
解释器只能运行一个线程的代码,大大影响了 Python
多线程的性能,而由于历史原因,现在几乎无法消除。
之所以会影响线程性能,是当线程获得一个全局锁的时候,那么该线程的代码才能运行,而全局锁只有一个,所以使用多线程时,同一时刻也只有一个线程在运行,因此始终只能发挥出单核的性能,在如今的多核流行的时代,多线程显得很鸡肋。
17、TCP 和 UDP
的区别?
-
tcp
:面向连接,无消息保护边界,没有接收完的消息下次可以继续接收,除非ack
断开连接,数据可靠,粘包 -
udp
:面向消息,有消息保护边界,数据会丢失,不可靠,不会粘包
18、OSI
七层协议,TCP/IP
分别输入哪一层?
-
tcp
属于传输层,ip
属于网络层 -
OSI
七层:应用层、表示层、会话层、传输层(tcp/udp)、网络层(ip、icmp)、数据链路层(arp、硬件接口、rarp)、物理层
socket
是应用层与传输层通讯的中间软件抽象层,它封装了 tcp/udp
协议,并提供一组接口,供开发软件使用。
19、 手写一个 socket
客户端
import socket
sk = socket.socket()
sk.connect(('192.168.131.131', 9999))
while True:
msg = input('>>>').strip()
sk.send(msg.encode('utf-8'))
data = sk.recv(1024)
print(data.decode('utf-8'))
sk.close()
服务端
import socket
sk = socket.socket()
sk.bind(('192.168.131.130', 9999))
sk.listen(5)
while True:
conn, addr = sk.accept()
while True:
try:
data = conn.recv(1024)
print(data.decode('utf-8'))
msg = input('>>>').strip()
conn.send(msg.encode('utf-8'))
except Exception as e:
break
conn.close()
sk.close()
20、如何提高并发能力?
-
多线程模型:创建新的线程处理请求(但是不可能无限地增加线程
-
多进程模型:创建新的进程处理请求(占用资源,难以同时创建太多)
-
IO 多路复用:实现多线程同时处理多个
socket
IO 多路复用
即操作系统提供同时监听多个 socket
的机制,常见的有:select/poll/epoll
,可以单进程处理多个 socket
,阻塞 IO
和 IO
多路复用区别:
- 阻塞
IO
:遇到IO
会阻塞,直到阻塞过去 -
IO
多路复用:也会阻塞,但是它可以同时监听多个socket
文件描述符,当阻塞时可以处理别的socket
请求
常用并发编程库
Tornado、Gevent、Asyncio
21、进程、线程、协程的区别?
程序运行时,会在内存空间里形成一个独立的内存提,这个内存体有独立的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源。
1、进程
是系统进行资源分配和调度的一个独立单位,拥有独立的内存空间,不同进程间通过进程间通信来通信。由于比较重量,占据独立的内存空间,所以上下文进程间的切换(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对稳定安全。
2、线程
进程的一个实体,CPU
调度和分派的基本单位,不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是可以与同一线程的其他线程共享进程所拥有的全部资源。
线程间通信主要通过共享内存,上下文切换很快,资源开销很小,但相比进程不够稳定易丢失数据。
3、协程
一种用户态的轻量级线程,调度完全由用户自己控制,拥有自己的寄存器、上下文和栈。在调度切换时将寄存器上下文和栈保存到其他地方,切换回来时,恢复先前保存的寄存器、上下文和栈,直接操作栈则基本没有内存切换的开销
因为只有一个线程,不存在同时写变量冲突,可以不加锁的访问全局变量,所以上下文的切换非常快。
总结
- 进程是资源分配的单位,切换需要的资源最大,效率很低
- 线程是操作系统调度的单位,切换需要的资源一般,效率一般
- 协程切换任务资源很小,效率高
- 多线程、多线程根据
CPU
核数不一样可能是并行的,但协程在一个线程中,所以是并发 - 协程是异步,线程和进程是同步机制
- 多进程:进程间相互独立,适合计算密集型
- 多线程:共享同一内存空间,同步是必须考虑的问题,不合适做计算密集型任务,而是适合 IO 密集型任务
- 协程:另一种实现并发的手段,异步 IO,更适合 IO 密集型
并发和并行
- 并发:
CPU
在同一时间段内处理多个任务的能力 - 并行:
CPU
在同一时间间隔内交替处理多个任务的能力
22、分布式的理解
-
不同的业务分散到不同的业务上,将
MySQL
数据库服务、web
服务分布到不同的机器上。 -
微服务架构:分布式服务中的一种,在同一个系统中把业务拆分中单独的服务,而对于这个服务呢可以交给一个团队去独立的技术选型,独立的部署、独立的运维。