Requests is an elegant and simple HTTP library for Python, built for human beings.

Python requests是最常用的Python第三方库之一,可用于发送HTTP请求,其采用了直观的API设计风格,使用起来非常简单方便。

Requests库是出自于大神 Kenneth Reitz 之手,我之前看过他写的《Python编程之美》,这本书可以让我们编写的代码更Pythonic,工程结构更加优美。Requests库的源码地址在:Github psf/requests,将其源码下载到本地之后,打开工程我们会发现其工程结构和代码规范都非常的优美,而且代码的核心逻辑也非常简洁,特别适合刚开始尝试阅读源码的同学学习。今天这篇文章就对Python requests库进行一个简单的源码分析。

1. 简单实例


首先,我们还是先用requests库编写一个简单的入门实例,然后再从实例作为入口深入分析其源码。

>>> import requests
>>> url = 'https://api.github.com/repos/psf/requests'
>>> r = requests.get(url)
>>> r.ok
True
>>> r.json()['description']
'A simple, yet elegant HTTP library.'
>>>

Requests库将常用的HTTP操作在api层进行了封装,我们可以非常方便地发起get/post/put/delete/head等操作,如果我们的应用场景比较复杂,也可以直接使用其内部对象进行定制化开发。

2. 核心流程


下图呈现了我们上面实例中get请求的核心时序流程,这里将一些非关键的步骤给忽略掉了,有兴趣的话可以自己去细读源码。接下来我们逐层解析时序图中涉及到的对象和流程。

首先是api层,requests库的api模块中封装了 get, post, options, head, put, delete等方法,我们可以直接调用这几个方法发起HTTP请求,这些方法中都调用了request()方法,request方法的代码如下,这里实例化了一个Session对象,然后调用了Session对象的request方法发起请求。

def request(method, url, **kwargs):with sessions.Session() as session:return session.request(method=method, url=url, **kwargs)

3. Session


Session对象可用于保存请求的状态,比如证书、cookies、proxy等信息,可实现在多个请求之间保持长连接。如果我们是直接使用的api层的方法发起的请求,那么在请求结束之后,所有的状态都会被清理掉。如果我们需要频繁向服务端发起请求,那么使用Session实现长连接可以大大提升处理性能。

Session类中维护了多个HTTPAdpater对象,分别用于处理不同scheme的请求,代码如下,我们也可以通过Session.mount()方法设置我们自定义的HTTPAdapter,比如根据要求重新设置重试次数等等。

class Session(SessionRedirectMixin):def __init__(self):......self.adapters = OrderedDict()self.mount('https://', HTTPAdapter())self.mount('http://', HTTPAdapter())

Session类继承自SessionRedirectMixin类,SessionRedirectMixin类中实现了用于进行HTTP重定向的能力,这块就不细看了。

我们来看Session对象的request()方法,该方法主要用于发送请求,方法中首先调用了prepare_request()方法将一些请求数据提前封装到了一个PreparedRequest对象中,然后在后面请求的过程中都是使用的该对象。PreparedRequest对象中将请求的method, url, header, cookie, body等数据进行了预处理。

class PreparedRequest(...):......def prepare(self,method=None, url=None, headers=None, files=None, data=None,params=None, auth=None, cookies=None, hooks=None, json=None):self.prepare_method(method)self.prepare_url(url, params)self.prepare_headers(headers)self.prepare_cookies(cookies)self.prepare_body(data, files, json)self.prepare_auth(auth, url)self.prepare_hooks(hooks)

回到request方法中,下一步会将该request对象和一些请求配置信息一起通过调用Session.send()方法将请求发送出去。send方法的逻辑比较简单,就是先获取对应的HTTPAdapter,然后调用adapter的send方法发起请求,最后收到应答之后再判断是否要进行重定向。方法中包含了很多非关键代码,这里就不贴源码了。

4. HTTPAdapter


接下来看HTTPAdapter类,之前讲了Session对象主要是用来保存请求的状态的,其实HTTPAdapter才是实际用于发送请求的组件。

