山东大学软件学院项目实训纪实(八)

一、个人任务

我的任务主要有以下两个

  • 对后端进行单元测试及集成测试等,找出存在的功能、性能等问题
  • 针对存在的问题进一步改进后端

二、任务1——测试后端

这里主要展示几个不同类型的后端问题

1.问题1:requests.exceptions.SSLError: HTTPSConnectionPool(host=‘huggingface.co‘, port=443): Max ret

解决过程:出现问题的原因是运行程序的时候没有联网,但是使用了未下载的chatGLM模型。我们可以将模型手动下载,然后放入到对应文件夹即可。

2.问题2:文件编码格式报错

解决过程:

对于Unicode字符,python解释器需要先将Unicode字符编码为GBK,然后再写入csv文件。但是由于该Unicode字符串中包含一些GBK中无法显示的字符,导致此时提示“’gbk’ codec can’t encode”的错误。
解决方法:尝试了许多网上的解决方案,最后设置了一下输出结果存放的csv文件的编码格式,解决了问题。

3.ValueError: not enough values to unpack (expected 2, got 1)

  • 分析原因:原因:由于自定义的idf语料库的txt文件中,存在空行或换行符\n,以及存在△、$等特殊字符。
  • 解决方法:写了两个函数,用于去除空行、换行符和特殊字符,只保留中文和英文字符。
# 去掉每行头尾空格及空行、换行符\n
def clearBlankLine(lines):
    result = []
    for line in lines:
        line = line.strip()  # 去掉每行头尾空格
        if not len(line):  # 如果是空行
            continue
        result.append(line)
    return result

# 去掉特殊字符,只保留中英文字符
def clearSpecialSymbols(lines):
    result = []
    for line in lines:
        valid1 = re.search('^[a-zA-Z]',line)
        valid2 = re.search('^[\u4e00-\u9fa5]',line)
        if valid1 or valid2:
            result.append(line)
    return result

4.RuntimeError: view size is not compatible with input tensor‘s size and stride

分析问题:在PyTorch中,view() 函数用于重新调整张量(Tensor)的形状,而不改变其数据。然而,view() 操作有一个重要的前提:新形状下的元素必须是连续存储的(contiguous)。这意味着张量中的元素在内存中是连续排列的,没有间隔或“空洞”。

解决问题:为了解决这个问题,可以使用 .contiguous() 方法来返回张量的一个连续副本。这个操作会重新排列张量中的元素,以确保它们在内存中是连续存储的。然后,就可以在这个连续副本上使用 view() 了。

使用下面这一行代码即可

out = out.contiguous().view(out.size()[0], -1)

5.TypeError: can't multiply sequence by non-int of type 'numpy.float64'

can't multiply sequence by non-int of type 'float'

分析问题:这个问题表明,我们正在将这些序列中的任何一个(通常是一个字符串和一个元组)与一个浮点数(小数)相乘。但是我们可以将一个序列与一个数字相乘,如下,不会报错

site_name = 'freeCodeCamp '

print(site_name * 2)
# freeCodeCamp freeCodeCamp 

print(site_name * 3)
# freeCodeCamp freeCodeCamp freeCodeCamp
stringfied_num = '10 '

print(stringfied_num * 3)
# 10 10 10

解决方法:进行np.array(),将这个序列变成矩阵就可以

三、任务2——改进后端

对后端初步的改进是主要是针对后端进行分层处理,主要分为以下几层:

  1. 分离数据库连接和查询逻辑:创建一个数据库服务层来处理数据库连接和查询。
  2. 定义数据模型:用于表示从数据库中检索到的数据。
  3. 创建服务层:处理业务逻辑,如根据用户ID检索记录。
  4. 创建控制器:处理HTTP请求,调用服务层函数,并返回适当的HTTP响应。
  5. 添加错误处理和日志记录(此处未详细展示)。

初步是针对数据库连接和数据模型的优化,我适当的增加更多的功能、异常处理、日志记录以及注释,使得代码更加健壮和易于理解。

1.数据库连接和查询逻辑(优化后)

# -*- coding:utf-8 -*-
"""

    功能:连接数据库,封装了操作数据库的增删改查等常见操作函数
"""

import pymysql
import logging


