【问题标题】:How do I write a python HTTP server to listen on multiple ports?如何编写 python HTTP 服务器来监听多个端口?
【发布时间】:2019-06-14 08:39:33
【问题描述】:

我正在用 Python 编写一个小型 Web 服务器,使用 BaseHTTPServer 和 BaseHTTPServer.BaseHTTPRequestHandler 的自定义子类。是否可以在多个端口上进行监听?

我现在在做什么:

class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  def doGET
  [...]

class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): 
    pass

server = ThreadingHTTPServer(('localhost', 80), MyRequestHandler)
server.serve_forever()

【问题讨论】:

    标签: python webserver


    【解决方案1】:

    当然;只需在两个不同线程中的两个不同端口上启动两个不同的服务器,每个线程都使用相同的处理程序。这是我刚刚编写和测试的一个完整的工作示例。如果您运行此代码,那么您将能够在 http://localhost:1111/http://localhost:2222/ 获得一个 Hello World 网页

    from threading import Thread
    from SocketServer import ThreadingMixIn
    from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
    
    class Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            self.send_response(200)
            self.send_header("Content-type", "text/plain")
            self.end_headers()
            self.wfile.write("Hello World!")
    
    class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
        daemon_threads = True
    
    def serve_on_port(port):
        server = ThreadingHTTPServer(("localhost",port), Handler)
        server.serve_forever()
    
    Thread(target=serve_on_port, args=[1111]).start()
    serve_on_port(2222)
    

    更新:

    这也适用于 Python 3,但需要稍微更改三行:

    from socketserver import ThreadingMixIn
    from http.server import HTTPServer, BaseHTTPRequestHandler
    

    self.wfile.write(bytes("Hello World!", "utf-8"))
    

    【讨论】:

    • @scrat:GIL 对这段代码没有多大影响,因为这段代码主要是 i/o 绑定的,而且 Python 中的大多数 i/o 是使用发布的低级 C 库编写的吉尔。与大多数性能问题一样,我的建议是不要担心它,除非您已经对代码进行了基准测试并确定它确实是一个问题。
    • 您必须将daemon_threads = True 添加为class ThreadingHTTPServer(ThreadingMixIn, HTTPServer) 的成员,否则生成的线程将不会停止并且您的Python 进程将不会退出,直到您发送更多请求以帮助这些线程意识到它们需要停止.我相应地编辑了您的答案,如果有问题请告诉我。
    【解决方案2】:

    不容易。你可以有两个 ThreadingHTTPServer 实例,编写你自己的 serve_forever() 函数(别担心它不是一个复杂的函数)。

    现有功能:

    def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.
    
        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__serving = True
        self.__is_shut_down.clear()
        while self.__serving:
            # XXX: Consider using another file descriptor or
            # connecting to the socket to wake this up instead of
            # polling. Polling reduces our responsiveness to a
            # shutdown request and wastes cpu at all other times.
            r, w, e = select.select([self], [], [], poll_interval)
            if r:
                self._handle_request_noblock()
        self.__is_shut_down.set()
    

    所以我们的替换是这样的:

    def serve_forever(server1,server2):
        while True:
            r,w,e = select.select([server1,server2],[],[],0)
            if server1 in r:
                server1.handle_request()
            if server2 in r:
                server2.handle_request()
    

    【讨论】:

      【解决方案3】:

      我会说这种简单的事情的线程是矫枉过正的。你最好使用某种形式的异步编程。

      这里是一个使用Twisted的例子:

      from twisted.internet import reactor
      from twisted.web import resource, server
      
      class MyResource(resource.Resource):
          isLeaf = True
          def render_GET(self, request):
              return 'gotten'
      
      site = server.Site(MyResource())
      
      reactor.listenTCP(8000, site)
      reactor.listenTCP(8001, site)
      reactor.run()
      

      我还认为让每个端口都以相同的方式处理看起来更简洁,而不是让主线程处理一个端口而另一个线程处理另一个。可以说这可以在线程示例中修复,但是您使用的是三个线程。

      【讨论】:

      • 您提出了一些非常好的观点,我很想建议在我的回答中使用 Twisted。我的方法的主要优点是它不需要标准库之外的任何东西。对于一个真正的应用程序,我只使用 Apache 后面的 CherryPy 或其他不是 BaseHTTPServer 的东西。
      猜你喜欢
      • 1970-01-01
      • 2021-07-10
      • 2013-03-11
      • 2017-03-30
      • 2014-04-19
      • 1970-01-01
      • 1970-01-01
      • 2019-03-30
      相关资源
      最近更新 更多