Пример #1
0
class ControllerRole:
    """Mixin class for enabling `controller` role on service."""
    auto_register_on_discovery = False
    master = None

    @method_decorator(retry(max_retries=0, delay=3, exception_types=(RequestError,),
                            on_exception=_cannot_register_on_master))
    async def register(self):
        kwargs = dict(controller='game_controller', metadata=self.app.metadata)
        await self.internal_request(self.master, 'register_controller', **kwargs)
        logger.info('Registered on master: %s' % self.master)

    @staticmethod
    def setup_internal_api():
        @as_internal()
        async def heartbeat_report(api, **options):
            return {
                'memory': virtual_memory().percent,
                'cpu': cpu_percent()
            }

    def setup(self):
        self.setup_internal_api()
        super().setup()

    async def on_start(self):
        await super().on_start()
        await self.register()
Пример #2
0
class AdminService(PlainService):
    update_services_meta_period = 5
    update_services_meta = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.update_services_meta:
            self.services_meta_updater = PeriodicCallback(
                self.set_services_meta, self.update_services_meta_period * 1000)
        else:
            self.services_meta_updater = None

    def setup(self) -> None:
        self.settings.update(services_meta={})
        self.settings.update(services_all_meta={})
        super().setup()

    async def get_services_metadata(self, exclude_names=None):
        services_metadata = []
        exclude_names = exclude_names or []
        try:
            services_names = await self.discovery_request('get_services_names')
        except RequestTimeoutError:
            pass  # ¯\_(ツ)_/¯
        else:
            for name in services_names:
                if name in exclude_names:
                    continue
                try:
                    metadata = await self.internal_request(name, method='get_service_metadata')
                    services_metadata.append(metadata)
                except RequestTimeoutError:
                    pass  # ¯\_(ツ)_/¯
        return services_metadata

    @method_decorator(retry(max_retries=0, delay=3, exception_types=(RequestError,),
                            on_exception=lambda func, e: logger.error('Cannot get services meta. Retry...'), ))
    async def set_services_meta(self):
        services_meta = await self.get_services_metadata(exclude_names=[self.name])
        services_all_meta = services_meta + [self.app.metadata]
        self.settings.update(services_meta=services_meta)
        self.settings.update(services_all_meta=services_all_meta)

    async def on_start(self) -> None:
        await super().on_start()
        await self.set_services_meta()
        if self.services_meta_updater is not None:
            self.services_meta_updater.start()

    async def on_stop(self) -> None:
        if self.services_meta_updater is not None:
            self.services_meta_updater.stop()
        await super().on_stop()
Пример #3
0
class DiscoveryService(BaseService):
    cleanup_storage_on_start = True
    cleanup_services_period = 5
    ping_services = True
    ping_max_retries = 1
    ping_timeout = 1

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.ping_services:
            self.ping_monitor = PeriodicCallback(
                self.check_services, self.cleanup_services_period * 1000)
        else:
            self.ping_monitor = None
        self.registry = self.app.registry
        self.storage = caches['services']

    async def on_start(self) -> None:
        await super().on_start()
        await self.setup_services(cleanup=self.cleanup_storage_on_start)
        if self.ping_monitor is not None:
            self.ping_monitor.start()

    async def on_stop(self) -> None:
        await super().on_stop()
        if self.ping_monitor is not None:
            self.ping_monitor.stop()

    @method_decorator(retry(max_retries=ping_max_retries, delay=0,
                            exception_types=(RequestTimeoutError, KeyError, TypeError)))
    async def is_service_alive(self, name):
        internal_request = partial(self.internal_request, name)
        try:
            response = await internal_request('ping', timeout=self.ping_timeout)
            return response['message'] == 'pong'
        except Exception as e:
            logger.error('Service `%s` is unreachable. %s' % (name, str(e)))
            raise

    async def check_services(self):
        for name in self.registry.keys():
            if name == self.name:
                # Prevent self pinging
                continue
            if not await self.is_service_alive(name):
                await self.remove_service(name)
            else:
                if name not in await self.list_services():
                    await self.setup_service(name, self.registry[name])

    async def setup_services(self, cleanup=False) -> None:
        if cleanup:
            await self.remove_services()
        self.storage.set_many(self.registry, timeout=None)

    async def remove_services(self) -> None:
        self.storage.delete_many(keys=self.registry.keys())

    async def list_services(self) -> list:
        """Returns a list of services names."""
        return list(self.storage.get_many(keys=self.registry.keys()).keys())

    async def setup_service(self, name: str, networks: dict) -> None:
        self.storage.set(name, networks, timeout=None)

    async def remove_service(self, name: str) -> None:
        self.storage.delete(name)

    async def is_service_exists(self, name: str) -> bool:
        return name in self.storage

    async def get_service(self, name: str, networks: list = None) -> dict:
        if not await self.is_service_exists(name):
            raise ServiceDoesNotExist(name)
        return dict_filter(self.storage.get(name), keys=networks)

    async def get_services(self) -> dict:
        return self.storage.get_many(keys=self.registry.keys())
