Exemple #1
0
class FutureService(Service):
    """

    """

    __abstract__ = True

    #:
    #:
    future_class = ConfigAttribute(Future)

    #:
    #:
    future_collection_class = ConfigAttribute(FutureCollection)

    @cached
    def futures(self):
        return self.future_collection_class(self.future_class)

    #@running
    def next(self):
        """
        """
        return self.futures.next()

    #@running
    def get(self, future_id, default=None):
        """

        """
        return self.futures.get(future_id, default)
Exemple #2
0
class ChannelService(Service):
    """

    """

    __abstract__ = True

    #:
    #:
    poison_pill = object()

    #:
    #:
    channel_class = ConfigAttribute(Channel)

    #:
    #:
    channel_max_size = ConfigAttribute()

    @cached
    def channel(self):
        return self.channel_class(self.channel_max_size)

    def __init__(self):
        self.channels = []

    def on_initializing(self, *args, **kwargs):
        """
        """
        self.channel = None

    def add_queue(self, queue):
        pass

    def get(self, block=True, timeout=None):
        return self.channel.get(block, timeout)

    def put(self, channel, item, block=True, timeout=None):
        return self.channel.put((channel, item), block, timeout)

    @contextlib.contextmanager
    def select(self):
        pass
Exemple #3
0
class IOLoopService(Service):
    """

    """

    #:
    #:
    ioloop_class = ConfigAttribute(IOLoop)

    #:
    #:
    ioloop_stop_timeout = ConfigAttribute(5)

    #:
    #:
    context_class = ConfigAttribute(zero.Context)

    @cached
    def context(self):
        return self.context_class.instance()

    @cached
    def ioloop(self):
        """
        """
        return self.ioloop_class.current()

    def on_started(self):
        """
        """
        self.worker = self.greenlets.spawn(self.ioloop.run)

    def on_stopping(self):
        """
        """
        self.ioloop.add_callback(self.ioloop.stop)

        self.worker.join(self.ioloop_stop_timeout)
        if self.worker.running():
            self.log.warning('IOLoop worker {0} failed to stop.'.format(
                self.worker))
Exemple #4
0
class PoolWorker(Service):
    """
    """

    # a pool worker has a channel
    # a pool worker spawns a worker and feeds it the channel and execution ctx (settings)
    # a pool worker holds onto the worker instance
    # a pool worker, when told to stop by the pool, tells the remote worker to stop and waits.

    __abstract__ = True

    #:
    #:
    worker = None

    #:
    #:
    worker_class = ConfigAttribute()

    def on_started(self, *args, **kwargs):
        """
        """
        self.worker = self.worker_class.spawn(self.worker_class.run,
                                              pool_worker, *args, **kwargs)
        self.worker.name = self.name

    def on_stopped(self, *args, **kwargs):
        """
        """
        # Normally, a pool worker is stopped when its worker has notified the channel
        # that is is stopping. The pool will receive this message and tell the pool worker to stop.
        # The other case is when the pool tells the pool worker to stop because the pool itself is stopping.
        # As part of the pool stop process, it will notify its channels to stop, which should be picked
        # up by the workers. The pool worker responsibility here is to make sure its worker stops,
        # forcing it if necessary.
        if not self.worker.completed():
            self.log.info(
                'Worker {0} is still running, waiting for it to finish.'.
                format(self.worker))
            timeout = self.stop_timeout
            stopped = self.worker.join(timeout)
            if not stopped:
                self.log.info(
                    'Worker {0} is still running, attempting to forcefully stop it'
                    .format(self.worker))
                stopped = self.worker.stop(timeout=timeout)
                if not stopped:
                    msg = 'Worker {0} is still running, failed to stop it after {1} seconds'
                    self.log.warning(msg.format(self.worker, timeout))