我们看HTTPAdapter类的构建方法可以知道,Adapter中主要维护了三个对象,max_retries用于请求重试,proxy_manager用于维护与proxy的连接,poolmanager属性维护了一个连接池PoolManager对象。我们在创建HTTPAdapter的时候可以指定连接池的大小和请求最大重试次数。

这里之所以使用连接池,是为了对连接到相同服务端的请求的连接进行复用,我们知道在一次请求过程中,TCP连接的建立和断开是非常耗时的,如果能够把建立连接这一步省掉那将会大大提升请求性能。连接池的细节我们在下一小节再详细解析。

def send(self, request, ...):conn = self.get_connection(request.url, proxies)self.cert_verify(conn, request.url, verify, cert)resp = conn.urlopen(...)return self.build_response(request, resp)

接下来我们来看HTTPAdapter类的send方法,该方法的代码比较长,清除掉其他非核心代码,主要包括以下四步,代码如上。

  1. 首先是调用get_connection()方法从proxy_manager或者poolmanager中获取到连接,这里代码中用的虽然是connection变量名,但是实际上这里获取的是一个连接池HTTPConnectionPool对象,具体使用的是哪个连接发送请求其实是在连接池HTTPConnectionPool中去自动选择的。这里如果有使用proxy,那么这里就是获取的连接到proxy的一个连接池。
  2. 然后下一步就是调用cert_verify()方法将TLS证书设置到连接中去,如果使用HTTPS的话。
  3. 接下来就是调用连接池HTTPConnectionPool的urlopen()方法,获取一个连接,发送请求,获取响应,这里获取到的响应还是urllib3库中的原生response对象。
  4. 最后一步是调用build_response()方法将urllib3库中的原生response对象封装为requests库中的Response对象。

5. PoolManager


PoolManager对象是urllib3库中的成员,requests库直接在HTTPAdapater类中封装了urllib3库的组件实现了连接池。我们这里对PoolManager对象进行一个简单的解析。

PoolManager中维护了若干个连接池HTTPConnectionPool或者HTTPSConnectionPool,每个连接池又维护了若干条Connection,这里默认的大小是10个连接池,每个连接池10条连接,但是我们可以在创建PoolManager的时候自行指定。

PoolManager中针对具有scheme、host、port三个属性相同的请求使用同一个连接池,每个连接池维护了一个连接队列,当获取连接时会优先从队列中获取一条现成的连接,如果没有现成的则新创建一条连接,连接使用完成之后会再次添加回队列中,以便后续可以继续使用。

6. 优秀工程实践


其实requests库除了代码非常优美值得我们学习之外,其使用到的工程实践也是值得我们在自己的项目中学习借鉴的。

Requests项目的主目录下除了requests目录用于放置源码外,还包含了docs目录存放文档的网页源码,一个项目除了源码,清晰准确的文档也是非常重要的。在tests目录下存放的是在编码过程中同步编写的测试用例,另外主目录下还有CI脚本,每次往项目中集成新代码时都会执行CI,自动运行测试用例,保证新合入代码未破坏之前的功能。另外主目录下还有HISTORY文件,用于详细记录每个版本的变更内容,方便版本发布,另外还有项目依赖包文件、README等。

Requests库的代码组织结构也是值得学习的,模块根据功能组织,清晰明确。另外__version__模块存放了项目的版本相关信息,在__init__中将api层的接口方法以及一些核心模型直接暴露出去,这样对于使用者使用起来会更加的方便。

7. 总结


这篇文章从源码的核心流程以及工程实践对Python requests库进行了基本的解析,如果想进一步学习其中优美的地方可以自己深度阅读源码。我觉得requests库在Python开发者中之所以这么受欢迎,最主要的原因就是其功能简洁并稳定,接口使用非常方便,另外其清晰的文档也起到了很重要的作用。

