Привет, Хабр!
Nameko, как фреймворк для Python, предлагает удобные абстракции и инструменты для создания микросервисов, ориентированных на сообщения и события.
С Nameko можно получить не только инструмент для создания микросервисов, но и платформу для реализации распределенных систем с поддержкой Dependency Injection, позволяющей легко интегрировать различные компоненты приложения.
Установим через пип:
pip install nameko
Это установит Nameko и все зависимости.
После установки проверим:
nameko --version
Если выдало версию - все ок.
Встроенные расширения
AMQP — это протокол обмена сообщениями, который Nameko использует для межсервисного взаимодействия. Встроенное расширение AMQP позволяет настроить и использовать очереди сообщений для связи между сервисами:
from nameko.messaging import Publisher, rpc
class ServiceA:
name = "service_a"
publisher = Publisher()
@rpc
def send_message(self, message):
self.publisher.publish(message)
class ServiceB:
name = "service_b"
@rpc
def process_message(self, message):
print("Received message:", message)
ServiceA
отправляет сообщение через очередь, а ServiceB
принимает и обрабатывает его.
DependencyProvider позволяет инжектировать зависимости в ваши сервисы, обеспечивая более чистый и модульный код. Создадим сервис, который использует БД:
from nameko.dependency_providers import DependencyProvider
class DatabaseWrapper:
def __init__(self, db_url):
self.db_url = db_url
def execute_query(self, query):
# Execute query logic here
pass
class DatabaseProvider(DependencyProvider):
def setup(self):
self.database = DatabaseWrapper(self.container.config['DATABASE_URL'])
def get_dependency(self, worker_ctx):
return self.database
class MyService:
name = "my_service"
db = DatabaseProvider()
def some_method(self):
result = self.db.execute_query("SELECT * FROM table")
return result
Юзаем DatabaseProvider
для предоставления экземпляра DatabaseWrapper
в сервис.
Встроенное расширение HTTP позволяет создавать веб-сервисы на базе Nameko, обеспечивая взаимодействие с внешними клиентами через HTTP. Например:
from nameko.web.handlers import http
class HttpService:
name = "http_service"
@http('GET', '/hello')
def hello_world(self, request):
return 200, "Hello, World!"
RPC также нашло свою реализацию в nameko, пример:
from nameko.rpc import rpc
class GreetingService:
name = "greeting_service"
@rpc
def greet(self, name):
return f"Hello, {name}!"
GreetingService
предоставляет метод greet
, который может быть вызван удаленно другими сервисами.
Events позволяет сервисам подписываться на события и реагировать на них:
from nameko.events import event_handler
class NotificationService:
name = "notification_service"
@event_handler("email_service", "email_sent")
def handle_email_sent(self, payload):
print("Email sent:", payload)
NotificationService
подписывается на событие email_sent
от сервиса email_service
и обрабатывает его.
Timer позволяет создавать периодические задачи в приложении:
from nameko.timer import timer
class CleanupService:
name = "cleanup_service"
@timer(interval=60) # Выполнять каждую минуту
def cleanup(self):
print("Performing cleanup...")
# Логика очистки данных
Сервис будет выполнять метод cleanup
каждую минуту.
Что там с тестами?
Для написания unit-тестов в Nameko можно использовать стандартные фреймворки для тестирования Python, какpytest
или unittest
. Для этого создаем экземпляр сервиса в тестовой среде и вызываем его методы для проверки их поведения. Предположим, у нас есть простой сервис для работы с мат. операциями:
# math_service.py
from nameko.rpc import rpc
class MathService:
name = "math_service"
@rpc
def add(self, x, y):
return x + y
Напишем unit-тест для этого сервиса с использованием pytest
:
# test_math_service.py
from math_service import MathService
def test_add():
service = MathService()
assert service.add(2, 3) == 5
Интеграционные тесты в Nameko позволяют проверить взаимодействие между различными сервисами и убедиться, что они взаимодействуют правильно в рамках системы. Для этого можно использовать механизм запуска сервисов в тестовом режиме и проверять их взаимодействие через RPC вызовы или события. Пример интеграционного теста с использованием pytest
:
# test_order_service.py
from nameko.testing.services import entrypoint_hook
from order_service import OrderService
from product_service import ProductService
def test_create_order():
# создаем экземпляр сервиса OrderService
order_service = OrderService()
# создаем экземпляр сервиса ProductService
product_service = ProductService()
# входим в контекст сервиса OrderService
with entrypoint_hook(order_service, "create_order") as create_order:
# вызываем метод create_order
order_id = create_order(user_id=1, product_id=1, quantity=2)
# проверяем, что заказ успешно создан
assert order_id is not None
Иногда может потребоваться использовать mock объекты для имитации зависимостей в тестах. В Nameko это можно реализовать с помощью entrypoint
механизма:
# test_email_service.py
from nameko.testing.services import entrypoint_hook
from unittest.mock import Mock
from email_service import EmailService
def test_send_email():
# создаем экземпляр сервиса EmailService
email_service = EmailService()
# создаем mock объект для отправки email
mock_send_email = Mock()
# подменяем реальный метод отправки email на mock объект
email_service.send_email = mock_send_email
# входим в контекст сервиса EmailService
with entrypoint_hook(email_service, "send_email") as send_email:
# вызываем метод send_email
send_email(recipient="test@example.com", subject="Test", body="This is a test email")
# проверяем, что метод send_email был вызван с правильными аргументами
mock_send_email.assert_called_once_with("test@example.com", "Test", "This is a test email")
Написание расширений
Путем написания расширений можнонастраивать поведение Nameko под конкретные потребности.
Допустим, нужно добавить механизм аутентификации к сервисам Nameko. Можно написать расширение, которое будет обрабатывать проверку подлинности запросов к сервисам:
from nameko.extensions import Extension
class AuthExtension(Extension):
def __init__(self, auth_service):
self.auth_service = auth_service
def bind(self, container):
super(AuthExtension, self).bind(container)
self.container.auth = self
def get_dependency(self, worker_ctx):
return AuthServiceWrapper(self.auth_service)
class AuthServiceWrapper:
def __init__(self, auth_service):
self.auth_service = auth_service
def authenticate(self, token):
# логика аутентификации
pass
Или к примеру хотим добавить мониторинг производительности в сервисы Nameko. Можно написать расширение, которое будет собирать статистику о времени выполнения запросов и других метриках:
from nameko.extensions import Extension
class MonitoringExtension(Extension):
def __init__(self, statsd_client):
self.statsd_client = statsd_client
def bind(self, container):
super(MonitoringExtension, self).bind(container)
self.container.monitoring = self
def record_request_time(self, service_name, method_name, time_taken):
metric_name = f"{service_name}.{method_name}.request_time"
self.statsd_client.timing(metric_name, time_taken)
После написания расширения, можно использовать его в своих сервисах. Для этого просто добавляем его в контейнер Nameko:
from nameko.rpc import rpc
from my_extensions import AuthExtension
class MyService:
name = "my_service"
auth = AuthExtension()
@rpc
def my_method(self, request):
# проверка аутентификации
self.auth.authenticate(request.token)
# логика метода
Статья подготовлена в рамках набора на курс Python Developer. Professional. По ссылке вы сможете узнать о курсе подробнее, а также зарегистрироваться на бесплатный вебинар.
winmasta
Подскажите:
1) насколько проект живой ? Последнюю активную работу по коммитам вижу там до 2018 года
2) знаете ли Вы примеры реального использования в проде данного фреймворка или, может быть, сами используете ?