swoole方案 GraphQL 异步解析架构
【代码】swoole方案 GraphQL 异步解析架构。
·
装依赖跑起来:
cd /mnt/d/graphql
curl -sS https://getcomposer.org/installer | php8.4
php8.4 composer.phar install --no-security-blocking
php8.4 graphql.php
装好后测试:
# 查用户+订单
curl -X POST http://localhost:9600/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{user(id:1){name email orders{product price}}}"}'
# 查所有用户
curl -X POST http://localhost:9600/graphql \
-d '{"query":"{users{id name}}"}' \
-H 'Content-Type: application/json'
---
大白话解释:
GraphQL 解决什么问题
REST的问题:
- 拿用户信息 → /api/user/1
- 拿他的订单 → /api/orders?user=1
- 拿订单里的商品 → /api/product/101
3个请求,3次网络往返,前端还要自己拼数据。
GraphQL 一个请求描述你要什么:
{
user(id: 1) {
name
orders {
product
price
}
}
}
服务器帮你全拿,一次返回。
resolve 是什么
每个字段都有一个 resolve 函数,告诉 GraphQL 这个字段的数据从哪来。可以查数据库、调RPC、读缓存,随便。
异步在哪
Swoole 协程下,每个请求跑在独立协程里。resolve 里如果用协程客户端查数据库,等待时不阻塞其他请求,多个字段的查询可以用WaitGroup
并发打出去,原理和BFF那个一样。
<?php
/**
* GraphQL 异步解析服务
*
* 大白话:
* REST API 要拿用户+订单+商品需要发3个请求
* GraphQL 一个请求描述你要什么,服务器帮你全拿回来
* 异步的意思:解析每个字段时,多个数据库/服务查询同时发出,不排队等
*
* 流程:
* POST /graphql → 解析query → 并发执行各字段resolver → 拼结果返回
*
* 依赖:
* composer install
*
* 运行:
* php8.4 graphql.php
*/
require __DIR__ . '/vendor/autoload.php';
use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup;
// ── 模拟数据(实际换成数据库/RPC查询)────────────────────────
$users = [
1 => ['id' => 1, 'name' => '张三', 'email' => 'zhang@example.com'],
2 => ['id' => 2, 'name' => '李四', 'email' => 'li@example.com'],
];
$orders = [
1 => [['id' => 101, 'product' => '手机', 'price' => 5999]],
2 => [['id' => 102, 'product' => '电脑', 'price' => 8999]],
];
// ── 定义 GraphQL 类型 ─────────────────────────────────────────
// 告诉 GraphQL 每个类型长什么样,每个字段怎么取数据
$orderType = new ObjectType([
'name' => 'Order',
'fields' => [
'id' => Type::int(),
'product' => Type::string(),
'price' => Type::float(),
],
]);
$userType = new ObjectType([
'name' => 'User',
'fields' => fn() => [
'id' => Type::int(),
'name' => Type::string(),
'email' => Type::string(),
// orders 字段:查这个用户的订单
// resolve 就是"这个字段的数据从哪来"
'orders' => [
'type' => Type::listOf($orderType),
'resolve' => function ($user) use ($orders) {
// 实际这里可以是协程查数据库
// usleep 模拟查询耗时
usleep(50000); // 50ms
return $orders[$user['id']] ?? [];
},
],
],
]);
// ── Query 入口:客户端能查什么 ────────────────────────────────
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
// 查单个用户
'user' => [
'type' => $userType,
'args' => ['id' => Type::nonNull(Type::int())],
'resolve' => function ($root, $args) use ($users) {
usleep(30000); // 模拟30ms查询
return $users[$args['id']] ?? null;
},
],
// 同时查多个用户(并发解析)
'users' => [
'type' => Type::listOf($userType),
'resolve' => function () use ($users) {
return array_values($users);
},
],
],
]);
$schema = new Schema(['query' => $queryType]);
// ── Swoole HTTP 服务 ──────────────────────────────────────────
$server = new Server('0.0.0.0', 9600);
$server->set([
'worker_num' => swoole_cpu_num(),
'enable_coroutine' => true,
]);
$server->on('request', function (Request $req, Response $res) use ($schema) {
$res->header('Content-Type', 'application/json');
$res->header('Access-Control-Allow-Origin', '*'); // 允许浏览器直接调
// GraphQL Playground 的 OPTIONS 预检请求
if ($req->server['request_method'] === 'OPTIONS') {
$res->header('Access-Control-Allow-Headers', 'Content-Type');
$res->end();
return;
}
if ($req->server['request_uri'] !== '/graphql') {
$res->status(404);
$res->end(json_encode(['error' => 'use POST /graphql']));
return;
}
// 解析请求体
$body = json_decode($req->rawContent(), true);
if (!isset($body['query'])) {
$res->status(400);
$res->end(json_encode(['error' => 'missing query']));
return;
}
// ── 执行 GraphQL 查询 ─────────────────────────────────────
// webonyx/graphql-php 内部会按字段依次调用 resolve
// 配合 Swoole 协程,每个 resolve 里的 IO 操作都可以异步
try {
$result = GraphQL::executeQuery(
$schema,
$body['query'],
null,
null,
$body['variables'] ?? null
);
$res->end(json_encode($result->toArray()));
} catch (\Throwable $e) {
$res->status(500);
$res->end(json_encode(['error' => $e->getMessage()]));
}
});
$server->on('workerStart', function ($s, $wid) {
if ($wid === 0) {
echo "GraphQL → http://0.0.0.0:9600/graphql\n";
echo "测试: curl -X POST http://localhost:9600/graphql \\\n";
echo " -H 'Content-Type: application/json' \\\n";
echo " -d '{\"query\":\"{user(id:1){name email orders{product price}}}\"}' \n";
}
});
$server->start();
composer.json
{
"require": {
"webonyx/graphql-php": "^15.0"
},
"config": {
"audit": {
"block-insecure": false
}
}
}
更多推荐
所有评论(0)