Exemplo n.º 1
0
def main():
    platform = Platform()
    # you can register a method of a class
    platform.register('hello.world.1', Hi().hello, 5)
    # or register a function
    platform.register('hello.world.2', hello, 10)

    po = PostOffice()
    # demonstrate sending asynchronously. Note that key-values in the headers will be encoded as strings
    po.send('hello.world.1', headers={'one': 1}, body='hello world one')
    po.send('hello.world.2', headers={'two': 2}, body='hello world two')

    # demonstrate a RPC request
    try:
        result = po.request('hello.world.2', 2.0, headers={'some_key': 'some_value'}, body='hello world')
        if isinstance(result, EventEnvelope):
            print('Received RPC response:')
            print("HEADERS =", result.get_headers(), ", BODY =", result.get_body(),
                  ", STATUS =",  result.get_status(),
                  ", EXEC =", result.get_exec_time(), ", ROUND TRIP =", result.get_round_trip(), "ms")
    except TimeoutError as e:
        print("Exception: ", str(e))

    # illustrate parallel RPC requests
    event_list = list()
    event_list.append(EventEnvelope().set_to('hello.world.1').set_body("first request"))
    event_list.append(EventEnvelope().set_to('hello.world.2').set_body("second request"))
    try:
        result = po.parallel_request(event_list, 2.0)
        if isinstance(result, list):
            print('Received', len(result), 'RPC responses:')
            for res in result:
                print("HEADERS =", res.get_headers(), ", BODY =", res.get_body(),
                      ", STATUS =",  res.get_status(),
                      ", EXEC =", res.get_exec_time(), ", ROUND TRIP =", res.get_round_trip(), "ms")
    except TimeoutError as e:
        print("Exception: ", str(e))

    # connect to the network
    platform.connect_to_cloud()
    # wait until connected
    while not platform.cloud_ready():
        try:
            time.sleep(0.1)
        except KeyboardInterrupt:
            # this allows us to stop the application while waiting for cloud connection
            platform.stop()
            return

    # Demonstrate broadcast feature
    # the event will be broadcast to multiple application instances that serve the same route
    po.broadcast("hello.world.1", body="this is a broadcast message from "+platform.get_origin())

    # demonstrate deferred delivery
    po.send_later('hello.world.1', headers={'hello': 'world'}, body='this message arrives 5 seconds later', seconds=5.0)

    #
    # this will keep the main thread running in the background
    # so we can use Control-C or KILL signal to stop the application
    platform.run_forever()
Exemplo n.º 2
0
 def send_event(self, event: EventEnvelope, broadcast=False) -> None:
     if not isinstance(event, EventEnvelope):
         raise ValueError("event object must be an EventEnvelope class")
     # restore distributed tracing info from current thread
     trace_info = self.get_trace()
     if trace_info:
         event.set_trace(trace_info.get_id(), trace_info.get_path())
     # regulate rate for best performance
     self._seq += 1
     self._throttle.regulate_rate(self._seq)
     route = event.get_to()
     if broadcast:
         event.set_broadcast(True)
     reply_to = event.get_reply_to()
     if reply_to:
         target = reply_to[2:] if reply_to.startswith('->') else reply_to
         if route == target:
             raise ValueError("route and reply_to must not be the same")
     if route in self._function_queues:
         if event.is_broadcast() and self._cloud.is_connected():
             self._cloud.send_payload({
                 'type': 'event',
                 'event': event.to_map()
             })
         else:
             self._loop.call_soon_threadsafe(self._send, route,
                                             event.to_map())
     else:
         if self._cloud.is_connected():
             self._cloud.send_payload({
                 'type': 'event',
                 'event': event.to_map()
             })
         else:
             raise ValueError("route " + route + " not found")
Exemplo n.º 3
0
 def send_keep_alive(self):
     message = "Keep-Alive " + self.util.get_iso_8601(time.time(),
                                                      show_ms=True)
     envelope = EventEnvelope()
     envelope.set_to(self.OUTGOING_WS_PATH).set_header(
         'type', 'text').set_body(message)
     self.platform.send_event(envelope)
