python实现Web服务器 监听8080端口

工程结构

image

 web_server.py

# web_server.py
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import json
import threading
import time

# 注意:这里不直接初始化log_manager,由main.py控制
# 使用一个全局变量来存储logger,在init函数中设置
_web_logger = None

def init_web_server(logger_instance):
    """初始化Web服务器,设置logger实例"""
    global _web_logger
    _web_logger = logger_instance

class RequestHandler(BaseHTTPRequestHandler):
    """自定义HTTP请求处理器"""
    
    def _get_logger(self):
        """获取logger实例"""
        global _web_logger
        if _web_logger is None:
            # 如果没有设置logger,创建一个基本的
            import logging
            return logging.getLogger('web_server_fallback')
        return _web_logger
    
    def do_GET(self):
        """处理GET请求"""
        logger = self._get_logger()
        try:
            # 解析URL和参数
            parsed_url = urlparse(self.path)
            query_params = parse_qs(parsed_url.query)
            
            # 记录请求信息
            logger.info(f"收到GET请求: {self.path}")
            logger.debug(f"客户端: {self.client_address}")
            logger.debug(f"请求头: {dict(self.headers)}")
            logger.info(f"解析的参数: {query_params}")
            
            # 根据路径进行路由
            if parsed_url.path == '/':
                self._handle_home(query_params)
            elif parsed_url.path == '/api/data':
                self._handle_api_data(query_params)
            elif parsed_url.path == '/api/user':
                self._handle_api_user(query_params)
            elif parsed_url.path == '/health':
                self._handle_health_check()
            else:
                self._handle_not_found()
                
        except Exception as e:
            logger.error(f"处理请求时出错: {e}", exc_info=True)
            self._send_error_response(500, f"服务器内部错误: {str(e)}")
    
    def do_POST(self):
        """处理POST请求"""
        logger = self._get_logger()
        try:
            # 获取内容长度
            content_length = int(self.headers.get('Content-Length', 0))
            
            # 读取POST数据
            post_data = self.rfile.read(content_length)
            
            # 解析URL和参数
            parsed_url = urlparse(self.path)
            query_params = parse_qs(parsed_url.query)
            
            # 尝试解析JSON数据
            json_data = {}
            if post_data:
                try:
                    json_data = json.loads(post_data.decode('utf-8'))
                except json.JSONDecodeError:
                    logger.warning("无法解析POST数据为JSON")
            
            # 记录请求信息
            logger.info(f"收到POST请求: {self.path}")
            logger.debug(f"POST数据: {post_data.decode('utf-8')}")
            logger.debug(f"JSON数据: {json_data}")
            logger.info(f"URL参数: {query_params}")
            
            # 根据路径进行路由
            if parsed_url.path == '/api/submit':
                self._handle_api_submit(query_params, json_data)
            else:
                self._handle_not_found()
                
        except Exception as e:
            logger.error(f"处理POST请求时出错: {e}", exc_info=True)
            self._send_error_response(500, f"服务器内部错误: {str(e)}")
    
    def _handle_home(self, params):
        """处理首页请求"""
        self._get_logger().info("处理首页请求")
        
        response_data = {
            "message": "欢迎使用Web服务器",
            "endpoints": {
                "/": "首页",
                "/api/data": "获取数据API",
                "/api/user": "用户信息API",
                "/api/submit": "提交数据API (POST)",
                "/health": "健康检查"
            },
            "params_received": params
        }
        
        self._send_json_response(200, response_data)
    
    def _handle_api_data(self, params):
        """处理数据API请求"""
        logger = self._get_logger()
        logger.info("处理数据API请求")
        
        # 从参数中获取数据
        limit = int(params.get('limit', [10])[0])
        offset = int(params.get('offset', [0])[0])
        filter_type = params.get('type', ['all'])[0]
        
        # 模拟数据
        data = [
            {"id": i + offset, "name": f"Item {i + offset}", "type": filter_type}
            for i in range(1, limit + 1)
        ]
        
        response_data = {
            "status": "success",
            "data": data,
            "pagination": {
                "limit": limit,
                "offset": offset,
                "total": 100
            },
            "params_used": {
                "limit": limit,
                "offset": offset,
                "type": filter_type
            }
        }
        
        logger.debug(f"返回数据: {len(data)} 条记录")
        self._send_json_response(200, response_data)
    
    def _handle_api_user(self, params):
        """处理用户API请求"""
        logger = self._get_logger()
        logger.info("处理用户API请求")
        
        user_id = params.get('id', [None])[0]
        name = params.get('name', [None])[0]
        
        if not user_id and not name:
            self._send_error_response(400, "需要提供id或name参数")
            return
        
        # 模拟用户数据
        user_data = {
            "id": user_id or "123",
            "name": name or "Unknown",
            "email": f"{name or 'user'}@example.com" if name else "user@example.com",
            "role": "user",
            "status": "active"
        }
        
        response_data = {
            "status": "success",
            "user": user_data,
            "request_params": params
        }
        
        self._send_json_response(200, response_data)
    
    def _handle_api_submit(self, params, json_data):
        """处理提交API请求"""
        logger = self._get_logger()
        logger.info("处理提交API请求")
        
        # 验证数据
        if not json_data:
            self._send_error_response(400, "需要提供JSON数据")
            return
        
        # 处理提交的数据
        response_data = {
            "status": "success",
            "message": "数据提交成功",
            "received_data": json_data,
            "url_params": params,
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
        }
        
        logger.info(f"成功处理提交数据: {json_data}")
        self._send_json_response(201, response_data)
    
    def _handle_health_check(self):
        """处理健康检查"""
        self._get_logger().debug("处理健康检查请求")
        
        response_data = {
            "status": "healthy",
            "service": "Python Web Server",
            "version": "1.0.0",
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
        }
        
        self._send_json_response(200, response_data)
    
    def _handle_not_found(self):
        """处理404错误"""
        self._get_logger().warning(f"请求的路径不存在: {self.path}")
        self._send_error_response(404, "请求的路径不存在")
    
    def _send_json_response(self, status_code, data):
        """发送JSON响应"""
        try:
            response_json = json.dumps(data, ensure_ascii=False, indent=2)
            
            self.send_response(status_code)
            self.send_header('Content-Type', 'application/json; charset=utf-8')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            
            self.wfile.write(response_json.encode('utf-8'))
            
            self._get_logger().debug(f"发送响应: 状态码 {status_code}, 数据长度 {len(response_json)}")
            
        except Exception as e:
            self._get_logger().error(f"发送响应时出错: {e}")
            raise
    
    def _send_error_response(self, status_code, message):
        """发送错误响应"""
        error_data = {
            "status": "error",
            "code": status_code,
            "message": message
        }
        
        self._get_logger().warning(f"发送错误响应: {status_code} - {message}")
        self._send_json_response(status_code, error_data)
    
    def log_message(self, format, *args):
        """重写默认的日志方法,使用我们的logger"""
        self._get_logger().info(format % args)

