在Python3.0中处理web请求3-多线程

来源:互联网 发布:某市优化投资环境 编辑:程序博客网 时间:2024/06/04 18:36
继续研究Python3进行处理web请求。在第一篇文章的Hello World程序中,是没有进行多线程处理的,导致的情况是当第一个人执行了一个操作,如果这个操作所需要的时间比较长,那么其他人就需要等他执行完后才能访问,这是非常不符合逻辑的,我看了下源码,HTTPServer确实没有进行任何线程处理,若运行以下代码:
#!coding=UTF-8from http.server import HTTPServer,BaseHTTPRequestHandlerimport io,shutil,timeclass MyHttpHandler(BaseHTTPRequestHandler):    def do_GET(self):        print(self.path)        if self.path=='/':time.sleep(5)        print(self.path)        r_str="Hello World";        enc="UTF-8"        encoded = ''.join(r_str).encode(enc)        f = io.BytesIO()        f.write(encoded)        f.seek(0)        self.send_response(200)        self.send_header("Content-type", "text/html; charset=%s" % enc)        self.send_header("Content-Length", str(len(encoded)))        self.end_headers()        shutil.copyfileobj(f,self.wfile)httpd=HTTPServer(('',8080),MyHttpHandler)print("Server started on 127.0.0.1,port 8080.....")httpd.serve_forever()

然后用浏览器分别访问http://localhost:8080/  和http://localhost:8080/?t ,这里先以A页面代表第一个连接,B页面代表第二个连接,如果先访问A页面,则B页面也必须等待A页面完成后(5秒后)才出结果,请求被阻塞了几经努力,在python3的socketserver模块找到了解决办法。其中解决这个问题的一个非常重要的类就是:ThreadingMixIn看这个类的__doc__:
Python代码:
"""Mix-in class to handle each request in a new thread."""
太好了,正是我们要找到,参考官方的例子,得到这个Hello World多线程版代码如下:
Python代码:
#!coding=UTF-8from http.server import HTTPServer,BaseHTTPRequestHandlerimport io,shutil,time,socketserverclass MyThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer):    passclass MyHttpHandler(BaseHTTPRequestHandler):    def do_GET(self):        print(self.path)        if self.path=='/':time.sleep(5)        print(self.path)        r_str="Hello World";        enc="UTF-8"        encoded = ''.join(r_str).encode(enc)        f = io.BytesIO()        f.write(encoded)        f.seek(0)        self.send_response(200)        self.send_header("Content-type", "text/html; charset=%s" % enc)        self.send_header("Content-Length", str(len(encoded)))        self.end_headers()        shutil.copyfileobj(f,self.wfile)httpd=MyThreadingHTTPServer(('',8080),MyHttpHandler)print("Server started on 127.0.0.1,port 8080.....")httpd.serve_forever()

 只需要很少改动,就能将程序变成多线程了,得益于python的多继承机制。官方的文档和例子:
http://docs.python.org/3.0/library/socketserver.html 
同时,socketserver里还要一个多进程处理的类:ForkingMixIn,用法和这个一样,只是改下父类名。

在Python3.0中使用HTTPServer处理web请求
继昨天发现wsgi在有点问题而无法使用它来处理web请求后,我在官方文档中看到了一个http.server模块,于是转而研究它而非WSGI。这个模块中有两个重要的类,分别为HTTPServer和BaseHTTPRequestHandler,还有两个示例类,SimpleHTTPRequestHandler,CGIHTTPRequestHandler。还有一个测试方法(test),我参考其中的示例类写了一个HelloWorld程序。上代码:
Python代码  
from http.server import HTTPServer,BaseHTTPRequestHandler  import io,shutil    class MyHttpHandler(BaseHTTPRequestHandler):      def do_GET(self):          r_str="Hello World"          enc="UTF-8"          encoded = ''.join(r_str).encode(enc)          f = io.BytesIO()          f.write(encoded)          f.seek(0)          self.send_response(200)          self.send_header("Content-type", "text/html; charset=%s" % enc)          self.send_header("Content-Length", str(len(encoded)))          self.end_headers()          shutil.copyfileobj(f,self.wfile)    httpd=HTTPServer(('',8080),MyHttpHandler)  print("Server started on 127.0.0.1,port 8080.....")  httpd.serve_forever()  
 运行代码,成功,浏览器输出正常。
如果将这行代码:
Python代码  
shutil.copyfileobj(f,self.wfile)  
 改为:
Java代码  
self.wfile.write(r_str)  
 就会出现昨天的错误。 TypeError: send() argument 1 must be string or buffer, not str
OK,这个错误到此算是有了一个圆满的结局。
各位对于处理web请求还有什么其他的方法麻烦告诉我。