def main():
    # register a function
    platform.register('hello.world', hello, 10)
    platform.register('distributed.tracing', tracing, 10)

    # demonstrate sending asynchronously. Note that key-values in the headers will be encoded as strings
    po.send('hello.world', headers={'one': 1}, body='hello world one')
    po.send('hello.world', headers={'two': 2}, body='hello world two')

    # demonstrate a RPC request
    try:
        trace_id = str(uuid.uuid4()).replace('-', '')
        trace_path = 'GET /api/hello/world'
        event = EventEnvelope().set_to("hello.world").set_header('some_key', 'some_value').set_body('hello world')
        event.set_trace(trace_id, trace_path).set_from('this.demo')
        result = po.single_request(event, 2.0)
        if isinstance(result, EventEnvelope):
            log.info('Received RPC response:')
            log.info(f'HEADERS = {result.get_headers()}, BODY = {result.get_body()}, STATUS = {result.get_status()}, '
                     f'EXEC = {result.get_exec_time()} ms, ROUND TRIP = {result.get_round_trip()} ms')
    except TimeoutError as e:
        log.error(f'Exception: {e}')

    # demonstrate drop-n-forget
    for n in range(20):
        po.send('hello.world', body=f'drop-n-forget message {n}')

    # demonstrate deferred delivery
    po.send_later('hello.world', headers={'hello': 'world'}, body='this message arrives 5 seconds later', seconds=5.0)
    #
    # This will keep the main thread running in the background.
    # We can use Control-C or KILL signal to stop the application.
    platform.run_forever()
Exemplo n.º 5
0
 def send_event_later(self,
                      event: EventEnvelope,
                      seconds: float = 1.0,
                      me=True):
     if event.get_reply_to() is not None:
         event.set_reply_to(event.get_reply_to(), me)
     self.platform.send_event_later(event, seconds)
Exemplo n.º 6
0
 def exists(self, routes: any):
     if isinstance(routes, str):
         single_route = routes
         if self.has_route(single_route):
             return True
         if self.cloud_ready():
             event = EventEnvelope()
             event.set_to(self.SERVICE_QUERY).set_header(
                 'type', 'find').set_header('route', single_route)
             result = self.request(event, 8.0)
             if isinstance(result, EventEnvelope):
                 if result.get_body() is not None:
                     return result.get_body()
     if isinstance(routes, list):
         if len(routes) > 0:
             remote_routes = list()
             for r in routes:
                 if not self.platform.has_route(r):
                     remote_routes.append(r)
             if len(remote_routes) == 0:
                 return True
             if self.platform.cloud_ready():
                 # tell service query to use the route list in body
                 event = EventEnvelope()
                 event.set_to(self.SERVICE_QUERY).set_header('type', 'find')
                 event.set_header('route', '*').set_body(routes)
                 result = self.request(event, 8.0)
                 if isinstance(
                         result,
                         EventEnvelope) and result.get_body() is not None:
                     return result.get_body()
     return False
Exemplo n.º 7
0
def hello(headers: dict, body: any, instance: int):
    # regular function signature (headers: dict, body: any, instance: int)
    Platform().log.info("#"+str(instance)+" "+str(headers)+" body="+str(body))
    # to set status, headers and body, return them in an event envelope
    result = EventEnvelope().set_header('hello', 'world').set_body(body)
    for h in headers:
        result.set_header(h, headers[h])
    return result
Exemplo n.º 8
0
def hello(headers: dict, body: any, instance: int):
    # regular function signature (headers: dict, body: any, instance: int)
    log.info(f'#{instance} {headers} body={body}')
    # to set status, headers and body, return them in an event envelope
    result = EventEnvelope().set_header('hello', 'world').set_body(body)
    for h in headers:
        result.set_header(h, headers[h])
    return result
Exemplo n.º 9
0
 def _life_cycle(self, headers: dict, body: any):
     for subscriber in self._subscription:
         try:
             event = EventEnvelope()
             event.set_to(subscriber).set_headers(headers).set_body(body)
             self.platform.send_event(event)
         except ValueError as e:
             self.log.warn(
                 f'Unable to relay life cycle event {headers} to {subscriber} - {e}'
             )
