06-微服务架构

34 阅读5分钟

微服务架构

1. 微服务概述

1.1 什么是微服务?

定义:将一个大型应用拆分成多个小型、独立的服务,每个服务负责单一的业务功能。

单体架构 vs 微服务架构

单体架构:
┌─────────────────────────┐
│   一个大应用             │
│  ┌──────────────────┐   │
│  │ 用户模块         │   │
│  │ 订单模块         │   │
│  │ 商品模块         │   │
│  │ 支付模块         │   │
│  └──────────────────┘   │
│   一个数据库             │
└─────────────────────────┘

微服务架构:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户服务 │ │ 订单服务 │ │ 商品服务 │ │ 支付服务 │
│   DB1    │ │   DB2    │ │   DB3    │ │   DB4    │
└──────────┘ └──────────┘ └──────────┘ └──────────┘

1.2 微服务的优势

  • 独立部署:每个服务可以独立部署和扩展
  • 技术多样性:不同服务可以用不同技术栈
  • 团队自治:每个团队负责一个服务
  • 故障隔离:一个服务出问题不影响其他服务

1.3 微服务的挑战

  • 复杂性增加:需要服务注册、发现、治理
  • 数据一致性:分布式事务难以处理
  • 网络延迟:服务间调用增加网络开销
  • 运维成本:需要更多的监控和部署工具

2. 服务注册与发现

2.1 为什么需要服务注册与发现?

在微服务架构中,服务的 IP 和端口是动态的(容器化部署、自动扩缩容),需要一个中心化的注册中心。

没有注册中心:
订单服务  如何找到用户服务的地址?需要硬编码 IP

有注册中心:
用户服务启动  注册到 Consul(IP: 192.168.1.100, Port: 9501
订单服务   Consul 查询用户服务的地址  调用

2.2 Consul 服务注册

安装 Consul
# Docker 方式
docker run -d --name=consul -p 8500:8500 consul

# 访问 Web UI
http://localhost:8500
配置 Hyperf

config/autoload/consul.php

<?php
return [
    'uri' => 'http://127.0.0.1:8500',
    'token' => '',
    'check' => [
        'deregister_critical_service_after' => '90m',
        'interval' => '1s',
    ],
];
注册服务

config/autoload/services.php

<?php
return [
    'enable' => [
        'discovery' => true,
        'register' => true,
    ],
    'consumers' => [],
    'providers' => [],
    'drivers' => [
        'consul' => [
            'uri' => 'http://127.0.0.1:8500',
            'token' => '',
        ],
    ],
];

启动服务后,会自动注册到 Consul。

2.3 服务发现

<?php
use Hyperf\Di\Annotation\Inject;
use Hyperf\LoadBalancer\LoadBalancerInterface;
use Hyperf\ServiceGovernance\DriverManager;

class OrderService
{
    #[Inject]
    private DriverManager $driverManager;
    
    public function callUserService()
    {
        // 从注册中心获取用户服务的节点
        $nodes = $this->driverManager->getNodes('user-service');
        
        // 负载均衡选择一个节点
        $node = $nodes[array_rand($nodes)];
        
        // 调用服务
        $url = "http://{$node['host']}:{$node['port']}/api/user/1";
        $response = file_get_contents($url);
        
        return json_decode($response, true);
    }
}

3. RPC 服务调用

3.1 什么是 RPC?

RPC(Remote Procedure Call):远程过程调用,像调用本地方法一样调用远程服务。

// 传统 HTTP 调用
$url = 'http://user-service/api/user/1';
$response = file_get_contents($url);
$user = json_decode($response, true);

// RPC 调用(像本地方法一样)
$user = $userService->getUserById(1);

3.2 JSON-RPC 实现

服务端

定义服务接口

<?php
namespace App\JsonRpc;

interface UserServiceInterface
{
    public function getUserById(int $id): array;
    
    public function createUser(array $data): array;
}

实现服务

<?php
namespace App\JsonRpc;

use Hyperf\RpcServer\Annotation\RpcService;

#[RpcService(
    name: 'UserService',
    protocol: 'jsonrpc-http',
    server: 'jsonrpc-http',
    publishTo: 'consul'
)]
class UserService implements UserServiceInterface
{
    public function getUserById(int $id): array
    {
        return [
            'id' => $id,
            'name' => 'John Doe',
            'email' => 'john@example.com',
        ];
    }
    
    public function createUser(array $data): array
    {
        // 创建用户逻辑
        return ['id' => 1, ...$data];
    }
}

配置服务端口

config/autoload/server.php