Exemple #5
0
class PoolService(Service):
    """
    """

    __abstract__ = True

    #:
    #:
    queue_flush_timeout = ConfigAttribute(5)

    #:
    #:
    pool_min_size = None

    #:
    #:
    pool_max_size = None

    #:
    #:
    request_runner_class = ConfigAttribute()

    #:
    #:
    response_runner_class = ConfigAttribute()

    #:
    #:
    callback_runner_class = ConfigAttribute()

    #:
    #:
    pool_worker_class = ConfigAttribute()

    #:
    #:
    request_queue_class = ConfigAttribute()

    #:
    #:
    response_queue_class = ConfigAttribute()

    #:
    #:
    callback_queue_class = ConfigAttribute()

    #:
    #:
    futures_class = ConfigAttribute()

    #:
    #:
    requests = DependencyAttribute(config='REQUEST_QUEUE_CLASS')

    #:
    #:
    responses = DependencyAttribute(config='RESPONSE_QUEUE_CLASS')

    #:
    #:
    callbacks = DependencyAttribute(config='CALLBACK_QUEUE_CLASS')

    #:
    #:
    futures = DependencyAttribute(config='FUTURES_CLASS')

    @running
    def spawn(self, func, *args, **kwargs):
        """
        """
        future = self.futures.next()
        request = Request(future.id, func, args, kwargs, self.workers)
        self.requests.put(request)
        return future

    @running
    def spawn_later(self, seconds, func, *args, **kwargs):
        """
        """
        future = self.futures.next()
        request = Request(future.id, func, args, kwargs, self.workers, seconds)
        self.requests.put(request)
        return future

    @running
    def spawn_runner(self, func, *args, **kwargs):
        """
        """
        pass

    @running
    def spawn_runner_later(self, seconds, func, *args, **kwargs):
        """
        """
        pass

    def workers(self):
        """
        """
        return self.services.by_type(self.pool_worker_class)

    def grow(self):
        """
        """
        workers = self.services.by_type(self.pool_worker_class)
        size = len(workers)
        lwm = self.pool_min_size
        hwm = self.pool_max_size

        #if empty or (size < lwm or ):
        if size < lwm or (lwm <= size <= hwm and self.tasks.qsize() > 0):
            # spawn new f****r
            pass