继上次用HTTPServer写了一个简单的HTTPHandler后,我发现如果采用HTTPServer处理WEB请求的话系统没有提供获得请求参数的方法(如Java里的request.getParameter),这哪成,不能获取参数还跟用户交互个屁啊。于是又一头扎进了一望无垠的类库中。下面就是今天早上看来一个多小时的结果。
除上次说用到的那几个之外,这次新加了一个urllib
核心代码:
Python代码  
urllib.parse.parse_qs(urllib.parse.unquote(s))  
urllib.parse.unquote  将被quote的字符串解码,即常规的url编码解码操作
urllib.parse.parse_qs 将字符串解释为dict
这里解释下为什么说是字符串转为dict:从客户端过来的参数,无论是GET还是POST过来的,其值都是一个字符串,"a=1&b=2",
如果是POST那么字符串最后还有一个\n或者\r\n,所以这个方法是GET,POST通用的
那么,先上代码吧:
Python代码  
#!coding=UTF-8    from http.server import HTTPServer,BaseHTTPRequestHandler     import io,shutil,urllib         class MyHttpHandler(BaseHTTPRequestHandler):         def do_GET(self):             name="World"            if '?' in self.path:#如果带有参数             self.queryString=urllib.parse.unquote(self.path.split('?',1)[1])                 #name=str(bytes(params['name'][0],'GBK'),'utf-8')                 params=urllib.parse.parse_qs(self.queryString)                 print(params)                 name=params["name"][0] if "name" in params else None             r_str="Hello "+name+" <form action='' method='POSt'>Name:<input name='name' /><br /><input type='submit' value='submit' /></form>"            enc="UTF-8"            encoded = ''.join(r_str).encode(enc)             f = io.BytesIO()             f.write(encoded)             f.seek(0)             self.send_response(200)             self.send_header("Content-type", "text/html; charset=%s" % enc)             self.send_header("Content-Length", str(len(encoded)))             self.end_headers()             shutil.copyfileobj(f,self.wfile)         def do_POST(self):             s=str(self.rfile.readline(),'UTF-8')#先解码             print(urllib.parse.parse_qs(urllib.parse.unquote(s)))#解释参数             self.send_response(301)#URL跳转             self.send_header("Location", "/?"+s)             self.end_headers()         httpd=HTTPServer(('',8080),MyHttpHandler)     print("Server started on 127.0.0.1,port 8080.....")     httpd.serve_forever()     
 其实这个程序没做什么特殊操作,就是把原来的Hello World改了一下,让客户先输入姓名再显示Hello xxx...
获取参数,最重要的是解码操作,do_GET是最郁闷的,如果客户直接在地址栏输入  xxx/?name=某人  则此时的编码比较麻烦,必须使用name=str(bytes(params["name"][0],'iso-8859-1'),'GBK'),当然了,这是针对我的IE7是这样,其他浏览器我还没进行测试。
这里使用了params["name"][0],parse_sq将每个参数看做是一个数组,就算你只传了一个过来,也会被认为是数组,与java的request.getParametersMap类似。

在Python3.0中处理web请求4-回归WSGI
前面一直使用HTTPServer对web请求进行处理,今天突然想能不能改下,用python3提供的wsgiref进行处理,原来的程序:
Python代码  
from wsgiref.simple_server import make_server    def hello_world_app(env,start_response):      start_response("200 OK",[("Content-type","text/plain;charset=utf-8")])      return ["Hello World!!"]    if __name__ == "__main__":      httpd=make_server('',8080,hello_world_app)      httpd.handle_request()  
问题并不在这个程序,异常报的是在write的时候,于是,要改的就是write,改哪里呢?
就改ServerHandler
这个类在wsgiref包里的simple_server.py模块中(Lib/wsgiref/simple_server.py)
原来的ServerHandler是:
Java代码  
class ServerHandler(SimpleHandler):        server_software = software_version        def close(self):          try:              self.request_handler.log_request(                  self.status.split(' ',1)[0], self.bytes_sent              )          finally:              SimpleHandler.close(self)  
 重写_write方法即可:
Java代码  
class ServerHandler(SimpleHandler):        server_software = software_version        def close(self):          try:              self.request_handler.log_request(                  self.status.split(' ',1)[0], self.bytes_sent              )              self.buf_data.seek(0)              shutil.copyfileobj(self.buf_data,self.stdout)          finally:              SimpleHandler.close(self)        def _write(self,data):          if not hasattr(self,"buf_data"):              self.buf_data=io.BytesIO()          if type(data) is str:              data=data.encode("UTF-8")          self.buf_data.write(data)  
 大功告成。
测试  http://localhost:8080,Hello World出来了! 
为了方便调试,这里的编码直接写UTF-8了。各位根据自己的需要配置。如果需要使用多线程处理,可以用上一篇文章提到的ThreadingMixIn即可。