模拟建立简化apache

闲余时间研究了一下apache,饶有兴趣的模仿着写了个简化版的,可以完成基本需求的。

应用事例

在同一IP和端口上配置三家公司的服务器,可以通过不同的域名访问不同的服务器。其中A公司的访问量较大,使用反向代理实现负载均衡,如下图所示。
在这里插入图片描述

用到的技术:负载均衡反向代理

反向代理负载均衡普通代理方式是代理内部网络用户访问internet上服务器的连接请求,客户端必须指定代理服务器,并将本来要直接发送到internet上服务器的连接请求发送给代理服务器处理。反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。反向代理负载均衡技术是把将来自internet上的连接请求以反向代理的方式动态地转发给内部网络上的多台服务器进行处理,从而达到负载均衡的目的。

用到的技术:静态web服务

客户端发送请求,tcp三次握手,握手成功后发起http request包,服务器收到后利用正则获取 request 头部信息。抓取 url 中的 path 资源路径。连接默认documentroot 路径
形成完整路径,申请 fd 访问资源内容,构建 response报头+内容进行 send 实现访问。

源码展示

包组织

在这里插入图片描述

配置文件

[default]
ip = 172.16.2.12
port = 8080
documentroot=E:\补课学习\补课学习\8月26\apatcp
redirect="/stock/a.html" "172.16.2.12:8080/stock/a.html"

[www.a.com]
ip = 172.16.2.12
port = 8080
documentroot=E:\补课学习\补课学习\8月26\apatcpa
#upstream=True

[www.b.com]
ip = 172.16.2.12
port = 8080
documentroot=E:\补课学习\补课学习\8月26\apatcpb
deny=\stock

[www.c.com]
ip = 172.16.2.12
port = 8080
documentroot=E:\补课学习\补课学习\8月26\apatcpc
deny=\stock\a.html

[upstream:www.a.com]
algo = leastsession
server01 = 172.16.2.12:8080
server02 = 172.16.2.12:8080
server03 = 172.16.2.12:8080

源码

apache.py模块