<?php
return [
    'servers' => [
        [
            'name' => 'jsonrpc-http',
            'type' => Server::SERVER_HTTP,
            'host' => '0.0.0.0',
            'port' => 9504,
        ],
    ],
];
客户端

配置服务消费者

config/autoload/services.php

<?php
return [
    'consumers' => [
        [
            'name' => 'UserService',
            'service' => App\JsonRpc\UserServiceInterface::class,
            'protocol' => 'jsonrpc-http',
            'load_balancer' => 'random',
            'nodes' => [
                ['host' => '127.0.0.1', 'port' => 9504],
            ],
        ],
    ],
];

调用服务

<?php
namespace App\Controller;

use App\JsonRpc\UserServiceInterface;
use Hyperf\Di\Annotation\Inject;

class OrderController
{
    #[Inject]
    private UserServiceInterface $userService;
    
    public function createOrder()
    {
        // 像调用本地方法一样调用远程服务
        $user = $this->userService->getUserById(1);
        
        // 创建订单
        $order = Order::create([
            'user_id' => $user['id'],
            'total_amount' => 100,
        ]);
        
        return $order;
    }
}

3.3 gRPC 实现

gRPC 是 Google 开发的高性能 RPC 框架,基于 HTTP/2 和 Protocol Buffers。

定义 .proto 文件

grpc/user.proto

syntax = "proto3";

package user;

service UserService {
    rpc GetUser (GetUserRequest) returns (UserReply) {}
}

message GetUserRequest {
    int32 id = 1;
}

message UserReply {
    int32 id = 1;
    string name = 2;
    string email = 3;
}
生成 PHP 代码
php bin/hyperf.php gen:grpc-code
实现服务
<?php
namespace App\Grpc;

use Hyperf\GrpcServer\Annotation\GrpcService;
use User\UserServiceInterface;
use User\GetUserRequest;
use User\UserReply;

#[GrpcService(name: 'user.UserService', server: 'grpc')]
class UserService implements UserServiceInterface
{
    public function GetUser(GetUserRequest $request): UserReply
    {
        $user = User::find($request->getId());
        
        $reply = new UserReply();
        $reply->setId($user->id);
        $reply->setName($user->name);
        $reply->setEmail($user->email);
        
        return $reply;
    }
}

4. 配置中心

4.1 为什么需要配置中心?

在微服务架构中,配置分散在多个服务中,修改配置需要重新部署。配置中心可以:

  • 集中管理配置
  • 动态更新配置(无需重启)
  • 配置版本管理

4.2 Apollo 配置中心

配置

config/autoload/apollo.php

<?php
return [
    'enable' => true,
    'server' => 'http://127.0.0.1:8080',
    'appid' => 'my-app',
    'cluster' => 'default',
    'namespaces' => ['application'],
    'interval' => 5,  // 轮询间隔(秒)
];
读取配置
<?php
use Hyperf\Config\Annotation\Value;

class UserService
{
    #[Value('user.max_age')]
    private int $maxAge;
    
    public function checkAge(int $age)
    {
        if ($age > $this->maxAge) {
            throw new \Exception('年龄超过限制');
        }
    }
}
监听配置变更
<?php
namespace App\Listener;

use Hyperf\Apollo\ConfigProvider;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Psr\Container\ContainerInterface;

#[Listener]
class ConfigChangedListener implements ListenerInterface
{
    public function __construct(private ContainerInterface $container)
    {
    }
    
    public function listen(): array
    {
        return [
            ConfigProvider::class,
        ];
    }
    
    public function process(object $event): void
    {
        echo "配置已更新\n";
        
        // 重新加载配置
        $this->container->get(ConfigInterface::class)->reload();
    }
}

5. 服务治理

5.1 负载均衡

Hyperf 支持多种负载均衡算法:

<?php
// config/autoload/services.php
return [
    'consumers' => [
        [
            'name' => 'UserService',
            'load_balancer' => 'random',  // 随机
            // 或者:
            // 'load_balancer' => 'round-robin',  // 轮询
            // 'load_balancer' => 'weighted-random',  // 加权随机
        ],
    ],
];

5.2 熔断器

当服务频繁出错时,自动熔断,避免雪崩。

<?php
namespace App\Service;

use Hyperf\CircuitBreaker\Annotation\CircuitBreaker;

class OrderService
{
    #[CircuitBreaker(
        timeout: 3.0,          // 超时时间
        failCounter: 10,       // 失败次数阈值
        successCounter: 5,     // 成功次数阈值(半开状态)
        fallback: 'fallbackMethod'  // 降级方法
    )]
    public function callUserService()
    {
        // 调用用户服务
        return $this->userService->getUserById(1);
    }
    
    public function fallbackMethod()
    {
        // 降级逻辑:返回默认数据
        return ['id' => 0, 'name' => '游客'];
    }
}

