Heim > Artikel > Backend-Entwicklung > Detaillierte Erläuterung des mit Python gelieferten http-Moduls
Ich habe schon lange nicht mehr gebloggt, denn der Blogger hat dieses Jahr ein weiteres wunderbares Praktikumserlebnis begonnen, bei dem er lernte und an Projekten arbeitete, und die Zeit ist bereits voll für diese beiden Erfahrungen Jahr für die Möglichkeit, mit Golang und Python in Kontakt zu kommen, verschiedene Sprachen zu lernen, die Einschränkungen des vorherigen Erlernens von C/C++ zu überwinden, die hervorragenden Funktionen von Golang und Python zu erlernen und die Anwendung verschiedener Sprachen zu verstehen in verschiedenen Szenarien; und das Erlernen von Linux und C/C++ hat mich auch schnell dazu gebracht, mit Golang und Python anzufangen;
Meine Lerngewohnheit, zusätzlich zum Erlernen der Verwendung, Ich studiere auch gerne den Quellcode und lerne den Betriebsmechanismus, damit ich ihn einfach verwenden kann, oder mit anderen Worten, diese Sprachen verwenden oder Das Framework ist wie Essen und Schlafen, weil ich es in letzter Zeit sehr natürlich gemacht habe Wenn ich mit den Web-Frameworks „Flasche“ und „Flasche“ in Berührung komme, möchte ich mir die Quellcodes dieser beiden Frameworks ansehen, aber diese beiden Frameworks basieren auf Pythons eigenem http, daher gibt es diesen Artikel
Ein einfaches Beispiel für Python http
Das Python-HTTP-Framework besteht hauptsächlich aus Server und Handler. Der Server wird hauptsächlich zum Erstellen von Netzwerkmodellen verwendet, z. B. zur Verwendung von Epoll zum Überwachen von Sockets Um jeden fertigen Socket zu verarbeiten, schauen wir uns zunächst die einfache Verwendung von Python http an:
import sys from http.server import HTTPServer,SimpleHTTPRequestHandler ServerClass = HTTPServer HandlerClass = SimpleHTTPRequestHandler if__name__ =='__main__': port = int(sys.argv[2]) server_address = (sys.argv[1],port) httpd = ServerClass(server_address,HandlerClass) sa=httpd.socket.getsockname() print("Serving HTTP on",sa[0],"port",sa[1],"...") try: httpd.serve_forever() except KeyboardInterrupt: print("\nKeyboard interrupt received, exiting.") httpd.server_close() sys.exit(0)
Wenn Sie das obige Beispiel ausführen, erhalten Sie Folgendes:
python3 myhttp.py 127.0.0.1 9999
Wenn Sie zu diesem Zeitpunkt eine neue index.html-Datei im aktuellen Ordner erstellen, können Sie http://127.0.0.1:9999 übergeben /index.html Die Seite index.html wurde besucht.
Die Serverklasse in diesem Beispiel verwendet HTTPServer und die Handlerklasse ist SimpleHTTPRequestHandler. Wenn HTTPServer den Eingang einer Anforderung überwacht, wird die Anforderung daher zur Verarbeitung an die SimpleHTTPRequestHandler-Klasse weitergeleitet. Wir starten die Analyse von Server und Handler.
http-Server
Das Design des http-Moduls nutzt den objektorientierten Vererbungspolymorphismus voll aus, da ich TFS-Dateien gelesen habe Vorher. Wenn Sie sich Python-http ansehen, gibt es nicht so viel Druck. Geben Sie zunächst die Vererbungsbeziehung des Servers an Die Vererbungsbeziehung ist wie im Bild oben gezeigt, wobei BaseServer und TCPServer in der Datei socketserver.py sind, HTTPServer ist in http/server.py, schauen wir uns zuerst BaseServer an; >
Da BaseServer die Basisklasse aller Server ist, abstrahiert BaseServer die Gemeinsamkeiten aller Server so weit wie möglich, z. B. das Einschalten der Ereignisüberwachungsschleife. Dies ist die Gemeinsamkeit aller Server, also auch Was BaseServer hauptsächlich tut; werfen wir einen Blick auf den Hauptcodeteil von BaseServer
+------------------+ +------------+| tcpserver基类 | | BaseServer +-------->| 开启事件循环监听 | +-----+------+ | 处理客户端请求 | | +------------------+ v +-----------------+ +------------+| httpserver基类 | | TCPServer +-------->+设置监听socket | +-----+------+ | 开启监听 | | +-----------------+ v +------------+ | HTTPServer | +------------+
Der Selektor im Code kapselt tatsächlich das IO-Multiplexing von select, poll, epoll usw. und registriert dann den vom Dienst selbst überwachten Socket beim io-Multiplexing. Aktivieren Sie die Ereignisüberwachung. Wenn ein Client eine Verbindung herstellt, wird self._handle_request_noblock() aufgerufen, um die Anforderung zu verarbeiten Funktion tut;
_handle_request_noblock Funktion ist eine interne Funktion. Die unterste Ebene kapselt tatsächlich die Systemaufrufannahmefunktion. Überprüfen Sie dann die Anforderung und rufen Sie schließlich Process_Request auf, um die Anforderung zu verarbeiten. Dabei gehört get_request zur Unterklassenmethode, da TCP und UDP Clientanforderungen unterschiedlich empfangen (TCP hat eine Verbindung, UDP hat keine Verbindung)
defserve_forever(self, poll_interval=0.5): self.__is_shut_down.clear() try: with_ServerSelector()asselector: selector.register(self, selectors.EVENT_READ) whilenotself.__shutdown_request: ready = selector.select(poll_interval) ifready: self._handle_request_noblock() self.service_actions() finally: self.__shutdown_request = False self.__is_shut_down.set()
Lassen Sie uns Sehen Sie sich an, was „process_request“ konkret macht.
def_handle_request_noblock(self): try: request, client_address = self.get_request() exceptOSError: return ifself.verify_request(request, client_address): try: self.process_request(request, client_address) except: self.handle_error(request, client_address) self.shutdown_request(request) else: self.shutdown_request(request)
Die Funktion „process_request“ ruft zunächst „finish_request“ auf, um eine Verbindung zu verarbeiten wird aufgerufen, um die Verbindung zu schließen, und die Funktion „finish_request“ instanziiert intern eine Handler-Klasse und übermittelt den Socket und die Adresse des Clients, was bedeutet, dass die Handler-Klasse die Anforderungsverarbeitung abgeschlossen hat, wenn die Initialisierung abgeschlossen ist Wenn wir den Handler später analysieren, ist dies der Fall. Dieser BaseServer kann nicht direkt verwendet werden, da einige Funktionen noch nicht implementiert sind und nur als Abstraktionsschicht für TCP/UDP verwendet werden zusammenfassen:
Rufen Sie zuerst servo_forever auf, um die Ereignisüberwachung zu aktivieren.
Wenn dann eine Client-Anfrage eingeht, wird die Anfrage an den Handler zur Verarbeitung übergeben. TCPServerdefprocess_request(self, request, client_address): self.finish_request(request, client_address) self.shutdown_request(request) # ------------------------------------------------- deffinish_request(self, request, client_address): self.RequestHandlerClass(request, client_address, self) defshutdown_request(self, request): self.close_request(request)Anhand der vom oben genannten BaseServer abstrahierten Funktionen können wir wissen, dass die Funktionen, die TCPServer oder UDPServer ausführen sollten, die Initialisierung des Listening-Sockets sind, den Listener binden und schließlich den Client empfangen, wenn ein vorhanden ist Client-Anfrage; schauen wir uns den Code an
TCPServer-Initialisierung Rufen Sie zunächst die Initialisierungsfunktion der Basisklasse BaseServer auf, um die Serveradresse zu initialisieren. Handler-Klasse usw., initialisieren Sie dann ihren eigenen Listening-Socket und rufen Sie schließlich server_bind auf, um den Socket zu binden, und server_activate den Listening-Socket
TCPServer auch implementiert eine weitere Funktion, die darin besteht, Clientanfragen zu empfangen,
BaseServer==> def__init__(self, server_address, RequestHandlerClass): """Constructor. May be extended, do not override.""" self.server_address = server_address self.RequestHandlerClass = RequestHandlerClass self.__is_shut_down = threading.Event() self.__shutdown_request = False #-------------------------------------------------------------------------------- TCPServer==> def__init__(self, server_address, RequestHandlerClass, bind_and_activate=True): BaseServer.__init__(self, server_address, RequestHandlerClass) self.socket = socket.socket(self.address_family, self.socket_type) ifbind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raisewenn vorher Wenn Sie Linux-Programmierung studiert haben, sollten Sie sich beim Anblick dieser sehr vertraut fühlen Codes, da die Funktionsnamen genau mit den von Linux bereitgestellten Systemaufrufnamen übereinstimmen, daher werde ich hier nicht auf Details eingehen TCPServer hat tatsächlich das Hauptframework des Servers basierend auf TCP integriert up, sodass HTTPServer nur die server_bind-Funktion überlastet, reuse_address usw. auf der Grundlage der Vererbung von TCPServer festlegt;
ok,这里分析下上述例子程序的开启过程;
httpd = ServerClass(server_address,HandlerClass)这行代码在初始化HTTPServer时,主要是调用基类TCPServer的初始化方法,初始化了监听的套接字,并绑定和监听;
httpd.serve_forever()这行代码调用的是基类BaseServer的serve_forever方法,开启监听循环,等待客户端的连接;
如果有看过redis或者一些后台组件的源码,对这种并发模型应该很熟悉;ok,分析了server之后,接下来看下handler是如何处理客户端请求的。
http之handler
handler类主要分析tcp层的handler和http应用层的handler,tcp层的handler是不能使用的,因为tcp层只负责传输字节,但是并不知对于接收到的字节要如何解析,如何处理等;因此应用层协议如该要使用TCP协议,必须继承TCP handler,然后实现handle函数即可;例如,http层的handler实现handle函数,解析http协议,处理业务请求以及结果返回给客户端;先来看下tcp层的handler
tcp层handler
tcp层handler主要有BaseRequestHandler和StreamRequestHandler(都在socketserver.py文件),先看下BaseRequestHandler代码,
classBaseRequestHandler: def__init__(self, request, client_address, server): self.request = request self.client_address = client_address self.server = server self.setup() try: self.handle() finally: self.finish() defsetup(self): pass defhandle(self): pass deffinish(self): pass
之前在看server时,知道处理客户端请求就是在handler类的初始化函数中完成;由这个基类初始化函数,我们知道处理请求大概经历三个过程:
setup对客户端的socket做一些设置;
handle真正处理请求的函数;
finish关闭socket读写请求;
这个BaseRequestHandler是handler top level 基类,只是抽象出handler整体框架,并没有实际的处理;我们看下tcp handler,
classStreamRequestHandler(BaseRequestHandler): timeout = None disable_nagle_algorithm = False defsetup(self): self.connection = self.request ifself.timeoutisnotNone: self.connection.settimeout(self.timeout) ifself.disable_nagle_algorithm: self.connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) self.rfile = self.connection.makefile('rb', self.rbufsize) self.wfile = self.connection.makefile('wb', self.wbufsize) deffinish(self): ifnotself.wfile.closed: try: self.wfile.flush() exceptsocket.error: pass self.wfile.close() self.rfile.close()
tcp handler实现了setup和finish函数,setup函数设置超时时间,开启nagle算法以及设置socket读写缓存;finish函数关闭socket读写;
由上述两个tcp层的handler可知,要实现一个基于http的服务器handler,只需要继承StreamRequestHandler类,并实现handle函数即可;因此这也是http层handler主要做的事;
http层handler
由之前tcp层handler的介绍,我们知道http层handler在继承tcp层handler基础上,主要是实现了handle函数处理客户端的请求;还是直接看代码吧;
defhandle(self): self.close_connection = True self.handle_one_request() whilenotself.close_connection: self.handle_one_request()
这就是BaseHTTPRequestHandler的handle函数,在handle函数会调用handle_one_request函数处理一次请求;默认情况下是短链接,因此在执行了一次请求之后,就不会进入while循环在同一个连接上处理下一个请求,但是在handle_one_request函数内部会进行判断,如果请求头中的connection为keep_alive或者http版本大于等于1.1,则可以保持长链接;接下来看下handle_one_request函数是如何处理;
defhandle_one_request(self): try: self.raw_requestline =self.rfile.readline(65537) iflen(self.raw_requestline) >65536: self.requestline ='' self.request_version ='' self.command ='' self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG) return ifnotself.raw_requestline: self.close_connection = True return ifnotself.parse_request(): return mname = 'do_'+self.command ifnothasattr(self, mname): self.send_error( HTTPStatus.NOT_IMPLEMENTED, "Unsupported method (%r)"%self.command) return method = getattr(self, mname) method() self.wfile.flush() except socket.timeout as e: self.log_error("Request timed out: %r", e) self.close_connection = True return
这个handle_one_request执行过程如下:
先是调用parse_request解析客户端http请求内容
通过"do_"+command构造出请求所对于的函数method
调用method函数,处理业务并将response返回给客户端
这个BaseHTTPRequestHandler是http handler基类,因此也是无法直接使用,因为它没有定义请求处理函数,即method函数;好在python为我们提供了一个简单的SimpleHTTPRequestHandler,该类继承了BaseHTTPRequestHandler,并实现了请求函数;我们看下get函数:
# SimpleHTTPRequestHandler # --------------------------------------------- defdo_GET(self): """Serve a GET request.""" f = self.send_head() iff: try: self.copyfile(f, self.wfile) finally: f.close()
这个get函数先是调用do_GET函数给客户端返回response头部,并返回请求的文件,最后调用copyfile函数将请求文件通过连接返回给客户端;
以上就是http模块最基础的内容,最后,总结下例子程序handler部分:
server把请求传给SimpleHTTPRequestHandler初始化函数;
SimpleHTTPRequestHandler在初始化部分,对这个客户端connection进行一些设置;
接着调用handle函数处理请求;
在handle函数接着调用handle_one_request处理请求;
在handle_one_request函数内部,解析请求,找到请求处理函数;
我之前的访问属于get访问,因此直接调用do_GET函数将index.html文件返回给客户端;
Die Analyse des Python-http-Moduls ist an dieser Stelle abgeschlossen. Ich weiß nicht, ob Ihnen aufgefallen ist, dass das mit Python gelieferte http-Modul nicht sehr praktisch zu verwenden ist, da es das aufruft Anforderungsfunktion über die Anforderungsmethode. Wenn also dieselbe Methode sehr oft aufgerufen wird, z. B. bei Get- und Post-Methoden, ist die Anforderungsfunktion extrem groß, was das Schreiben des Codes und natürlich die Beurteilung in verschiedenen Situationen erschwert , SimpleHTTPRequestHandler ist nur ein einfaches Beispiel von Python. Natürlich bietet Python offiziell ein nützlicheres Framework für http, nämlich den WSGI-Server und die WSGI-Anwendung mit Python und analysieren Sie dann flask;