class WebServer:
    """Web服务器封装类"""
    
    def __init__(self, host='', port=8080, logger=None):
        self.host = host
        self.port = port
        self.logger = logger
        self.httpd = None
        self.server_thread = None
        
        # 初始化web_server的logger
        if logger:
            init_web_server(logger)
    
    def start(self, daemon=False):
        """启动服务器"""
        if self.logger:
            self.logger.info(f"启动Web服务器,监听端口: {self.port}")
            self.logger.info(f"服务器地址: http://localhost:{self.port}")
            self.logger.info("可用端点:")
            self.logger.info("  GET  /           - 首页")
            self.logger.info("  GET  /api/data   - 获取数据")
            self.logger.info("  GET  /api/user   - 用户信息")
            self.logger.info("  POST /api/submit - 提交数据")
            self.logger.info("  GET  /health     - 健康检查")
        
        self.httpd = HTTPServer((self.host, self.port), RequestHandler)
        
        if daemon:
            # 在后台线程中运行
            self.server_thread = threading.Thread(target=self.httpd.serve_forever, daemon=True)
            self.server_thread.start()
            if self.logger:
                self.logger.info("服务器已在后台线程中启动")
        else:
            # 在前台运行
            if self.logger:
                self.logger.info("服务器开始处理请求...")
            try:
                self.httpd.serve_forever()
            except KeyboardInterrupt:
                self.stop()
    
    def stop(self):
        """停止服务器"""
        if self.httpd:
            self.httpd.shutdown()
            self.httpd.server_close()
            if self.logger:
                self.logger.info("服务器已停止")
    
    def get_server_address(self):
        """获取服务器地址"""
        return f"http://{self.host or 'localhost'}:{self.port}"