#     def size(self):
#         return self.pool_max_size
#
#     def full(self):
#         return len(self) >= self.size()
#
#     def empty(self):
#         """
#         """
#         return len(self) == 0
#
#     def remove(self, service_id):
#         """
#         """
#         service = self.services.get(service_id)
#         if service is not None:
#             self.detach(service)

    def on_started(self, *args, **kwargs):
        """
        Callback fired when the service and all of its children are started.
        """
        # Start request runner which spawns new workers and populates the task queue.
        self.spawn_runner(self.request_runner, self.services, self.futures,
                          self.requests, self.tasks, self.pool_worker_class)

        # Start response runner which processes completed jobs and populates futures.
        self.spawn_runner(self.response_runner, self.services, self.futures,
                          self.responses, self.callbacks)

        # Start callback runner which processes future callbacks outside of the response processing loop.
        self.spawn_runner(self.callback_runner, self.futures, self.callbacks)

    def on_stopping(self, *args, **kwargs):
        """
        Callback fired when the service first begins to stop.
        """
        msg = 'Stopping with {0} requests, {1} responses and {2} callbacks remaining.'.format(
            self.requests.qsize(), self.responses.qsize(),
            self.callbacks.qsize())
        self.log.debug(msg)

        # Notify queues that the hammer is coming down and to finish up.
        for queue in (self.requests, self.responses, self.callbacks):
            queue.notify_stop()

        # Be nice and allow the queues some time to flush.
        # If they don't finish before the timeout, we continue on with stopping the service. In this
        # case, they'll still have a bit more time to flush as the workers which consume these
        # queues will provide some time to finish before a forceful stop.
        for queue in (self.requests, self.responses, self.callbacks):
            queue.join(self.queue_flush_timeout)

    def on_stopped(self, *args, **kwargs):
        """
        Callback fired when the service and all of its children have stopped.
        """
        timeout = self.queue_flush_timeout

        # Notify if we failed to process all requests before timeout.
        if not self.requests.empty():
            msg = 'Failed to complete processing of {0} requests after {1} seconds.'.format(
                self.requests.qsize(), timeout)
            self.log.warning(msg)

        # Notify if we failed to process all responses before timeout.
        if not self.responses.empty():
            msg = 'Failed to complete processing of {0} responses after {1} seconds.'.format(
                self.responses.qsize(), timeout)
            self.log.warning(msg)

        # Notify if we failed to process all callbacks before timeout.
        if not self.callbacks.empty():
            msg = 'Failed to complete processing of {0} callbacks after {1} seconds.'.format(
                self.callbacks.qsize(), timeout)
            self.log.warning(msg)

    @staticmethod
    def request_runner(service, pool):
        """
        Function executed by runner pool which is responsible for processing incoming async requests,
        updating futures, and scheduling requests on the worker pool.
        """
        service.log.debug('Request runner starting')
        try:
            futures = pool.futures
            requests = pool.requests
            tasks = pool.tasks
            while True:
                try:
                    request = requests.get(block=True)

                    # Got the poison pill, begin shutdown process.
                    if request is requests.poison_pill:
                        if requests.empty():
                            service.log.debug(
                                'Request runner received shutdown command')
                            break
                        # Still have requests to process in the queue, put the poison pill back into the queue
                        # and attempt to finish the remaining requests.
                        requests.notify_stop()
                        continue

                    # Find the future for the request and confirm it's ready to run.
                    future = futures.get(request.future_id)
                    if future is None:
                        service.log.warning(
                            'Request contains unknown future id {0}.'.format(
                                request.future_id))
                        continue

                    # If the future was already cancelled by the user, we should ignore it.
                    # if future.cancelled():
                    #     future.notify_cancel()
                    #     continue

                    pool.grow()

                    future.set_state(ExecutionState.Queued)
                    tasks.put(request.func, request.args, request.kwargs)
                except Exception as e:
                    service.log.info('F**K  {0}'.format(str(e)))
                finally:
                    requests.task_done()
        except Exception:
            service.log.exception('Request runner raised unhandled exception')
        finally:
            service.log.info('Request runner shutting down')

    @staticmethod
    def response_runner(service, services, futures, responses, callbacks):
        """
        Function executed by runner pool which is responsible for processing incoming async responses
        from workers, updating futures and shuffling futures to the callback worker for processing.
        """
        service.log.info('Response runner starting')
        try:
            while True:
                try:
                    response = responses.get(block=True)
                    service.log.error('GOT RESPONSE!@')
                    service.log.error(response)

                    # Got the poison pill, begin shutdown process.
                    if response is responses.poison_pill:
                        if responses.empty():
                            service.log.info(
                                'Response runner received shutdown command')
                            break
                        # Still have responses to process in the queue, put the poison pill back into the queue
                        # and attempt to finish the remaining responses.
                        responses.notify_stop()
                        continue

                    # Sanity
                    if response is None:
                        service.log.warning(
                            'Response runner got an empty response!')
                        continue

                    # A worker response with no future id or state means it didn't process
                    # any tasks and shutdown.
                    future_id = response.future_id
                    if future_id is None:
                        worker = services.get(response.service_id)
                        if worker is None:
                            service.log.warning(
                                'Response contains unknown worker id {0}'.
                                format(response.service_id))
                            continue
                        services.detach(worker)
                        service.log.info('Worker {0} shutdown: {1}.'.format(
                            response.service_id, response.msg))

                    # Grab future tied to this response so we can notify caller.
                    future = futures.get(response.future_id)
                    if future is None:
                        service.log.warning(
                            'Response contains unknown future id {0}.'.format(
                                response.future_id))
                        continue

                    # If the future has callbacks, pass it off to a different runner so we don't block
                    # the response worker if some caller happens to attach a blocking function to the future.
                    # If there aren't any callbacks, just mark the future from the response as it shouldn't block.
                    if future.has_callbacks():
                        callbacks.put(response)
                        continue

                    future.set_from_response(response)
                finally:
                    responses.task_done()
        except Exception:
            service.log.exception('Response runner raised unhandled exception')
        finally:
            service.log.info('Response runner shutting down')

    @staticmethod
    def callback_runner(service, futures, callbacks):
        """
        Function executed by runner pool which is responsible for processing incoming requests
        from the response runner and executing the future updates in a separate context so user callbacks
        are run outside of the request/response loop.
        """
        service.log.info('Callback runner starting')
        try:
            while True:
                try:
                    response = callbacks.get(block=True)

                    # Got the poison pill, begin shutdown process.
                    if response is callbacks.poison_pill:
                        if callbacks.empty():
                            service.log.info(
                                'Callback runner received shutdown command')
                            break
                        # Still have callbacks to process in the queue. Put the poison pill back into the queue
                        # and attempt to finish the remaining callbacks.
                        callbacks.notify_stop()
                        continue

                    # Grab future tied to this response so we can notify the caller
                    # and raises its callbacks.
                    future = futures.get(response.future_id)
                    if future is None:
                        service.log.warning(
                            'Callback contains unknown future id {0}.'.format(
                                response.future_id))
                        continue

                    # Set the future result from the response which will fire the attached callbacks.
                    future.set_from_response(response)
                finally:
                    callbacks.task_done()
        except Exception:
            service.log.exception('Callback runner raised unhandled exception')
        finally:
            service.log.info('Callback runner shutting down')
