当前位置:首页>python>一天一个Python知识点——Day 138:RPC框架基础

一天一个Python知识点——Day 138:RPC框架基础

  • 2026-02-08 05:27:12
一天一个Python知识点——Day 138:RPC框架基础

1. RPC简介

什么是RPC?

RPC(Remote Procedure Call,远程过程调用)是一种允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数的协议,而不需要程序员显式编码这个远程调用的细节。

RPC的核心思想

  • 透明性:远程调用看起来像本地调用

  • 抽象性:隐藏网络通信的复杂性

  • 服务化:将功能封装为可远程调用的服务

RPC与REST API的区别

特性
RPC
REST API
通信协议
多种(HTTP、TCP等)
主要HTTP
数据格式
二进制/文本(Protobuf、JSON等)
主要JSON/XML
设计理念
面向操作/函数
面向资源
性能
通常更高(二进制协议)
相对较低
服务发现
需要单独的机制
基于URL

2. RPC架构与工作原理

RPC架构组件

客户端应用 → 客户端存根 → 网络传输 → 服务器存根 → 服务器应用    ↑         ↓           ↓          ↓           ↓   调用     序列化      网络通信     反序列化      执行   函数     参数        发送/接收     参数        函数

RPC调用流程

  • 客户端调用:客户端调用本地存根方法

  • 参数编组:客户端存根将参数序列化为消息

  • 网络传输:通过网络发送到服务器

  • 服务器接收:服务器存根接收并反序列化消息

  • 方法调用:调用实际的服务器方法

  • 结果返回:将结果序列化并返回给客户端

3. RPC通信模式

同步RPC

客户端等待服务器响应后再继续执行

# 伪代码示例result = remote_service.add(12)  # 阻塞直到返回print(f"Result: {result}")

异步RPC

客户端不等待响应,通过回调或Future处理结果

# 伪代码示例future = remote_service.add_async(12)# 继续其他工作...result = future.result()  # 需要时获取结果

流式RPC

支持持续的数据流传输:

  • 客户端流:客户端发送多个请求,服务器返回一个响应

  • 服务器流:客户端发送一个请求,服务器返回多个响应

  • 双向流:双方都可以发送多个消息

4. Python RPC框架比较

常用Python RPC框架

框架
协议
序列化
特点
gRPC
HTTP/2
Protobuf
高性能,Google出品,多语言支持
XML-RPC
HTTP
XML
简单,Python标准库内置
JSON-RPC
HTTP
JSON
轻量级,适合Web集成
Pyro4
自定义
Pickle
纯Python,易用
ZeroRPC
ZeroMQ
MessagePack
异步,高性能
Thrift
TCP/HTTP
二进制
Facebook出品,跨语言

选择建议

  • 简单项目:XML-RPC或JSON-RPC

  • 性能关键:gRPC或Thrift

  • 纯Python环境:Pyro4

  • 实时通信:ZeroRPC

5. XML-RPC实现

使用Python标准库实现

服务端

# server.py - XML-RPC服务器from xmlrpc.server import SimpleXMLRPCServerfrom xmlrpc.server import SimpleXMLRPCRequestHandlerimport datetime# 限制路径为 /RPC2class RequestHandler(SimpleXMLRPCRequestHandler):    rpc_paths = ('/RPC2',)class MathService:    """数学服务"""    def add(self, a, b):        return a + b    def multiply(self, a, b):        return a * b    def divide(self, a, b):        if b == 0:            raise ValueError("除数不能为零")        return a / bclass TimeService:    """时间服务"""    def current_time(self):        return datetime.datetime.now().isoformat()    def server_info(self):        return {            "name""XML-RPC Server",            "version""1.0",            "started_at": datetime.datetime.now().isoformat()        }def main():    # 创建服务器    server = SimpleXMLRPCServer(        ('localhost'8000),        requestHandler=RequestHandler,        allow_none=True    )    server.register_introspection_functions()  # 启用内省    # 注册服务实例    math_service = MathService()    time_service = TimeService()    # 注册方法    server.register_instance(math_service)    server.register_instance(time_service)    # 注册单个函数    def greet(name):        return f"Hello, {name}!"    server.register_function(greet, 'greet')    # 注册多调用函数(接收多个参数)    server.register_multicall_functions()    print("XML-RPC服务器运行在 http://localhost:8000")    print("可用方法:")    for method in server.system_listMethods():        print(f"  - {method}")    # 启动服务器    server.serve_forever()if __name__ == '__main__':    main()

