当前位置: 首页 > news >正文

深入解析:49、【Ubuntu】【Gitlab】拉出内网 Web 服务:http.server 单/多线程分析(一)

【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除

背景

上篇 blog
【Ubuntu】【Gitlab】拉出内网 Web 服务:Nginx 事件驱动分析(二)
分析了 Nginx 的高性能设计,多进程模型,配合事件驱动 + 非阻塞 I/O + epoll(Linux),下面继续

Python http.server 单/多线程分析

分析完了 Nginx 的高性能模型,下面再对比下之前的 Python http.server 模型

首先,在分析多线程模型之前,有个比较有意思的点,首先终端输入

python3 --version

可以看到本地版本号是 v3.12.3
在这里插入图片描述
然后查看 http/server.py 实现如下

# ... 前面省略
def test(HandlerClass=BaseHTTPRequestHandler,
ServerClass=ThreadingHTTPServer,
protocol="HTTP/1.0", port=8000, bind=None):
"""Test the HTTP request handler class.
This runs an HTTP server on port 8000 (or the port argument).
"""
ServerClass.address_family, addr = _get_best_family(bind, port)
HandlerClass.protocol_version = protocol
with ServerClass(addr, HandlerClass) as httpd:
host, port = httpd.socket.getsockname()[:2]
url_host = f'[{host}]' if ':' in host else host
print(
f"Serving HTTP on {host} port {port} "
f"(http://{url_host}:{port}/) ..."
)
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nKeyboard interrupt received, exiting.")
sys.exit(0)
if __name__ == '__main__':
import argparse
import contextlib
parser = argparse.ArgumentParser()
parser.add_argument('--cgi', action='store_true',
help='run as CGI server')
parser.add_argument('-b', '--bind', metavar='ADDRESS',
help='bind to this address '
'(default: all interfaces)')
parser.add_argument('-d', '--directory', default=os.getcwd(),
help='serve this directory '
'(default: current directory)')
parser.add_argument('-p', '--protocol', metavar='VERSION',
default='HTTP/1.0',
help='conform to this HTTP version '
'(default: %(default)s)')
parser.add_argument('port', default=8000, type=int, nargs='?',
help='bind to this port '
'(default: %(default)s)')
args = parser.parse_args()
if args.cgi:
handler_class = CGIHTTPRequestHandler
else:
handler_class = SimpleHTTPRequestHandler
# ensure dual-stack is not disabled; ref #38907
class DualStackServer(ThreadingHTTPServer):
def server_bind(self):
# suppress exception when protocol is IPv4
with contextlib.suppress(Exception):
self.socket.setsockopt(
socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
return super().server_bind()
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self,
directory=args.directory)
test(
HandlerClass=handler_class,
ServerClass=DualStackServer,
port=args.port,
bind=args.bind,
protocol=args.protocol,
)

可以看到这里默认启用的是多线程模型

在这里插入图片描述
这里要先解释先 http.server单线程和多线程模式,首先说单线程模式,单线程的处理类如下
在这里插入图片描述

可以看到,HTTPServersocketserver.TCPServer 的子类,HTTPServer 继承的是 TCPServer 单线程同步处理的默认行为,其执行流程如下:

  • Python http.server 启动一个单线程/进程
  • 接收一个客户端连接(accept 方法阻塞接收)
  • 调用 handle 方法处理这个请求(比如读文件,返回 HTTP 响应)
  • 这个请求需要完全处理完,连接关闭后,才能处理下一个请求

这里单线程执行的逻辑点在于,第二个请求必须等第一个请求结束才能被处理,无法并发,下面来测试验证一下这个单线程阻塞行为

首先,在 index.html 目录下新建一个 slow_server.py 启动文件,用来模拟低速处理服务器,slow_server.py 的内容如下

#!/usr/bin/env python3
from http.server import HTTPServer, SimpleHTTPRequestHandler
import time
class SlowHandler(SimpleHTTPRequestHandler):
def do_GET(self):
print(f"Handling {self.path} ... (will sleep 10s)")
time.sleep(10)  # 模拟耗时操作
"""Serve a GET request."""
f = self.send_head()
if f:
try:
self.copyfile(f, self.wfile)
finally:
f.close()
server = HTTPServer(('localhost', 2027), SlowHandler)
server.serve_forever()

