wbe后台原理|python-web

概述 【wbe后台原理|python-web】本文是简单web服务程序,采用进程发并发实现请求处理,采用进程队列保证数据一致性,即完成不同进程间的同步

  1. 监听socket,用于监听并接受http请求的tcp连接
  2. 服务程序,用于处理http请求
  3. 通信socket用于与请求浏览器通信
  4. 多进程调度并发处理请求
    效果如下图:
    wbe后台原理|python-web
    文章图片

    wbe后台原理|python-web
    文章图片
服务程序
  1. 对请求头进行分析,采用re正则表达式取出请求内容
# GET /index.html HTTP/1.1 ret = re.match(r"[^/]+(/[^ ]*)", request_line[0]) flie_name = ret.group(1)

  1. 判断请求类型是否为静态文件,若为静态文件则在静态文件夹寻找该文件,将该文件通过socket发送给客户端,若为动态请求则查询路由表找到对应请求的服务
#访问静态文件 if not flie_name.endswith(".py"): #读取静态文件并发送 pass #get访问 else: func = URL_FUNC_DICT[flie_name]

  1. 利用装饰器构建路由字典
URL_FUNC_DICT = dict() #路由字典 #闭包 def route(url): def set_func(func): URL_FUNC_DICT[url] = func def call_func(*args, **kwargs): return func(*args, **kwargs) return call_func return set_func@route("/index.py") #装饰器 def index(): with open("./html/index.html") as f: content = f.read() return content

整个服务程序参考源码
'''路由''' # _*_coding:utf-8 _*_ ''' URL_FUNC_DICT = { "/index.py":index, "/login.py":login } ''' URL_FUNC_DICT = dict()def route(url): def set_func(func): URL_FUNC_DICT[url] = func def call_func(*args, **kwargs): return func(*args, **kwargs) return call_func return set_func@route("/index.py") def index(): with open("./html/index.html") as f: content = f.read() return content@route("/login.py") def login(): return b"logining!..."'''服务程序''' def server(new_socket, static,flag=False): # 获取浏览器数据,GET/HTTP/1.1 overtime = 500 if flag:overtime=0 new_socket.setblocking(False)#解阻塞 begin = time.time() while True: request = new_socket.recv(1024).decode("utf-8") print(request) if request == b'': break request_line = request.splitlines() print(">>>" * 50) # GET /index.html HTTP/1.1 flie_name = "" ret = re.match(r"[^/]+(/[^ ]*)", request_line[0]) if ret: flie_name = ret.group(1) if flie_name == "/": flie_name = "/index.html" #访问静态文件 if not flie_name.endswith(".py"): try: f = open(static + flie_name, 'rb') except: response = "HTTP/1.1 404 NOT FOUND\r\n" response += "\r\n" new_socket.send(response.encode("utf-8")) else: response = "HTTP/1.1 200 OK\r\n\r\n" new_socket.send(response.encode("utf-8")) for each_connect in f.readlines(): new_socket.send(each_connect) f.close() #get访问 else: #请求头 status = '200 OK' headers = ('Contene-Type', 'text/html; charset=utf-8') print(flie_name) try: func = URL_FUNC_DICT[flie_name] body = func() header = "HTTP/1.1 %s\r\n" % status for temp in headers: header += "%s:%s\r\n" % (temp[0], temp[1]) header += "\r\n" response = header + body except Exception as ret: response = "HTTP/1.1 404 NOT FOUND\r\n" response += "\r\n" new_socket.send(response.encode('utf-8')) #超时退出 if overtime <= 0:break else: overtime = overtime-int(time.time() - begin) new_socket.close()

调度程序(wbe类)
  1. 建立进程池,用于并发服务,建立进程队列用于进程通信
pool = multiprocessing.Pool(5) queue = multiprocessing.Manager().Queue(10)

2.创建socket并进行初始化
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) tcp_server_socket.bind(("", port)) tcp_server_socket.listen(128) #非阻塞 tcp_server_socket.setblocking(False)

参考源码
class Web(object): def __init__(self, port, static): self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.tcp_server_socket.bind(("", port)) self.tcp_server_socket.listen(128) #非阻塞 self.tcp_server_socket.setblocking(False) self.static = static#静态文件目录 self.pool = multiprocessing.Pool(5)#进程池 self.queue = multiprocessing.Manager().Queue(10)#进程队列 self.__isStop = Truedef __del__(self): self.stop() self.tcp_server_socket.close() self.queue.clear() # 进程池停止接受新的任务 self.pool.close() # 等待进程结束 self.pool.join()def stop(self): self.__isStop = False while not self.queue.empty(): self.pool.apply_async(server, (self.queue.get(), self.static,True)) def run_forever(self): while self.__isStop: try: new_socket, client_addr = self.tcp_server_socket.accept() self.queue.put(new_socket) except Exception as e: pass finally: if not self.queue.empty(): work_socket = self.queue.get() #进程调度 self.pool.apply_async(server, (work_socket, self.static, False)) else: pass

最后补上main函数调用
def main(): #用于配置静态目录{"static":"./html",} with open("./server.cnf") as f: config_info = eval(f.read()) if len(sys.argv) == 2: try: port = int(sys.argv[1]) print(port) except Exception as ret: print("端口输入错误!!") else: port = 7890 print("使用python server.py 端口号 ") minwbe = Web(port, config_info["static"]) minwbe.run_forever()

    推荐阅读