Exemple #6
0
class QueueService(Service):
    """

    """

    __abstract__ = True

    #:
    #:
    poison_pill = object()

    #:
    #:
    queue_class = ConfigAttribute(Queue)

    #:
    #:
    queue_max_size = ConfigAttribute()

    #:
    #:
    queue_empty_exception = ConfigAttribute()

    #:
    #:
    queue_full_exception = ConfigAttribute()

    @cached
    def queue(self):
        return self.queue_class(maxsize=self.queue_max_size)

    def get(self, block=True, timeout=None):
        """
        """
        return self.queue.get(block, timeout)

    def get_nowait(self):
        """
        """
        try:
            return self.queue.get_nowait()
        except self.queue_empty_exception:
            raise QueueEmpty

    def put(self, obj, block=True, timeout=None):
        """
        """
        return self.queue.put(obj, block, timeout)

    def put_nowait(self, obj):
        """
        """
        try:
            return self.queue.put_nowait(obj)
        except self.queue_full_exception:
            raise QueueFull

    def join(self, timeout=None):
        """
        """
        return self.queue.join(timeout)

    def qsize(self):
        """
        """
        return self.queue.qsize()

    def full(self):
        """
        """
        return self.queue.full()

    def empty(self):
        """
        """
        return self.queue.empty()

    def task_done(self):
        """
        """
        return self.queue.task_done()

    def notify_stop(self):
        """

        """
        return self.queue.put(self.poison_pill)