Exemplo n.º 10
0
 def request(self,
             route: str,
             timeout_seconds: float,
             headers: dict = None,
             body: any = None,
             correlation_id: str = None) -> EventEnvelope:
     self.util.validate_service_name(route, True)
     if headers is None and body is None:
         raise ValueError(
             'Unable to make RPC call because both headers and body are missing'
         )
     timeout_value = self.util.get_float(timeout_seconds)
     if timeout_value <= 0:
         raise ValueError(
             'timeout value in seconds must be positive number')
     event = EventEnvelope().set_to(route)
     if headers is not None:
         if not isinstance(headers, dict):
             raise ValueError('headers must be dict')
         for h in headers:
             event.set_header(h, str(headers[h]))
     if body is not None:
         event.set_body(body)
     if correlation_id is not None:
         event.set_correlation_id(str(correlation_id))
     response = self.platform.request(event, timeout_seconds)
     if isinstance(response, EventEnvelope):
         if response.get_tag('exception') is None:
             return response
         else:
             raise AppException(response.get_status(), response.get_body())
     raise ValueError(
         f'Expect response is EventEnvelope, actual: ({response})')
Exemplo n.º 11
0
 def send_later(self,
                route: str,
                headers: dict = None,
                body: any = None,
                reply_to: str = None,
                me=True,
                seconds: float = 1.0) -> None:
     self.util.validate_service_name(route, True)
     if isinstance(seconds, float) or isinstance(seconds, int):
         if seconds > 0:
             if headers is None and body is None:
                 raise ValueError(
                     'Unable to send because both headers and body are missing'
                 )
             event = EventEnvelope().set_to(route)
             if headers is not None:
                 if not isinstance(headers, dict):
                     raise ValueError('headers must be dict')
                 for h in headers:
                     event.set_header(str(h), str(headers[h]))
             if body is not None:
                 event.set_body(body)
             if reply_to is not None:
                 if not isinstance(reply_to, str):
                     raise ValueError('reply_to must be str')
                 # encode 'me' in the "call back" if replying to this instance
                 event.set_reply_to(reply_to, me)
             self.platform.send_event_later(event, seconds)
         else:
             raise ValueError('delay in seconds must be larger than zero')
     else:
         raise ValueError('delay in seconds must be int or float')
Exemplo n.º 12
0
 def request(self,
             route: str,
             timeout_seconds: float,
             headers: dict = None,
             body: any = None,
             correlation_id: str = None) -> EventEnvelope:
     self.util.validate_service_name(route, True)
     if headers is None and body is None:
         raise ValueError(
             'Unable to make RPC call because both headers and body are missing'
         )
     timeout_value = self.util.get_float(timeout_seconds)
     if timeout_value <= 0:
         raise ValueError(
             "timeout value in seconds must be positive number")
     event = EventEnvelope().set_to(route)
     if headers is not None:
         if not isinstance(headers, dict):
             raise ValueError('headers must be dict')
         for h in headers:
             event.set_header(h, str(headers[h]))
     if body is not None:
         event.set_body(body)
     if correlation_id is not None:
         event.set_correlation_id(str(correlation_id))
     return self.platform.request(event, timeout_seconds)
Exemplo n.º 13
0
 def request(self, event: EventEnvelope, timeout_seconds: float):
     timeout_value = self.util.get_float(timeout_seconds)
     if timeout_value <= 0:
         raise ValueError(
             'timeout value in seconds must be positive number')
     if not isinstance(event, EventEnvelope):
         raise ValueError('event object must be an EventEnvelope')
     # restore distributed tracing info from current thread
     trace_info = self.get_trace()
     if trace_info:
         if trace_info.get_route() is not None and event.get_from() is None:
             event.set_from(trace_info.get_route())
         if trace_info.get_id() is not None and trace_info.get_path(
         ) is not None:
             event.set_trace(trace_info.get_id(), trace_info.get_path())
     # emulate RPC
     inbox = Inbox(self)
     temp_route = inbox.get_route()
     inbox_queue = inbox.get_queue()
     try:
         route = event.get_to()
         event.set_reply_to(temp_route, me=True)
         if route in self._function_queues:
             self._loop.call_soon_threadsafe(self._send, route,
                                             event.to_map())
         else:
             if self._cloud.is_connected():
                 self._cloud.send_payload({
                     'type': 'event',
                     'event': event.to_map()
                 })
             else:
                 raise ValueError(f'route {route} not found')
         # wait until response event is delivered to the inbox
         return inbox_queue.get(True, timeout_value)
     except Empty:
         raise TimeoutError(
             f'Route {event.get_to()} timeout for {round(timeout_value, 3)} seconds'
         )
     finally:
         inbox.close()