class server:
    def __init__(self,ip,port):
        self.socket = socket.socket()
        self.iport = (ip,port)
        self.event = threading.Event()
        self.serverroot = "E:\\补课学习\\补课学习\\8月26\\apatcp"
        self.clients = {}
        self.webserver = {}
        self.num = 0
        # self.config = Confread(contents="E:\\python-3.7.0\\conf")

    def reg(self,bytestring,sok):
        regx = re.compile("GET (?P<url>\S+) (?P<method>\S+)\r\nHost: (?P<host>\S+)\r\n.*")
        data = {}
        dic = read("peizhi.conf")
        tag = False
        try:
            data = regx.match(bytestring.decode("utf-8")).groupdict()
            domain,port = data["host"].split(":")
            if domain[0:1] in "0123456789" or domain not in dic.keys():
                documentroot = self.serverroot
            else:
                documentroot = dic[domain]["documentroot"]
                tag = dic[domain].get("upstream",False)
                if tag:
                    name = threading.current_thread().getName()
                    self.clients[name] = sok
                    self.addserver(domain)
            if data["url"] == "/":
                # redirect = dic[domain]["redirect"]
                data["url"] = Path(documentroot + "/" + "index.html")
            else:
                p = Path(documentroot + "/" + data["url"])
                if p.exists() and p.is_file():
                    data["url"] = p
                else:
                    data["url"] = Path(documentroot + "/" + "error.html")
            pathfile = str(data["url"])[len(documentroot) + 1:]
            print(pathfile)
        except Exception as f:
            longer.info(f)
        return data,tag,pathfile

    def addserver(self,domain):
        servers = dic["upstream:{}".format(domain)]
        for key in servers.keys():
            if not key.endswith("w") and key.startswith("s"):
                self.webserver[servers[key]] = 0

    def algo(self):
        if len(self.clients) < 10:
            for server in self.webserver.keys():
                return server
        lst = sorted(self.webserver.items(),key=lambda x:x[1])
        return lst[0][0]

    def resdata(self,data,tag,pathfile):
        string = ""
        # <html>
        # <head>
        # <meta http-equiv="refresh" content="0; url='http://172.16.2.12:8080/stock/a.html'" />
        # <title><h1>301 Moved Permanently</h1></title>
        # </head>
        # </html>
        # """

        if not tag:
            with open(data["url"],"r") as f:
                lines = f.readlines()
                for line in lines:
                    string = string + line
        else:
            server = self.algo()
            url = "http://{}/{}".format(self.algo(),pathfile)
            # url = "http://www.baidu.com"
            self.webserver[server] += 1
            print(url)
            string = requests.get(url).content.decode("utf-8")
            self.webserver[server] -= 1
        return string

    def response(self,string):
        head = '''HTTP/1.1 200 OK
        Connection: keep-alive
        Data: Thur, 22 Mar ''' + "500" + ''' GMT
        Server:PythonWebServer 1.0
        Last-Modified: Thur, 22 Mar 2017 19:26:03 GMT
        Content-Lenth: ''' + str(len(string)) + '\nContent-Type:text/html\n\n'
        # "<html>
        # <head>
        # <meta http-equiv="refresh" content="0; url='http://www.baidu.com'" />
        # <title><h1>301 Moved Permanently</h1></title>
        # </head>
        # </html>
        # """

        databytes = (head + string).encode("utf-8")
        return databytes

    def start(self):
        self.socket.bind(self.iport)
        self.socket.listen()
        threading.Thread(target=self.accept).start()

    def accept(self):
        while not self.event.is_set():
            try:
                sok, radd = self.socket.accept()
                self.num += 1
                print(sok,radd)
                longer.info("sok:{},radd:{}".format(sok,radd))
                threading.Thread(target=self.reves, args=(sok,),name="thread{}".format(self.num)).start()
            except OSError:
                break

    def reves(self, sok):
        try:
            data = sok.recv(1024)
            requestdict, tag, pathfile = self.reg(data, sok)
            bstring = self.response(self.resdata(requestdict, tag, pathfile))
            if bstring:
                sok.send(bstring)
                self.clients.pop(threading.current_thread().getName())
                sok.close()
        except Exception as f:
            longer.info(f)

    def stop(self):
        self.event.set()
        self.socket.close()
        return True

if __name__ == "__main__":
    Server = server("172.16.2.12",8080)
    Server.start()
    while True:
        cmd = input("config#")
        if cmd == "quit" or cmd =="退出":
            Server.stop()
            break
        print("lll:",cmd.strip("? ?"))

confread.py读配置模块

import configparser
import json
import pathlib
class Confread:
    def __init__(self,file=None,jfile=None,contents=None,suffix=None):
        self.file = file
        self.contet = contents
        self.suffix = suffix
        self.jfile = jfile
        self.parser = configparser.ConfigParser()
    def pread(self):
        self.parser.read(self.file)
        return self.parser
def read(file):#self,
    confdict = {}
    #key = "line"
    with open(file,"r",encoding="utf-8")as f:
        lines = f.readlines()
        lines = filter(lambda x: not x.startswith("\n"),lines)
        lines = filter(lambda x: not x.startswith("#"),lines)
        for line in lines:
            if line.startswith("["):
                key = line.strip("[ ] \n")
                confdict[key] = {}
            else:
                k,v = line.strip('\n').split('=')
                confdict[key][k] = v
    return confdict

        p = pathlib.Path(self.content)
        if p.is_dir():
            for file in p.iterdir():
                if file.suffix ==".conf":
                    confdict.update(self.read(file))
        return confdict

cc = Confread(contents)

longer.py日志模块

import logging
def log():
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    handler = logging.FileHandler("file.log",mode='w')
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    return logger
longer = log()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值