zoukankan      html  css  js  c++  java
  • Python 开发web服务器,多线程

    https://blog.csdn.net/u012887259/article/details/102425450

    https://blog.csdn.net/u012887259/article/details/102425450

    https://www.jianshu.com/p/27a8cd3ec0f2

    https://blog.csdn.net/u012887259/article/details/102425450

    Python 开发web服务器,多线程
               
               
                   
                       
                        原创                                                                                                                                            海洋的渔夫
                        最后发布于2018-12-28 00:16:40                   
                        阅读数 16
                       
                           
                            收藏
                       
                                       
                                    发布于2018-12-28 00:16:40
                   
                                                                                                                                                                       
                           
                               
                               
                                    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。                           
                               
                                    本文链接:https://blog.csdn.net/u012887259/article/details/102425450
                               
                            
                           
                                                                                   
                   
                                                                    展开
                                       
               
           
       
       
           
                   
                   
                                       
            
               
                               
                   
                                               
       
    仅供学习参考,转载请注明出处

    前面介绍了使用进程的方式来优化处理http请求
    Python 开发web服务器,多进程优化

    但是多进程其实也存在一个资源的问题,当一个请求过来就要开启一个子进程的话,那么如果并发来了10万的http请求,那么就可能要开启10万个子进程。
    这样是非常消耗服务器资源的。

    那么另一个解决的方式就是使用线程。
    改写线程的方式如下

    运行效果如下:

    https://blog.csdn.net/u012887259/article/details/102425450

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

    仅供学习,转载请注明出处

    前情回顾

    前面写了两个篇章,主要介绍了使用tcp开发web服务器的功能。
    Python 开发Web静态服务器 - 返回固定值:胖子老板,来包槟榔
    Python 开发web服务器,返回HTML页面

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

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

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

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

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

    #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
    " # 200 表示找到这个资源
           response_headers += "
    " # 空一行与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
    " # 200 表示找到这个资源
           response_headers += "
    " # 空一行与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加入子进程中

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

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

     
     

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

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

    在主进程增加client_socket的关闭

     
     

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

     
     

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

    完整代码如下

    #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
    " # 200 表示找到这个资源
           response_headers += "
    " # 空一行与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
    " # 200 表示找到这个资源
           response_headers += "
    " # 空一行与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()
    
     
     

    关注微信公众号,回复【资料】、Python、PHP、JAVA、web,则可获得Python、PHP、JAVA、前端等视频资料。

     
     
     

    推荐阅读更多精彩内容

    其实线程对于性能的提升在python中并不会很高,因为GIL这个全局锁的方式会对多线程进行锁定,导致性能损耗偏大。
    关于GIL可参考该文章:Python 的 GIL 是什么鬼,多线程性能究竟如何
    那么下一步,考虑可以使用协程gevent来优化。
    完整代码
    #coding=utf-8from socket import *import reimport threading 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 " # 200 表示找到这个资源       response_headers += " " # 空一行与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 " # 200 表示找到这个资源       response_headers += " " # 空一行与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_thread = threading.Thread(target=handle_client,args=(client_socket,))      new_thread.start() # 开启线程  if __name__ == "__main__":   main()


    关注微信公众
    ————————————————
    版权声明:本文为CSDN博主「海洋的渔夫」的原创文章,遵循CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/u012887259/java/article/details/102425450

  • 相关阅读:
    Codeforces Round #113 (Div. 2) Tetrahedron(滚动DP)
    Codeforces Round #300 Quasi Binary(DP)
    Codeforces Round #119 (Div. 2) Cut Ribbon(DP)
    Codeforces Round #260 (Div. 1) Boredom(DP)
    Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Cards Sorting(树状数组)
    Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Office Keys(思维)
    图灵杯 E 简单的RMQ(UVA 11235)(RMQ)
    qwb与学姐 (带秩并查集)
    计蒜客 UCloud 的安全秘钥(困难)(哈希)
    第八届山东省ACM大学生程序设计竞赛个人总结
  • 原文地址:https://www.cnblogs.com/xinxihua/p/12788725.html
Copyright © 2011-2022 走看看