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

基于之前的架构设计,进一步提供完整的可落地方案:

一、服务注册中心增强实现(Consul + PHP)

  1. 健康检查与自动注销
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 服务注册时增加健康检查端点
$consul->get(AgentInterface::class)->registerService([
'ID' => 'payment-service-'.uniqid(),
'Name' => 'payment-service',
'Tags' => ['primary', 'v2'],
'Address' => get_current_ip(),
'Port' => 8000,
'Check' => [
'HTTP' => 'http://'.get_current_ip().':8000/health',
'Interval' => '5s',
'Timeout' => '2s',
'DeregisterCriticalServiceAfter' => '30s'
]
]);

// PHP健康检查端点实现
$app->get('/health', function() {
return json_encode([
'status' => 'UP',
'db_connected' => DB::connection()->getPdo() ? true : false,
'redis_connected' => Redis::ping() ? true : false,
'load' => sys_getloadavg()[0]
]);
});
  1. 服务发现负载均衡
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
class ServiceLocator {
private static $services = [];

public static function getInstance($serviceName) {
if (!isset(self::$services[$serviceName]) || count(self::$services[$serviceName]) == 0) {
$instances = $consul->getCatalog()->service($serviceName)->json();
self::$services[$serviceName] = array_map(
fn($s) => new Instance($s['ServiceAddress'], $s['ServicePort']),
$instances
);
}

// 使用加权随机算法选择实例
$totalWeight = array_sum(array_map(fn($i) => $i->weight, self::$services[$serviceName]));
$rand = mt_rand(1, $totalWeight);

foreach (self::$services[$serviceName] as $instance) {
$rand -= $instance->weight;
if ($rand <= 0) {
return $instance;
}
}
}

// 定时刷新服务列表
public static function refresh() {
swoole_timer_tick(30000, function() {
foreach (array_keys(self::$services) as $name) {
$instances = $consul->getCatalog()->service($name)->json();
self::$services[$name] = $instances;
}
});
}
}

二、API 网关深度集成(Kong + OpenResty)

  1. 动态路由配置
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
#lua
-- kong/plugins/dynamic-router/handler.lua
local DynamicRouterHandler = {
PRIORITY = 1000,
VERSION = "1.0"
}

function DynamicRouterHandler:access(conf)
-- 从Consul获取最新服务实例
local res, err = kong.request.make({
method = "GET",
url = "http://consul:8500/v1/catalog/service/" .. kong.request.get_header("X-Service-Name")
})

if not res then
kong.log.err("Consul request failed: ", err)
return kong.response.exit(503)
end

local instances = cjson.decode(res.body)
if #instances == 0 then
return kong.response.exit(404, { message = "Service unavailable" })
end

-- 更新上游目标
local ok, err = kong.admin_api.post("/upstreams/service-"..conf.service_name.."/targets", {
target = instances[1].ServiceAddress..":"..instances[1].ServicePort,
weight = 100
})
end

return DynamicRouterHandler
  1. PHP 插件开发示例(JWT 验证)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// kong/plugins/jwt-validator/validate.php
<?php

$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (!preg_match('/Bearer\s+(.*)$/i', $token, $matches)) {
header('HTTP/1.1 401 Unauthorized');
exit;
}

$jwt = $matches[1];
$userInfo = AuthService::validateJWT($jwt);

if (!$userInfo) {
header('HTTP/1.1 403 Forbidden');
exit;
}

// 将用户信息传递给上游服务
header('X-User-Id: ' . $userInfo['id']);
header('X-User-Roles: ' . implode(',', $userInfo['roles']));

三、配置中心完整方案(Apollo + PHP)

  1. 配置监听长轮询
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 ApolloWatcher {
private $client;
private $callbacks = [];

public function __construct() {
$this->client = new \ApolloClient\Client([...]);
$this->startWatch();
}

public function watch($namespace, callable $callback) {
$this->callbacks[$namespace][] = $callback;
}

private function startWatch() {
swoole_timer_tick(1000, function() {
foreach ($this->callbacks as $namespace => $cbs) {
$notifications = $this->client->getNotifications([$namespace]);

if ($notifications[$namespace] > $this->versions[$namespace] ?? 0) {
$config = $this->client->getConfig($namespace);

foreach ($cbs as $cb) {
$cb($config);
}

$this->versions[$namespace] = $notifications[$namespace];
}
}
});
}
}

// 使用示例
$watcher = new ApolloWatcher();
$watcher->watch('application', function($config) {
Redis::set('app_config', json_encode($config));
});
  1. 配置加密存储
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SecureConfig {
private $key;

public function __construct() {
$this->key = file_get_contents('/etc/apollo/key.pem');
}

public function get($key) {
$encrypted = Apollo::get($key);
return openssl_decrypt(
base64_decode($encrypted),
'aes-256-cbc',
$this->key,
0,
substr($this->key, 0, 16)
);
}
}

四、服务总线可靠消息方案(RabbitMQ)

  1. 消息事务与确认
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
// 生产者端
$channel->tx_select();
try {
$msg = new AMQPMessage($data, [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
'message_id' => uniqid()
]);

$channel->basic_publish($msg, 'orders');
DB::table('outbox')->insert(['message_id' => $msg->get('message_id')]);
$channel->tx_commit();
} catch (Exception $e) {
$channel->tx_rollback();
Metrics::counter('publish_failed')->inc();
}

