php架构方面的设计与思考(三)

PHP微服务架构终极整合方案:

  graph TD
      A[客户端] --> B{Kong网关}
      B -->|路由| C[用户服务]
      B -->|路由| D[订单服务]
      B -->|路由| E[支付服务]

      C -->|注册| F[Consul集群]
      D -->|注册| F
      E -->|注册| F

      C -->|配置| G[Apollo]
      D -->|配置| G
      E -->|配置| G

      D -->|消息| H[RabbitMQ集群]
      H --> E
      H --> I[通知服务]

      F --> J[Prometheus]
      G --> J
      H --> J
      J --> K[Grafana]

      L[Jaeger] --> M[服务追踪]
  1. 基础设施快速部署
    Docker Compose 全栈配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    version: '3.8'

    services:
    # 注册中心
    consul-server:
    image: consul:1.15
    ports: ["8500:8500"]
    command: "agent -server -bootstrap-expect=1 -ui -client 0.0.0.0"

    # API网关
    kong:
    image: kong:3.4
    depends_on: [consul-server]
    environment:
    KONG_DATABASE: "off"
    KONG_DECLARATIVE_CONFIG: "/etc/kong/kong.yml"
    KONG_PROXY_ACCESS_LOG: "/dev/stdout"
    KONG_ADMIN_ACCESS_LOG: "/dev/stdout"
    KONG_PROXY_ERROR_LOG: "/dev/stderr"
    ports: ["8000:8000", "8001:8001"]
    volumes:
    - ./kong:/etc/kong

    # 配置中心
    apollo:
    image: apolloconfig/apollo-portal:2.1.0
    ports: ["8070:8070"]
    environment:
    SPRING_DATASOURCE_URL: "jdbc:mysql://apollo-db:3306/ApolloPortalDB"

    # 消息队列
    rabbitmq:
    image: rabbitmq:3.11-management
    ports: ["5672:5672", "15672:15672"]
    environment:
    RABBITMQ_DEFAULT_USER: "admin"
    RABBITMQ_DEFAULT_PASS: "secret"

    # 监控系统
    prometheus:
    image: prom/prometheus:v2.47
    ports: ["9090:9090"]
    volumes:
    - ./prometheus.yml:/etc/prometheus/prometheus.yml
  2. 核心服务PHP实现包

    composer.json 关键依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "require": {
    "php": ">=8.2",
    "ext-swoole": "*",
    "ext-redis": "*",
    "ext-amqp": "*",

    "sensiolabs/consul-php-sdk": "^2.0",
    "guzzlehttp/guzzle": "^7.8",
    "php-amqplib/php-amqplib": "^3.2",
    "resilience-php/resilience-php": "^1.3",
    "apolloconfig/apollo-client": "^2.0"
    }
    }
  3. 服务注册与发现完整实现

    服务注册(bootstrap.php)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    use SensioLabs\Consul\ServiceFactory;

    $consul = new ServiceFactory(['base_uri' => 'http://consul-server:8500']);

    // 自动获取本机IP
    $ip = trim(shell_exec("hostname -i"));

    // 注册服务
    $consul->get(AgentInterface::class)->registerService([
    'ID' => 'order-service-'.gethostname(),
    'Name' => 'order-service',
    'Address' => $ip,
    'Port' => 8000,
    'Check' => [
    'HTTP' => "http://{$ip}:8000/health",
    'Interval' => '5s',
    'Timeout' => '2s',
    'DeregisterCriticalServiceAfter' => '30s'
    ],
    'Tags' => ['v2', 'primary']
    ]);

    // 健康检查端点
    $app->get('/health', function() {
    check_database();
    check_redis();
    return json_response(['status' => 'UP']);
    });

    服务发现与负载均衡

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    class ServiceDiscovery {
    private static $cache = [];
    private static $ttl = 5; // 缓存5秒

    public static function getInstance(string $service): string {
    $now = time();

    // 缓存有效期内直接返回
    if (isset(self::$cache[$service]) &&
    $now - self::$cache[$service]['timestamp'] < self::$ttl) {
    return self::selectInstance(self::$cache[$service]['instances']);
    }

    // 从Consul获取新实例
    $instances = $consul->getCatalog()->service($service)->json();
    self::$cache[$service] = [
    'instances' => $instances,
    'timestamp' => $now
    ];

    return self::selectInstance($instances);
    }

    private static function selectInstance(array $instances): string {
    // 加权随机算法
    $total = array_sum(array_column($instances, 'Weight'));
    $rand = mt_rand(1, $total);
    $current = 0;

    foreach ($instances as $instance) {
    $current += $instance['Weight'];
    if ($rand <= $current) {
    return "http://{$instance['ServiceAddress']}:{$instance['ServicePort']}";
    }
    }
    }
    }
  4. 统一配置中心接入

    Apollo配置监听

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    $apollo = new \ApolloClient\Client([
    'config_server' => 'http://apollo:8070',
    'app_id' => 'order-service',
    'cluster' => 'default'
    ]);

    // 初始化配置
    $mysqlConfig = $apollo->get('mysql');
    DB::connect($mysqlConfig);

    // 动态监听
    $apollo->listen(['mysql', 'redis'], function($namespace, $config) {
    switch ($namespace) {
    case 'mysql':
    DB::reconnect($config);
    break;
    case 'redis':
    Redis::setConfig($config);
    break;
    }

    Logger::info("Config updated: $namespace");
    });
  5. 服务通信完整方案

    同步调用(HTTP)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class HttpServiceClient {
    use CircuitBreaker;

    public function call(string $service, string $endpoint, array $data) {
    return $this->protect(function() use ($service, $endpoint, $data) {
    $baseUrl = ServiceDiscovery::getInstance($service);
    $client = new GuzzleHttp\Client([
    'base_uri' => $baseUrl,
    'timeout' => 2.0
    ]);

    return $client->post($endpoint, [
    'json' => $data,
    'headers' => [
    'X-Trace-Id' => Trace::getId()
    ]
    ]);
    }, function() { // 降级处理
    return ['status' => 'degraded'];
    });
    }
    }

    异步通信(RabbitMQ)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    class EventPublisher {
    private $channel;

    public function __construct() {
    $conn = new AMQPStreamConnection('rabbitmq', 5672, 'admin', 'secret');
    $this->channel = $conn->channel();

    // 声明死信交换器
    $this->channel->exchange_declare('dlx', 'direct', false, true);
    $this->channel->queue_declare('dlq', false, true);
    $this->channel->queue_bind('dlq', 'dlx');
    }

    public function publish(string $event, array $data) {
    $this->channel->tx_select();

    try {
    $message = new AMQPMessage(json_encode($data), [
    'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
    'message_id' => Uuid::uuid4(),
    'timestamp' => time()
    ]);

    $this->channel->basic_publish($message, 'events', $event);
    DB::table('outbox')->insert(['message_id' => $message->get('message_id')]);
    $this->channel->tx_commit();
    } catch (Exception $e) {
    $this->channel->tx_rollback();
    throw $e;
    }
    }
    }
  6. 服务降级熔断策略

    多级降级配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    class DegradeManager {
    private static $levels = [
    'order-service' => [
    'full' => ['threshold' => 0.95, 'fallback' => 'cache'],
    'cache' => ['threshold' => 0.8, 'fallback' => 'readonly'],
    'readonly' => ['threshold' => 0.5, 'fallback' => 'static']
    ]
    ];

    public static function handle(string $service, callable $func) {
    $status = self::getServiceStatus($service);

    try {
    switch ($status) {
    case 'full':
    return $func();
    case 'cache':
    return Cache::remember("fallback:$service", 60, $func);
    case 'readonly':
    if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    return $func();
    }
    throw new DegradeException('只读模式');
    case 'static':
    return ['status' => 'degraded'];
    }
    } catch (Exception $e) {
    self::recordFailure($service);
    return self::handle($service, $func); // 自动降级
    }
    }

    private static function getServiceStatus(string $service): string {
    $failureRate = Prometheus::getFailureRate($service);

    foreach (self::$levels[$service] as $level => $config) {
    if ($failureRate <= $config['threshold']) {
    return $level;
    }
    }

    return 'static';
    }
    }
  7. 监控与告警配置

    Prometheus指标收集

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    class Metrics {
    private static $counter;

    public static function init() {
    $registry = new CollectorRegistry(new InMemory());

    self::$counter = $registry->registerCounter(
    'php',
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
    );

    // 暴露指标端点
    $app->get('/metrics', function() use ($registry) {
    $renderer = new RenderTextFormat();
    return $renderer->render($registry->getMetricFamilySamples());
    });
    }

    public static function countRequest($method, $path, $status) {
    self::$counter->inc([
    $method,
    preg_replace('/\d+/', '{id}', $path),
    $status
    ]);
    }
    }

    // 在中间件中调用
    $app->addMiddleware(function($req, $handler) {
    $start = microtime(true);
    $response = $handler->handle($req);
    $duration = microtime(true) - $start;

    Metrics::countRequest(
    $req->getMethod(),
    $req->getUri()->getPath(),
    $response->getStatusCode()
    );

    return $response;
    });

  8. 生产环境部署建议

  1. 服务注册中心:

    • 部署3节点Consul集群

    • 启用ACL和TLS加密

    • 设置自动备份策略

  • API网关:

    • Kong集群 + Nginx负载均衡

    • 启用JWT插件和速率限制

    • 配置WAF规则防止攻击

  • 配置中心:

    • Apollo多环境隔离(DEV/TEST/PROD)

    • 敏感配置加密存储

    • 设置配置变更审批流程

  • 消息队列:

    • RabbitMQ镜像队列

    • 设置合理的TTL和死信策略

    • 监控队列积压情况

  • 监控系统:

    • Prometheus联邦集群

    • Grafana统一看板

    • 关键指标告警(P99延迟>500ms,错误率>1%)

完整架构调用流程示例
  1. 客户端请求 → Kong网关(认证+限流)
  • 网关查询Consul获取订单服务实例

  • 订单服务处理时:

    • 从Apollo获取当前配置

    • 通过HTTP调用支付服务(带熔断)

    • 发送消息到RabbitMQ

  • 支付服务消费消息后:

    • 更新数据库

    • 记录Prometheus指标

    • 推送结果到通知服务

  • 全链路追踪数据上报Jaeger