WSGI网站部署以及requests请求的一些随想.

一直想项目,没怎么写过后端服务,但很多时候,有些服务又是公用的,平时一般都用redis来当做通信的中间件,但这个标准的通用型与扩展信太差了.

与一些群友交流,建议还是起http服务比较好,自己也偏向与写一个后端服务,一来可以练手,二来分分钟可以部署到外网。

首先定型的是后端的框架为flask,没啥原因。但flask有一个问题就是通过自身内置的WSGI启动服务,会弹出警告,说该WSGI应用只能用于测试环境,不能用于工作环境,虽然以前部署了一个工作环境,也不知道具体效果,但出于完美的要求,网上随便专业的WSGI工作。

首先,不推荐uWSGI,因为我捣鼓了老半天,给我弹出编译安装的时候,gcc版本过高。我后面用了gunicorn, 这个pip安装方便多了。

这里写一写我自己的感悟,我的mac打字实在太卡了。明天到单位再码字吧

 

继续,继续。先来说一下我对于WSGI的理解,这个其实就是一个开启端口监控,并按照规定的协议,解析http协议内容,并调用框架写的试图函数。

(environ, start_response)

这是调用函数的参数,第一个environ是http请求过来的参数,第二个是后面响应需要的头部信息,函数的return是需要响应的response的响应体内容

有了这一层理解,所有的框架只不过是WSGI的调用函数内部的扩展,也就是通过读取environ的内容,然后内部定义具体逻辑,将start_response写入头部参数,

再更具实际需要是否提供响应体

 

有了这一层理解,就很好理解了。WSGI还负责接收解析http请求,如果没有nginx的前置,那所有请求的IO多路复用的任务就算交到他手上了,这里面就又涉及到IO多路复用的

SELECT,POLL,EPOLL,虽然我不能很好的理解相关具体内容,但大概的逻辑还是有的,都是通过操作系统提供的该方便,能够在一个进程的情况下,方便的监控维护多个IO连接。

因为如果一个IO开一条线程或者进程实在太浪费了,而且大多数时候一个连接并没有数据传输。就我自己的有限知识理解,因为IO传输涉及到中断,通过操作系统的中断信号,能够知道网卡是否有信号传输,然后IO的中断信号传输给EPOLL函数。通过一些标记就可以选出有数据需要读写的连接。前面的那套想法应该在EPOLL的时候应该是有的,我网上有查到一些资料,但SELECT与POLL未知,我就知道,说是每次轮询所有的连接,这样的话,传那个中断信号哦给它确实也没什么意思。

 

当取到一个需要读取信息的连接socket时,应用层已经及时取出在系统缓存区中的信息,第一不及时取出,浪费内存,第二,该连接的用户那边将无法及时获得响应。当一个应用层取出数据,然后逻辑处理,其中的逻辑处理,就是那些后端框架需要做的事情。

这里我记录一下,自己的感觉,当应用层收到需要操作的socket连接时,处理的两种方式,一种是开线程,还有一种就是所谓的异步操作协程。

就我个人理解,除非你的后端逻辑也都是异步协程操作,要不然还是老老实实的用开进程或者线程的逻辑,因为协程的操作是阻塞操作的地方都需要用到协程,对于一些数据库的操作,你必须都用到协程操作。我网上查tornado那个异步框架,据说连接处理用了EPOOL,然后内部的逻辑处理用了协程,但相关的联想出来的收缩就是阻塞了杂办,后面还是老老实实的开条线程去处理阻塞业务,要不然整个线程都卡住了。EPOOL的好处是能够快速的维护大量的连接socket,我怀疑tornado的快可能是在大量连接的时候,因为EPOOL的原因,能够快速找到处理的连接。

所以我个人认为,在数据库压力的范围内,没必要上协程或者EPOOL,就算EPOOL以及协程包括数据库的处理都是协程处理,但你的数据库顶的住吗?所以,一般的条件下,我自己还是选出简单的多线程就够了,关于多路复用更加无所谓了,普通网站没有那么高的并发,无论哪一种都够了。

 

上述的记录是自己的一些简单理解,如有不对还请指出

 

后面就是光遇requests的一些感悟吧,工作中,一次两个线程操作同一个requests.Sessioon。项目跑了很久了,两个线程也没有加锁,还想着两个线程操作着同一个socket连接,会出问题吗?其实我是多了吧了吗,当我用两个线程操作同一个线程的时候,其实框架的底部,已经给你做了连接池,两个线程默认情况下应该操作的是两个不同的socket连接。

这里有ression的介绍:https://requests.readthedocs.io/en/latest/user/advanced/

Session Objects

The Session object allows you to persist certain parameters across requests. It also persists cookies across all requests made from the Session instance, and will use urllib3’s connection pooling. So if you’re making several requests to the same host, the underlying TCP connection will be reused, which can result in a significant performance increase (see HTTP persistent connection).

然后我又去了urllib3网站,查看了具体的信息

网址: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#customizing-pool-behavior

最后在这个网站,作者进行了试验

网址: https://www.kawabangga.com/posts/2740

这里要感觉早一些时间大概的学习了一些计算机网络,当我们对一个计算机进行请求的时候,http首先要求的是tcp连接,所以在请求之前,必须要建立tcp连接,然后再发送具体的数据报文

这里就用到了连接池的作用,如果你发送完了数据,连接池可以帮助你维护着你的主机到请求目标服务器的连接,这样当你下次要发送内容的时候,无需从建立连接开发。直接拿一个连接好的socket发送数据既可。那为什么维护这些socket这么简单,不需要通过EPOOL,SELECT,POLL的方式,因为一般情况下,你请求服务器,服务器返回信息,处理完毕,后续服务器不会再主动给你发送信息, 就算给你发了信息,你也不需要了,缓存区满了以后,服务器那边想发通过这个连接也发不过来了。

这里我给自己再记录一下,我请求服务器,是一个主动行为,我可以把控所有的局面,但作为服务器来说就不一样,维护着一堆连接socket,你不知道下一秒哪一个会亮,所以必须一直监控着这些王八蛋

    def __init__(self, pool_connections=DEFAULT_POOLSIZE,
                 pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES,
                 pool_block=DEFAULT_POOLBLOCK):
        if max_retries == DEFAULT_RETRIES:
            self.max_retries = Retry(0, read=False)

上面是requests.Session的连接池默认的初始化,那个DEFAULT_POOLSIZE的值为10,也就是默认有10个连接池,一个池子10个连接。

一个池子对应着一个目标地址,也就是可以维护10个目标地址,每个目标地址10个连接。但如果你用了11个线程操作同一个目标地址,也没关系

多的一个,还会新开一个socket连接,只不过这个连接用完就丢了。

所以,简单的总结,就是爬虫的时候,如果多个线程使用同一个requests.Session,可以放心大胆的用,不用考虑线程安全问题。

 

两天的自学下来,乱七八糟写了一堆,写的不对请指出。收笔

 

posted @ 2022-10-20 21:59  就是想学习  阅读(88)  评论(0编辑  收藏  举报