当前位置: 首页 > 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.rkmt.cn/news/198268.html

相关文章:

  • ZGC分代模式揭秘:如何实现亚毫秒级停顿与高效内存管理
  • 微PE官网同源技术社区推荐:AI语音新星VoxCPM-1.5-TTS-WEB-UI发布
  • GitHub镜像网站同步更新:VoxCPM-1.5-TTS-WEB-UI开源语音模型上线
  • Asyncio事件监听机制详解:5个关键点让你避开90%的陷阱
  • 基于YOLOv8的道路坑洼识别检测系统(YOLOv8深度学习+YOLO数据集+UI界面+Python项目源码+模型)
  • 火星殖民地设想:第一批移民将携带语音数据库
  • Java虚拟线程时代来临(线程池配置终极指南)
  • 奥运会开幕式解说:AI同时提供数十种语言服务
  • Python和C#x2B;#x2B;数据结构学习笔记
  • 题解:AT_abc257_d [ABC257D] Jumping Takahashi 2
  • Python如何精准控制3D场景视角?这4个库你必须了解
  • 为什么你的模型训练越来越慢?根源可能出在多模态存储结构上
  • 告别卡顿视角!Python 3D渲染中的平滑控制优化策略(性能提升90%)
  • 题解:B4350 [信息与未来 2025] 美味水果
  • 社交软件动态播报:好友更新内容自动语音朗读
  • 题解:P5663 [CSP-J2019] 加工零件
  • 广东广州早茶:茶楼伙计穿梭间喊出地道粤语
  • 香港维多利亚港:灯光秀期间新增AI解说服务
  • imapi2fs.dll文件丢失损坏找不到 打不开程序 免费下载方法
  • 【Linux命令大全】002.文件传输之lpq命令(实操篇)
  • 题解:P5017 [NOIP2018 普及组] 摆渡车
  • 跨境电商客服系统:不同国家客户听到本地化语音
  • 【Linux命令大全】002.文件传输之lprm命令(实操篇)
  • 【赵渝强老师】国产金仓数据库的表空间
  • 【Linux命令大全】002.文件传输之lpr命令(实操篇)
  • 图书馆闭馆提醒:温柔语音取代刺耳铃声
  • 灵遁者:春华秋实年复年,青丝渐成雪满巅
  • 矿山安全监控系统:危险区域进入时触发语音警告
  • 雾霾指数语音提醒:环保部门发布空气质量通知
  • 建筑工地安全广播:每日开工前自动播放注意事项