class DBUtils(object):
    _db_conn = None
    _db_cursor = None

    def __init__(self, host, user, password, db, port=3306, charset='utf8'):
        """
        初始化数据库连接
        :param host: 数据库主机名
        :param user: 数据库用户名
        :param password: 数据库密码
        :param db: 数据库名
        :param port: 数据库端口,默认为3306
        :param charset: 字符集,默认为utf8
        """
        self.connect_to_db(host, user, password, db, port, charset)

    def connect_to_db(self, host, user, password, db, port=3306, charset='utf8'):
        """
        建立数据库连接
        :param host: 数据库主机名
        :param user: 数据库用户名
        :param password: 数据库密码
        :param db: 数据库名
        :param port: 数据库端口
        :param charset: 字符集
        """
        try:
            self._db_conn = pymysql.connect(host=host, user=user, password=password, port=port, db=db, charset=charset)
            self._db_cursor = self._db_conn.cursor()
            logging.info("数据库连接成功")
        except pymysql.MySQLError as e:
            logging.error(f"数据库连接失败: {e}")
            raise

    def __del__(self):
        """
        析构函数,关闭数据库连接
        """
        self.close_connection()

    def close_connection(self):
        """
        关闭数据库连接和游标
        """
        try:
            if self._db_cursor:
                self._db_cursor.close()
                self._db_cursor = None
            if self._db_conn:
                self._db_conn.close()
                self._db_conn = None
            logging.info("数据库连接已关闭")
        except Exception as e:
            logging.error(f"关闭数据库连接时发生错误: {e}")

    def reconnect(self, host, user, password, db, port=3306, charset='utf8'):
        """
        重新建立数据库连接
        :param host: 数据库主机名
        :param user: 数据库用户名
        :param password: 数据库密码
        :param db: 数据库名
        :param port: 数据库端口
        :param charset: 字符集
        """
        try:
            self._db_conn.close()
            self._db_conn = None
            self._db_cursor = None
            self.connect_to_db(host, user, password, db, port, charset)
        except Exception as e:
            logging.error(f"重新连接数据库时发生错误: {e}")

    def execute_sql(self, sql_str, args=None):
        """
        执行SQL语句,并返回受影响的行数
        :param sql_str: SQL语句
        :param args: 参数列表
        :return: 受影响的行数
        """
        try:
            if args is None:
                args = ()
            self._db_cursor.execute(sql_str, args)
            return self._db_cursor.rowcount
        except pymysql.MySQLError as e:
            logging.error(f"执行SQL语句时发生错误: {e}, SQL: {sql_str}")
            return -1

    def get_one(self, sql_str, args=None):
        """
        查询单个结果,返回具体的数据内容
        :param sql_str: SQL语句
        :param args: 参数列表
        :return: 查询结果(单个)
        """
        try:
            result = self.execute_sql(sql_str, args)
            if result == -1:
                return None
            return self._db_cursor.fetchone()
        except pymysql.MySQLError as e:
            logging.error(f"获取单个结果时发生错误: {e}")
            return None


            # 查询特定
    def get_oneById(self, sql_str, args):
        """
        查询单个结果,返回具体的数据内容
        :param sql_str: sql语句
        :param args: 参数列表
        :return: result
        """
        try:
            if self._db_conn is None or self._db_cursor is None:
                # 如果连接或游标为空,可能需要重新连接或初始化游标
                self.connect_to_db()  # 调用连接数据库的方法

            if args is None:
                args = ()  # 如果没有提供参数,则使用空元组

            self._db_cursor.execute(sql_str, args)  # 使用参数执行SQL语句
            result = self._db_cursor.fetchone()
            return result
        except Exception as e:
            logging.error(f"执行SQL查询时发生错误: {e}")
            return None  # 或者可以抛出一个异常

    def execute_many(self, sql_str, args_list):
        """
        执行多条SQL语句(通常用于批量插入或更新)
        """
        try:
            self._db_cursor.executemany(sql_str, args_list)
            return self._db_cursor.rowcount
        except Exception as e:
            logging.error(f"执行多条SQL语句时发生错误: {e}")
            return -1




    # 查询多个结果
    def get_all(self, sql_str, args=None):
        """
        查询多个结果,返回元组对象
        :param sql_str: sql 语句
        :param args: 参数列表
        :return: result
        """
        try:
            if self._db_conn is not None:
                self._db_cursor.execute(sql_str, args=args)
                result = self._db_cursor.fetchall()
                return result
            else:
                logging.error("请检查数据库连接")
        except Exception as e:
            logging.error(e)

    # 插入数据
    def insert(self, sql_str, args=None):
        """
        向数据库插入数据,返回影响行数(int)
        :param sql_str: sql 语句
        :param args: 参数列表
        :return: affect_rows
        """
        try:
            if self._db_conn is not None:
                affect_rows = self._db_cursor.execute(sql_str, args=args)
                self._db_conn.commit()
                return affect_rows
            else:
                logging.error("请检查数据库连接")
        except Exception as e:
            self._db_conn.rollback()
            logging.error(e)

    # 修改数据
    def modify(self, sql_str, args=None):
        """
        更新数据,返回影响行数(int)
        :param sql_str: sql 语句
        :param args: 参数列表
        :return: affect_rows
        """
        return self.insert(sql_str=sql_str, args=args)

    # 删除数据
    def delete(self, sql_str, args=None):
        """
        删除数据,返回影响行数(int)
        :param sql_str: sql 语句
        :param args: 参数列表
        :return: affect_rows
        """
        return self.insert(sql_str=sql_str, args=args)

    def __del__(self):
        """
        程序运行结束后,会默认调用 __del__ 方法
        销毁对象
        """
        if self._db_conn is not None:
            self._db_cursor.close()
            self._db_conn.close()

