如今,“微服务”一词已广为人知—突然间,它们变得非常流行,许多公司宣布在未经适当研究的情况下过渡到这种架构模式。无论如何,我们将在本文之外继续讨论模式的实用性。
传统上,微服务集合上有一个额外的层-所谓的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()
{
this->app->make(RouteRegistry::class);
if (registry->bind(app());
}
}
非常简单-我们一一解析路由,然后将它们传递到容器(Lumen应用程序),所有内容都绑定到同一控制器。我们还添加了OAuth2令牌验证中间件和我们的助手中间件。
现在,每条公开的微服务路由在我们的API网关上都有一条对应的路由。此外,还添加了聚合(综合)路由,所有内容都发送到同一控制器。该控制器处理所有GET请求的方式如下:
<?php
class RouteRegistry
{
/**
* @param Application app)
{
route) use (method = strtolower(app->{route->getPath(), [
'uses' => 'App\Http\Controllers\GatewayController@' . route->getId() ]
]);
});
}
}
我们从微服务收集响应,然后使用数组精简器将它们粘合在一起。此外,我们收集参数并在必要时注入它们(例如,第一个微服务可能会提供一个参数作为响应,稍后将与另一个微服务一起使用)。
Guzzle之所以被选为Web客户端是因为它能够很好地处理异步请求,并且包装盒中还提供了一些集成测试功能。
汇总查询
已经支持复杂的聚合查询,这些查询对应于多个微服务请求。例如。“给我,还有那个和一点点。”
聚合路由配置的示例:
<?php
return (static function() {
sections = ['services', 'routes', 'global'];
foreach (section) {
section), false);
section} = config, true) : 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查询。
网友评论