Exemple #7
0
class ChannelService(Service):
    """

    """

    #:
    #:
    message_handler_class = ConfigAttribute()

    #:
    #:
    stream_class = ConfigAttribute()

    #:
    message_handler = DependencyAttribute(
        type='scatter.protocol.MessageHandler')

    @property
    def streams(self):
        return self.services.by_type(self.stream_class).all()

    # def add_stream(self, stream):
    #     # stream.on_recv(self.message_handler.on_recv_callback)
    #     # stream.on_send(self.message_handler.on_send_callback)
    #     # stream.on_close(self.on_close_callback)
    #     self.attach(stream)

    def connect(self):
        """
        """

    def bind(self):
        pass

    # def connect(self, endpoints):
    #     for endpoint in iterable(endpoints):
    #         pass
    #
    # def bind(self, endpoints):
    #     for endpoint in iterable(endpoints):
    #         pass
    #
    # def send(self, msg):
    #     for s in self.streams:
    #         s.send(msg)
    #
    # def recv(self):
    #     pass

    def on_msg_recv(self, stream, msg):
        """

        :param stream:
        :param msg:
        :return:
        """
        self.log.info('Channel recv message! {0} {1}'.format(stream, msg))

    def on_msg_send(self, stream, msg):
        """

        :param stream:
        :param msg:
        :return:
        """
        self.log.info("Channel sent message!")

    def on_recv_stream(self, stream, msg):
        """

        :param stream:
        :param msg:
        :return:
        """
        self.message_hander.on_recv_callback(stream, msg)

    def on_send_stream(self, stream, msg):
        """

        :param stream:
        :param msg:
        :return:
        """
        self.message_handler.on_send_callback(stream, msg)

    def on_close_callback(self, stream):
        """

        :param stream:
        :return:
        """
        self.log.info('{0} closed'.format(stream))

    def on_initialized(self):
        """

        :return:
        """
#        self.config.setdefault('MESSAGE_HANDLER_CLASS', MessageHandler)
#        self.config.setdefault('STREAM_CLASS', Stream)
#        self.dependencies.

    def on_attached(self, parent):
        pass
