前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3.Python 开发web服务器,多进程优化

3.Python 开发web服务器,多进程优化

作者头像
Devops海洋的渔夫
发布2022-01-17 08:37:39
6450
发布2022-01-17 08:37:39
举报
文章被收录于专栏:Devops专栏

回顾

前面写了两个篇章,主要介绍了使用tcp开发web服务器的功能。

1.Python 开发Web静态服务器 - 返回固定值

2.Python 开发web服务器,返回HTML页面

但是这服务端是有一个致命的性能问题,那就是采用循环接收http请求。当在处理一个http请求的时候,需要等待这个请求处理完毕了,才会开始处理下一个http请求。

数量少的http请求当时还可以使用。

但是当并发http请求的数量巨大的时候,这样就会导致堵塞的情况,或者访问超时。

那么该怎么去优化这个问题呢?

查看上一篇章代码:使用html页面返回的web服务器

代码语言:javascript
复制
#coding=utf-8
from socket import *
import re

def handle_client(client_socket):
    """为一个客户端服务"""
    # 接收对方发送的数据
    recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
    # 打印从客户端发送过来的数据内容
    #print("client_recv:",recv_data)
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)

    # 返回浏览器数据
    # 设置内容body
    # 使用正则匹配出文件路径
    print("------>",request_header_lines[0])
    print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
    ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
    if ret:
       file_path = "./html/" + ret.group(1)
       if file_path == "./html/":
          file_path = "./html/index.html"
       print("file_path *******",file_path)

    try:
       # 设置返回的头信息 header
       response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
       response_headers += "\r\n" # 空一行与body隔开
       # 读取html文件内容
       file_name = file_path # 设置读取的文件路径
       f = open(file_name,"rb") # 以二进制读取文件内容
       response_body = f.read()
       f.close()   
       # 返回数据给浏览器
       client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
       client_socket.send(response_body)   #转码utf-8并send数据到浏览器
    except:
       # 如果没有找到文件,那么就打印404 not found
       # 设置返回的头信息 header
       response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
       response_headers += "\r\n" # 空一行与body隔开
       response_body = "<h1>sorry,file not found</h1>"
       response = response_headers + response_body
       client_socket.send(response.encode("utf-8"))

    client_socket.close()

def main():
   # 创建套接字
   server_socket = socket(AF_INET, SOCK_STREAM)
   # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
   server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
   # 设置服务端提供服务的端口号
   server_socket.bind(('', 7788))
   # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
   server_socket.listen(128) #最多可以监听128个连接
   # 开启while循环处理访问过来的请求 
   while True:
      # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
      # client_socket用来为这个客户端服务
      # server_socket就可以省下来专门等待其他新的客户端连接while True:
      client_socket, clientAddr = server_socket.accept()
      handle_client(client_socket)

if __name__ == "__main__":
   main()

从上面的文字解析可能不够形象,先使用代码实现一下。

将client_socket加入子进程中

代码语言:javascript
复制
    import multiprocessing 

      # 设置子进程
      new_process = multiprocessing.Process(target=handle_client,args=(client_socket,))
      new_process.start() # 开启子进程

好了,这里就已经加好了子进程。那么下面来运行一下,看看会有什么结果。

从上面的访问请求中,一直在转圈,说明client_socket在处理完毕请求之后,并无法进行关闭。

其实,这个就是多进程的特性,子进程会从开启之前复制前面的代码,包含了client_socket接口,当子进程运行的时候,并无法关闭,这就需要从主进程来关闭了。

在主进程增加client_socket的关闭

运行看看浏览器还会不会转圈:

好了,那么到这里,就可以采用多进程的访问处理http请求了。

完整代码如下

代码语言:javascript
复制
#coding=utf-8
from socket import *
import re
import multiprocessing

def handle_client(client_socket):
    """为一个客户端服务"""
    # 接收对方发送的数据
    recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
    # 打印从客户端发送过来的数据内容
    #print("client_recv:",recv_data)
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)

    # 返回浏览器数据
    # 设置内容body
    # 使用正则匹配出文件路径
    print("------>",request_header_lines[0])
    print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
    ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
    if ret:
       file_path = "./html/" + ret.group(1)
       if file_path == "./html/":
          file_path = "./html/index.html"
       print("file_path *******",file_path)

    try:
       # 设置返回的头信息 header
       response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
       response_headers += "\r\n" # 空一行与body隔开
       # 读取html文件内容
       file_name = file_path # 设置读取的文件路径
       f = open(file_name,"rb") # 以二进制读取文件内容
       response_body = f.read()
       f.close()   
       # 返回数据给浏览器
       client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
       client_socket.send(response_body)   #转码utf-8并send数据到浏览器
    except:
       # 如果没有找到文件,那么就打印404 not found
       # 设置返回的头信息 header
       response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
       response_headers += "\r\n" # 空一行与body隔开
       response_body = "<h1>sorry,file not found</h1>"
       response = response_headers + response_body
       client_socket.send(response.encode("utf-8"))

    #client_socket.close()

def main():
   # 创建套接字
   server_socket = socket(AF_INET, SOCK_STREAM)
   # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
   server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
   # 设置服务端提供服务的端口号
   server_socket.bind(('', 7788))
   # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
   server_socket.listen(128) #最多可以监听128个连接
   # 开启while循环处理访问过来的请求 
   while True:
      # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
      # client_socket用来为这个客户端服务
      # server_socket就可以省下来专门等待其他新的客户端连接while True:
      client_socket, clientAddr = server_socket.accept()
      # handle_client(client_socket)
      # 设置子进程
      new_process = multiprocessing.Process(target=handle_client,args=(client_socket,))
      new_process.start() # 开启子进程

      # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
      client_socket.close()


if __name__ == "__main__":
   main()
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 海洋的渔夫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 查看上一篇章代码:使用html页面返回的web服务器
    • 将client_socket加入子进程中
      • 在主进程增加client_socket的关闭
        • 完整代码如下
        相关产品与服务
        云服务器
        云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档