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

继续研究Python3进行处理web请求。在第一篇文章的Hello World程序中,是没有进行多线程处理的,导致的情况是当第一个人执行了一个操作,如果这个操作所需要的时间比较长,那么其他人就需要等他执行完后才能访问,这是非常不符合逻辑的,我看了下源码,HTTPServer确实没有进行任何线程处理,若运行以下代码:
#!coding=UTF-8
from http.server import HTTPServer,BaseHTTPRequestHandler
import io,shutil,time

class 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-8
from http.server import HTTPServer,BaseHTTPRequestHandler
import io,shutil,time,socketserver
class MyThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer):
    pass

class 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即可。

你可能感兴趣的:(多线程,Web,python,server,header,import)