Exemple #8
0
class AsyncService(Service):
    """

    """

    #:
    #:
    __abstract__ = True

    #:
    #:
    queue_flush_timeout = ConfigAttribute(5)

    #:
    #:
    request_queue_class = ConfigAttribute(QueueService)

    #:
    #:
    response_queue_class = ConfigAttribute(QueueService)

    #:
    #:
    callback_queue_class = ConfigAttribute(QueueService)

    #:
    #:
    greenlet_pool_class = ConfigAttribute(GreenletPoolService)

    #:
    #:
    thread_pool_class = ConfigAttribute(PoolService)

    #:
    #:
    multiprocess_pool_class = ConfigAttribute(PoolService)

    #:
    #:
    runner_pool_class = ConfigAttribute(PoolService)

    #:
    #:
    futures_class = ConfigAttribute(FutureService)

    # #:
    # #:
    # channel_class = ConfigAttribute(ChannelService)

    #:
    #:
    greenlets = DependencyAttribute(config='GREENLET_POOL_CLASS')

    #:
    #:
    runners = DependencyAttribute(config='RUNNER_POOL_CLASS')

    #:
    #:
    requests = DependencyAttribute(config='REQUEST_QUEUE_CLASS')

    #:
    #:
    responses = DependencyAttribute(config='RESPONSE_QUEUE_CLASS')

    #:
    #:
    callbacks = DependencyAttribute(config='CALLBACK_QUEUE_CLASS')

    #:
    #:
    futures = DependencyAttribute(config='FUTURES_CLASS')

    # #:
    # #:
    # responses_channel = DependencyAttribute(config='CHANNEL_CLASS')

    # ...
    # find all registered worker pool types
    # channel = register all queues for all worker pools created
    # response runner will consume the channel
    # worker pools will produce results and PUT into their re

    @running
    def spawn(self, func, *args, **kwargs):
        """
        """
        return self.spawn_greenlet(func, *args, **kwargs)

    @running
    def spawn_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self.spawn_greenlet_later(seconds, func, *args, **kwargs)

    @running
    def spawn_runner(self, func, *args, **kwargs):
        """
        """
        return self.spawn_greenlet_runner(func, *args, **kwargs)

    @running
    def spawn_runner_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self.spawn_greenlet_runner_later(seconds, func, *args, **kwargs)

    @running
    def spawn_greenlet(self, func, *args, **kwargs):
        """
        """
        return self._spawn(self.greenlets, func, *args, **kwargs)

    @running
    def spawn_greenlet_runner(self, func, *args, **kwargs):
        """
        """
        return self._spawn_runner(self.greenlets, func, *args, **kwargs)

    @running
    def spawn_greenlet_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self._spawn_later(self.greenlets, seconds, func, *args,
                                 **kwargs)

    @running
    def spawn_greenlet_runner_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self._spawn_runner_later(self.greenlets, seconds, func, *args,
                                        **kwargs)

    @running
    def spawn_thread(self, func, *args, **kwargs):
        """
        """
        return self._spawn(self.threads, func, *args, **kwargs)

    @running
    def spawn_thread_runner(self, func, *args, **kwargs):
        """
        """
        return self._spawn_runner(self.threads, func, *args, **kwargs)

    @running
    def spawn_thread_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self._spawn_later(self.threads, seconds, func, *args, **kwargs)

    @running
    def spawn_thread_runner_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self._spawn_runner_later(self.threads, seconds, func, *args,
                                        **kwargs)

    @running
    def spawn_multiprocess(self, func, *args, **kwargs):
        """
        """
        return self._spawn(self.multiprocesses, func, *args, **kwargs)

    @running
    def spawn_multiprocess_runner(self, func, *args, **kwargs):
        """
        """
        return self._spawn_runner(self.multiprocesses, func, *args, **kwargs)

    @running
    def spawn_multiprocess_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self._spawn_later(self.multiprocesses, seconds, func, *args,
                                 **kwargs)

    @running
    def spawn_multiprocess_runner_later(self, seconds, func, *args, **kwargs):
        """
        """
        return self._spawn_runner_later(self.multiprocesses, seconds, func,
                                        *args, **kwargs)

    def _spawn(self, pool, func, *args, **kwargs):
        """
        """
        return pool.spawn(func, *args, **kwargs)
        #return FutureProxy(future)
        # future = self.futures.next()
        # request = Request(future.id, func, args, kwargs, pool)
        # self.requests.put(request)
        # return future

    def _spawn_later(self, pool, seconds, func, *args, **kwargs):
        """
        """
        return pool.spawn_later(seconds, func, *args, **kwargs)
        #return FutureProxy(future)
        # future = self.futures.next()
        # request = Request(future.id, func, args, kwargs, pool, seconds)
        # self.requests.put(request)
        # return future

    def _spawn_runner(self, pool, func, *args, **kwargs):
        """
        """
        return pool.spawn(func, *args, **kwargs)

    def _spawn_runner_later(self, pool, seconds, func, *args, **kwargs):
        """
        """
        return pool.spawn_later(seconds, func, *args, **kwargs)

    # @running
    # def spawn_later(self, seconds, func, *args, **kwargs):
    #     """
    #     """
    #     future = self.futures.next()
    #     request = Request(future.id, func, args, kwargs, seconds)
    #     self.requests.put(request)
    #     return future
    #
    # @running
    # def spawn_runner(self, func, *args, **kwargs):
    #     """
    #     """
    #     future = self.futures.next()
    #     request = Request(future.id, func, args, kwargs)
    #     #job = Job(self, request, self.runners)
    #     #self.runners.spawn(job)
    #     self.requests.put(request)
    #     return future
    #
    # @running
    # def spawn_runner_later(self, seconds, func, *args, **kwargs):
    #     """
    #     """
    #     future = self.futures.next()
    #     request = Request(future.id, func, args, kwargs, seconds)
    #     #job = Job(self, request, self.runners)
    #     self.runners.spawn(job)
    #     return future

    def on_initialized(self, *args, **kwargs):
        """
        """
        pass

    def on_started(self, *args, **kwargs):
        """
        Callback fired when the service and all of its children are started.
        """
        # Start request runner which spawns new workers and populates the task queue.
        self.spawn_runner(self.request_runner, self.futures, self.requests)

        # Start response runner which processes completed jobs and populates futures.
        self.spawn_runner(self.response_runner, self.futures, self.responses,
                          self.callbacks)

        # Start callback runner which processes future callbacks outside of the response processing loop.
        self.spawn_runner(self.callback_runner, self.futures, self.callbacks)

    def on_stopping(self, *args, **kwargs):
        """
        Callback fired when the service first begins to stop.
        """
        msg = 'Stopping with {0} requests, {1} responses and {2} callbacks remaining.'.format(
            self.requests.qsize(), self.responses.qsize(),
            self.callbacks.qsize())
        self.log.info(msg)

        # Notify queues that the hammer is coming down and to finish up.
        for queue in (self.requests, self.responses, self.callbacks):
            queue.notify_stop()

        # Be nice and allow the queues some time to flush.
        # If they don't finish before the timeout, we continue on with stopping the service. In this
        # case, they'll still have a bit more time to flush as the workers which consume these
        # queues will provide some time to finish before a forceful stop.
        for queue in (self.requests, self.responses, self.callbacks):
            queue.join(self.queue_flush_timeout)

    def on_stopped(self, *args, **kwargs):
        """
        Callback fired when the service and all of its children have stopped.
        """
        timeout = self.queue_flush_timeout

        # Notify if we failed to process all requests before timeout.
        if not self.requests.empty():
            msg = 'Failed to complete processing of {0} requests after {1} seconds.'.format(
                self.requests.qsize(), timeout)
            self.log.warning(msg)

        # Notify if we failed to process all responses before timeout.
        if not self.responses.empty():
            msg = 'Failed to complete processing of {0} responses after {1} seconds.'.format(
                self.responses.qsize(), timeout)
            self.log.warning(msg)

        # Notify if we failed to process all callbacks before timeout.
        if not self.callbacks.empty():
            msg = 'Failed to complete processing of {0} callbacks after {1} seconds.'.format(
                self.callbacks.qsize(), timeout)
            self.log.warning(msg)

    @staticmethod
    def request_runner(service, futures, requests):
        """
        Function executed by runner pool which is responsible for processing incoming async requests,
        updating futures, and scheduling requests on the worker pool.
        """
        service.log.info('Request runner starting')
        try:
            while True:
                try:
                    request = requests.get(block=True)

                    # Got the poison pill, begin shutdown process.
                    if request is requests.poison_pill:
                        if requests.empty():
                            service.log.info(
                                'Request runner received shutdown command')
                            break
                        # Still have requests to process in the queue, put the poison pill back into the queue
                        # and attempt to finish the remaining requests.
                        requests.notify_stop()
                        continue

                    # Find the future for the request and confirm it's ready to run.
                    future = futures.get(request.future_id)
                    if future is None:
                        service.log.warning(
                            'Request contains unknown future id {0}.'.format(
                                request.future_id))
                        continue

                    # If the future was already cancelled by the user, we should ignore it.
                    # if future.cancelled():
                    #     future.notify_cancel()
                    #     continue

                    future.set_state(ExecutionState.Running)

                    # Pass work down to specific async pool to run. Each pool manages its own
                    # set of futures, so link the top-level one to be notified when its underlying
                    # work is complete.
                    if request.seconds is None:
                        worker_future = request.pool.spawn(
                            request.func, request.args, request.kwargs)
                    else:
                        worker_future = request.pool.spawn_later(
                            request.seconds, request.func, request.args,
                            request.kwargs)

                    #future.link(worker_future)

                    #workers.spawn(Job(service, request, workers))
                    #import time
                    #time.sleep(0.0001)
                except Exception as e:
                    service.log.info('F**K  {0}'.format(str(e)))
                finally:
                    requests.task_done()
        except Exception:
            service.log.exception('Request runner raised unhandled exception')
        finally:
            service.log.info('Request runner shutting down')

    @staticmethod
    def response_runner(service, futures, responses, callbacks):
        """
        Function executed by runner pool which is responsible for processing incoming async responses
        from workers, updating futures and shuffling futures to the callback worker for processing.
        """
        service.log.info('Response runner starting')
        try:
            while True:
                try:
                    response = responses.get(block=True)
                    service.log.error('GOT RESPONSE!@')
                    service.log.error(response)

                    # Got the poison pill, begin shutdown process.
                    if response is responses.poison_pill:
                        if responses.empty():
                            service.log.info(
                                'Response runner received shutdown command')
                            break
                        # Still have responses to process in the queue, put the poison pill back into the queue
                        # and attempt to finish the remaining responses.
                        responses.notify_stop()
                        continue

                    # Sanity
                    if response is None:
                        service.log.warning(
                            'Response runner got an empty response!')
                        continue

                    # Grab future tied to this response so we can notify caller.
                    future = futures.get(response.future_id)
                    if future is None:
                        service.log.warning(
                            'Response contains unknown future id {0}.'.format(
                                response.future_id))
                        continue

                    # If the future has callbacks, pass it off to a different runner so we don't block
                    # the response worker if some caller happens to attach a blocking function to the future.
                    # If there aren't any callbacks, just mark the future from the response as it shouldn't block.
                    if future.has_callbacks():
                        callbacks.put(response)
                        continue

                    future.set_from_response(response)

                    # # Cleanup worker service which hosted job execution as these are "one and done".
                    # worker = workers.get(response.service_id)
                    # if worker is None:
                    #     service.log.warning('Response contains unknown service id {0}'.format(response.service_id))
                    #     continue
                    # workers.detach(worker)
                finally:
                    responses.task_done()
        except Exception:
            service.log.exception('Response runner raised unhandled exception')
        finally:
            service.log.info('Response runner shutting down')

    @staticmethod
    def callback_runner(service, futures, callbacks):
        """
        Function executed by runner pool which is responsible for processing incoming requests
        from the response runner and executing the future updates in a separate context so user callbacks
        are run outside of the request/response loop.
        """
        service.log.info('Callback runner starting')
        try:
            while True:
                try:
                    response = callbacks.get(block=True)

                    # Got the poison pill, begin shutdown process.
                    if response is callbacks.poison_pill:
                        if callbacks.empty():
                            service.log.info(
                                'Callback runner received shutdown command')
                            break
                        # Still have callbacks to process in the queue. Put the poison pill back into the queue
                        # and attempt to finish the remaining callbacks.
                        callbacks.notify_stop()
                        continue

                    # Grab future tied to this response so we can notify the caller
                    # and raises its callbacks.
                    future = futures.get(response.future_id)
                    if future is None:
                        service.log.warning(
                            'Callback contains unknown future id {0}.'.format(
                                response.future_id))
                        continue

                    # Set the future result from the response which will fire the attached callbacks.
                    future.set_from_response(response)
                finally:
                    callbacks.task_done()
        except Exception:
            service.log.exception('Callback runner raised unhandled exception')
        finally:
            service.log.info('Callback runner shutting down')
