大型PHP网站架构演进历程与优化经验:从单机到微服务的实战之路
作为一名在PHP领域深耕多年的开发者,我有幸参与并主导了多个大型网站的架构演进。今天想和大家分享这段从单机部署到微服务架构的完整历程,其中包含了我们在各个阶段遇到的坑和优化经验。希望这些实战经验能为正在面临类似挑战的团队提供参考。
第一阶段:单机架构的起步与优化
记得项目初期,我们采用的是最传统的LAMP架构。所有组件都部署在一台服务器上,这种架构简单直接,但也很快暴露了性能瓶颈。
当并发用户超过500时,网站响应时间开始明显变慢。我们首先从代码层面进行了优化:
// 优化前:每次请求都创建数据库连接
function getUserInfo($userId) {
$conn = new mysqli("localhost", "user", "password", "database");
// ... 查询逻辑
}
// 优化后:使用连接池
class DBConnectionPool {
private static $instance;
private $connections = [];
public static function getInstance() {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection() {
// 连接池实现逻辑
}
}
除了代码优化,我们还引入了OPcache来提升PHP执行效率:
# 安装和配置OPcache
sudo apt-get install php-opcache
echo "opcache.enable=1" >> /etc/php/7.4/fpm/php.ini
echo "opcache.memory_consumption=128" >> /etc/php/7.4/fpm/php.ini
这些优化让我们的单机架构支撑到了日均5万PV,但随着业务增长,单机架构很快达到了天花板。
第二阶段:读写分离与负载均衡
当用户量突破10万时,数据库成为了新的瓶颈。我们决定实施读写分离,这是架构演进的关键一步。
首先配置MySQL主从复制:
# 在主库配置
server-id=1
log-bin=mysql-bin
binlog-format=row
# 在从库配置
server-id=2
relay-log=mysql-relay-bin
read-only=1
然后在代码层面实现读写分离:
class DatabaseRouter {
private $writeConn;
private $readConns = [];
public function getConnection($isWrite = false) {
if ($isWrite) {
return $this->writeConn;
} else {
// 随机选择读库,实现负载均衡
return $this->readConns[array_rand($this->readConns)];
}
}
}
同时,我们引入了Nginx作为负载均衡器:
upstream backend {
server 192.168.1.10:9000 weight=3;
server 192.168.1.11:9000 weight=2;
server 192.168.1.12:9000 weight=2;
}
server {
location / {
proxy_pass http://backend;
}
}
这个阶段我们踩过一个坑:由于主从同步延迟,导致刚写入的数据立即查询时出现不一致。解决方案是在需要强一致性的场景下,强制走主库查询。
第三阶段:引入缓存与队列
当用户量达到百万级别时,即使做了读写分离,数据库压力仍然很大。我们引入了Redis缓存和消息队列。
缓存策略的设计很关键:
class CacheManager {
const CACHE_TTL = 3600; // 1小时
public function getUserCache($userId) {
$cacheKey = "user_info_{$userId}";
$userInfo = $this->redis->get($cacheKey);
if (!$userInfo) {
// 缓存穿透保护:使用布隆过滤器或缓存空值
$userInfo = $this->getUserFromDB($userId);
if ($userInfo) {
$this->redis->setex($cacheKey, self::CACHE_TTL, serialize($userInfo));
} else {
// 缓存空值,防止缓存穿透
$this->redis->setex($cacheKey, 300, 'NULL');
}
}
return $userInfo === 'NULL' ? null : unserialize($userInfo);
}
}
对于高并发写场景,我们使用消息队列进行削峰填谷:
// 用户注册后发送欢迎邮件
public function registerUser($userData) {
// 保存用户数据
$userId = $this->userModel->create($userData);
// 发送消息到队列,异步处理
$message = [
'type' => 'welcome_email',
'user_id' => $userId,
'email' => $userData['email']
];
$this->queue->push('email_queue', json_encode($message));
return $userId;
}
这个阶段我们学到了重要一课:缓存失效策略要谨慎设计,错误的缓存策略可能导致雪崩效应。
第四阶段:服务化与微服务架构
当业务复杂度急剧增加时,单体架构的维护成本变得难以承受。我们开始向微服务架构演进。
首先将用户服务独立出来:
// 用户服务API定义
class UserService {
public function getUserById($userId) {
// 独立的用户数据操作
}
public function updateUserProfile($userId, $profile) {
// 独立的用户资料更新
}
}
// 在其他服务中通过HTTP调用
class OrderService {
public function createOrder($orderData) {
// 调用用户服务验证用户状态
$userInfo = $this->httpClient->get(
"http://user-service/api/users/{$orderData['user_id']}"
);
// 创建订单逻辑
}
}
服务发现和配置管理我们使用了Consul:
# 注册服务到Consul
curl -X PUT -d '{
"ID": "user-service-1",
"Name": "user-service",
"Address": "192.168.1.100",
"Port": 8080
}' http://consul-server:8500/v1/agent/service/register
微服务化过程中最大的挑战是分布式事务和数据一致性。我们最终采用了 Saga 模式来解决跨服务的事务问题。
第五阶段:容器化与持续部署
随着服务数量增加,部署和运维复杂度呈指数级增长。我们引入了Docker和Kubernetes。
编写Dockerfile:
FROM php:7.4-fpm
# 安装扩展
RUN docker-php-ext-install pdo_mysql opcache
# 复制代码
COPY . /var/www/html
# 设置工作目录
WORKDIR /var/www/html
EXPOSE 9000
Kubernetes部署配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: registry.example.com/user-service:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
通过GitLab CI实现持续集成:
# .gitlab-ci.yml
stages:
- test
- build
- deploy
php_test:
stage: test
script:
- composer install
- vendor/bin/phpunit
build_image:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
经验总结与未来展望
回顾整个架构演进历程,有几个关键经验值得分享:
1. 不要过度设计:在项目早期,简单的架构往往更合适,随着业务发展再逐步演进
2. 监控是关键:建立完善的监控体系,包括应用性能监控、业务指标监控等
3. 自动化一切:从测试到部署,自动化能显著提升效率和稳定性
4. 团队能力建设:架构演进需要团队成员具备相应的技术能力
展望未来,我们正在探索Service Mesh、Serverless等新技术方向。架构演进永无止境,重要的是找到适合当前业务阶段的最佳方案。
希望我的这些经验能够对大家有所启发。架构演进是一个持续的过程,需要根据业务需求和技术发展不断调整。记住,没有最好的架构,只有最适合的架构。

评论(0)