客户端

# client.py - XML-RPC客户端import xmlrpc.clientimport pprintclass XMLRPCClient:    def __init__(self, server_url="http://localhost:8000"):        self.server = xmlrpc.client.ServerProxy(server_url)        self.pp = pprint.PrettyPrinter(indent=2)    def test_basic_operations(self):        """测试基本操作"""        print("=== 测试基本操作 ===")        # 调用greet函数        result = self.server.greet("Alice")        print(f"greet('Alice'): {result}")        # 数学运算        print(f"add(5, 3): {self.server.add(53)}")        print(f"multiply(5, 3): {self.server.multiply(53)}")        try:            print(f"divide(10, 2): {self.server.divide(102)}")            print(f"divide(10, 0): {self.server.divide(100)}")        except Exception as e:            print(f"错误: {e}")    def test_time_service(self):        """测试时间服务"""        print("\n=== 测试时间服务 ===")        current_time = self.server.current_time()        server_info = self.server.server_info()        print(f"当前时间: {current_time}")        print("服务器信息:")        self.pp.pprint(server_info)    def test_introspection(self):        """测试内省功能"""        print("\n=== 测试内省 ===")        # 列出所有方法        methods = self.server.system.listMethods()        print("可用方法:")        for method in methods:            print(f"  - {method}")        # 获取方法签名        print("\n方法签名:")        for method in methods:            try:                signature = self.server.system.methodSignature(method)                help_text = self.server.system.methodHelp(method)                print(f"{method}:")                print(f"  签名: {signature}")                print(f"  帮助: {help_text[:50]}..." if help_text else "  帮助: 无")            except:                pass    def test_multicall(self):        """测试多调用(批量操作)"""        print("\n=== 测试多调用 ===")        multicall = xmlrpc.client.MultiCall(self.server)        multicall.add(12)        multicall.multiply(34)        multicall.greet("Bob")        multicall.current_time()        results = list(multicall())        print("批量调用结果:")        for i, result in enumerate(results):            print(f"  结果{i+1}{result}")    def test_fault_handling(self):        """测试错误处理"""        print("\n=== 测试错误处理 ===")        # 测试错误响应        try:            # 调用不存在的方法            self.server.nonexistent_method()        except xmlrpc.client.Fault as fault:            print(f"Fault错误: 代码={fault.faultCode}, 消息={fault.faultString}")        except Exception as e:            print(f"其他错误: {type(e).__name__}{e}")def main():    client = XMLRPCClient()    # 运行测试    client.test_basic_operations()    client.test_time_service()    client.test_introspection()    client.test_multicall()    client.test_fault_handling()if __name__ == '__main__':    main()

6. gRPC实现

安装gRPC

pip install grpcio grpcio-tools

定义Protocol Buffers接口

// calculator.protosyntax = "proto3";package calculator;service Calculator {    // 一元RPC    rpc Add (AddRequest) returns (AddResponse) {}    rpc Multiply (MultiplyRequest) returns (MultiplyResponse) {}    // 服务器流式RPC    rpc PrimeFactors (PrimeFactorsRequest) returns (stream PrimeFactorsResponse) {}    // 客户端流式RPC    rpc ComputeAverage (stream ComputeAverageRequest) returns (ComputeAverageResponse) {}    // 双向流式RPC    rpc FindMax (stream FindMaxRequest) returns (stream FindMaxResponse) {}}message AddRequest {    int32 a = 1;    int32 b = 2;}message AddResponse {    int32 result = 1;}message MultiplyRequest {    int32 a = 1;    int32 b = 2;}message MultiplyResponse {    int32 result = 1;}message PrimeFactorsRequest {    int32 number = 1;}message PrimeFactorsResponse {    int32 factor = 1;}message ComputeAverageRequest {    int32 number = 1;}message ComputeAverageResponse {    double average = 1;}message FindMaxRequest {    int32 number = 1;}message FindMaxResponse {    int32 current_max = 1;}

生成Python代码

python -m grpc_tools.protoc \    -I. \    --python_out=. \    --grpc_python_out=. \    calculator.proto

gRPC服务器实现

