当前位置: 首页 > news >正文

PHPAPI网关实现与请求路由

PHP API网关实现与请求路由

API网关是微服务架构的入口。它负责请求路由、认证、限流、日志等功能。今天从零实现一个简单的API网关。

网关的核心功能是接收客户端请求,根据路由规则转发到对应的后端服务。

```php
class APIGateway
{
private array $routes = [];
private array $middlewares = [];
private array $rateLimits = [];

public function addRoute(string $path, string $targetUrl, array $methods = ['GET', 'POST', 'PUT', 'DELETE']): void
{
$this->routes[] = compact('path', 'targetUrl', 'methods');
}

public function addMiddleware(callable $middleware): void
{
$this->middlewares[] = $middleware;
}

public function setRateLimit(string $path, int $maxRequests, int $window): void
{
$this->rateLimits[$path] = compact('maxRequests', 'window');
}

public function handle(): void
{
$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

// 执行中间件
foreach ($this->middlewares as $middleware) {
$result = $middleware($method, $uri);
if ($result !== null) {
echo json_encode($result);
return;
}
}

// 路由匹配
foreach ($this->routes as $route) {
if (!in_array($method, $route['methods'])) continue;

$pattern = preg_replace('/\{(\w+)\}/', '(\w+)', $route['path']);
$pattern = '#^' . $pattern . '$#';

if (preg_match($pattern, $uri, $matches)) {
$this->forward($route['targetUrl'], $method, $uri);
return;
}
}

http_response_code(404);
echo json_encode(['error' => 'Route not found']);
}

private function forward(string $targetUrl, string $method, string $uri): void
{
$url = rtrim($targetUrl, '/') . $uri;

$ch = curl_init($url);
$options = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HEADER => true,
];

if ($method === 'POST' || $method === 'PUT') {
$options[CURLOPT_CUSTOMREQUEST] = $method;
$options[CURLOPT_POSTFIELDS] = file_get_contents('php://input');
}

curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);

$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);

http_response_code($httpCode);
echo $body;
}
}
?>

// 限流中间件
class RateLimitMiddleware
{
private Redis $redis;
private int $maxRequests;
private int $window;

public function __construct(Redis $redis, int $maxRequests = 100, int $window = 60)
{
$this->redis = $redis;
$this->maxRequests = $maxRequests;
$this->window = $window;
}

public function __invoke(string $method, string $uri): ?array
{
$key = 'ratelimit:' . $_SERVER['REMOTE_ADDR'];
$current = $this->redis->get($key);

if ($current === false) {
$this->redis->setex($key, $this->window, 1);
return null;
}

if ($current >= $this->maxRequests) {
$ttl = $this->redis->ttl($key);
header('Retry-After: ' . $ttl);
http_response_code(429);
return ['error' => '请求过于频繁', 'retry_after' => $ttl];
}

$this->redis->incr($key);
return null;
}
}

// 认证中间件
class AuthMiddleware
{
private string $jwtSecret;

public function __construct(string $jwtSecret)
{
$this->jwtSecret = $jwtSecret;
}

public function __invoke(string $method, string $uri): ?array
{
// 公开路由跳过认证
$publicRoutes = ['/api/login', '/api/register', '/health'];
if (in_array($uri, $publicRoutes)) {
return null;
}

$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
$token = str_replace('Bearer ', '', $token);

if (empty($token)) {
http_response_code(401);
return ['error' => '未提供认证token'];
}

// 验证JWT
$parts = explode('.', $token);
if (count($parts) !== 3) {
http_response_code(401);
return ['error' => '无效的token格式'];
}

$payload = json_decode(base64_decode($parts[1]), true);
if (!$payload || $payload['exp'] < time()) {
http_response_code(401);
return ['error' => 'token已过期'];
}

return null;
}
}

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$gateway = new APIGateway();

$gateway->addMiddleware(new RateLimitMiddleware($redis));
$gateway->addMiddleware(new AuthMiddleware('your-jwt-secret'));

$gateway->addRoute('/api/users', 'http://user-service:9001');
$gateway->addRoute('/api/orders', 'http://order-service:9002');
$gateway->addRoute('/api/products', 'http://product-service:9003');

$gateway->setRateLimit('/api/users', 60, 60);
$gateway->setRateLimit('/api/orders', 30, 60);

$gateway->handle();
?>

API网关作为系统的统一入口,可以集中处理横切关注点。认证、限流、日志、监控都在网关层实现,后端服务可以专注于业务逻辑。但网关也可能成为性能瓶颈和单点故障,设计时要注意容错和扩展。

http://www.jsqmd.com/news/926947/

相关文章:

  • 从手机到单片机:聊聊ARM Cortex家族那些事,A、R、M系列到底有啥不同?
  • 偏振片不止于实验室:从手机屏幕到3D电影,聊聊身边的偏振光应用
  • Infineon XC16x/XC2xxx调试端口配置与Flash编程实践
  • 避开这些坑!用UK Biobank蛋白质数据做孟德尔随机化与共定位分析的实战指南
  • 别再只听个响!手把手教你用AudioExpert和U 964搭建汽车RNC降噪测试系统
  • 想让LQR控制器跟踪轨迹?别急着调参,先搞懂‘增广系统’这个核心概念
  • RT-Thread实战:用信号量、互斥量和事件集搞定嵌入式多线程数据同步(附完整代码)
  • 避坑指南:在Jetson上为YOLOv8安装匹配的GPU版PyTorch和torchvision(附版本对照表)
  • 多智能体系统架构风险:从分布式系统视角看AI协同的工程挑战
  • Arm Neoverse V2调试寄存器架构与实战解析
  • 从‘发热怪’到‘冷静王’:我的DCDC电源模块升级实战(XL4003 vs 传统LDO)
  • SEO新手别慌!用Google自带的‘免费工具’(site:、intitle:等命令)快速自查网站健康度
  • 告别采样难题:手把手教你用差分运放给交流信号加个2.5V直流偏置(附Multisim仿真文件)
  • 告别串口!手把手教你用J-Link RTT在STM32上实现彩色日志打印与交互调试
  • 别再只会Stegsolve了!手把手教你用Kali玩转图片隐写:binwalk、foremost与outguess实战(附WUSTCTF例题)
  • Cadence Virtuoso新手避坑指南:手把手教你画反相器并跑通第一个仿真(附常见错误排查)
  • 基于电话线DTMF信号的远程电器控制系统设计与实现
  • Venusaur项目全面解析:高效句子嵌入模型的终极指南
  • 告别数据丢失!STM32 HAL库串口DMA双缓冲接收机制详解(附USART2配置)
  • 老旧电视盒子焕新指南:给中兴B862AV3.2M刷入当贝桌面,实现开机自启、语音遥控和Root权限
  • Python代码保护与分发新思路:除了PyInstaller,试试用Cython生成.so/.pyd文件
  • 告别Root冲突!雷电模拟器9.0.20+保姆级Magisk Delta(狐狸面具)安装指南
  • 基于个人数据构建AI自我认知系统:从文本分析到数字分身
  • Pyecharts 3D散点图实战:用‘点的大小和透明度’讲好你的数据故事
  • 手把手教你搞定Paradigm SKUA-GOCAD 2022.06.20安装与破解(附详细图文步骤)
  • 手机电脑互传文件太慢?试试这个被遗忘的宝藏:HandShaker修改版保姆级安装配置指南(支持Win/Mac)
  • 用Matlab复现合同网协议(CNP):一个多无人机协同任务分配的保姆级仿真教程
  • 保姆级教程:用Wireshark抓包分析PCIe Recovery状态机(附TS1/TS2 Ordered Set解析)
  • 一根网线搞定树莓派SSH:Windows 11下免路由器直连保姆级教程(含IP地址查找避坑)
  • 不止于连线:用嘉立创EDA的铺铜、丝印和3D功能,让你的PCB作品更专业