log_manager.py

# log_manager.py
import logging
import logging.handlers
import os
from datetime import datetime

class LogManager:
    _instance = None
    _loggers = {}
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(LogManager, cls).__new__(cls)
            cls._instance._initialize()
        return cls._instance
    
    def _initialize(self):
        """初始化日志配置"""
        self.log_dir = 'logs'
        os.makedirs(self.log_dir, exist_ok=True)
        print(f"日志管理器初始化完成,日志目录: {self.log_dir}")
    
    def get_logger(self, name='app'):
        """获取或创建logger"""
        if name not in self._loggers:
            logger = logging.getLogger(name)
            logger.setLevel(logging.INFO)
            
            # 清除已有的handler
            if logger.handlers:
                logger.handlers.clear()
            
            # 创建每日目录
            current_date = datetime.now().strftime('%Y-%m-%d')
            daily_dir = os.path.join(self.log_dir, current_date)
            os.makedirs(daily_dir, exist_ok=True)
            
            # 文件handler - 每小时轮转,最大10MB
            log_file = os.path.join(daily_dir, f'{name}.log')
            file_handler = logging.handlers.RotatingFileHandler(
                log_file, 
                maxBytes=10*1024*1024, 
                backupCount=24, 
                encoding='utf-8'
            )
            
            # 控制台handler
            console_handler = logging.StreamHandler()
            
            # 格式
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
            )
            file_handler.setFormatter(formatter)
            console_handler.setFormatter(formatter)
            
            logger.addHandler(file_handler)
            logger.addHandler(console_handler)
            logger.propagate = False
            
            self._loggers[name] = logger
            print(f"创建logger: {name}")
        
        return self._loggers[name]

# 创建全局实例
log_manager = LogManager()

main.py

# main.py
import argparse
import signal
import sys
from log_manager import log_manager
from web_server import WebServer

# 获取logger实例
logger = log_manager.get_logger('main')

def signal_handler(sig, frame):
    """处理中断信号"""
    logger.info("收到中断信号,正在关闭服务器...")
    if 'server' in globals():
        globals()['server'].stop()
    sys.exit(0)

def parse_arguments():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='启动Web服务器')
    parser.add_argument('--port', '-p', type=int, default=8080, 
                       help='服务器端口 (默认: 8080)')
    parser.add_argument('--host', '-H', default='', 
                       help='服务器主机 (默认: 所有接口)')
    parser.add_argument('--daemon', '-d', action='store_true',
                       help='以守护进程模式运行')
    parser.add_argument('--debug', action='store_true',
                       help='启用调试模式')
    return parser.parse_args()

def setup_server(host, port, debug=False):
    """设置并返回服务器实例"""
    # 设置日志级别
    if debug:
        #logger.setLevel(logging.DEBUG)
        logger.info("调试模式已启用")
    
    # 创建服务器实例
    server = WebServer(host=host, port=port, logger=logger)
    return server

def main():
    """主函数"""
    # 解析命令行参数
    args = parse_arguments()
    
    logger.info("=" * 50)
    logger.info("启动Web服务器应用程序")
    logger.info(f"主机: {args.host or '所有接口'}")
    logger.info(f"端口: {args.port}")
    logger.info(f"守护模式: {args.daemon}")
    logger.info(f"调试模式: {args.debug}")
    logger.info("=" * 50)
    
    try:
        # 设置信号处理
        signal.signal(signal.SIGINT, signal_handler)
        signal.signal(signal.SIGTERM, signal_handler)
        
        # 设置服务器
        global server
        server = setup_server(args.host, args.port, args.debug)
        
        # 启动服务器
        server.start(daemon=args.daemon)
        
        # 如果不是守护模式,等待服务器运行
        if not args.daemon:
            logger.info("服务器正在运行,按 Ctrl+C 停止...")
            #signal.pause()  # 等待信号
            
    except Exception as e:
        logger.error(f"启动服务器时发生错误: {e}", exc_info=True)
        return 1
    
    return 0

if __name__ == "__main__":
    # 注册全局server变量,以便信号处理器访问
    server = None
    
    # 运行主程序
    exit_code = main()
    sys.exit(exit_code)

 

posted @ 2025-08-22 16:57  海乐学习  阅读(33)  评论(0)    收藏  举报