python 数据库性能提升 - TCP聊天+传输文件服务器服务器套接字v2.7

2023-02-06 10:12:47 浏览数 (2)

TCP聊天 传输文件服务器服务器套接字v2.7

刚创建服务器的时候为了后期便于管理, 主要也是MySQL对我不适合, 跨平台使用, 一打包还有得装, 所以直接自己做了个

这是我写的服务器的数据库代码, 可见一看就能看出来, 数据库只存在于单个文件data.json中, I/O十分频繁, 用户信息文件存于运行内存中, 在小数据的情况下速度快, 但到数据存于一定程度, 性能断崖式下跌, 且 在taskmgr(任务管理器) 中内存一举超过了 1.78G的pycharm, 成为第一.

提升性能的方法:

  1. 变量使用 set可变,无序的,不重复的元素集合 (不可出现 listsetbytearray 等无__hash__(self)哈希值的类) – set 的 时间复杂度为 O1, list 的 时间复杂度为 On. – set内部存储元素必是可hash的,而且还是不可重复的. 当每一个set中的元素都有一个独立hash的编码,虽然外面看元素是乱序的,但是内部其实是hash编码的排序,当运行时是通过编码查询,所以会如此之快(warning:是有可能出现hash冲突,但是极少)
  2. 避免单文件频繁调用I/O
  3. 用户建立文件夹, 一个文件夹对应一个用户的md5值(sha256的都行), 这是为了创建文件夹时候避免非法字符的出现.
  4. 类似于文件传输服务器, 传来的文件最好解压分割切片
  5. 只将用户名存于运行内存中, 节省空间, 一般数据库也不会大于几TB, 把密码, 注册时间这些杂七杂八的东西放在文件夹下面

行了, 后面的直接不用看了, 按左上角的退出键退出吧