# grpc_server.pyimport grpcfrom concurrent import futuresimport timeimport mathimport calculator_pb2import calculator_pb2_grpcclass CalculatorServicer(calculator_pb2_grpc.CalculatorServicer):    def Add(self, request, context):        """一元RPC:加法"""        result = request.a + request.b        print(f"Add: {request.a} + {request.b} = {result}")        return calculator_pb2.AddResponse(result=result)    def Multiply(self, request, context):        """一元RPC:乘法"""        result = request.a * request.b        print(f"Multiply: {request.a} * {request.b} = {result}")        return calculator_pb2.MultiplyResponse(result=result)    def PrimeFactors(self, request, context):        """服务器流式RPC:计算质因数"""        number = request.number        print(f"PrimeFactors: 分解 {number}")        # 分解质因数        n = number        divisor = 2        while n > 1:            while n % divisor == 0:                yield calculator_pb2.PrimeFactorsResponse(factor=divisor)                n //= divisor            divisor += 1            if divisor * divisor > n:                if n > 1:                    yield calculator_pb2.PrimeFactorsResponse(factor=n)                break    def ComputeAverage(self, request_iterator, context):        """客户端流式RPC:计算平均数"""        total = 0        count = 0        for request in request_iterator:            total += request.number            count += 1            print(f"ComputeAverage: 收到 {request.number}, 当前总数 {total}, 计数 {count}")        average = total / count if count > 0 else 0        print(f"ComputeAverage: 平均数 = {average}")        return calculator_pb2.ComputeAverageResponse(average=average)    def FindMax(self, request_iterator, context):        """双向流式RPC:寻找最大值"""        current_max = None        for request in request_iterator:            number = request.number            print(f"FindMax: 收到 {number}")            if current_max is None or number > current_max:                current_max = number                print(f"FindMax: 更新最大值 = {current_max}")                yield calculator_pb2.FindMaxResponse(current_max=current_max)def serve():    """启动gRPC服务器"""    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))    calculator_pb2_grpc.add_CalculatorServicer_to_server(CalculatorServicer(), server)    # 监听端口    server.add_insecure_port('[::]:50051')    server.start()    print("gRPC服务器启动,监听端口 50051")    try:        # 保持服务器运行        while True:            time.sleep(86400)  # 24小时    except KeyboardInterrupt:        server.stop(0)if __name__ == '__main__':    serve()

gRPC客户端实现

# grpc_client.pyimport grpcimport calculator_pb2import calculator_pb2_grpcimport timeclass CalculatorClient:    def __init__(self, host='localhost', port=50051):        self.channel = grpc.insecure_channel(f'{host}:{port}')        self.stub = calculator_pb2_grpc.CalculatorStub(self.channel)    def test_unary_rpc(self):        """测试一元RPC"""        print("=== 测试一元RPC ===")        # 加法        response = self.stub.Add(calculator_pb2.AddRequest(a=10, b=20))        print(f"10 + 20 = {response.result}")        # 乘法        response = self.stub.Multiply(calculator_pb2.MultiplyRequest(a=10, b=20))        print(f"10 * 20 = {response.result}")    def test_server_streaming(self):        """测试服务器流式RPC"""        print("\n=== 测试服务器流式RPC ===")        request = calculator_pb2.PrimeFactorsRequest(number=360)        responses = self.stub.PrimeFactors(request)        print(f"360的质因数分解:")        factors = []        for response in responses:            factors.append(str(response.factor))        print(f"360 = {' × '.join(factors)}")    def test_client_streaming(self):        """测试客户端流式RPC"""        print("\n=== 测试客户端流式RPC ===")        def generate_numbers():            numbers = [12345678910]            for num in numbers:                yield calculator_pb2.ComputeAverageRequest(number=num)                time.sleep(0.1)  # 模拟延迟        response = self.stub.ComputeAverage(generate_numbers())        print(f"1到10的平均数: {response.average}")    def test_bidirectional_streaming(self):        """测试双向流式RPC"""        print("\n=== 测试双向流式RPC ===")        def generate_numbers():            numbers = [314159265]            for num in numbers:                yield calculator_pb2.FindMaxRequest(number=num)                time.sleep(0.2)  # 模拟延迟        responses = self.stub.FindMax(generate_numbers())        print("发送序列: 3, 1, 4, 1, 5, 9, 2, 6, 5")        print("实时最大值:")        for response in responses:            print(f"  当前最大值: {response.current_max}")    def run_all_tests(self):        """运行所有测试"""        self.test_unary_rpc()        self.test_server_streaming()        self.test_client_streaming()        self.test_bidirectional_streaming()def main():    client = CalculatorClient()    client.run_all_tests()if __name__ == '__main__':    main()