Python requests库核心源码解析相关推荐

  1. 新书上市 | Vue 3.0 核心源码解析,这本书给Vue学习提供新方法

    Vue.js 作为一款极简的 MVVM 框架,因其轻量.易上手,得到了众多开发者的喜爱. 自从 2014 年 Vue 诞生以来,这个框架设计的初衷,尤大说只是为了设计一个让自己用起来舒服的框架,随着受 ...

  2. 面试官系统精讲Java源码及大厂真题 - 09 TreeMap 和 LinkedHashMap 核心源码解析

    09 TreeMap 和 LinkedHashMap 核心源码解析 更新时间:2019-09-05 10:15:03 人的影响短暂而微弱,书的影响则广泛而深远. --普希金 引导语 在熟悉 HashM ...

  3. RocketMQ源码系列(一) NameServer 核心源码解析

    目录 一.NameServer 介绍 二.NameServer 功能列表 三.NameServer 架构分析 四.NameServer 工程目录解析 五.NameServer 启动流程分析 1)  创 ...

  4. Sentinel核心源码解析

    Sentinel核心源码解析 Sentinel是分布式系统的防御系统.以流量为切入点,通过动态设置的流量控制.服务熔断等手段达到保护系统的目的,通过服务降级增强服务被拒后用户的体验. 一.Sentin ...

  5. Netty 核心源码解析

    Netty 第一讲:Netty 架构与原理 本文是<Netty 三讲>的第二讲:Netty 核心源码解析(部分),大纲如下: 前言 1. Netty 服务端启动过程源码剖析 1.1. 执行 ...

  6. Android Volley核心源码解析

    君不见,黄河之水天上来,奔流到海不复回. 君不见,高堂明镜悲白发,朝如青丝暮成雪! 人生得意须尽欢,莫使金樽空对月. 天生我材必有用,千金散尽还复来. 烹羊宰牛且为乐,会须一饮三百杯. 岑夫子,丹丘生 ...

  7. Kafka核心源码解析 - KafkaController源码解析

    在进入源码解析之前,我先来介绍一些KafkaController在Kafka集群中的作用. (1)负责监听zookeeper上所有的元数据变更请求: (2)负责topic的partition迁移任务分 ...

  8. Vue3核心源码解析第一课 看懂Vue3的优化

    开篇词 解析 Vue.j 源码,提升编码能力 我现任 Zoom 前端架构师,曾先后于百度.滴滴从事前端研发工作.我平时喜欢钻研新技术.新框架,关注前端自动化.工程化.前端架构.和很多常年打磨自身编程能 ...

  9. Kafka核心源码解析 - KafkaApis源码解析

    KafkaApis负责处理所有请求,转发到相应到方法 1.KafkaApis初始化 /* start processing requests */ apis = new KafkaApis(socke ...

最新文章

  1. 排序算法大集锦_合并排序_1(分治思想)
  2. 研究生一年级,非计算机专业,自学机器学习现实吗?
  3. flutter 返回指定界面_Flutter页面路由导航及传参
  4. linux-基本开发环境搭建
  5. html5自由者,排球自由人可以一直不下场吗?就是可不可以一直在后排轮换?
  6. access orcad 数据库_OrCAD Capture CIS使用MySQL数据库
  7. php技术聊天室源码,PHP聊天室_WebSocket技术实战
  8. 针对前端项目选择不同的前端框架
  9. 全面理解Gatner的企业信息管理成熟度模型
  10. 选择困难症? 看看这几款主流的文档管理系统
  11. 导出android app安装包,Android app导出apk方法
  12. 最新计算机教育 小学教育,2017年小学计算机教学计划
  13. udpping检测与对端udp协议通信状况
  14. Android 底层知识拾零,app架构升级
  15. 阿里的社区梦 能靠闲鱼完成吗?
  16. Socket 套接字之UDP通信
  17. 乱世浮生犹若梦——黑色,黑色,致无尽的黑色(《黑帮×××》影评)
  18. 关于自动付款中预付款的处理-转
  19. 关于研究生发论文的一些事
  20. 二进制的巧妙理解——白鼠喝酒问题

热门文章

  1. Android图片切片热点区域点击
  2. java mediator_浅谈Java设计模式——中介者模式(Mediator)
  3. 基于php汽车销售可视化管理系统
  4. 【彩彩只能变身队】开题报告
  5. 已停止访问该网页怎么解决?
  6. UML关联关系和组合关系以及聚合关系的区别
  7. 落幕在即的科技巨头苹果,厄运才刚刚开始?
  8. 安卓开发基础入门系列教程
  9. UE4Material_节点介绍(Unfinished)
  10. 纳尼亚传奇2 片尾曲