// 消费者端
$channel->basic_consume('orders', '', false, false, false, false,
function($msg) {
try {
processOrder($msg->body);
$msg->ack();

// 幂等处理
if (!DB::table('processed_messages')->where('msg_id', $msg->get('message_id'))->exists()) {
DB::table('processed_messages')->insert([
'msg_id' => $msg->get('message_id'),
'processed_at' => now()
]);
}
} catch (Exception $e) {
$msg->nack(true); // 重新入队
}
}
);
  1. 死信队列配置
1
2
3
4
5
6
7
8
9
10
11
// 声明死信交换器
$channel->exchange_declare('dlx', 'direct', false, true, false);
$channel->queue_declare('dlq', false, true, false, false);
$channel->queue_bind('dlq', 'dlx', 'dead');

// 主队列配置
$channel->queue_declare('orders', false, true, false, false, false, [
'x-dead-letter-exchange' => 'dlx',
'x-dead-letter-routing-key' => 'dead',
'x-message-ttl' => 600000 // 10分钟后进入DLQ
]);

五、服务降级完整策略

  1. 多级降级方案

    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
    class OrderService {
    const DEGRADE_LEVELS = [
    'full' => 0, // 正常服务
    'readonly' => 1, // 只读模式
    'cache' => 2, // 返回缓存数据
    'static' => 3 // 返回静态页面
    ];

    public function getOrder($id) {
    $level = CircuitBreaker::getStatus('order-service');

    switch ($level) {
    case self::DEGRADE_LEVELS['full']:
    return DB::table('orders')->find($id);

    case self::DEGRADE_LEVELS['readonly']:
    if (request()->isMethod('GET')) {
    return DB::table('orders')->find($id);
    }
    throw new DegradeException('只读模式');

    case self::DEGRADE_LEVELS['cache']:
    return Cache::remember("order:$id", 60, function() use ($id) {
    return DB::table('orders')->find($id) ?? [];
    });

    case self::DEGRADE_LEVELS['static']:
    return [
    'id' => $id,
    'status' => 'degraded',
    'message' => '系统繁忙,请稍后查看订单状态'
    ];
    }
    }
    }
  2. 自动降级规则配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # degradation-rules.yaml
    services:
    order-service:
    failure_threshold: 50% # 失败率阈值
    slow_call_threshold: 2000ms # 慢调用阈值
    minimum_calls: 20 # 最小调用量
    sliding_window: 60s # 统计窗口
    wait_duration: 300s # 半开状态等待时间

    fallbacks:
    - condition: "error instanceof TimeoutException"
    action: "return cachedData()"
    - condition: "error instanceof DBException"
    action: "switchToReadonlyMode()"

六、部署架构优化方案

  1. Kubernetes 部署模板

    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
    45
    46
    47
    # deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: order-service
    spec:
    replicas: 3
    strategy:
    rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0
    template:
    spec:
    containers:
    - name: php
    image: my-registry/order-service:v1.2
    ports:
    - containerPort: 8000
    livenessProbe:
    httpGet:
    path: /health
    port: 8000
    initialDelaySeconds: 30
    periodSeconds: 10
    readinessProbe:
    httpGet:
    path: /ready
    port: 8000
    initialDelaySeconds: 5
    periodSeconds: 5
    envFrom:
    - configMapRef:
    name: apollo-config
    ---
    # service.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: order-service
    annotations:
    consul.hashicorp.com/service-name: order-service
    spec:
    ports:
    - port: 8000
    targetPort: 8000
    selector:
    app: order-service
  2. 监控告警配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# prometheus-rules.yml
groups:
- name: php-services
rules:
- alert: HighErrorRate
expr: sum(rate(http_requests_total{status=~"5.."}[1m])) by (service) / sum(rate(http_requests_total[1m])) by (service) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate on {{ $labels.service }}"
description: "Error rate is {{ $value }}"

- alert: ServiceDown
expr: up{job="php-services"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} is down"

七、开发环境工具链

  1. 本地调试方案:
1
2
3
4
5
6
7
8
9
# 使用docker-compose启动依赖服务
docker-compose -f dev-env.yml up consul rabbitmq apollo

# 使用Xdebug远程调试
PHP_IDE_CONFIG="serverName=my-service" php -dxdebug.mode=debug \
-dxdebug.client_host=host.docker.internal \
-dxdebug.client_port=9003 \
-dxdebug.start_with_request=yes \
artisan serve
  1. API 文档生成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @OA\Post(
* path="/orders",
* tags={"Orders"},
* @OA\RequestBody(
* @OA\JsonContent(ref="#/components/schemas/OrderRequest")
* ),
* @OA\Response(
* response=201,
* description="Order created",
* @OA\JsonContent(ref="#/components/schemas/Order")
* ),
* @OA\Response(
* response=503,
* description="Service degraded",
* @OA\JsonContent(ref="#/components/schemas/Error")
* )
* )
*/
public function createOrder(Request $request) {
// ...
}
  1. 性能测试工具:
1
2
3
4
5
6
# 使用wrk进行压力测试
wrk -t4 -c100 -d60s --latency http://localhost:8000/api/orders

# 生成火焰图
php -dxdebug.profiler_enable=1 -dxdebug.profiler_output_dir=/tmp \
-dxdebug.profiler_output_name=cachegrind.out.%p artisan serve

关键优化点总结

  1. 服务发现:采用 Consul 集群+健康检查+客户端缓存,实现秒级服务状态更新
  • 消息可靠:通过事务消息+死信队列+幂等处理保障消息不丢失

  • 配置管理:Apollo 长轮询+加密存储+版本回溯实现安全配置中心

  • 降级策略:多级降级(全功能 → 只读 → 缓存 → 静态)保障核心链路

  • 可观测性:Prometheus 指标+Jaeger 追踪+ELK 日志三位一体监控