Exemplo n.º 14
0
 def logger(self, event: EventEnvelope):
     if isinstance(event, EventEnvelope):
         self.log.info(
             f'trace={event.get_headers()}, annotations={event.get_body()}')
         if self.platform.is_trace_supported():
             # forward to user provided distributed trace logger if any
             current_time = time.time()
             if self._dt_last_check is None or current_time - self._dt_last_check > 5.0:
                 self._dt_last_check = current_time
                 self._dt_found = self.platform.exists(self._dt_processor)
             if self._dt_found:
                 trace_event = EventEnvelope()
                 trace_event.set_to(self._dt_processor).set_body(
                     {'annotations': event.get_body()})
                 for h in event.get_headers():
                     trace_event.set_header(h, event.get_header(h))
                 self.platform.send_event(trace_event)
Exemplo n.º 15
0
 def _normalize_result(result: EventEnvelope, result_obj: any):
     if isinstance(result, EventEnvelope):
         if result.get_status() == 200:
             if isinstance(result.get_body(), type(result_obj)):
                 return result.get_body()
             else:
                 raise AppException(500, str(result.get_body()))
         else:
             raise AppException(result.get_status(), str(result.get_body()))
Exemplo n.º 16
0
 def _get_server_config(self, headers: dict, body: any):
     if 'type' in headers:
         # at this point, login is successful
         if headers['type'] == 'system.config' and isinstance(body, dict):
             if self.MAX_PAYLOAD in body:
                 self.max_ws_payload = body[self.MAX_PAYLOAD]
                 self.log.info(
                     'Automatic segmentation when event payload exceeds ' +
                     format(self.max_ws_payload, ',d'))
             # advertise public routes to language connector
             for r in self.platform.get_routes('public'):
                 self.send_payload({'type': 'add', 'route': r})
             # tell server that I am ready
             self.send_payload({'type': 'ready'})
         # server acknowledges my ready signal
         if headers['type'] == 'ready':
             self.ready = True
             self.log.info('Ready')
             # redo subscription if any
             if self.platform.has_route('pub.sub.sync'):
                 event = EventEnvelope()
                 event.set_to('pub.sub.sync').set_header(
                     'type', 'subscription_sync')
                 self.platform.send_event(event)
Exemplo n.º 17
0
    def _incoming(self, headers: dict, body: any):
        """
        This function handles incoming messages from the websocket connection with the Mercury language connector.
        It must be invoked using events. It should not be called directly to guarantee proper event sequencing.

        :param headers: type is open, close, text or bytes
        :param body: string or bytes
        :return: None
        """
        if self.ws and 'type' in headers:
            if headers['type'] == 'open':
                self.ready = False
                self.log.info("Login to language connector")
                self.send_payload({'type': 'login', 'api_key': self.api_key})
            if headers['type'] == 'close':
                self.ready = False
                self.log.info("Closed")
            if headers['type'] == 'text':
                self.log.debug(body)
            if headers['type'] == 'bytes':
                event = msgpack.unpackb(body, raw=False)
                if 'type' in event:
                    event_type = event['type']
                    if event_type == 'block' and 'block' in event:
                        envelope = EventEnvelope()
                        inner_event = envelope.from_map(event['block'])
                        inner_headers = inner_event.get_headers()
                        if 'id' in inner_headers and 'count' in inner_headers and 'total' in inner_headers:
                            msg_id = inner_headers['id']
                            msg_count = inner_headers['count']
                            msg_total = inner_headers['total']
                            data = inner_event.get_body()
                            if isinstance(data, bytes):
                                buffer = self.cache.get(msg_id)
                                if buffer is None:
                                    buffer = io.BytesIO()
                                buffer.write(data)
                                self.cache.put(msg_id, buffer)
                                if msg_count == msg_total:
                                    buffer.seek(0)
                                    # reconstruct event for processing
                                    event = msgpack.unpackb(buffer.read(),
                                                            raw=False)
                                    event_type = 'event'
                                    self.cache.remove(msg_id)
                    if event_type == 'event' and 'event' in event:
                        envelope = EventEnvelope()
                        inner_event = envelope.from_map(event['event'])
                        if self.platform.has_route(inner_event.get_to()):
                            self.platform.send_event(inner_event)
                        else:
                            self.log.warn('Incoming event dropped because ' +
                                          str(inner_event.get_to()) +
                                          ' not found')
