11.24 速率限制

This commit is contained in:
zy123 2024-11-25 10:13:39 +08:00
parent d2b47af57e
commit 89d665adae
4 changed files with 152 additions and 91 deletions

View File

@ -1,31 +1,21 @@
from threading import Semaphore # flask_app/ConnectionLimiter.py
import threading
from functools import wraps from functools import wraps
from flask import jsonify, current_app
class ConnectionLimiter: class ConnectionLimiter:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, max_connections=10): def __init__(self, max_connections=10):
if not hasattr(self, 'semaphore'): self.semaphore = threading.Semaphore(max_connections)
self.semaphore = Semaphore(max_connections)
def limit_connections(self, f): def limit_connections(self, f):
"""装饰器:限制并发连接数"""
@wraps(f) @wraps(f)
def decorated_function(*args, **kwargs): def wrapped(*args, **kwargs):
if not self.semaphore.acquire(blocking=False): self.semaphore.acquire()
return jsonify({
'error': 'Server is busy. Maximum number of concurrent connections reached.',
'code': 429
}), 429
try: try:
return f(*args, **kwargs) return f(*args, **kwargs)
finally: finally:
self.semaphore.release() self.semaphore.release()
return decorated_function return wrapped

View File

@ -98,35 +98,48 @@ def preprocess_files(output_folder, downloaded_file_path, file_type,unique_id,lo
# 基本信息 # 基本信息
def fetch_project_basic_info(invalid_path, merged_baseinfo_path, merged_baseinfo_path_more,tobidders_notice, clause_path, logger): def fetch_project_basic_info(invalid_path, merged_baseinfo_path, merged_baseinfo_path_more, tobidders_notice, clause_path, logger):
logger.info("starting 基础信息...") logger.info("starting 基础信息...")
start_time = time.time() start_time = time.time()
try:
if not merged_baseinfo_path: if not merged_baseinfo_path:
merged_baseinfo_path = invalid_path merged_baseinfo_path = invalid_path
if not merged_baseinfo_path_more: if not merged_baseinfo_path_more:
merged_baseinfo_path_more=invalid_path merged_baseinfo_path_more = invalid_path
if not tobidders_notice: if not tobidders_notice:
tobidders_notice = invalid_path tobidders_notice = invalid_path
basic_res = combine_basic_info(merged_baseinfo_path,merged_baseinfo_path_more,tobidders_notice, clause_path) basic_res = combine_basic_info(merged_baseinfo_path, merged_baseinfo_path_more, tobidders_notice, clause_path)
result = basic_res
except Exception as exc:
logger.error(f"Error in 基础信息: {exc}")
# 返回默认值
result = {"基础信息": {}}
end_time = time.time() end_time = time.time()
logger.info(f"基础信息 done耗时{end_time - start_time:.2f}") logger.info(f"基础信息 done耗时{end_time - start_time:.2f}")
return basic_res return result
# 形式、响应、资格评审 def fetch_qualification_review(evaluation_method, qualification, output_folder, tobidders_notice_table, clause_path, invalid_path, merged_baseinfo_path, notice_path, logger):
def fetch_qualification_review(evaluation_method, qualification, output_folder, tobidders_notice_table, clause_path, invalid_path, merged_baseinfo_path,notice_path,logger):
logger.info("starting 资格审查...") logger.info("starting 资格审查...")
start_time = time.time() start_time = time.time()
try:
if not notice_path: if not notice_path:
notice_path=invalid_path notice_path = invalid_path
if not evaluation_method: if not evaluation_method:
evaluation_method = invalid_path evaluation_method = invalid_path
if not merged_baseinfo_path: if not merged_baseinfo_path:
merged_baseinfo_path = invalid_path merged_baseinfo_path = invalid_path
review_standards_res = combine_review_standards(evaluation_method, qualification, output_folder, tobidders_notice_table, clause_path, invalid_path, merged_baseinfo_path,notice_path) review_standards_res = combine_review_standards(
evaluation_method, qualification, output_folder, tobidders_notice_table, clause_path, invalid_path, merged_baseinfo_path, notice_path
)
result = {"资格审查": review_standards_res}
except Exception as exc:
logger.error(f"Error in 资格审查: {exc}")
# 返回默认值
result = {"资格审查": {}}
end_time = time.time() end_time = time.time()
logger.info(f"资格审查 done耗时{end_time - start_time:.2f}") logger.info(f"资格审查 done耗时{end_time - start_time:.2f}")
return review_standards_res return result
# 评分细则 流式 # 评分细则 流式
@ -147,38 +160,56 @@ def fetch_evaluation_standards(invalid_path, evaluation_method,logger):
# 无效、废标项解析 # 无效、废标项解析
def fetch_invalid_requirements(invalid_docpath, output_folder,logger): def fetch_invalid_requirements(invalid_docpath, output_folder, logger):
logger.info("starting 无效标与废标...") logger.info("starting 无效标与废标...")
start_time = time.time() start_time = time.time()
try:
find_invalid_res = combine_find_invalid(invalid_docpath, output_folder) find_invalid_res = combine_find_invalid(invalid_docpath, output_folder)
result = find_invalid_res
except Exception as exc:
logger.error(f"Error in 无效标与废标: {exc}")
# 返回默认值
result = {"无效标与废标": {}}
end_time = time.time() end_time = time.time()
logger.info(f"无效标与废标 done耗时{end_time - start_time:.2f}") logger.info(f"无效标与废标 done耗时{end_time - start_time:.2f}")
return find_invalid_res return result
# 投标文件要求 # 投标文件要求
def fetch_bidding_documents_requirements(invalid_path, merged_baseinfo_path_more,clause_path,logger): def fetch_bidding_documents_requirements(invalid_path, merged_baseinfo_path_more, clause_path, logger):
logger.info("starting 投标文件要求...") logger.info("starting 投标文件要求...")
start_time = time.time() start_time = time.time()
try:
if not merged_baseinfo_path_more: if not merged_baseinfo_path_more:
merged_baseinfo_path_more=invalid_path merged_baseinfo_path_more = invalid_path
selection = 1 selection = 1
fetch_bidding_documents_requirements_json = extract_from_notice(merged_baseinfo_path_more, clause_path, selection) fetch_bidding_documents_requirements_json = extract_from_notice(merged_baseinfo_path_more, clause_path, selection)
result = {"投标文件要求": fetch_bidding_documents_requirements_json}
except Exception as exc:
logger.error(f"Error in 投标文件要求: {exc}")
# 返回默认值
result = {"投标文件要求": {}}
end_time = time.time() end_time = time.time()
logger.info(f"投标文件要求 done耗时{end_time - start_time:.2f}") logger.info(f"投标文件要求 done耗时{end_time - start_time:.2f}")
return {"投标文件要求": fetch_bidding_documents_requirements_json} return result
# 开评定标流程 # 开评定标流程
def fetch_bid_opening(invalid_path, merged_baseinfo_path_more,clause_path,logger): def fetch_bid_opening(invalid_path, merged_baseinfo_path_more, clause_path, logger):
logger.info("starting 开评定标流程...") logger.info("starting 开评定标流程...")
start_time = time.time() start_time = time.time()
try:
if not merged_baseinfo_path_more: if not merged_baseinfo_path_more:
merged_baseinfo_path_more=invalid_path merged_baseinfo_path_more = invalid_path
selection = 2 selection = 2
fetch_bid_opening_json = extract_from_notice(merged_baseinfo_path_more, clause_path, selection) fetch_bid_opening_json = extract_from_notice(merged_baseinfo_path_more, clause_path, selection)
result = {"开评定标流程": fetch_bid_opening_json}
except Exception as exc:
logger.error(f"Error in 开评定标流程: {exc}")
# 返回默认值
result = {"开评定标流程": {}}
end_time = time.time() end_time = time.time()
logger.info(f"开评定标流程 done耗时{end_time - start_time:.2f}") logger.info(f"开评定标流程 done耗时{end_time - start_time:.2f}")
return {"开评定标流程": fetch_bid_opening_json} return result
#分段返回 #分段返回
@ -226,7 +257,14 @@ def engineering_bid_main(output_folder, downloaded_file_path, file_type, unique_
except Exception as exc: except Exception as exc:
logger.error(f"Error processing {key}: {exc}") logger.error(f"Error processing {key}: {exc}")
yield json.dumps({'error': f'Error processing {key}: {str(exc)}'}, ensure_ascii=False) if key == 'evaluation_standards':
# 返回默认的商务评分和技术评分
default_evaluation = {
'technical_standards': {"技术评分": ""},
'commercial_standards': {"商务评分": ""}
}
yield json.dumps(default_evaluation, ensure_ascii=False)
# yield json.dumps({'error': f'Error processing {key}: {str(exc)}'}, ensure_ascii=False)
#TODO:基本信息,判断是否这里,打勾逻辑取消了。 #TODO:基本信息,判断是否这里,打勾逻辑取消了。
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -22,20 +22,16 @@ def require_connection_limit():
@wraps(f) @wraps(f)
def wrapped(*args, **kwargs): def wrapped(*args, **kwargs):
limiter = current_app.connection_limiter limiter = current_app.connection_limiter
return limiter.limit_connections(f)(*args, **kwargs) limiter.semaphore.acquire() # 阻塞式获取信号量
try:
return f(*args, **kwargs)
finally:
limiter.semaphore.release()
return wrapped return wrapped
return decorator return decorator
@upload_bp.route('/upload', methods=['POST']) @upload_bp.route('/upload', methods=['POST'])
@require_connection_limit()
def zbparse(): def zbparse():
# 获取当前应用的 connection_limiter
limiter = current_app.connection_limiter
if not limiter.semaphore.acquire(blocking=False):
return jsonify({
'error': 'Server is busy. Maximum number of concurrent connections reached.',
'code': 429
}), 429
try: try:
logger = g.logger logger = g.logger
logger.info("zbparse start!!!") logger.info("zbparse start!!!")
@ -55,8 +51,9 @@ def zbparse():
except Exception as e: except Exception as e:
logger.error('Exception occurred: ' + str(e)) logger.error('Exception occurred: ' + str(e))
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500
finally: except Exception as e:
limiter.semaphore.release() logger.error('Unexpected exception: ' + str(e))
return jsonify({'error': 'Internal server error'}), 500
def process_and_stream(file_url, zb_type): def process_and_stream(file_url, zb_type):
""" """

View File

@ -78,28 +78,39 @@ def preprocess_files(output_folder, file_path, file_type,logger):
'merged_baseinfo_path': merged_baseinfo_path 'merged_baseinfo_path': merged_baseinfo_path
} }
def fetch_project_basic_info(invalid_path,merged_baseinfo_path, procurement_path, clause_path,logger): def fetch_project_basic_info(invalid_path, merged_baseinfo_path, procurement_path, clause_path, logger):
logger.info("starting 基础信息...") logger.info("starting 基础信息...")
start_time = time.time() start_time = time.time()
try:
if not merged_baseinfo_path: if not merged_baseinfo_path:
merged_baseinfo_path = invalid_path merged_baseinfo_path = invalid_path
if not procurement_path: if not procurement_path:
procurement_path=invalid_path procurement_path = invalid_path
basic_res = combine_basic_info(merged_baseinfo_path, procurement_path,clause_path,invalid_path) basic_res = combine_basic_info(merged_baseinfo_path, procurement_path, clause_path, invalid_path)
base_info, good_list = post_process_baseinfo(basic_res,logger) base_info, good_list = post_process_baseinfo(basic_res, logger)
result = base_info, good_list
except Exception as exc:
logger.error(f"Error in 基础信息: {exc}")
# 返回默认值
result = {"基础信息": {}}, []
end_time = time.time() end_time = time.time()
logger.info(f"基础信息 done耗时{end_time - start_time:.2f}") logger.info(f"基础信息 done耗时{end_time - start_time:.2f}")
return base_info, good_list return result
def fetch_qualification_review(invalid_path,qualification_path, notice_path,logger): def fetch_qualification_review(invalid_path, qualification_path, notice_path, logger):
logger.info("starting 资格审查...") logger.info("starting 资格审查...")
start_time = time.time() start_time = time.time()
review_standards_res = combine_qualification_review(invalid_path,qualification_path, notice_path) try:
review_standards_res = combine_qualification_review(invalid_path, qualification_path, notice_path)
result = {"资格审查": review_standards_res}
except Exception as exc:
logger.error(f"Error in 资格审查: {exc}")
# 返回默认值
result = {"资格审查": {}}
end_time = time.time() end_time = time.time()
logger.info(f"资格审查 done耗时{end_time - start_time:.2f}") logger.info(f"资格审查 done耗时{end_time - start_time:.2f}")
return review_standards_res return result
def fetch_evaluation_standards(invalid_path, evaluation_method_path,logger): def fetch_evaluation_standards(invalid_path, evaluation_method_path,logger):
logger.info("starting 商务评分和技术评分...") logger.info("starting 商务评分和技术评分...")
@ -116,37 +127,55 @@ def fetch_evaluation_standards(invalid_path, evaluation_method_path,logger):
"commercial_standards": commercial_standards "commercial_standards": commercial_standards
} }
def fetch_invalid_requirements(invalid_docpath, output_folder,logger): def fetch_invalid_requirements(invalid_docpath, output_folder, logger):
logger.info("starting 无效标与废标...") logger.info("starting 无效标与废标...")
start_time = time.time() start_time = time.time()
try:
find_invalid_res = combine_find_invalid(invalid_docpath, output_folder) find_invalid_res = combine_find_invalid(invalid_docpath, output_folder)
result = find_invalid_res
except Exception as exc:
logger.error(f"Error in 无效标与废标: {exc}")
# 返回默认值
result = {"无效标与废标": {}}
end_time = time.time() end_time = time.time()
logger.info(f"无效标与废标 done耗时{end_time - start_time:.2f}") logger.info(f"无效标与废标 done耗时{end_time - start_time:.2f}")
return find_invalid_res return result
def fetch_bidding_documents_requirements(invalid_path,merged_baseinfo_path,clause_path,logger): def fetch_bidding_documents_requirements(invalid_path, merged_baseinfo_path, clause_path, logger):
logger.info("starting 投标文件要求...") logger.info("starting 投标文件要求...")
if not merged_baseinfo_path: if not merged_baseinfo_path:
merged_baseinfo_path=invalid_path merged_baseinfo_path = invalid_path
start_time = time.time() start_time = time.time()
selection=1 selection = 1
fetch_bidding_documents_requirements_json = extract_from_notice(merged_baseinfo_path,clause_path, selection) try:
fetch_bidding_documents_requirements_json = extract_from_notice(merged_baseinfo_path, clause_path, selection)
result = {"投标文件要求": fetch_bidding_documents_requirements_json}
except Exception as exc:
logger.error(f"Error in 投标文件要求: {exc}")
# 返回默认值,假设默认值为一个空字典
result = {"投标文件要求": {}}
end_time = time.time() end_time = time.time()
logger.info(f"投标文件要求 done耗时{end_time - start_time:.2f}") logger.info(f"投标文件要求 done耗时{end_time - start_time:.2f}")
return {"投标文件要求": fetch_bidding_documents_requirements_json} return result
# 开评定标流程 # 开评定标流程
def fetch_bid_opening(invalid_path,merged_baseinfo_path,clause_path,logger): def fetch_bid_opening(invalid_path, merged_baseinfo_path, clause_path, logger):
logger.info("starting 开评定标流程...") logger.info("starting 开评定标流程...")
if not merged_baseinfo_path: if not merged_baseinfo_path:
merged_baseinfo_path=invalid_path merged_baseinfo_path = invalid_path
start_time = time.time() start_time = time.time()
selection=2 selection = 2
fetch_bid_opening_json = extract_from_notice(merged_baseinfo_path,clause_path, selection) try:
fetch_bid_opening_json = extract_from_notice(merged_baseinfo_path, clause_path, selection)
result = {"开评定标流程": fetch_bid_opening_json}
except Exception as exc:
logger.error(f"Error in 开评定标流程: {exc}")
# 返回默认值,假设默认值为一个空字典
result = {"开评定标流程": {}}
end_time = time.time() end_time = time.time()
logger.info(f"开评定标流程 done耗时{end_time - start_time:.2f}") logger.info(f"开评定标流程 done耗时{end_time - start_time:.2f}")
return {"开评定标流程": fetch_bid_opening_json} return result
def post_process_baseinfo(base_info,logger): def post_process_baseinfo(base_info,logger):
@ -230,7 +259,14 @@ def goods_bid_main(output_folder, file_path, file_type, unique_id):
yield json.dumps({key: transform_json_values(result)}, ensure_ascii=False) yield json.dumps({key: transform_json_values(result)}, ensure_ascii=False)
except Exception as exc: except Exception as exc:
logger.error(f"Error processing {key}: {exc}") logger.error(f"Error processing {key}: {exc}")
yield json.dumps({'error': f'Error processing {key}: {str(exc)}'}, ensure_ascii=False) if key == 'evaluation_standards':
# 返回默认的商务评分和技术评分
default_evaluation = {
'technical_standards': {"技术评分": ""},
'commercial_standards': {"商务评分": ""}
}
yield json.dumps(default_evaluation, ensure_ascii=False)
# yield json.dumps({'error': f'Error processing {key}: {str(exc)}'}, ensure_ascii=False)
if collected_good_list is not None: if collected_good_list is not None:
yield json.dumps({'good_list': transform_json_values(collected_good_list)}, ensure_ascii=False) yield json.dumps({'good_list': transform_json_values(collected_good_list)}, ensure_ascii=False)