Exemple #9
0
class StreamService(Service):
    """

    """

    #:
    #:
    codec_class = ConfigAttribute()

    #:
    #:
    stream_class = ConfigAttribute()

    #:
    #:
    socket_class = ConfigAttribute()

    #:
    #;
    socket_type = ConfigAttribute()

    @classmethod
    def req(cls):
        return cls(config=dict(socket_type=zero.REQ))

    def send(self, msg):
        pass

    def recv(self, msg):
        pass

    def create_stream(self, parent):
        """

        :param parent:
        :return:
        """
#        s = Socket(zero.REQ, parent.context)
#        return Stream(s, parent.ioloop)

    def on_initializing(self):
        """

        :param parent:
        :param conf:
        :return:
        """
        self.config.setdefault('CODEC_CLASS', Codec)
        self.config.setdefault('STREAM_CLASS', Stream)
        self.config.setdefault('SOCKET_CLASS', Socket)
        self.config.setdefault('SOCKET_TYPE', None)

    def on_started(self):
        """

        :return:
        """
        pass
        #self.stream = create_stream(self.socket_type)

    def on_stopping(self):
        """

        :return:
        """
        events = self.stream.flush()
        self.log.info('Closing stream after {0} events flushed'.format(events))
        self.stream.close()

    def on_attached(self, parent):
        """