Exemplo n.º 18
0
 def broadcast(self, route: str, headers: dict = None, body: any = None) -> None:
     self.util.validate_service_name(route)
     if headers is None and body is None:
         raise ValueError('Unable to broadcast because both headers and body are missing')
     event = EventEnvelope().set_to(route)
     if headers is not None:
         if not isinstance(headers, dict):
             raise ValueError('headers must be dict')
         for h in headers:
             event.set_header(h, str(headers[h]))
     if body is not None:
         event.set_body(body)
     self.platform.send_event(event, True)
Exemplo n.º 19
0
 def _send_life_cycle_event(self, headers: dict):
     event = EventEnvelope()
     event.set_to(self.CONNECTOR_LIFECYCLE).set_headers(headers)
     self.platform.send_event(event)
Exemplo n.º 20
0
 def listener(self, event: EventEnvelope):
     event.set_round_trip((time.time() - self.begin) * 1000)
     self.inbox_queue.put(event)
Exemplo n.º 21
0
    def handle_event(self, event, instance):
        headers = dict() if 'headers' not in event else event['headers']
        body = None if 'body' not in event else event['body']
        result = None
        error_code = None
        error_msg = None
        # start distributed tracing if the event contains trace_id and trace_path
        if 'trace_id' in event and 'trace_path' in event:
            self.platform.start_tracing(self.route,
                                        trace_id=event['trace_id'],
                                        trace_path=event['trace_path'])
        else:
            self.platform.start_tracing(self.route)
        # execute user function
        begin = end = time.time()
        try:
            if instance == 0:
                # service is an interceptor. e.g. inbox for RPC call
                self.user_function(EventEnvelope().from_map(event))
            elif instance == -1:
                # service is a singleton
                result = self.user_function(headers, body)
            else:
                # service with multiple instances
                result = self.user_function(headers, body, instance)
            end = time.time()
        except AppException as e:
            error_code = e.get_status()
            error_msg = e.get_message()
        except ValueError as e:
            error_code = 400
            error_msg = str(e)
        except Exception as e:
            error_code = 500
            error_msg = str(e)

        # execution time is rounded to 3 decimal points
        exec_time = float(format((end - begin) * 1000, '.3f'))

        if error_code:
            if 'reply_to' in event:
                # set exception as result
                result = EventEnvelope().set_status(error_code).set_body(
                    error_msg)
            else:
                self.log.warn("Unhandled exception for " + self.route +
                              " - code=" + str(error_code) + ", message=" +
                              error_msg)
        #
        # interceptor should not send regular response because it will forward the request to another function.
        # However, if error_code exists, the system will send the exception response.
        # This allows interceptor to simply throw exception to indicate an error case.
        #
        if 'reply_to' in event and (error_code or not self.interceptor):
            reply_to = event['reply_to']
            # in case this is a RPC call from within
            if reply_to.startswith('->'):
                reply_to = reply_to[2:]
            response = EventEnvelope().set_to(reply_to)
            if not error_code:
                response.set_exec_time(exec_time, False)
            if 'extra' in event:
                response.set_extra(event['extra'])
            if 'cid' in event:
                response.set_correlation_id(event['cid'])
            if 'trace_id' in event and 'trace_path' in event:
                response.set_trace(event['trace_id'], event['trace_path'])
            if isinstance(result, EventEnvelope):
                for h in result.get_headers():
                    response.set_header(h, result.get_header(h))
                response.set_body(result.get_body())
                response.set_status(result.get_status())
            else:
                response.set_body(result)

            try:
                self.platform.send_event(response.set_from(self.route))
            except Exception as e:
                self.log.warn("Event dropped because " + str(e))

        # send tracing info to distributed trace logger
        trace_info = self.platform.stop_tracing()
        if self.tracing and trace_info is not None and isinstance(trace_info, TraceInfo) \
                and trace_info.get_id() is not None and trace_info.get_path() is not None:
            dt = EventEnvelope().set_to(self.DISTRIBUTED_TRACING).set_body(
                trace_info.get_annotations())
            dt.set_header('origin', self.platform.get_origin())
            dt.set_header('id', trace_info.get_id()).set_header(
                'path', trace_info.get_path())
            dt.set_header('service',
                          self.route).set_header('start',
                                                 trace_info.get_start_time())
            if not error_code:
                dt.set_header('success', 'true')
                dt.set_header('exec_time', exec_time)
            else:
                dt.set_header('success', 'false')
                dt.set_header('status', error_code)
                dt.set_header('exception', error_msg)
            self.platform.send_event(dt)

        self._loop.call_soon_threadsafe(self._ack)