7. 实现自定义RPC框架

简单的JSON-RPC框架

# simple_rpc.pyimport jsonimport socketimport threadingfrom typing import DictAnyCallablefrom dataclasses import dataclassfrom enum import Enumclass RPCErrorCode(Enum):    PARSE_ERROR = -32700    INVALID_REQUEST = -32600    METHOD_NOT_FOUND = -32601    INVALID_PARAMS = -32602    INTERNAL_ERROR = -32603    SERVER_ERROR = -32000@dataclassclass RPCRequest:    jsonrpc: str = "2.0"    method: str = None    params: list = None    idAny = None    def to_dict(self):        return {            "jsonrpc"self.jsonrpc,            "method"self.method,            "params"self.params or [],            "id"self.id        }    @classmethod    def from_dict(cls, data):        return cls(            jsonrpc=data.get("jsonrpc""2.0"),            method=data.get("method"),            params=data.get("params"),            id=data.get("id")        )@dataclassclass RPCResponse:    jsonrpc: str = "2.0"    result: Any = None    error: Dict[strAny] = None    idAny = None    def to_dict(self):        response = {"jsonrpc"self.jsonrpc}        if self.error:            response["error"] = self.error        else:            response["result"] = self.result        if self.id is not None:            response["id"] = self.id        return response    @classmethod    def success(cls, result, request_id):        return cls(result=result, id=request_id)    @classmethod    def error_response(cls, code, message, request_id=None):        return cls(            error={"code": code.value, "message": message},            id=request_id        )class SimpleRPCServer:    def __init__(self, host='localhost', port=8080):        self.host = host        self.port = port        self.methods = {}        self.server_socket = None        self.running = False    def register_method(self, name: str, func: Callable):        """注册RPC方法"""        self.methods[name] = func    def register(self, name=None):        """装饰器注册方法"""        def decorator(func):            method_name = name or func.__name__            self.register_method(method_name, func)            return func        return decorator    def handle_request(self, data: str) -> str:        """处理单个请求"""        try:            request_dict = json.loads(data)            request = RPCRequest.from_dict(request_dict)            # 验证请求            if request.method is None:                return json.dumps(RPCResponse.error_response(                    RPCErrorCode.INVALID_REQUEST,                    "Missing method"                ).to_dict())            if request.method not in self.methods:                return json.dumps(RPCResponse.error_response(                    RPCErrorCode.METHOD_NOT_FOUND,                    f"Method '{request.method}' not found",                    request.id                ).to_dict())            # 执行方法            try:                method = self.methods[request.method]                params = request.params or []                if isinstance(params, list):                    result = method(*params)                elif isinstance(params, dict):                    result = method(**params)                else:                    result = method()                response = RPCResponse.success(result, request.id)                return json.dumps(response.to_dict())            except Exception as e:                return json.dumps(RPCResponse.error_response(                    RPCErrorCode.INTERNAL_ERROR,                    f"Internal error: {str(e)}",                    request.id                ).to_dict())        except json.JSONDecodeError:            return json.dumps(RPCResponse.error_response(                RPCErrorCode.PARSE_ERROR,                "Parse error"            ).to_dict())    def handle_connection(self, client_socket, address):        """处理客户端连接"""        print(f"客户端连接: {address}")        try:            # 接收数据            data = client_socket.recv(4096).decode('utf-8')            if not data:                return            # 处理请求            response = self.handle_request(data)            # 发送响应            client_socket.send(response.encode('utf-8'))        except Exception as e:            print(f"处理连接错误: {e}")        finally:            client_socket.close()    def start(self):        """启动服务器"""        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)        self.server_socket.bind((self.host, self.port))        self.server_socket.listen(5)        self.running = True        print(f"Simple RPC服务器启动在 {self.host}:{self.port}")        try:            while self.running:                client_socket, address = self.server_socket.accept()                thread = threading.Thread(                    target=self.handle_connection,                    args=(client_socket, address)                )                thread.daemon = True                thread.start()        except KeyboardInterrupt:            print("\n服务器关闭中...")        finally:            self.stop()    def stop(self):        """停止服务器"""        self.running = False        if self.server_socket:            self.server_socket.close()# 使用示例if __name__ == '__main__':    # 创建服务器实例    server = SimpleRPCServer(port=9090)    # 注册方法    @server.register()    def add(a, b):        return a + b    @server.register("multiply")    def multiply_numbers(a, b):        return a * b    @server.register("greet")    def greet(name, greeting="Hello"):        return f"{greeting}{name}!"    class MathService:        @staticmethod        @server.register("square")        def square(x):            return x * x    # 启动服务器    server.start()

