블로거가 올해 또 다른 멋진 인턴십 경험을 시작하여 프로젝트를 배우고 작업하면서 시간이 이미 꽉 찼기 때문에 오랫동안 블로그를 하지 않았습니다. 이번 두 가지 경험에 매우 감사드립니다. golang과 Python을 접할 수 있게 해준 한 해, 다양한 언어를 배우고, 기존 c/C++ 학습의 한계를 뛰어넘고, golang과 Python의 뛰어난 기능을 배우고, 다양한 언어의 응용을 이해할 수 있었습니다. 이전에 Linux와 c/C++를 배운 덕분에 golang과 Python을 시작해 보겠습니다.
사용 방법을 배우는 것 외에도 내 학습 습관은 저도 소스코드를 공부하고 동작 메커니즘을 배워서 쉽게 사용할 수 있게, 즉 이런 언어들을 사용하는 걸 좋아합니다. 프레임워크가 일상생활에서 먹고 자는 것과 같아서 아주 자연스럽습니다. ; 최근에 접한 병앤플라스크 웹 프레임워크 때문에 이 둘의 소스코드를 살펴보고 싶은데, 이 두 프레임워크는 파이썬에 기본으로 제공되는 http를 기반으로 하고 있어서 이 글이 있습니다;
Python http의 간단한 예
Python http 프레임워크는 주로 서버와 핸들러로 구성됩니다. 서버는 주로 epoll을 사용하는 등 네트워크 모델을 구축하는 데 사용됩니다. 핸들러는 각 준비된 소켓을 처리하는 데 사용됩니다. 먼저 Python http의 간단한 사용을 살펴보겠습니다.
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)
다음을 얻을 수 있습니다:
python3 myhttp.py 127.0.0.1 9999
이때, 현재 폴더에 새로운 index.html 파일을 생성하면 http를 통해 해당 인덱스에 접근할 수 있습니다: //127.0.0.1:9999/index.html.html 페이지.
이 예제의 서버 클래스는 HTTPServer를 사용하고 핸들러 클래스는 SimpleHTTPRequestHandler입니다. 따라서 HTTPServer는 요청 도착을 모니터링할 때 처리를 위해 SimpleHTTPRequestHandler 클래스에 요청을 보냅니다. 서버와 핸들러를 각각 분석합니다.
http 서버
http 모듈의 디자인은 tfs 파일을 읽었기 때문에 객체 지향 상속 다형성을 최대한 활용합니다. 이전에는 시스템 코드이므로 Python http를 볼 때 먼저 서버의 상속 관계를 제공하십시오.
+------------------+ +------------+| tcpserver基类 | | BaseServer +-------->| 开启事件循环监听 | +-----+------+ | 处理客户端请求 | | +------------------+ v +-----------------+ +------------+| httpserver基类 | | TCPServer +-------->+设置监听socket | +-----+------+ | 开启监听 | | +-----------------+ v +------------+ | HTTPServer | +------------+
상속 관계는 위 그림과 같습니다. 그 중 BaseServer와 TCPServer는 소켓서버.py 파일에 있고, HTTPServer는 http/server.py에 있습니다.
BaseServer
BaseServer는 모든 서버 클래스의 기반이기 때문에 BaseServer는 이벤트 청취 루프를 켜는 등 모든 서버의 공통성을 최대한 추상화합니다. BaseServer가 주로 수행하는 작업이기도 합니다.
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()
코드의 선택기는 실제로 IO 다중화입니다. select, poll, epoll 등을 캡슐화한 다음 서비스 자체에서 모니터링하는 소켓을 io 멀티플렉싱에 등록하고 이벤트 모니터링을 켭니다. 클라이언트가 연결되면 self._handle_request_noblock()이 호출되어 요청을 처리합니다. 이 처리 기능이 무엇인지 확인하세요.
_handle_request_noblock 함수는 먼저 클라이언트 연결 요청을 수신합니다. 실제로 시스템 호출 수락을 캡슐화합니다. 함수를 확인한 후 마지막으로 process_request를 호출하여 요청을 처리합니다. 여기서 get_request는 하위 클래스에 속하는 메소드입니다. 왜냐하면 tcp는 udp로 클라이언트 요청을 수신하는 것과 다르기 때문입니다(tcp에는 연결이 있고 udp에는 연결이 없습니다)
process_request가 구체적으로 어떤 일을 하는지 살펴보겠습니다.
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)
그런 다음 클라이언트 요청이 도착하면 요청이 처리를 위해 핸들러로 전달됩니다.
위에서 언급한 BaseServer에 의해 추상화된 기능을 보면 TCPServer 또는 UDPServer가 완료해야 하는 기능은 청취 소켓을 초기화하고 청취를 바인딩하고 마지막으로 클라이언트 요청이 있을 때 수행된다는 것을 알 수 있습니다. , 클라이언트를 수신합니다.
defprocess_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)
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() raise
defserver_bind(self): ifself.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() defserver_activate(self): self.socket.listen(self.request_queue_size)
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文件返回给客户端;
이 시점에서 Python http 모듈 분석이 끝났습니다. Python과 함께 제공되는 http 모듈이 요청 메소드를 통해 요청 기능을 수행하므로 get 및 post 메소드와 같이 동일한 메소드가 매우 많이 호출되면 요청 기능이 매우 커져 코드 작성이 어렵고 다양한 상황에서 판단하기가 어려워집니다. , SimpleHTTPRequestHandler는 Python에서 제공하는 간단한 예일 뿐입니다.
물론 Python은 공식적으로 http에 더 유용한 프레임워크, 즉 wsgi 서버 및 wsgi 애플리케이션을 제공합니다. 다음 기사에서는 먼저 제공되는 wsgiref 모듈과 병을 분석합니다.
더 많은 Python이 함께 제공됩니다. http 모듈에 대한 관련 기사는 PHP 중국어 웹사이트에 주목하세요!