Hyperf 题库
📋 题库分类
- 基础概念题(15 题)
- 协程相关题(12 题)
- 依赖注入与 AOP(10 题)
- 数据库与缓存(15 题)
- 性能优化题(10 题)
- 微服务架构(8 题)
- 综合应用题(10 题)
总计:80 道题
一、基础概念题(15 题)
Q1: Hyperf 是什么?有什么特点?
答案:
Hyperf 是一个基于 Swoole 协程的高性能 PHP 框架。
核心特点:
- 高性能:基于 Swoole 协程,QPS 可达数万
- 全协程化:从框架底层到应用层全面协程化
- 常驻内存:Swoole 常驻内存,减少框架加载开销
- 依赖注入:强大的 DI 容器和注解支持
- 微服务:内置服务注册、发现、治理能力
- 组件丰富:提供 70+ 官方组件
- 云原生:支持容器化部署、配置中心
适用场景:高并发 API、微服务、WebSocket、实时通信
Q2: Hyperf 和 Laravel 有什么区别?
答案:
| 维度 | Laravel | Hyperf |
|---|---|---|
| 运行方式 | PHP-FPM(每次请求启动) | Swoole 常驻内存 |
| 并发模型 | 同步阻塞 | 协程异步非阻塞 |
| 性能 | QPS 数百 | QPS 数万 |
| 启动方式 | 每次请求重新加载 | 启动一次,常驻内存 |
| 适用场景 | 通用 Web 应用 | 高并发、微服务 |
| 学习曲线 | 平缓 | 需要理解协程 |
核心差异:
- Laravel 基于 PHP-FPM,每次请求都要重新加载框架
- Hyperf 基于 Swoole,框架常驻内存,只加载一次
- Hyperf 使用协程实现异步 IO,性能是 Laravel 的几十倍
Q3: 什么是 Swoole?为什么 Hyperf 要基于 Swoole?
答案:
Swoole 是一个 PHP 协程网络通信引擎,使用 C/C++ 编写的 PHP 扩展。
核心功能:
- 协程调度器
- 异步 IO(网络、文件)
- 协程客户端(MySQL、Redis、HTTP)
- 定时器
- 进程管理
Hyperf 选择 Swoole 的原因:
- 性能优势:协程和异步 IO,性能远超传统 PHP-FPM
- 常驻内存:减少框架加载开销
- 并发能力:单进程可处理数万并发连接
- 丰富功能:提供网络服务、定时器、进程管理等
- 生态完善:Swoole 社区活跃
Q4: 常驻内存有什么优势?有什么需要注意的?
答案:
优势:
- 减少框架加载时间:框架只加载一次
- 减少文件 IO:配置文件只读取一次
- 支持连接池:数据库、Redis 连接可以复用
- 可以缓存数据:在内存中缓存热点数据
需要注意:
- 修改代码需要重启:代码修改后必须重启服务(或使用热重载)
- 内存泄漏:需要注意内存泄漏问题(不要在静态变量中无限累积数据)
- 全局变量:全局变量会被所有请求共享,需要使用协程上下文隔离
- max_request:配置 Worker 定期重启,防止内存泄漏
Q5: Hyperf 适合什么场景?不适合什么场景?
答案:
适合的场景:
- ✅ 高并发 API 服务(QPS > 1000)
- ✅ 微服务架构
- ✅ WebSocket 实时通信
- ✅ 消息队列消费者
- ✅ 定时任务系统
- ✅ RPC 服务
不适合的场景:
- ❌ 低并发场景(QPS < 1000)
- ❌ 简单的内容管理系统(CMS)
- ❌ 需要共享主机的项目
- ❌ 团队对协程不熟悉
原因:
- Hyperf 的优势在于高并发,低并发场景用传统框架更简单
- Hyperf 需要独立服务器,不能用虚拟主机
- Hyperf 有学习成本,团队需要掌握协程概念
二、协程相关题(12 题)
Q6: 什么是协程?和线程有什么区别?
答案:
协程:用户态的轻量级线程,由程序自己控制调度。
主要区别:
| 特性 | 线程 | 协程 |
|---|---|---|
| 调度者 | 操作系统内核 | 用户程序 |
| 切换开销 | 较大(几 KB) | 很小(几百字节) |
| 内存占用 | 几百 KB | 几 KB |
| 并发数量 | 几百个 | 几万个 |
| 创建速度 | 较慢 | 极快 |
协程的优势:
- 轻量级,可以创建大量协程
- 切换开销小,性能高
- 同步写法,异步执行
- 遇到 IO 自动挂起,让出 CPU
示例:
// 协程自动调度
Coroutine::create(function () {
$result = Db::query(...); // IO 操作,自动挂起
// IO 完成后恢复执行
});
Q7: 协程是如何调度的?
答案:
协程调度流程:
1. 事件循环(Event Loop)不断检查事件
2. 当协程遇到 IO 操作时,主动挂起(yield)
3. 调度器切换到其他可运行的协程
4. 当 IO 操作完成时,触发事件
5. 调度器恢复(resume)该协程
挂起时机:
- 执行
Coroutine::sleep() - 网络 IO(MySQL、Redis、HTTP 请求)
- 文件 IO(启用 Hook 时)
- 调用
Coroutine::yield()主动挂起
恢复时机:
- 定时器触发
- IO 操作完成
- 调用
Coroutine::resume()
执行示例:
Coroutine::create(function () {
echo "1. 开始\n";
Coroutine::sleep(1); // 挂起,切换到其他协程
echo "3. 恢复\n";
});
Coroutine::create(function () {
echo "2. 其他协程\n";
});
// 输出:1. 开始 → 2. 其他协程 → 3. 恢复
Q8: 什么是协程上下文?为什么需要它?
答案:
协程上下文:每个协程独立的数据存储空间,用于隔离不同协程的数据。
为什么需要:
在传统 PHP-FPM 中,每个请求都是独立的进程,全局变量天然隔离。但在 Swoole 中,多个请求在同一个进程中并发执行,共享全局变量,会导致数据混乱。
问题示例:
// 全局变量会被所有协程共享
$userId = 0;
function handleRequest($id) {
global $userId;
$userId = $id; // 请求 A: userId = 100
Coroutine::sleep(0.1); // 挂起,切换到请求 B
echo "User: $userId\n"; // 可能输出 200(被请求 B 修改)
}
解决方案:
use Hyperf\Context\Context;
function handleRequest($id) {
Context::set('user_id', $id);
Coroutine::sleep(0.1);
echo "User: " . Context::get('user_id') . "\n"; // 正确
}
Q9: 如何在 Hyperf 中实现并发请求?
答案:
使用 Coroutine::parallel() 实现并发执行。
串行执行(慢):
public function getUserDetail(int $userId)
{
$user = Db::table('users')->find($userId); // 10ms
$orders = Db::table('orders')->where('user_id', $userId)->get(); // 20ms
$profile = Redis::get("profile:{$userId}"); // 5ms
// 总耗时:10 + 20 + 5 = 35ms
return compact('user', 'orders', 'profile');
}
并行执行(快):
use Hyperf\Utils\Coroutine;
public function getUserDetail(int $userId)
{
[$user, $orders, $profile] = Coroutine::parallel([
fn() => Db::table('users')->find($userId),
fn() => Db::table('orders')->where('user_id', $userId)->get(),
fn() => Redis::get("profile:{$userId}"),
]);
// 总耗时:max(10, 20, 5) = 20ms
return compact('user', 'orders', 'profile');
}
注意事项:
- 只有独立的、无依赖关系的操作才适合并发
- 并发操作会创建多个协程
Q10: 什么是协程安全?如何保证?
答案:
协程安全:在协程环境下,多个协程共享同一个进程的内存,需要确保数据不会互相干扰。
不安全的代码:
class UserService
{
private $currentUser; // 实例变量,所有协程共享
public function handle(int $userId)
{
$this->currentUser = Db::table('users')->find($userId);
// 这里可能协程切换,currentUser 被其他协程修改
Coroutine::sleep(0.1);
return $this->currentUser; // 可能返回错误的用户
}
}
保证协程安全的方法:
- 使用协程上下文:
use Hyperf\Context\Context;
public function handle(int $userId)
{
$user = Db::table('users')->find($userId);
Context::set('current_user', $user);
Coroutine::sleep(0.1);
return Context::get('current_user'); // 正确
}
- 使用局部变量:
public function handle(int $userId)
{
$user = Db::table('users')->find($userId); // 局部变量
Coroutine::sleep(0.1);
return $user; // 正确
}
- 避免使用全局变量和静态变量
三、依赖注入与 AOP(10 题)
Q11: 什么是依赖注入?有什么优势?
答案:
依赖注入:将类所依赖的对象从外部注入,而不是在类内部创建。
优势:
- 降低耦合度:依赖接口而非具体实现
- 便于测试:可以注入 Mock 对象
- 便于替换:切换实现无需修改代码
- 自动管理:容器自动创建和注入依赖
示例对比:
// 传统方式(高耦合)
class UserController
{
private $userService;
public function __construct()
{
$this->userService = new UserService(); // 在类内部创建
}
}
// 依赖注入(低耦合)
class UserController
{
public function __construct(
private UserServiceInterface $userService // 从外部注入
) {
}
}
Q12: Hyperf 有哪些依赖注入方式?
答案:
三种方式:
- 构造函数注入:
class UserController
{
public function __construct(
private UserService $userService
) {
}
}
- 属性注入(推荐):
use Hyperf\Di\Annotation\Inject;
class UserController
{
#[Inject]
private UserService $userService;
}
- 从容器获取:
use Hyperf\Context\ApplicationContext;
$userService = ApplicationContext::getContainer()->get(UserService::class);
推荐使用属性注入:代码更简洁,减少构造函数参数。
Q13: 什么是 AOP?有什么应用场景?
答案:
AOP(Aspect Oriented Programming):面向切面编程,允许在不修改原有代码的情况下,对方法进行增强。
应用场景:
- 日志记录:记录方法调用、参数、返回值
- 性能监控:统计方法执行时间
- 权限校验:检查用户权限
- 缓存:自动缓存方法结果
- 事务管理:自动开启和提交事务
- 限流:防止接口被频繁调用
- 异常处理:统一处理异常
示例:
#[Aspect]
class LogAspect extends AbstractAspect
{
public array $classes = [UserService::class];
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
echo "方法调用前\n";
$result = $proceedingJoinPoint->process(); // 执行原方法
echo "方法调用后\n";
return $result;
}
}
Q14: AOP 为什么有时候不生效?
答案:
常见原因:
- 对象不是从容器获取的:
$service = new UserService(); // ❌ AOP 不生效
正确做法:
$service = ApplicationContext::getContainer()->get(UserService::class);
- 方法不是 public:
private function getList() { } // ❌ AOP 不生效
- 类被标记为 final:
final class UserService { } // ❌ AOP 不生效
-
切点配置不正确:检查
$classes或$annotations配置 -
代理类未生成:清除缓存重新生成
php bin/hyperf.php di:init-proxy
四、数据库与缓存(15 题)
Q15: 什么是连接池?为什么需要它?
答案:
连接池:预先创建并维护一定数量的连接,需要时从池中获取,用完后归还。
为什么需要:
传统 PHP-FPM:
每次请求:创建连接 → 查询 → 关闭连接
开销:TCP 三次握手 + MySQL 认证 + TCP 四次挥手
Hyperf 连接池:
启动时:创建 10 个连接,放入池中
请求时:从池中获取 → 查询 → 归还到池中
优势:复用连接,减少创建和销毁开销
配置示例:
'pool' => [
'min_connections' => 10, // 最小连接数
'max_connections' => 32, // 最大连接数
'wait_timeout' => 3.0, // 等待超时
],
性能提升:
- 创建连接:50ms
- 从连接池获取:1ms
- 提升 50 倍
Q16: 什么是 N+1 查询?如何解决?
答案:
N+1 查询:查询主表 1 次,然后循环查询关联表 N 次,导致大量数据库查询。
问题代码:
// 查询所有用户(1 次查询)
$users = User::all();
// 循环查询每个用户的订单(N 次查询)
foreach ($users as $user) {
echo $user->orders->count();
}
// 总查询次数:1 + N(如 1 + 100 = 101 次)
解决方案:使用预加载(Eager Loading)
// 只查询 2 次(1 次用户 + 1 次订单)
$users = User::with('orders')->get();
foreach ($users as $user) {
echo $user->orders->count(); // 直接从内存读取
}
性能提升:
- 优化前:101 次查询,耗时 1010ms
- 优化后:2 次查询,耗时 20ms
- 提升 50 倍
Q17: Hyperf 的缓存注解有哪几种?
答案:
三种注解:
- @Cacheable:生成缓存
- 首次调用时执行方法,并缓存结果
- 后续调用直接返回缓存
#[Cacheable(prefix: 'user', ttl: 3600, value: '#{id}')]
public function getUserById(int $id)
{
return Db::table('users')->find($id);
}
- @CachePut:更新缓存
- 每次都执行方法
- 并更新缓存
#[CachePut(prefix: 'user', ttl: 3600, value: '#{user.id}')]
public function updateUser(array $user)
{
Db::table('users')->where('id', $user['id'])->update($user);
return $user;
}
- @CacheEvict:删除缓存
#[CacheEvict(prefix: 'user', value: '#{id}')]
public function deleteUser(int $id)
{
Db::table('users')->where('id', $id)->delete();
}
// 删除所有缓存
#[CacheEvict(prefix: 'user', all: true)]
public function deleteAllCache() {}
Q18: 什么是缓存穿透、缓存击穿、缓存雪崩?如何解决?
答案:
1. 缓存穿透
定义:查询不存在的数据,缓存和数据库都没有,每次都打到数据库。
场景:用户恶意查询不存在的 ID(如 -1)
解决方案:缓存空值
if ($data === null) {
$redis->setex($key, 60, 'null'); // 缓存空值(短时间)
}
2. 缓存击穿
定义:热点 key 过期,瞬间大量请求打到数据库。
场景:热门商品的缓存过期,1 万个请求同时查询数据库
解决方案:分布式锁 + 双重检查
$locked = $redis->set($lockKey, $lockValue, ['NX', 'EX' => 10]);
if ($locked) {
// 只有一个请求查询数据库
$data = Db::query(...);
$redis->setex($key, 3600, $data);
}
3. 缓存雪崩
定义:大量 key 同时过期,导致数据库压力骤增。
场景:1000 个商品的缓存同时在凌晨 2 点过期
解决方案:过期时间加随机值
$ttl = 3600 + mt_rand(0, 600); // 加 0-10 分钟的随机值
$redis->setex($key, $ttl, $data);
Q19: 分布式锁如何实现?
答案:
使用 Redis 的 SET NX EX 命令实现分布式锁。
获取锁:
$lockKey = "lock:user:{$userId}";
$lockValue = uniqid(); // 唯一值,用于释放锁时验证
// NX:只在 key 不存在时设置
// EX:设置过期时间(防止死锁)
$locked = $redis->set($lockKey, $lockValue, ['NX', 'EX' => 10]);
if (!$locked) {
throw new \Exception('获取锁失败');
}
释放锁(使用 Lua 脚本保证原子性):
$script = <<<LUA
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
LUA;
$redis->eval($script, [$lockKey, $lockValue], 1);
为什么需要 Lua 脚本:
- 确保"检查 + 删除"是原子操作
- 防止删除其他请求的锁
五、性能优化题(10 题)
Q20: Hyperf 如何实现高性能?
答案:
核心机制:
-
Swoole 常驻内存:
- 框架只加载一次,常驻内存
- 减少框架加载和初始化开销
-
协程异步 IO:
- 遇到 IO 操作时挂起协程,让出 CPU
- 单进程可处理数万并发请求
-
连接池:
- 复用数据库、Redis 连接
- 减少连接建立和销毁开销
-
事件驱动:
- 基于 Reactor 模型
- 高效处理并发事件
-
协程并发:
- 可以并发执行多个 IO 操作
- 大幅减少总耗时
性能对比:
Laravel (PHP-FPM): QPS 500-800
ThinkPHP (PHP-FPM): QPS 600-900
Hyperf (Swoole): QPS 15,000-30,000
Q21: 如何优化 Hyperf 应用的性能?
答案:
优化策略(从多个层面):
1. 协程并发:
[$user, $orders] = Coroutine::parallel([
fn() => $this->getUser($id),
fn() => $this->getOrders($id),
]);
2. 缓存优化:
#[Cacheable(prefix: 'user', ttl: 3600)]
public function getUserById(int $id)
{
return Db::table('users')->find($id);
}
3. 数据库优化:
- 添加索引
- 避免 N+1 查询
- 使用批量操作
- 只查询需要的字段
4. 连接池配置:
'pool' => [
'min_connections' => 10,
'max_connections' => 32,
],
5. 异步队列:
// 将耗时操作放入队列异步执行
$this->driver->push(new EmailJob([...]));
6. 减少日志输出:生产环境只记录必要日志
7. 使用 OpCache:开启 PHP OpCache,减少代码解析开销
Q22: 连接池如何配置?参数含义是什么?
答案:
配置示例:
'pool' => [
'min_connections' => 10, // 最小连接数(预热)
'max_connections' => 32, // 最大连接数
'connect_timeout' => 10.0, // 连接超时时间
'wait_timeout' => 3.0, // 等待超时时间
'heartbeat' => -1, // 心跳检测间隔
'max_idle_time' => 60, // 最大空闲时间
],
参数说明:
| 参数 | 含义 | 推荐值 |
|---|---|---|
| min_connections | 启动时创建的连接数 | 10 |
| max_connections | 最大连接数,超过则等待 | CPU 核心数 × 2 |
| connect_timeout | 连接超时时间(秒) | 10.0 |
| wait_timeout | 等待可用连接的超时时间(秒) | 3.0 |
| max_idle_time | 连接最大空闲时间(秒) | 60 |
优化建议:
- CPU 密集型:
max_connections= CPU 核心数 - IO 密集型:
max_connections= CPU 核心数 × 2 - 监控连接池使用情况,动态调整
六、组件相关题(8 题)
Q23: 异步队列有什么用?如何使用?
答案:
作用:将耗时任务异步执行,避免阻塞主流程。
应用场景:
- 发送邮件、短信
- 数据统计和分析
- 图片处理
- 导出大文件
- 调用第三方 API
使用步骤:
- 创建任务:
namespace App\Job;
use Hyperf\AsyncQueue\Job;
class EmailJob extends Job
{
public function __construct(public array $params) {}
public function handle()
{
// 发送邮件逻辑
echo "发送邮件到: {$this->params['to']}\n";
}
}
- 推送任务:
use Hyperf\AsyncQueue\Driver\DriverFactory;
$driver = $this->driverFactory->get('default');
$driver->push(new EmailJob([
'to' => 'user@example.com',
'subject' => '欢迎注册',
]), 0); // 延迟 0 秒
性能提升:
- 优化前:注册接口响应时间 2 秒(包含发送邮件)
- 优化后:注册接口响应时间 10ms(邮件异步发送)
Q24: 如何配置定时任务?
答案:
使用 @Crontab 注解配置定时任务。
示例:
namespace App\Crontab;
use Hyperf\Crontab\Annotation\Crontab;
#[Crontab(
name: 'StatisticsTask',
rule: '0 2 * * *', // 每天凌晨 2 点
callback: 'execute',
memo: '统计每日数据'
)]
class StatisticsTask
{
public function execute()
{
// 统计逻辑
$count = Db::table('orders')
->whereDate('created_at', date('Y-m-d'))
->count();
echo "今日订单数:{$count}\n";
}
}
Cron 表达式:
* * * * *
│ │ │ │ │
│ │ │ │ └─ 星期 (0-7)
│ │ │ └─── 月份 (1-12)
│ │ └───── 日期 (1-31)
│ └─────── 小时 (0-23)
└───────── 分钟 (0-59)
常用示例:
0 2 * * *:每天凌晨 2 点*/5 * * * *:每 5 分钟0 */2 * * *:每 2 小时0 0 * * 0:每周日凌晨0 0 1 * *:每月 1 号凌晨
七、综合应用题(10 题)
Q25: 设计一个高并发秒杀系统,如何使用 Hyperf 实现?
答案:
系统设计:
1. 库存预热:
// 启动时将库存加载到 Redis
$redis->set("stock:{$productId}", 1000);
2. 秒杀接口:
public function seckill(int $productId, int $userId)
{
// 使用 Lua 脚本原子扣减库存
$script = <<<LUA
if redis.call('get', KEYS[1]) > '0' then
return redis.call('decr', KEYS[1])
else
return -1
end
LUA;
$result = $redis->eval($script, ["stock:{$productId}"], 1);
if ($result < 0) {
return ['code' => 1, 'msg' => '商品已售罄'];
}
// 异步创建订单(使用队列)
$this->driver->push(new CreateOrderJob([
'product_id' => $productId,
'user_id' => $userId,
]));
return ['code' => 0, 'msg' => '抢购成功'];
}
3. 创建订单(异步):
class CreateOrderJob extends Job
{
public function handle()
{
Db::transaction(function () {
// 创建订单
$orderId = Db::table('orders')->insertGetId([...]);
// 扣减数据库库存(双重保险)
Db::table('products')
->where('id', $this->params['product_id'])
->decrement('stock');
});
}
}
4. 限流:
#[RateLimit(create: 10, capacity: 100)] // 令牌桶限流
优化点:
- Redis 原子操作,防止超卖
- 异步创建订单,快速响应
- 令牌桶限流,防止恶意刷单
- Lua 脚本保证原子性
性能:
- 支持 10 万+并发
- 响应时间 < 10ms
Q26: 如何保证 Hyperf 服务的稳定性?
答案:
从多个层面保证稳定性:
1. 代码层面:
- 使用 try-catch 捕获异常
- 避免内存泄漏(不使用静态变量缓存)
- 使用协程上下文隔离数据
- 使用连接池,避免连接耗尽
2. 配置层面:
'settings' => [
'worker_num' => swoole_cpu_num() * 2,
'max_request' => 10000, // 定期重启 Worker
],
3. 架构层面:
- 使用限流防止过载
- 服务降级,保证核心功能
- 使用缓存,减轻数据库压力
- 使用熔断器,防止雪崩
4. 监控层面:
- 使用 Prometheus 监控性能指标
- 配置告警,及时发现问题
- 记录详细日志,便于排查
5. 运维层面:
- 使用 Supervisor 守护进程
- 使用 Nginx 负载均衡
- 多机房部署,容灾备份
- 定期压测,评估系统容量
八、高频题汇总(必背)
Q27: Hyperf 和 ThinkPHP 有什么区别?
答案:
| 维度 | ThinkPHP | Hyperf |
|---|---|---|
| 运行方式 | PHP-FPM | Swoole 常驻内存 |
| 性能 | QPS 数百 | QPS 数万 |
| 学习曲线 | 容易 | 需要理解协程 |
| 适用场景 | 传统 Web 应用 | 高并发、微服务 |
相似点:
- 都是 PHP 框架
- API 设计相似(路由、数据库、缓存)
核心差异:
- ThinkPHP 基于 PHP-FPM,传统同步模型
- Hyperf 基于 Swoole,协程异步模型
Q28: 协程和异步回调有什么区别?
答案:
异步回调(Node.js 风格):
// 回调地狱
queryDatabase(sql1, function(result1) {
queryDatabase(sql2, function(result2) {
queryDatabase(sql3, function(result3) {
// 使用 result1, result2, result3
});
});
});
协程(同步写法,异步执行):
$result1 = queryDatabase($sql1);
$result2 = queryDatabase($sql2);
$result3 = queryDatabase($sql3);
// 使用 result1, result2, result3
协程的优势:
- 代码更简洁,易于理解
- 避免回调地狱
- 同样是异步执行,性能相当
Q29: 你在项目中遇到过哪些 Hyperf 相关的问题?
参考答案:
问题 1:内存泄漏
现象:服务运行一段时间后,内存占用持续增长。
原因:在静态变量中缓存数据。
解决:
- 改用 Redis 缓存,设置过期时间
- 配置
max_request,定期重启 Worker
问题 2:协程数据串了
现象:用户 A 看到了用户 B 的数据。
原因:使用了实例变量存储请求相关数据。
解决:使用协程上下文存储数据。
问题 3:AOP 不生效
现象:切面没有执行。
原因:对象是通过 new 创建的。
解决:从容器获取对象。
问题 4:数据库连接超时
现象:偶尔报错 "MySQL server has gone away"。
原因:连接池中的连接长时间未使用,被数据库关闭。
解决:配置心跳检测或 max_idle_time。
Q30: 如何进行 Hyperf 应用的压力测试?
答案:
使用 ab(Apache Bench):
ab -n 10000 -c 100 http://localhost:9501/api/test
# -n: 总请求数
# -c: 并发数
输出结果解读:
Requests per second: 15000 [#/sec] # QPS
Time per request: 6.67 [ms] # 平均响应时间
使用 wrk(更强大):
wrk -t4 -c100 -d30s http://localhost:9501/api/test
# -t: 线程数
# -c: 并发连接数
# -d: 持续时间
压测注意事项:
- 关闭日志输出
- 使用生产环境配置
- 预热服务(先跑一轮)
- 逐步增加并发数
- 监控系统资源(CPU、内存)
性能指标:
- QPS:每秒请求数(越高越好)
- 响应时间:平均响应时间(越低越好)
- 成功率:成功请求占比(应该 100%)
九、项目经验题(重要!)
Q31: 介绍一个你用 Hyperf 做的项目
参考答案模板:
我在 [公司] 使用 Hyperf 开发了一个高并发的电商 API 服务。
【项目背景】
这个项目主要为 Web、App、小程序提供后端接口,
峰值 QPS 达到 15,000,支持 10 万+并发连接。
【技术架构】
- 后端:Hyperf 3.1 + MySQL 8.0 + Redis 7.0
- 部署:Docker + Nginx + Supervisor
- 监控:Prometheus + Grafana
【我的职责】
1. 负责用户、订单模块的开发
2. 进行性能优化,将 QPS 从 500 提升到 15,000
3. 设计秒杀系统,支持 10 万+并发
4. 解决缓存雪崩、数据一致性等问题
【技术亮点】
1. 使用协程并发,接口响应时间从 200ms 降到 50ms
2. 使用多级缓存,缓存命中率 95%
3. 使用异步队列,异步处理耗时任务
4. 使用 Redis + Lua 脚本实现秒杀,无超卖
【项目成果】
- QPS 提升 30 倍
- 响应时间降低 4 倍
- 支持 10 万+并发连接
- 系统稳定运行,无重大故障
Q32: 你做过哪些性能优化?
参考答案:
案例 1:接口响应时间优化
问题:订单详情接口响应时间 200ms
优化措施:
- 使用协程并发查询 → 降到 50ms
- 使用 Redis 缓存热点数据 → 降到 20ms
- 只查询需要的字段 → 降到 15ms
结果:响应时间从 200ms 降到 15ms,提升 13 倍
案例 2:数据库查询优化
问题:用户列表接口很慢
原因:N+1 查询问题
优化:使用 with 预加载关联数据
结果:查询次数从 101 次降到 2 次,耗时从 1 秒降到 20ms
案例 3:秒杀系统优化
问题:秒杀时 QPS 只有 500
优化措施:
- Redis 库存预热,Lua 脚本原子扣减
- 异步队列创建订单
- 令牌桶限流
结果:QPS 从 500 提升到 50,000,提升 100 倍
十、快速复习清单
必须掌握的 20 个知识点
- 1. Hyperf 的核心特点
- 2. 与 Laravel/ThinkPHP 的区别
- 3. 协程的定义和优势
- 4. 协程调度原理
- 5. 协程上下文的作用
- 6. 依赖注入的概念
- 7. Hyperf 的依赖注入方式
- 8. AOP 的应用场景
- 9. 连接池的作用
- 10. N+1 查询问题
- 11. 缓存的三种注解
- 12. 缓存穿透/击穿/雪崩
- 13. 异步队列的使用
- 14. 定时任务的配置
- 15. 协程并发的使用
- 16. 性能优化的方法
- 17. 分布式锁的实现
- 18. 服务注册与发现
- 19. RPC 调用
- 20. 项目经验总结
建议:
- 每天复习 5-10 题
- 用自己的话复述答案
- 结合实际项目经验
- 准备 2-3 个深入话题
祝你成功! 🎉