熔断器状态

正常状态 → 失败次数达到阈值 → 熔断状态(直接返回降级数据)
                                    ↓
                            等待一段时间后进入半开状态
                                    ↓
                            尝试调用,成功次数达到阈值 → 恢复正常

5.3 限流

防止服务被过多请求压垮。

<?php
namespace App\Middleware;

use Hyperf\Di\Annotation\Inject;
use Hyperf\RateLimit\Annotation\RateLimit;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

#[RateLimit(
    create: 10,        // 每秒创建 10 个令牌
    capacity: 100,     // 令牌桶容量
    consume: 1         // 每次请求消耗 1 个令牌
)]
class RateLimitMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 如果令牌不足,会抛出异常
        return $handler->handle($request);
    }
}

5.4 链路追踪

在微服务架构中,一个请求可能经过多个服务,链路追踪可以追踪请求的完整路径。

集成 Zipkin
composer require hyperf/tracer

config/autoload/opentracing.php

<?php
return [
    'default' => 'zipkin',
    'enable' => [
        'guzzle' => true,
        'redis' => true,
        'db' => true,
    ],
    'tracer' => [
        'zipkin' => [
            'driver' => Hyperf\Tracer\Adapter\ZipkinTracerFactory::class,
            'app' => [
                'name' => 'order-service',
            ],
            'options' => [
                'endpoint_url' => 'http://localhost:9411/api/v2/spans',
            ],
        ],
    ],
];

启动后,所有请求都会自动追踪到 Zipkin。

6. 分布式事务

6.1 问题

在微服务架构中,一个业务可能涉及多个服务,如何保证数据一致性?

创建订单:
1. 订单服务:创建订单
2. 库存服务:扣减库存
3. 支付服务:扣款

如果支付失败,如何回滚订单和库存?

6.2 解决方案

方案一:Saga 模式

每个服务定义正向操作和补偿操作(回滚)。

<?php
namespace App\Saga;

class OrderSaga
{
    public function execute()
    {
        try {
            // 1. 创建订单
            $orderId = $this->orderService->create();
            
            // 2. 扣减库存
            $this->inventoryService->deduct($productId, $quantity);
            
            // 3. 扣款
            $this->paymentService->pay($amount);
            
            return $orderId;
        } catch (\Exception $e) {
            // 补偿操作(回滚)
            $this->paymentService->refund($amount);
            $this->inventoryService->restore($productId, $quantity);
            $this->orderService->cancel($orderId);
            
            throw $e;
        }
    }
}
方案二:消息队列(最终一致性)

通过消息队列实现异步补偿。

<?php
// 订单服务
public function createOrder()
{
    // 1. 创建订单
    $order = Order::create([...]);
    
    // 2. 发送消息到队列
    $this->producer->produce(new OrderCreatedMessage([
        'order_id' => $order->id,
        'product_id' => $productId,
        'quantity' => $quantity,
    ]));
    
    return $order;
}

// 库存服务(消费者)
public function handleOrderCreated(OrderCreatedMessage $message)
{
    try {
        // 扣减库存
        $this->deduct($message->product_id, $message->quantity);
        
        // 发送成功消息
        $this->producer->produce(new InventoryDeductedMessage([...]));
    } catch (\Exception $e) {
        // 发送失败消息
        $this->producer->produce(new InventoryDeductFailedMessage([...]));
    }
}

7. 要点

必须掌握

  • 微服务的概念和优势
  • 服务注册与发现的原理
  • RPC 的概念和使用
  • 负载均衡的几种算法
  • 熔断器的作用

加分项

  • 分布式事务的解决方案
  • 配置中心的使用
  • 链路追踪的原理
  • 服务网格(Service Mesh)

高频题

1. 什么是微服务?有什么优势?

答:微服务是将大型应用拆分成多个小型、独立的服务。

优势:

  • 独立部署和扩展
  • 技术多样性
  • 团队自治
  • 故障隔离

2. 服务注册与发现是什么?

答:服务启动时将自己的地址注册到注册中心(如 Consul),其他服务从注册中心查询服务地址并调用。

3. 如何解决分布式事务问题?

答:常见方案:

  • Saga 模式:定义补偿操作
  • 消息队列:最终一致性
  • TCC(Try-Confirm-Cancel)

下一步:阅读 07-性能优化.md 学习性能优化