Exemplo n.º 22
0
    def _incoming(self, headers: dict, body: any):
        """
        This function handles incoming messages from the websocket connection with the Mercury language connector.
        It must be invoked using events. It should not be called directly to guarantee proper event sequencing.

        Args:
            headers: type is open, close, text or bytes
            body: string or bytes

        Returns: None

        """
        if self.ws and 'type' in headers:
            if headers['type'] == 'open':
                self.ready = False
                self.log.info("Login to language connector")
                self.send_payload({'type': 'login', 'api_key': self.api_key})
                self._send_life_cycle_event(headers)
            if headers['type'] == 'close':
                self.ready = False
                self.log.info("Closed")
                self._send_life_cycle_event(headers)
            if headers['type'] == 'text':
                self.log.debug(body)
            if headers['type'] == 'bytes':
                event = msgpack.unpackb(body, raw=False)
                if 'type' in event:
                    event_type = event['type']
                    if event_type == 'block' and 'block' in event:
                        envelope = EventEnvelope()
                        inner_event = envelope.from_map(event['block'])
                        inner_headers = inner_event.get_headers()
                        if self.MSG_ID in inner_headers and self.COUNT in inner_headers and self.TOTAL in inner_headers:
                            msg_id = inner_headers[self.MSG_ID]
                            msg_count = inner_headers[self.COUNT]
                            msg_total = inner_headers[self.TOTAL]
                            data = inner_event.get_body()
                            if isinstance(data, bytes):
                                buffer = self.cache.get(msg_id)
                                if buffer is None:
                                    buffer = io.BytesIO()
                                buffer.write(data)
                                if msg_count == msg_total:
                                    self.cache.remove(msg_id)
                                    # reconstruct event for processing
                                    buffer.seek(0)
                                    envelope = EventEnvelope()
                                    unpacked = msgpack.unpackb(buffer.read(),
                                                               raw=False)
                                    restored = envelope.from_map(unpacked)
                                    target = restored.get_to()
                                    if self.platform.has_route(target):
                                        self.platform.send_event(restored)
                                    else:
                                        self.log.warn(
                                            f'Incoming event dropped because {target} not found'
                                        )
                                else:
                                    self.cache.put(msg_id, buffer)
                    if event_type == 'event' and 'event' in event:
                        unpacked = msgpack.unpackb(event['event'], raw=False)
                        envelope = EventEnvelope()
                        inner_event = envelope.from_map(unpacked)
                        if self.platform.has_route(inner_event.get_to()):
                            self.platform.send_event(inner_event)
                        else:
                            self.log.warn(
                                f'Incoming event dropped because {inner_event.get_to()} not found'
                            )
Exemplo n.º 23
0
 def hello(self, headers: dict, body: any):
     # singleton function signature (headers: dict, body: any)
     log.info(f'{self.MY_NAME} {headers}, {body}')
     return EventEnvelope().set_body({'body': body, 'origin': platform.get_origin()}) \
         .set_header('x-notes', 'I am a python app')