8. RPC实践

1. 版本管理

# 在接口定义中添加版本class CalculatorServiceV1:    def add(self, a, b):        return a + bclass CalculatorServiceV2:    def add(self, a, b, c=0):        return a + b + c

2. 超时与重试

import grpcfrom tenacity import retry, stop_after_attempt, wait_exponentialclassResilientRPCClient:    def __init__(self):        options = [            ('grpc.enable_retries', 1),            ('grpc.service_config',              '{"methodConfig": [{"name": [{"service""calculator.Calculator"}], '             '"retryPolicy": {"maxAttempts"5"initialBackoff""0.1s"'             '"maxBackoff""1s""backoffMultiplier"2'             '"retryableStatusCodes": ["UNAVAILABLE"]}}]}')        ]        self.channel = grpc.insecure_channel(            'localhost:50051',            options=options        )    @retry(        stop=stop_after_attempt(3),        wait=wait_exponential(multiplier=1, min=1, max=10)    )    def call_with_retry(self, method, *args):        return method(*args)

3. 认证与授权

# gRPC SSL/TLS加密import grpcfrom grpc import ssl_channel_credentialsdef create_secure_channel():    # 加载证书    with open('server.crt''rb'as f:        trusted_certs = f.read()    # 创建凭证    credentials = ssl_channel_credentials(        root_certificates=trusted_certs    )    # 创建安全通道    channel = grpc.secure_channel(        'localhost:50051',        credentials    )    return channel# Token认证class AuthInterceptor(grpc.ServerInterceptor):    def intercept_service(self, continuation, handler_call_details):        metadata = dict(handler_call_details.invocation_metadata)        # 检查Token        if 'authorization' not in metadata:            raise grpc.RpcError(grpc.StatusCode.UNAUTHENTICATED)        token = metadata['authorization']        if not validate_token(token):            raise grpc.RpcError(grpc.StatusCode.UNAUTHENTICATED)        return continuation(handler_call_details)

4. 监控与日志

import loggingimport timefrom functools import wrapsdef rpc_logger(func):    """RPC方法日志装饰器"""    @wraps(func)    def wrapper(*args, **kwargs):        start_time = time.time()        method_name = func.__name__        logging.info(f"RPC调用开始: {method_name}")        try:            result = func(*args, **kwargs)            duration = time.time() - start_time            logging.info(                f"RPC调用成功: {method_name}, "                f"耗时: {duration:.3f}秒"            )            return result        except Exception as e:            duration = time.time() - start_time            logging.error(                f"RPC调用失败: {method_name}, "                f"错误: {e}, 耗时: {duration:.3f}秒"            )            raise    return wrapper

9. 性能优化

1. 连接池

import grpcfrom concurrent.futures import ThreadPoolExecutorclass ConnectionPool:    def __init__(self, max_size=10):        self.pool = []        self.max_size = max_size        self.lock = threading.Lock()    def get_channel(self, address):        with self.lock:            # 查找空闲连接            for channel, in_use in self.pool:                if not in_use:                    self.pool.remove((channel, False))                    self.pool.append((channel, True))                    return channel            # 创建新连接            if len(self.pool) < self.max_size:                channel = grpc.insecure_channel(address)                self.pool.append((channel, True))                return channel            # 等待空闲连接            raise Exception("连接池已满")    def release_channel(self, channel):        with self.lock:            for i, (ch, in_use) in enumerate(self.pool):                if ch == channel:                    self.pool[i] = (channel, False)                    break

2. 批量调用

class BatchRPCClient:    def __init__(self):        self.batch_requests = []    def add_request(self, method, *args):        self.batch_requests.append((method, args))    def execute_batch(self):        """执行批量调用"""        results = []        for method, args in self.batch_requests:            try:                result = method(*args)                results.append(("success", result))            except Exception as e:                results.append(("error"str(e)))        self.batch_requests.clear()        return results

3. 压缩传输

# gRPC压缩import grpcimport zlib# 客户端压缩options = [    ('grpc.default_compression_algorithm', grpc.Compression.Gzip)]# 手动压缩def compress_data(data):    return zlib.compress(json.dumps(data).encode())def decompress_data(compressed_data):    return json.loads(zlib.decompress(compressed_data).decode())

