Сначала давайте разберемся, что такое бессерверная архитектура и когда она нужна.
Бессерверная архитектура позволяет выполнять фрагменты кода без мороки с инфраструктурой: в этом случае управлением веб-сервером, физическим оборудованием и администрированием занимается облачный провайдер, позволяя вам сосредоточиться исключительно на коде.
AWS Lambda обеспечивает высокую доступность, причем плата взимается только за фактически затрачиваемое время вычислений. Этот сервис может быть весьма полезен для таких задач, как запуск cron-заданий, отправка уведомлений в режиме реального времени, предоставление доступа к API, обработка каких-нибудь событий при выполнении различных операций и т. д. В сети можно найти массу примеров использования сервиса.
В Symfony нет встроенной поддержки LinkedIn, поэтому несколько месяцев назад я создал поверх Notifier шлюз для публикации контента в этой социальной сети. Исходный код шлюза можно посмотреть по ссылке, его же мы будем использовать и в этой демонстрации.
Приступаем
$ symfony new --full aws-lambda-linkedin-notifier
$ cd aws-lambda-linkedin-notifier
$ composer require eniams/linkedin-notifier
Включаем шлюз (см. документацию)
<?php
// config/bundles.php
return [
// others bundles,
Eniams\Notifier\LinkedIn\LinkedInNotifierBundle::class => ['all' => true]];
// .env
LINKEDIN_DSN=
Логика публикации контента
<?php
class PostContentController
{
/**
* @Route("/contents", name="post_content", methods="POST")
*/
public function __invoke(NotifierInterface $notifier, Request $request)
{
if(null !== $message = (\json_decode($request->getContent(), true)['message'] ?? null)) {
$notifier->send(new Notification($message, ['chat/linkedin']));
return new JsonResponse('message posted with success', 201);
}
throw new BadRequestException('Missing "message" in body');
}
}
Логика проста: мы предоставляем доступ к API через маршрут /contents, который принимает запрос POST с сообщением message в его теле.
В 11-й строке мы отправляем публикуемое сообщение в LinkedIn — благодаря Symfony и шлюзу это делается очень просто!
Будучи профессиональными разработчиками, покроем этот код автотестами:
<?php
class PostContentControllerTest extends WebTestCase
/**
* @dataProvider methodProvider
*/
public function testANoPostRequestShouldReturnA405(string $method)
{
$client = static::createClient();
$client->request($method, '/contents');
self::assertEquals(405, $client->getResponse()->getStatusCode());
}
public function testAPostRequestWithoutAMessageInBodyShouldReturnA400()
{
$client = static::createClient();
$client->request('POST', '/contents');
self::assertEquals(400, $client->getResponse()->getStatusCode());
}
public function testAPostRequestWithAMessageInBodyShouldReturnA201()
{
$request = new Request([],[],[],[],[],[], json_encode(['message' => 'Hello World']));
$notifier = new class implements NotifierInterface {
public function send(Notification $notification, Recipient ...$recipients): void
{
}
};
$controller = new PostContentController();
$response = $controller->__invoke($notifier, $request);
self::assertEquals(201, $response->getStatusCode());
}
public function methodProvider()
{
return [
['GET'],
['PUT'],
['DELETE'],
];
}
view rawTestPostContentController.php hosted with by GitHub
Именно так: AWS Lambda поддерживает не все языки программирования, а только некоторые, в том числе Go, Java, Python, Ruby, NodeJS и .NET.
Теперь надо учить новый язык и переписывать код? Надеюсь, нет!
Ничего переписывать не придется благодаря Матье Напполи (Matthieu Nappoli), создателю Bref.sh, и замечательной команде, помогающей ему поддерживать этот проект с открытым исходным кодом.
Bref позволяет развертывать PHP-приложения в AWS и запускать их на AWS Lambda.
Конфигурация развертывания
Добавим в kernel.php обработку логов:
<?php
// Kernel.php
public function getLogDir(): string
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/log/';
}
return parent::getLogDir();
}
public function getCacheDir()
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/cache/'.$this->environment;
}
return parent::getCacheDir();
}
Подготовим фреймворк Serverless (см. документацию):
$ npm install -g serverless
$ serverless config credentials --provider aws --key --secret
$ composer require bref/bref
Зададим конфигурацию в файле serverless.yaml:
service: notifier-linkedin-api
provider:
name: aws
region: eu-west-3
runtime: provided
environment: # env vars
APP_ENV: prod
LINKEDIN_DSN: YOUR_DSN
plugins:
- ./vendor/bref/bref
functions:
website:
handler: public/index.php # bootstrap
layers:
- ${bref:layer.php-73-fpm} # https://bref.sh/docs/runtimes/index.html#usage
timeout: 28 # Timeout to stop the compute time
events:
- http: 'POST /contents' # Only POST to /contents are allowed
package:
exclude:
- 'tests/**'
view rawserverless.yaml hosted with by GitHub
Развертываем!
$ serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service notifier-linkedin-api.zip file to S3 (10.05 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
....................
Serverless: Stack update finished...
Service Information
service: notifier-linkedin-api
stage: dev
region: eu-west-3
stack: notifier-linkedin-api-dev
resources: 15
api keys:
None
endpoints:
POST - https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents
functions:
website: notifier-linkedin-api-dev-website
layers:
None
Serverless: Removing old service artifacts from S3...
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
Теперь наш код развернут в AWS Lambda, а API доступен по адресу https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents.
Попробуем:
Источник статьи: https://habr.com/ru/company/otus/blog/562392/
Бессерверная архитектура позволяет выполнять фрагменты кода без мороки с инфраструктурой: в этом случае управлением веб-сервером, физическим оборудованием и администрированием занимается облачный провайдер, позволяя вам сосредоточиться исключительно на коде.
AWS Lambda обеспечивает высокую доступность, причем плата взимается только за фактически затрачиваемое время вычислений. Этот сервис может быть весьма полезен для таких задач, как запуск cron-заданий, отправка уведомлений в режиме реального времени, предоставление доступа к API, обработка каких-нибудь событий при выполнении различных операций и т. д. В сети можно найти массу примеров использования сервиса.
Наш сценарий использования
Предоставить доступ к API, созданному с помощью Symfony, который публикует сообщения в LinkedIn. Процесс разработки будет включать этапы от написания до развертывания кода.Пишем код
В Symfony 5-й версии появился новый компонент под названием Notifier, который дает возможность отправлять уведомления через разные сервисы (Slack, Twitter, Twilio и др.).В Symfony нет встроенной поддержки LinkedIn, поэтому несколько месяцев назад я создал поверх Notifier шлюз для публикации контента в этой социальной сети. Исходный код шлюза можно посмотреть по ссылке, его же мы будем использовать и в этой демонстрации.
Приступаем
$ symfony new --full aws-lambda-linkedin-notifier
$ cd aws-lambda-linkedin-notifier
$ composer require eniams/linkedin-notifier
Включаем шлюз (см. документацию)
<?php
// config/bundles.php
return [
// others bundles,
Eniams\Notifier\LinkedIn\LinkedInNotifierBundle::class => ['all' => true]];
// .env
LINKEDIN_DSN=
Логика публикации контента
<?php
class PostContentController
{
/**
* @Route("/contents", name="post_content", methods="POST")
*/
public function __invoke(NotifierInterface $notifier, Request $request)
{
if(null !== $message = (\json_decode($request->getContent(), true)['message'] ?? null)) {
$notifier->send(new Notification($message, ['chat/linkedin']));
return new JsonResponse('message posted with success', 201);
}
throw new BadRequestException('Missing "message" in body');
}
}
Логика проста: мы предоставляем доступ к API через маршрут /contents, который принимает запрос POST с сообщением message в его теле.
В 11-й строке мы отправляем публикуемое сообщение в LinkedIn — благодаря Symfony и шлюзу это делается очень просто!
Будучи профессиональными разработчиками, покроем этот код автотестами:
<?php
class PostContentControllerTest extends WebTestCase
/**
* @dataProvider methodProvider
*/
public function testANoPostRequestShouldReturnA405(string $method)
{
$client = static::createClient();
$client->request($method, '/contents');
self::assertEquals(405, $client->getResponse()->getStatusCode());
}
public function testAPostRequestWithoutAMessageInBodyShouldReturnA400()
{
$client = static::createClient();
$client->request('POST', '/contents');
self::assertEquals(400, $client->getResponse()->getStatusCode());
}
public function testAPostRequestWithAMessageInBodyShouldReturnA201()
{
$request = new Request([],[],[],[],[],[], json_encode(['message' => 'Hello World']));
$notifier = new class implements NotifierInterface {
public function send(Notification $notification, Recipient ...$recipients): void
{
}
};
$controller = new PostContentController();
$response = $controller->__invoke($notifier, $request);
self::assertEquals(201, $response->getStatusCode());
}
public function methodProvider()
{
return [
['GET'],
['PUT'],
['DELETE'],
];
}
view rawTestPostContentController.php hosted with by GitHub
Код готов! Пришло время его развернуть
Стоп! Что?! AWS Lambda не поддерживает PHP!Именно так: AWS Lambda поддерживает не все языки программирования, а только некоторые, в том числе Go, Java, Python, Ruby, NodeJS и .NET.
Теперь надо учить новый язык и переписывать код? Надеюсь, нет!
Ничего переписывать не придется благодаря Матье Напполи (Matthieu Nappoli), создателю Bref.sh, и замечательной команде, помогающей ему поддерживать этот проект с открытым исходным кодом.
Bref позволяет развертывать PHP-приложения в AWS и запускать их на AWS Lambda.
Конфигурация развертывания
Добавим в kernel.php обработку логов:
<?php
// Kernel.php
public function getLogDir(): string
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/log/';
}
return parent::getLogDir();
}
public function getCacheDir()
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/cache/'.$this->environment;
}
return parent::getCacheDir();
}
Подготовим фреймворк Serverless (см. документацию):
$ npm install -g serverless
$ serverless config credentials --provider aws --key --secret
$ composer require bref/bref
Зададим конфигурацию в файле serverless.yaml:
service: notifier-linkedin-api
provider:
name: aws
region: eu-west-3
runtime: provided
environment: # env vars
APP_ENV: prod
LINKEDIN_DSN: YOUR_DSN
plugins:
- ./vendor/bref/bref
functions:
website:
handler: public/index.php # bootstrap
layers:
- ${bref:layer.php-73-fpm} # https://bref.sh/docs/runtimes/index.html#usage
timeout: 28 # Timeout to stop the compute time
events:
- http: 'POST /contents' # Only POST to /contents are allowed
package:
exclude:
- 'tests/**'
view rawserverless.yaml hosted with by GitHub
Развертываем!
$ serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service notifier-linkedin-api.zip file to S3 (10.05 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
....................
Serverless: Stack update finished...
Service Information
service: notifier-linkedin-api
stage: dev
region: eu-west-3
stack: notifier-linkedin-api-dev
resources: 15
api keys:
None
endpoints:
POST - https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents
functions:
website: notifier-linkedin-api-dev-website
layers:
None
Serverless: Removing old service artifacts from S3...
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
Теперь наш код развернут в AWS Lambda, а API доступен по адресу https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents.
Попробуем:
Источник статьи: https://habr.com/ru/company/otus/blog/562392/