连接在多个线程之间如何保证安全?
比如使用 http 长连接,一个全局变量 g_http_conn
。
线程 a
在此连接上发出 request A
,再收到 response A
之前,
线程 b
又使用该连接发出 request B
。此时接受 response
会不会串了?
可能 Response A
先接受到,也可以是 Response B
先接受到。怎么判断接受到的 response
应该分发给 A
还是 B
?
同样的问题,Mysql
的长连接、redis
的长连接?
我想到的解决问题就是:
- 加锁,不允许并发
- 使用 TLS 或者连接池,来避免多线程共用同一个连接
但是如果就是想多线程共用一个连接,并且不想加锁呢?
不加锁的意思是:不对整个发出 request 到接受 response 的整个过程加锁,但是可以在发出 request 和接受 response 的过程加锁
不能这样:with lock:
send_request()
rev_response()
但是可以这样
with lock:
send_request()
do_something()
with lock:
rev_response()
回答:
你好!看到这个问题后,我觉得蛮有意思,想基于自己学过的知识和你一起探讨一下这个问题。
首先,你说的response分发问题实际上Http2.0已经帮忙处理了。
可以从你说的办法出发,依次来看看HTTP2.0究竟是如何实现的。
一、不允许并发
对整个发起请求——等待返回的过程加锁,可以保证request-response顺序性,即每发起一个request,其他要发起的request等待,这样就能保证不乱序。
如果使用的是HTTP1.0,压根不需要自己在客户端去加锁。
因为HTTP1.0已经做了这个事情,即每次发送一个http请求就新建一个TCP连接。显然这样性能很低。由此有了HTTP1.1。
二、连接复用和连接池
这里的连接池需要强调一下,并不应该是http的连接池,而是TCP的连接池。因为HTTP是无连接的,实际上你问题中说的http长连接也是不准确的说法,本质上是基于TCP的长连接,可以把TCP连接想象成马路,http只是一封封邮件,必须要有路才能把邮件送到,当一开始没有路的时候就先修路(TCP三次握手)。
HTTP1.1为了解决1.0的性能问题,HTTP1.1 支持了keep-alive,只要请求或响应头带上 Connection: keep-alive,就可以告诉对方先不要断开TCP链接,实现了TCP的复用。”
但这只是解决了频繁“修路”的问题,还没有解决可以并发发送Request的问题。
于是HTTP1.1还给出了管道的概念,多个请求可以并行发送,返回响应后再依次处理,这里就是用到了TCP的连接池。
注意是依次处理,因为HTTP1.0还没解决request-response的一一对应的功能。
三、HTTP2.0
为了解决一一对应的问题,它给request设置一个id,response通过id就能找到发送它的人(线程)。
至于是怎么做的呢?这个回答怕是很难解释清楚,可以去维基百科等网站去查询HTTP2.0的Stream和Frame概念。
四、补充
我是一名后端开发,在看一些RPC(比如DUBBO)的实现上时,看到远程调用返回结果怎么找到发起请求的线程,也是用的这种id的机制。
以上是 连接在多个线程之间如何保证安全? 的全部内容, 来源链接: utcz.com/p/938456.html