Пример #4
0
class PlainService(BaseService):
    auto_register_on_discovery = True
    discovery_name = 'discovery'
    message_name = 'message'
    admin_name = 'admin'

    def __init__(self, handlers=None, default_host=None, transforms=None, **kwargs):
        super().__init__(handlers, default_host, transforms, **kwargs)
        self.messenger_client = None

    def setup(self) -> None:
        super().setup()
        self.settings.update(messenger_url=None)
        self.settings.update(registered_services={})
        self.setup_internal_request_methods()

    # noinspection PyAttributeOutsideInit
    def setup_internal_request_methods(self):
        self.discovery_request = partial(self.internal_request, self.discovery_name)
        self.message_request = partial(self.internal_request, self.message_name)
        self.admin_request = partial(self.internal_request, self.admin_name)

    @method_decorator(retry(max_retries=0, delay=3, exception_types=(RequestError,),
                            on_exception=lambda func, e: logger.error('Service `discovery` is unreachable.'), ))
    async def register_on_discovery(self) -> None:
        kwargs = dict(name=self.name, networks=self.app.registry_entry)
        await self.discovery_request('set_service_bulk', **kwargs)
        logger.info('Connected to `discovery` service.')

    async def unregister_on_discovery(self) -> None:
        await self.discovery_request('remove_service', name=self.name)
        logger.info('Disconnected from `discovery` service.')

    async def discover(self, name: str, network: str = None) -> dict:
        return await self.discovery_request('get_service', name=name, network=network)

    @method_decorator(retry(max_retries=0, delay=3, exception_types=(RequestError,),
                            on_exception=lambda func, e: logger.error('Cannot get registered services. Retry...'), ))
    async def set_registered_services(self) -> None:
        registered_services = await self.discovery_request('get_registered_services')
        self.settings.update(registered_services=registered_services)

    @method_decorator(retry(max_retries=0, delay=3, exception_types=(RequestError,),
                            on_exception=lambda func, e: logger.error('Cannot get login url. Retry...'), ))
    async def set_login_url(self) -> None:
        login_url = await self.admin_request('get_login_url')
        self.settings.update(login_url=login_url)
        logger.debug('Login url: %s' % login_url)

    def create_messenger_client(self) -> MessengerClient:
        messenger_url = self.get_service_location(self.message_name, 'internal')
        return MessengerClient(url=messenger_url)

    @method_decorator(retry(max_retries=0, delay=3, exception_types=(ConnectionError,),
                            on_exception=lambda func, e: logger.error('Cannot connect to messenger. Retry...'), ))
    async def messenger_client_connect(self) -> None:
        self.messenger_client = self.create_messenger_client()
        await self.messenger_client.connect()

    def get_service_location(self, service_name, network):
        return self.settings['registered_services'][service_name][network]

    async def on_start(self) -> None:
        await super().on_start()
        if self.auto_register_on_discovery:
            await self.register_on_discovery()
        await self.set_registered_services()
        await self.set_login_url()
        await self.messenger_client_connect()

    async def on_stop(self) -> None:
        self.messenger_client.close()
        if self.auto_register_on_discovery:
            await self.unregister_on_discovery()
        await super().on_stop()