Exemplo n.º 24
0
def main():
    # you can register a method of a class
    platform.register('hello.world.1', Hi().hello, 5)
    # or register a function
    platform.register('hello.world.2', hello, 10)
    # you can also create an alias for the same service
    platform.register('hello.world', Hi().hello, 5)

    # demonstrate sending asynchronously. Note that key-values in the headers will be encoded as strings
    po.send('hello.world.1', headers={'one': 1}, body='hello world one')
    po.send('hello.world.2', headers={'two': 2}, body='hello world two')

    # demonstrate a RPC request
    try:
        result = po.request('hello.world.2',
                            2.0,
                            headers={'some_key': 'some_value'},
                            body='hello world')
        if isinstance(result, EventEnvelope):
            log.info('Received RPC response:')
            log.info(
                f'HEADERS = {result.get_headers()}, BODY = {result.get_body()}, STATUS = {result.get_status()}, '
                f'EXEC = {result.get_exec_time()} ms, ROUND TRIP = {result.get_round_trip()} ms'
            )
    except TimeoutError as e:
        log.error(f'Exception: {e}')

    # illustrate parallel RPC requests
    event_list = list()
    event_list.append(
        EventEnvelope().set_to('hello.world.1').set_body("first request"))
    event_list.append(
        EventEnvelope().set_to('hello.world.2').set_body("second request"))
    try:
        result = po.parallel_request(event_list, 2.0)
        if isinstance(result, list):
            log.info(f'Received {len(result)} parallel RPC responses:')
            for res in result:
                log.info(
                    f'HEADERS = {res.get_headers()}, BODY = {res.get_body()}, STATUS = {res.get_status()}, '
                    f'EXEC = {res.get_exec_time()} ms, ROUND TRIP = {res.get_round_trip()} ms'
                )
    except TimeoutError as e:
        log.error(f'Exception: {e}')

    # demonstrate deferred delivery
    po.send_later('hello.world.1',
                  headers={'hello': 'world'},
                  body='this message arrives 5 seconds later',
                  seconds=5.0)

    def life_cycle_listener(headers: dict, body: any):
        # Detect when cloud is ready
        log.info(f'Cloud life cycle event - {headers}')
        if 'type' in headers and 'ready' == headers['type']:
            # Demonstrate broadcast feature:
            # To test this feature, please run multiple instances of this demo
            po.broadcast("hello.world",
                         body="this is a broadcast message from " +
                         platform.get_origin())

    platform.register('my.cloud.status', life_cycle_listener, is_private=True)
    platform.subscribe_life_cycle('my.cloud.status')

    # Connect to the network
    platform.connect_to_cloud()
    #
    # This will keep the main thread running in the background.
    # We can use Control-C or KILL signal to stop the application.
    platform.run_forever()
Exemplo n.º 25
0
 def send_event(self, event: EventEnvelope, me=True):
     if event.get_reply_to() is not None:
         event.set_reply_to(event.get_reply_to(), me)
     self.platform.send_event(event)
Exemplo n.º 26
0
 def listener(self, event: EventEnvelope):
     diff = time.perf_counter() - self.start_time
     event.set_round_trip(diff * 1000)
     self.inbox_queue.put(event)
