コード例 #1
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()
コード例 #2
0
ファイル: platform.py プロジェクト: dhvidding/mercury-python
 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:
         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())
     # 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")
コード例 #3
0
ファイル: platform.py プロジェクト: dhvidding/mercury-python
    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)