这里解释下里面几个点

  • 首先是 SlowHandler 是继承了 http.server 模块里面的静态处理类 SimpleHTTPRequestHandlerSlowHandler 就是准备模拟的低速服务器
  • 然后这里 SlowHandler 重写了下 SimpleHTTPRequestHandler 里面的 do_Get 方法,注意,这里重写的 do_Get 方法几乎就是拷贝的 SimpleHTTPRequestHandler 里面的 do_Get 方法,唯一不同的,就是加了两行,一行是 print 打印,另一行是 time.sleep 延时操作,相当于收到请求后,延时 10s 再处理
    在这里插入图片描述
  • 然后最后是用 HTTPServer 启动该服务,该 Web 服务可以通过 2027 端口进行访问,注意,这里必须是 HTTPServer,因为 HTTPServer 是单线程,现在就是要测试验证单线程的阻塞行为

OK,用 chmod 777slow_server.py 赋予执行权限,然后在该目录(index.html 同级目录)目录下执行 ./slow_server.py 启动这个模拟低速 Web 服务

然后终端输入

time curl http://localhost:2027/a

可以测试这个 Web 服务的连接时间

终端输入 man time,查看到该命令的描述如下
在这里插入图片描述
可以看到,time 命令是一个测量程序运行时资源消耗的命令(不仅仅是测量时间,虽然名字叫 time,比如花了多少 CPU 时间,实际耗时,内存使用等,但这个属于 GNU time,需要用 /usr/bin/time 命令(和上面的不是同一个命令,虽然都叫 time,关于 GNU time 就不展开分析了

直接在终端输入 time 命令的话(上面用的),只能测量时间如下
在这里插入图片描述


OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog
【Ubuntu】【Gitlab】拉出内网 Web 服务:http.server 单/多线程分析(二)

http://www.jsqmd.com/news/181943/

相关文章:

  • ZGC分代模式揭秘:如何实现亚毫秒级停顿与高效内存管理
  • DL 第一讲 PyTorch基础
  • 微PE官网同源技术社区推荐:AI语音新星VoxCPM-1.5-TTS-WEB-UI发布
  • GitHub镜像网站同步更新:VoxCPM-1.5-TTS-WEB-UI开源语音模型上线
  • Asyncio事件监听机制详解:5个关键点让你避开90%的陷阱
  • 超越BeyondCompare4永久激活密钥的价值?试试这颗开源语音明珠
  • 基于YOLOv8的道路坑洼识别检测系统(YOLOv8深度学习+YOLO数据集+UI界面+Python项目源码+模型)
  • 火星殖民地设想:第一批移民将携带语音数据库
  • Java虚拟线程时代来临(线程池配置终极指南)
  • 为什么VoxCPM-1.5-TTS-WEB-UI成为当前最受欢迎的TTS网页推理工具?
  • Spring Native AOT 编译太慢?:3个关键优化策略让你效率翻倍
  • 安徽黄山云海:松涛阵阵中隐约传来古人吟诗
  • 基于YOLOv8的汽车损坏识别检测系统(YOLOv8深度学习+YOLO数据集+UI界面+Python项目源码+模型)
  • 奥运会开幕式解说:AI同时提供数十种语言服务
  • 甘肃敦煌莫高窟:壁画修复师的工作语音日记
  • AI语音伦理边界:我们该不该禁止克隆逝者声音?
  • 手把手搞定FastAPI静态文件:安全、上传与访问
  • 题解:AT_abc257_e [ABC257E] Addition and Multiplication 2
  • 基于YOLOv8的蜜蜂识别检测系统(YOLOv8深度学习+YOLO数据集+UI界面+Python项目源码+模型)
  • 2025年4轴数控机床优选门店品牌,你知道哪些?4轴数控机床/水暖接头数控机床/无人机配件数控,4轴数控机床批发供应链 - 品牌推荐师
  • 印度尼西亚火山旅游:导游语音讲解地质奇观
  • 题解:AT_abc257_d [ABC257D] Jumping Takahashi 2
  • Python和C#x2B;#x2B;数据结构学习笔记
  • 乌克兰乡村婚礼:新娘父亲致辞感动全场
  • Python如何精准控制3D场景视角?这4个库你必须了解
  • Miller-Rabin素数测试算法
  • 职业面试模拟:求职者练习应对各种问题的回答
  • 社交软件动态播报:好友更新内容自动语音朗读
  • 题解:B4350 [信息与未来 2025] 美味水果
  • 为什么你的模型训练越来越慢?根源可能出在多模态存储结构上