Exemplo n.º 27
0
    async def connection_handler(self, url):
        try:
            async with aiohttp.ClientSession(
                    loop=self._loop,
                    timeout=aiohttp.ClientTimeout(total=10)) as session:
                full_path = url + '/' + self.origin
                self.ws = await session.ws_connect(full_path)
                envelope = EventEnvelope()
                envelope.set_to(self.INCOMING_WS_PATH).set_header(
                    'type', 'open')
                self.platform.send_event(envelope)
                self.log.info("Connected to " + full_path)
                closed = False
                self.last_active = time.time()

                while self.normal:
                    try:
                        msg = await self.ws.receive(timeout=1)
                    except asyncio.TimeoutError:
                        if not self.normal:
                            break
                        else:
                            # idle - send keep-alive
                            now = time.time()
                            if self.is_connected(
                            ) and now - self.last_active > 30:
                                self.last_active = now
                                self.send_keep_alive()
                            continue

                    # receive incoming event
                    self.last_active = time.time()
                    if msg.type == aiohttp.WSMsgType.TEXT:
                        if self.platform.has_route(self.INCOMING_WS_PATH):
                            envelope = EventEnvelope()
                            envelope.set_to(self.INCOMING_WS_PATH).set_header(
                                'type', 'text').set_body(msg.data)
                            self.platform.send_event(envelope)
                        else:
                            break
                    elif msg.type == aiohttp.WSMsgType.BINARY:
                        if self.platform.has_route(self.INCOMING_WS_PATH):
                            envelope = EventEnvelope()
                            envelope.set_to(self.INCOMING_WS_PATH).set_header(
                                'type', 'bytes').set_body(msg.data)
                            self.platform.send_event(envelope)
                        else:
                            break
                    else:
                        if msg.type == aiohttp.WSMsgType.ERROR:
                            self.log.error("Unexpected connection error")
                        if msg.type == aiohttp.WSMsgType.CLOSING:
                            # closing signal received - close the connection now
                            self.log.info("Disconnected, status=" +
                                          str(self.close_code) + ", message=" +
                                          self.close_message)
                            await self.ws.close(code=self.close_code,
                                                message=bytes(
                                                    self.close_message,
                                                    'utf-8'))
                            if self.platform.has_route(self.INCOMING_WS_PATH):
                                envelope = EventEnvelope()
                                envelope.set_to(self.INCOMING_WS_PATH).set_body(self.close_message)\
                                        .set_header('type', 'close').set_header('status', self.close_code)
                                self.platform.send_event(envelope)
                            closed = True
                        if msg.type == aiohttp.WSMsgType.CLOSE or msg.type == aiohttp.WSMsgType.CLOSED:
                            self.close_code = 1001 if msg.data is None else msg.data
                            self.close_message = 'OK' if msg.extra is None else str(
                                msg.extra)
                            self.log.info("Disconnected, status=" +
                                          str(self.close_code) + ", message=" +
                                          self.close_message)
                            if self.platform.has_route(self.INCOMING_WS_PATH):
                                envelope = EventEnvelope()
                                envelope.set_to(self.INCOMING_WS_PATH).set_body(self.close_message)\
                                        .set_header('type', 'close').set_header('status', self.close_code)
                                self.platform.send_event(envelope)
                            closed = True
                        break
                if not closed:
                    await self.ws.close(code=1000, message=b'OK')
                    self.ws = None
                    if self.platform.has_route(self.INCOMING_WS_PATH):
                        envelope = EventEnvelope()
                        envelope.set_to(self.INCOMING_WS_PATH).set_body('OK')\
                            .set_header('type', 'close').set_header('status', 1000)
                        self.platform.send_event(envelope)

        except aiohttp.ClientConnectorError:
            self._skip_url()
            self.log.warn("Unreachable " + url)
Exemplo n.º 28
0
 def send_payload(self, data: dict):
     payload = msgpack.packb(data, use_bin_type=True)
     payload_len = len(payload)
     if 'type' in data and data[
             'type'] == 'event' and 'event' in data and payload_len > self.max_ws_payload:
         evt = data['event']
         if 'id' in evt:
             msg_id = evt['id']
             total = int(payload_len / self.max_ws_payload)
             if payload_len > total:
                 total += 1
             buffer = io.BytesIO(payload)
             count = 0
             for i in range(total):
                 count += 1
                 block = EventEnvelope()
                 block.set_header('id', msg_id)
                 block.set_header('count', count)
                 block.set_header('total', total)
                 block.set_body(buffer.read(self.max_ws_payload))
                 block_map = dict()
                 block_map['type'] = 'block'
                 block_map['block'] = block.to_map()
                 block_payload = msgpack.packb(block_map, use_bin_type=True)
                 envelope = EventEnvelope()
                 envelope.set_to(self.OUTGOING_WS_PATH).set_header(
                     'type', 'bytes').set_body(block_payload)
                 self.platform.send_event(envelope)
     else:
         envelope = EventEnvelope()
         envelope.set_to(self.OUTGOING_WS_PATH).set_header(
             'type', 'bytes').set_body(payload)
         self.platform.send_event(envelope)
Exemplo n.º 29
0
 def logger(self, event: EventEnvelope):
     if isinstance(event, EventEnvelope):
         self.log.info('trace=' + str(event.get_headers()) +
                       ', annotations=' + str(event.get_body()))
         # forward to user provided distributed trace logger if any
         current_time = time.time()
         if self._dt_last_check is None or current_time - self._dt_last_check > 5.0:
             self._dt_last_check = current_time
             self._dt_found = self.platform.exists(self._dt_processor)
         if self._dt_found:
             te = EventEnvelope()
             te.set_to(self._dt_processor).set_body(event.get_body())
             for h in event.get_headers():
                 te.set_header(h, event.get_header(h))
             self.platform.send_event(te)