在一次项目应用过程中,需要调用另一个组件的API,在url中将其域名配置为了大写,并在本地的/etc/hosts配置了对应的IP和域名的映射关系,但是在项目运行过程中,调用接口的时候仍然出现了错误:
Failed to establish a new connection: [Errno -2] No address found
下面分析下在项目中调用API的代码:
API请求过程代码分析:
依然以requests.Session.request为例。
在request方法中,会调用self.send方法进行请求发送:
在Session.send方法中,会先根据url生成一个adapter对象,然后调用adapter对象的send方法发送请求:
adapter为requests/adapters.py中HTTPAdapter类的对象,其send方法中,则会调用self.get_connection获取一个conn对象,并调用conn的urlopen方法进行API调用:
在self.get_connection方法中,先调用 select_proxy获取代理,由于我在发送请求的时候没使用代理,所以proxy为None;接着调用self.poolmanager的connection_from_url方法获取url的连接
self.poolmanager为HTTPAdapter初始化的时候就配置好了:
PoolManager为导入的requests.packages.urllib3.poolmanager中的类:
在PoolManager中的connection_from_url方法里,返回的是调用self.connection_from_host的值:
而在connection_from_host中,又调用了connection_from_context;在connection_from_context中,又调用了connection_from_pool_key
在connection_from_pool_key中,调用了self._new_pool方法返回一个pool对象:
在_new_pool中,则是根据scheme获取对应的pool_cls,再对其进行初始化对象:
而pool_classes_by_scheme定义为:
HTTPConnectionPool又是从requests.connectionpool中导入进来的:
所以最终,conn就是一个requests.packages.urllib3.connectionpool.py中的HTTPConnectionPool对象。
下面则是对conn.urlopen方法的调用:
再来看看urlopen方法的定义。
在requests.packages.urllib3.connectionpool.py中,HTTPConnectionPool的urlopen方法定义为:
其中会先获取一个conn对象,再调用self._make_request方法发送请求。
在self._get_conn中,是从self.pool中获取block对应的conn对象,如果没有,则调用self._new_conn生成一个新的conn对象:
在HTTPConnectionPool中,_new_conn定义为:
而HTTPConnectionPool中,已经定义了ConnectionCls属性:
HTTPConnection是被导入进来的一个类:
所以,这里返回的就是一个requests/packages/urllib3/connection.py中的HTTPConnection对象
在_make_request中,chunked为默认的False,则调用conn.request方法:
而在requests/packages/urllib3/connection.py中,HTTPConnection为导入进来的模块:
from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection
在requests/packages/urllib3/packages/six.py中,
通过MoveModule将httplib模块命名为http_client(对于python3,则是将httplib命名为http.client)
所以最终调用的是httplib模块的request方法:
在httplib中,_send_request定义如下,会调用self.endheaders方法:
在endheaders方法中,定义如下,会调用self._send_output方法发送数据:
httplib中,_send_output方法定义如下,其中会调用self.send方法:
httplib中send方法定义为:
self.sock初始为None,self.auto_open初始为1:
所以会调用self.connect方法建立连接。
回到requests/packages/urllib3/connection.py中的HTTPConnection类中,connect方法定义为:
其中,_new_conn定义为:
这里会调用requests/packages/urllib3/util/connection.py中的create_connection方法:
在create_connection中,会调用socket的getaddrinfo获取host对应的IP信息:
如果本地域名解析中配置了对应的host的映射关系,如:
- 只配置大写:
100.73.54.21 IRONIC
则可以解析出对应的IP,如果没有配置映射关系,则解析失败:
- 只配置小写
100.73.54.21 ironic
则可以解析出对应的IP,如果没有配置映射关系,则解析失败:
- 大小写都配置:
100.73.54.21 IRONIC ironic
则都能解析:
以上是在项目运行过程发现的问题,但是我直接用python的IDLE的时候,如果只配置某一个,则仍然可以解析:
- 只配置大写
100.73.54.21 IRONIC
- 只配置小写
100.73.54.21 ironic
很神奇!!!