10. 测试RPC服务

单元测试

import unittestfrom unittest.mock import Mock, patchimport grpcclass TestCalculatorService(unittest.TestCase):    def setUp(self):        self.service = CalculatorServicer()    def test_add(self):        # 创建模拟上下文        mock_context = Mock()        # 创建请求        request = calculator_pb2.AddRequest(a=10, b=20)        # 调用方法        response = self.service.Add(request, mock_context)        # 验证结果        self.assertEqual(response.result, 30)    @patch('your_module.some_dependency')    def test_with_mock(self, mock_dependency):        mock_dependency.return_value = 42        request = calculator_pb2.SomeRequest()        response = self.service.SomeMethod(request, Mock())        self.assertEqual(response.result, 42)class TestRPCClient(unittest.TestCase):    def test_client_retry(self):        # 测试重试逻辑        client = ResilientRPCClient()        # 模拟失败然后成功的调用        mock_stub = Mock()        mock_stub.some_method.side_effect = [            grpc.RpcError(grpc.StatusCode.UNAVAILABLE),            "success_result"        ]        result = client.call_with_retry(mock_stub.some_method, "arg")        self.assertEqual(result, "success_result")        self.assertEqual(mock_stub.some_method.call_count, 2)if __name__ == '__main__':    unittest.main()

集成测试

import subprocessimport timeimport requestsclass RPCIntegrationTest(unittest.TestCase):    @classmethod    def setUpClass(cls):        # 启动测试服务器        cls.server_process = subprocess.Popen(            ['python''test_server.py'],            stdout=subprocess.PIPE,            stderr=subprocess.PIPE        )        time.sleep(2)  # 等待服务器启动    @classmethod    def tearDownClass(cls):        # 关闭服务器        cls.server_process.terminate()        cls.server_process.wait()    def test_server_responsive(self):        # 测试服务器响应        response = requests.get('http://localhost:8000/health')        self.assertEqual(response.status_code, 200)    def test_rpc_methods(self):        # 测试RPC方法        client = xmlrpc.client.ServerProxy('http://localhost:8000')        result = client.add(53)        self.assertEqual(result, 8)

总结

RPC是分布式系统中服务通信的重要方式。通过学习今天的内容,你应该掌握:

  • 理解RPC核心概念:存根、编组、传输、网络透明性

  • 掌握常见RPC框架:XML-RPC、gRPC的安装和使用

  • 实现自定义RPC框架:理解RPC协议的基本实现

  • 掌握RPC最佳实践:版本管理、错误处理、安全认证、性能优化

关键要点

  • RPC使远程调用像本地调用一样简单

  • 选择合适的RPC框架取决于项目需求

  • gRPC适合高性能、跨语言场景

  • XML-RPC适合简单、快速开发的场景

  • 始终考虑安全性、可观察性和错误处理

实践任务

  • 使用gRPC实现一个简单的聊天服务

  • 为现有的XML-RPC服务添加认证机制

  • 实现一个支持批量调用的RPC客户端

  • 为RPC服务添加监控和日志记录

明天我们将进入Python网络编程的另一个重要主题:HTTP协议实现。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-08 19:33:01 HTTP/2.0 GET : https://f.mffb.com.cn/a/465211.html
  2. 运行时间 : 0.265670s [ 吞吐率:3.76req/s ] 内存消耗:4,850.34kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=ec862ecf6d4ef846a22df595b8699482
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001064s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001639s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.001595s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000793s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001608s ]
  6. SELECT * FROM `set` [ RunTime:0.001193s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001525s ]
  8. SELECT * FROM `article` WHERE `id` = 465211 LIMIT 1 [ RunTime:0.002160s ]
  9. UPDATE `article` SET `lasttime` = 1770550381 WHERE `id` = 465211 [ RunTime:0.041037s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.001255s ]
  11. SELECT * FROM `article` WHERE `id` < 465211 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.012046s ]
  12. SELECT * FROM `article` WHERE `id` > 465211 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.003392s ]
  13. SELECT * FROM `article` WHERE `id` < 465211 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.033088s ]
  14. SELECT * FROM `article` WHERE `id` < 465211 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.036720s ]
  15. SELECT * FROM `article` WHERE `id` < 465211 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.037610s ]
0.267318s