这是data.py代码(全部data.py 在gitcode - https://gitcode.net/m0_60394896/python/-/blob/05267ff4f3c2267954dc87e505d034229eaa0f98/server/data.py)

代码语言:javascript复制
from json import load, dump
from os import path, mkdir
from hashlib import md5
from time import time


def encode(data: str):
    m = md5()
    m.update(data.encode('utf8'))
    return m.hexdigest()


file = '.clientsdata.json'
folder = '.clients'
if not path.exists(folder):
    mkdir(folder)


class data:
    def __init__(self):
        if path.exists(file):
            with open(file, 'r') as f:
                self.data = load(f)
        else:
            self.data = {}

    def __get__(self, username, default=None) -> tuple:
        return self.data.get(username, default)

    def __in__(self, username) -> bool:
        return username in self.data.keys()

    def __write__(self) -> None:
        with open(file, 'w') as f:
            dump(self.data, f, indent=4)

    def __register__(self, username, password, time: (int, float) = time()) -> None:
        ...
        self.__write__()

    def __login__(self, username, password) -> bool:
        return self.data[username][0] == encode(password)

    def get_time(self, username):
        return self.data[username][1]

    def handler(self, type: int, username: str, password: str):
		...

文章目录

  • 测试
  • 提升性能

所有版本记录: v1.0 : TCP聊天服务器套接字|PyQt5 socket(TCP端口映射 端口放行) logging Thread(含日志,html) anaconda打包32位exe(3.4万字)|python高阶 v1.1 : python TCP套接字服务器v1.1-新增服务端命令功能及修改bug(socket PyQt5) v1.2 : python TCP服务器v1.2 - 服务端新增用户登录注册(json, md5加密) v1.3 : python TCP服务器v1.3 - 服务器抗压测试及关闭套接字处理 v1.4 : python TCP服务器v1.4 - 客户端连接服务器异常(异常情况分类)处理 v1.5 : PyQt5可编辑下拉框(comboBox):editable - python TCP服务器v1.5 - 客户端连接界面增加自定义参数(设置超时, 连接地址可选) v1.6 : Python TCP服务器v1.6 - multiprocessing多进程及Ctrl-c(SIGINT)退出 v1.7 : Python TCP服务器v1.7 - PyQt5 server服务端来临 v1.8 : python TCP服务器v1.8 - PyQt5登录界面美化 淡入淡出 v1.9 : socketTCP协程文件 信息传递 - TCP聊天文件服务器v1.9 - 划时代的版本更新(4.6万字) v2.0 : TCP聊天文件服务器v2.0 - 重大bug修复 PyQt5文件传输可视化 v2.1 : TCP聊天文件服务器v2.1 - 服务端线程管理(threading.enumerate) v2.2 : TCP聊天文件服务器v2.2 - 服务端客户端套接字解决分包/粘包问题 - SocketQueue继承以及减少冗余 v2.3 : gzip的使用 - TCP聊天文件服务器v2.3 - 文件传输建立缓存制度和.gz的解压缩/压缩解决运行内存过大 v2.4 : 网络传输测速 - TCP聊天 传输文件服务器服务器套接字v2.4 - socket协程文件传送测速 v2.5 : TCP聊天 传输文件服务器服务器套接字v2.5 - socket测速规范已经gzip的弃用 v2.6 : TCP聊天 传输文件服务器服务器套接字v2.6 - 登录注册界面更新 - loading界面应用

测试

增加数据库 用户登录注册的时候还是在 v1.2,

代码语言:javascript复制
import itertools
from threading import Thread


def threading(Daemon, name=None, **kwargs):
    thread = Thread(**kwargs)
    thread.setDaemon(Daemon)
    if name:
        thread.setName(name)
    thread.start()
    return thread


data = data()
print(data.handler(0, "tqm", "asdf"))

a = 0

for u in itertools.product("qwertyuiop[]asdfghjkl;'\zxcvbnm,./`12345678900-", repeat=6):
    p = "阿斯蒂芬asdf"
    a  = 1
    if a % 400 == 0:
        data.handler(1, "".join(u), "".join(p), _show_detail=True)
        print(a)
    else:
        data.handler(1, "".join(u), "".join(p), _show_detail=False)
    if a > 1000000:
        sys.exit(a)
代码语言:javascript复制
2022-06-20 12:49:00,951 - data.py[line:51] - INFO: Execute the function User handle, timeit 0.000
(False, '用户不存在!', '')
2022-06-20 12:49:01,743 - data.py[line:51] - INFO: 
Execute the function User handle, timeit 0.002
2022-06-20 19:11:20,572 - data.py[line:145] - INFO: size: 799.9Mb(8319541 bytes)
... ...
2022-06-20 19:14:37,143 - data.py[line:51] - INFO: Execute the function User handle, timeit 2.496
2022-06-20 19:14:37,144 - data.py[line:145] - INFO: size: 800.0Mb(8354830 bytes)
2022-06-20 19:17:45,165 - data.py[line:51] - INFO: Execute the function User handle, timeit 2.531
2022-06-20 19:17:45,165 - data.py[line:145] - INFO: size: 800.0Mb(8385887 bytes)

在运行了长达3, 8400次迭代后, 连一个注册用户都已经超过了秒的单位.

提升性能

set

list

add

append

代码语言:javascript复制
from json import load, dump, decoder
from os import path, mkdir, listdir
from hashlib import md5
from time import time
import logging

logging.basicConfig(level=logging.DEBUG,
                    format="%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
logger = logging.getLogger(__name__)


def _mkdir(_folder):
    try:
        if not path.exists(_folder):
            mkdir(_folder)
            return True
    except NotImplementedError:
        logger.exception("")
    return False


def encode(_data: str):
    m = md5()
    m.update(_data.encode('utf8'))
    return m.hexdigest()


def timeit(objname: str, ign=True):
    def setup_function(func_name):
        def _exec_function(*args, _show_detail=True, **kwargs):
            startime = time()
            _resp = func_name(*args, **kwargs)
            if _show_detail:
                logger.info("Execute the function %s%s, timeit %0.3f" % (
                    objname.title(), "" if ign else f" (at {str(func_name)})", time() - startime))
            return _resp

        return _exec_function

    return setup_function


folder = r'.clients'
_mkdir(folder)

data = set()
if path.isdir(folder):
    try:
        data = set(listdir(folder))
    except decoder.JSONDecodeError:
        pass


def __in__(username) -> bool:
    return username in data


def register(username, password, register_time: (int, float) = time()) -> None:
    global data
    data.add(username)
    user_path = path.join(folder, encode(username))
    print(user_path, _mkdir(user_path))
    _mkdir(user_path)
    with open(path.join(user_path, "user.json"), "w") as f:
        dump({"username": username, "password": password, "register_time": int(register_time)}, f, indent=4)


def login(username, password) -> bool:
    with open(path.join(path.join(folder, encode(username)), "user.json"), "r") as f:
        _data = load(f)
        return _data.get("username", "") == username and _data.get("password", "") == password


def get_time(username):
    with open(path.join(path.join(folder, encode(username)), "user.json"), "r") as f:
        return load(f).get("register_time")


@timeit("User Handle")
def handler(_type: int, username: str, password: str):
    username = username.strip()
    if not username:
        return False, "未填写用户名!", ""
    password = password.strip()
    if not password:
        return False, "未填写密码!", ""
    if not 2 <= len(username) <= 12:
        return False, "用户名需在2~12位之间!", ""
    if not 4 <= len(password) <= 10:
        return False, "密码需在4~10位之间!", ""
    if _type == 0:  # login
        if not __in__(username):
            return False, "用户不存在!", ""
        if not login(username, password):
            return False, "用户名 / 密码错误!", ""
        return True, "欢迎回来, "   username, username
    elif _type == 1:  # register
        if __in__(username):
            return False, "已存在用户!", ""
        register(username, password)
        return True, "初来乍到, "   username, username

就是将每个用户的用户名的md5值存于文件夹, user.json放信息

0 人点赞