美文网首页
用PHP和Lumen开发一个简单的API网关

用PHP和Lumen开发一个简单的API网关

作者: 追梦人在路上不断追寻 | 来源:发表于2020-07-19 23:59 被阅读0次

    如今,“微服务”一词已广为人知—突然间,它们变得非常流行,许多公司宣布在未经适当研究的情况下过渡到这种架构模式。无论如何,我们将在本文之外继续讨论模式的实用性。
    传统上,微服务集合上有一个额外的层-所谓的API网关,可以解决一些问题(稍后将列出)。在撰写本文时,还没有许多开源实现,因此我们决定使用Lumen微框架(Laravel的核心)在PHP中发布自己的实现。
    在本文中,我们将证明现代PHP的任务是如此简单!
    什么是API网关?
    简而言之,API网关是用户与任意数量的API服务(因此称为名称)之间的智能代理服务器。
    开始部署微服务后,对这一层的需求就会出现:
    • 与许多(Netflix有600多个)单独的API地址相比,单个端点地址(URL)更容易记住和配置。
    • 一次验证顶级用户凭证(通常是令牌)是有意义的;
    • 在此级别上限制速率是有意义的。
    • 整个系统变得更加灵活-如果愿意,您可以每天更改内部结构。支持较旧的API版本和架构变得微不足道;
    • 您可以缓存或更改响应;
    • 为了方便用户(或您的前端开发人员),您可以合并来自不同服务的响应。
    当然还有更多的优势-这只是冰山一角。
    Nginx提供了一本不错的免费电子书,其中讨论了微服务和API网关—如果您对此模式感兴趣,建议您阅读它。
    现有解决方案
    • 用Lua编写的API伞;
    • Kong,用Lua编写;
    • AWS API Gateway,它是Amazon Web Services的一部分。
    如前所述,开放源代码选项并不多,而现有选项缺乏一些重要功能。
    为什么选择PHP和Lumen?
    PHP 7是一种高性能的语言,Laravel和Symfony之类的框架向世界证明,PHP既可以美观(表达)又可以功能。Lumen是Laravel的轻量级版本,是理想的选择,因为我们不需要会话,模板和全栈应用程序的其他功能。
    另外,我们碰巧对PHP和Lumen有更多的经验。但是,由于我们希望大多数未来的用户使用我们的Docker映像,因此语言甚至都不重要。它只是一个执行简单角色的服务层!
    术语
    我们提出以下架构和术语,以在整个代码中使用以避免任何混淆:

    内部架构和术语
    该应用程序称为Vrata(“Врата”字面意思是俄语,即我们的CTO的母语“网关”)。
    在API网关层的下面,您放置了N个微服务-响应Web请求的后端API。每个服务可以包含任意数量的实例,并且我们的API网关将通过所谓的服务注册表来选择特定实例。
    此外,每个服务托管一定数量的资源(使用REST语言),然后每个资源可以公开许多可能的操作。对于任何经验丰富的REST程序员而言,它都是简单而逻辑的结构,是吗?
    期望
    我们甚至还没有开始编码,但是我们已经可以列出即将推出的产品的一些要求:
    • 网关必须在水平方向上很好地扩展,因为我们生活在2016年,而事情在2016年只是水平地扩展。
    • 网关必须能够组合查询并向微服务发出异步请求;
    • 网关必须实施速率限制;
    • 网关必须实现身份验证。传统上,建议API网关执行身份验证,而底层微服务负责对其自身资源的授权。
    • 网关必须能够自动从微服务导入公开的资源。首先,我们选择Swagger作为当今最常见的API描述格式。
    • 网关应该能够修改(变异)微服务响应;
    • 最后:网关必须直接在仅使用环境变量配置的Docker映像上运行良好。我们不需要任何其他存储库或部署脚本或任何其他内容!
    我们必须承认,大多数功能已经启动并正在运行,并且实现过程轻而易举。就像有人说的那样,对于软件开发人员来说,我们生活在一个非常激动人心的时代!实施和部署软件从未如此简单。
    实作
    认证方式
    在这方面的工作并不多,我们只是安装了Laravel Passport(做了一些魔术使其可以与Lumen一起使用),并且开箱即用地获得了包括JWT在内的所有OAuth2功能的全套。您可以在GitHub上看到我的小型Lumen Passport集成包。
    路线和控制器
    所有微服务路由均已导入并以JSON格式保存。服务提供商负责在引导过程中安装以下路由:

    <?php
    class AppServiceProvider extends ServiceProvider
    {
    /**
    * @return void
    */
    protected function registerRoutes()
    {
    registry =this->app->make(RouteRegistry::class);
    if (registry->isEmpty()) { Log::info('Not adding any service routes - route file is missing'); return; }registry->bind(app());
    }
    }

    非常简单-我们一一解析路由,然后将它们传递到容器(Lumen应用程序),所有内容都绑定到同一控制器。我们还添加了OAuth2令牌验证中间件和我们的助手中间件。

    现在,每条公开的微服务路由在我们的API网关上都有一条对应的路由。此外,还添加了聚合(综合)路由,所有内容都发送到同一控制器。该控制器处理所有GET请求的方式如下:

    <?php
    class RouteRegistry
    {
    /**
    * @param Application app */ public function bind(Applicationapp)
    {
    this->getRoutes()->each(function (route) use (app) {method = strtolower(route->getMethod());app->{method}(route->getPath(), [
    'uses' => 'App\Http\Controllers\GatewayController@' . method, 'middleware' => [ 'auth', 'helper:' .route->getId() ]
    ]);
    });
    }
    }

    我们从微服务收集响应,然后使用数组精简器将它们粘合在一起。此外,我们收集参数并在必要时注入它们(例如,第一个微服务可能会提供一个参数作为响应,稍后将与另一个微服务一起使用)。
    Guzzle之所以被选为Web客户端是因为它能够很好地处理异步请求,并且包装盒中还提供了一些集成测试功能。
    汇总查询
    已经支持复杂的聚合查询,这些查询对应于多个微服务请求。例如。“给我,还有那个和一点点。”
    聚合路由配置的示例:
    <?php
    return (static function() {
    configTemplate = [ 'routes' => [ [ 'aggregate' => true, 'method' => 'GET', 'path' => '/v1/devices/{mac}/details', 'actions' => [ 'device' => [ 'service' => 'core', 'method' => 'GET', 'path' => 'devices/{mac}', 'sequence' => 0, 'critical' => true ], 'ping' => [ 'service' => 'history', 'output_key' => false, 'method' => 'POST', 'path' => '/{mac}/ping', 'sequence' => 0, 'critical' => false ], 'settings' => [ 'service' => 'core', 'output_key' => 'network.settings', 'method' => 'GET', 'path' => 'networks/{device%network_id}', 'sequence' => 1, 'critical' => false ] ] ] ], ];sections = ['services', 'routes', 'global'];
    foreach (sections assection) {
    config = env('GATEWAY_' . strtoupper(section), false);
    {section} = config ? json_decode(config, true) : configTemplate[section];
    }
    return compact($sections);
    })();

    如您所见,不仅聚合路由已经可用-它们还提供了不错的功能。您可以将基础请求标记为关键请求,也可以将其标记为关键请求,可以并行启动请求,也可以使用请求中一个微服务对另一个微服务的响应。性能也很好-该测试路由仅需56毫秒(流明自举时间约为20毫秒,其余为并行启动的后台请求)。
    服务注册表
    这是迄今为止最薄弱的部分,只有一种基本类型的实例解析可用:DNS。尽管具有明显的原始性,但它在AWS和Docker Cloud环境中仍然可以正常工作,在该环境中,云提供商将为您监控节点的运行状况并为您提供动态DNS记录。
    因此,当前Vrata仅使用服务的主机名而不问问题-它背后有很多实例或一台物理计算机。但是,我们很快将增加对当今最受欢迎的服务注册表-Consul的支持。
    服务注册中心的任务很简单-维护一个功能正常的微服务实例表,并在需要时公开此信息。AWS和Docker Cloud都能为​​您执行此操作,从而为您提供始终有效的“魔术”主机名。
    Docker镜像
    当谈论微服务而不是提及Docker时,这是过去几年中的一项突破性技术,简直是不可原谅的。微服务通常作为Docker映像构建和部署,这已成为一种标准做法,因此我们迅速在Docker Hub上准备了公共映像。
    在任何OS X,Windows或Linux计算机的终端中启动的单个命令,您将获得Vrata的工作实例:

    $ Docker run -d -e GATEWAY_SERVICES=… -e GATEWAY_GLOBAL=… -e GATEWAY_ROUTES=… pwred/vrata

    结语
    该API网关已被部署并在PoweredLocal上使用。整个代码是开源的(MIT许可证),可以在我们的GitHub存储库中找到。竭诚欢迎任何贡献。
    由于聚合查询的结构确实非常类似于GraphQL查询,因此缺点是支持GraphQL查询。

    相关文章

      网友评论

          本文标题:用PHP和Lumen开发一个简单的API网关

          本文链接:https://www.haomeiwen.com/subject/rhmdkktx.html