2.定义数据模型

这一部分主要是针对图片地址的处理,进行base64编码解码

import base64
import binascii


class Record:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def to_dict(self):
        # 对图片地址进行额外处理,解码base64  
        if 'question' in self.__dict__ and isinstance(self.__dict__['question'], str):
            try:
                # 尝试解码base64字符串为字节数据  
                decoded_bytes = decode_base64(self.__dict__['question'])
                # 我们只是简单地将字节数据存储在'question'属性中  
                self.__dict__['question'] = decoded_bytes
            except (ValueError, binascii.Error):
                # 如果解码失败,可以记录错误或采取其他措施  
                print("Error decoding base64 string for 'question'")
        return self.__dict__.copy()  # 返回字典的副本,避免外部修改影响实例内部状态  


def decode_base64(encoded_str):
    # 真正的base64解码逻辑  
    try:
        # 添加'data:image/jpeg;base64,'这样的前缀的字符串需要先去除这些前缀  
       
        decoded_bytes = base64.b64decode(encoded_str)
        return decoded_bytes
    except (ValueError, binascii.Error) as e:
        # 如果解码失败,抛出异常或返回None等  
        print(f"Error decoding base64 string: {e}")
        return None  

四、总结

1、测试阶段

在本次任务中,我主要负责对后端进行单元测试和集成测试,以发现潜在的功能和性能问题。

  1. 单元测试:我针对后端代码中的关键模块和函数编写了详细的单元测试案例,确保每个模块都能按照预期工作。通过单元测试,我发现了几个逻辑错误和边界条件处理不当的问题,并立即进行了修复。

  2. 集成测试:在单元测试通过后,我进行了集成测试,模拟了实际业务场景中的请求和响应。通过集成测试,我发现了几个模块间交互时出现的问题,如数据不一致、接口调用错误等。针对这些问题,我进行了深入的分析和调试,最终解决了这些问题。

  3. 性能测试:在测试过程中,我还对后端进行了性能测试,包括响应时间、吞吐量、并发处理能力等指标。通过性能测试,我发现了一些性能瓶颈和优化点,为后续的改进提供了方向。

2、改进阶段

在测试阶段发现问题后,我针对这些问题进行了后端的改进工作。

  1. 代码优化:针对逻辑错误和边界条件处理不当的问题,我重新审查了相关代码,并进行了优化和重构。通过优化代码结构、提高代码可读性和可维护性,我减少了潜在的错误和缺陷。

  2. 模块间交互优化:针对模块间交互时出现的问题,我重新梳理了模块间的依赖关系和调用流程,并进行了必要的调整和优化。通过优化模块间的交互方式,我提高了系统的稳定性和可靠性。

  3. 性能优化:针对性能测试中发现的问题,我进行了针对性的优化工作。通过优化数据库查询、减少不必要的网络请求、使用缓存等技术手段,我提高了系统的响应速度和吞吐量。

3、总结

通过本次任务,我对有效的测试和改进。测试阶段不仅帮助我发现了后端存在的问题。在改进阶段,我通过优化代码、调整模块间交互方式和提高系统性能等措施,成功解决了存在的问题,并提高了后端的质量和稳定性。

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值