コード例 #1
0
    def _maybe_connect(self, node_id):
        """Idempotent non-blocking connection attempt to the given node id."""
        with self._lock:
            conn = self._conns.get(node_id)

            if conn is None:
                broker = self.cluster.broker_metadata(node_id)
                assert broker, 'Broker id %s not in current metadata' % (node_id,)

                log.debug("Initiating connection to node %s at %s:%s",
                          node_id, broker.host, broker.port)
                host, port, afi = get_ip_port_afi(broker.host)
                cb = functools.partial(WeakMethod(self._conn_state_change), node_id)
                conn = BrokerConnection(host, broker.port, afi,
                                        state_change_callback=cb,
                                        node_id=node_id,
                                        **self.config)
                self._conns[node_id] = conn

            # Check if existing connection should be recreated because host/port changed
            elif self._should_recycle_connection(conn):
                self._conns.pop(node_id)
                return False

            elif conn.connected():
                return True

            conn.connect()
            return conn.connected()
コード例 #2
0
def test_init(conn):
    cli = KafkaClient()
    coordinator = ConsumerCoordinator(cli, SubscriptionState())

    # metadata update on init 
    assert cli.cluster._need_update is True
    assert WeakMethod(coordinator._handle_metadata_update) in cli.cluster._listeners
コード例 #3
0
    def _bootstrap(self, hosts):
        log.info('Bootstrapping cluster metadata from %s', hosts)
        # Exponential backoff if bootstrap fails
        backoff_ms = self.config[
            'reconnect_backoff_ms'] * 2**self._bootstrap_fails
        next_at = self._last_bootstrap + backoff_ms / 1000.0
        self._refresh_on_disconnects = False
        now = time.time()
        if next_at > now:
            log.debug("Sleeping %0.4f before bootstrapping again",
                      next_at - now)
            time.sleep(next_at - now)
        self._last_bootstrap = time.time()

        if self.config['api_version'] is None or self.config['api_version'] < (
                0, 10):
            metadata_request = MetadataRequest[0]([])
        else:
            metadata_request = MetadataRequest[1](None)

        for host, port, afi in hosts:
            log.debug("Attempting to bootstrap via node at %s:%s", host, port)
            cb = functools.partial(WeakMethod(self._conn_state_change),
                                   'bootstrap')
            bootstrap = BrokerConnection(host,
                                         port,
                                         afi,
                                         state_change_callback=cb,
                                         node_id='bootstrap',
                                         **self.config)
            if not bootstrap.connect_blocking():
                bootstrap.close()
                continue
            future = bootstrap.send(metadata_request)
            while not future.is_done:
                self._selector.select(1)
                for r, f in bootstrap.recv():
                    f.success(r)
            if future.failed():
                bootstrap.close()
                continue
            self.cluster.update_metadata(future.value)
            log.info('Bootstrap succeeded: found %d brokers and %d topics.',
                     len(self.cluster.brokers()), len(self.cluster.topics()))

            # A cluster with no topics can return no broker metadata
            # in that case, we should keep the bootstrap connection
            if not len(self.cluster.brokers()):
                self._conns['bootstrap'] = bootstrap
            else:
                bootstrap.close()
            self._bootstrap_fails = 0
            break
        # No bootstrap found...
        else:
            log.error('Unable to bootstrap from %s', hosts)
            # Max exponential backoff is 2^12, x4000 (50ms -> 200s)
            self._bootstrap_fails = min(self._bootstrap_fails + 1, 12)
        self._refresh_on_disconnects = True
コード例 #4
0
    def __init__(self, *args, **kwargs):
        if len(args) == len(self.SCHEMA.fields):
            for i, name in enumerate(self.SCHEMA.names):
                self.__dict__[name] = args[i]
        elif len(args) > 0:
            raise ValueError('Args must be empty or mirror schema')
        else:
            for name in self.SCHEMA.names:
                self.__dict__[name] = kwargs.pop(name, None)
            if kwargs:
                raise ValueError('Keyword(s) not in schema %s: %s'
                                 % (list(self.SCHEMA.names),
                                    ', '.join(kwargs.keys())))

        # overloading encode() to support both class and instance
        # Without WeakMethod() this creates circular ref, which
        # causes instances to "leak" to garbage
        self.encode = WeakMethod(self._encode_self)
コード例 #5
0
    def _maybe_connect(self, node_id):
        """Idempotent non-blocking connection attempt to the given node id."""
        with self._lock:
            broker = self.cluster.broker_metadata(node_id)
            conn = self._conns.get(node_id)

            if conn is None:
                assert broker, 'Broker id %s not in current metadata' % (
                    node_id, )

                log.debug("Initiating connection to node %s at %s:%s", node_id,
                          broker.host, broker.port)
                host, port, afi = get_ip_port_afi(broker.host)
                cb = functools.partial(WeakMethod(self._conn_state_change),
                                       node_id)
                conn = BrokerConnection(host,
                                        broker.port,
                                        afi,
                                        state_change_callback=cb,
                                        node_id=node_id,
                                        **self.config)
                self._conns[node_id] = conn

            # Check if existing connection should be recreated because host/port changed
            elif conn.disconnected() and broker is not None:
                host, _, __ = get_ip_port_afi(broker.host)
                if conn.host != host or conn.port != broker.port:
                    log.info(
                        "Broker metadata change detected for node %s"
                        " from %s:%s to %s:%s", node_id, conn.host, conn.port,
                        broker.host, broker.port)

                    # Drop old connection object.
                    # It will be recreated on next _maybe_connect
                    self._conns.pop(node_id)
                    return False

            elif conn.connected():
                return True

            conn.connect()
            return conn.connected()
コード例 #6
0
    def __init__(self,
                 value,
                 key=None,
                 magic=0,
                 attributes=0,
                 crc=0,
                 timestamp=None):
        assert value is None or isinstance(value, bytes), 'value must be bytes'
        assert key is None or isinstance(key, bytes), 'key must be bytes'
        assert magic > 0 or timestamp is None, 'timestamp not supported in v0'

        # Default timestamp to now for v1 messages
        if magic > 0 and timestamp is None:
            timestamp = int(time.time() * 1000)
        self.timestamp = timestamp
        self.crc = crc
        self._validated_crc = None
        self.magic = magic
        self.attributes = attributes
        self.key = key
        self.value = value
        self.encode = WeakMethod(self._encode_self)
コード例 #7
0
    def _maybe_connect(self, node_id):
        """Idempotent non-blocking connection attempt to the given node id."""
        with self._lock:
            conn = self._conns.get(node_id)

            if conn is None:
                # Note that when bootstrapping, each call to broker_metadata may
                # return a different host/port. So we need to be careful to only
                # call when necessary to avoid skipping some possible bootstrap
                # source.
                broker = self.cluster.broker_metadata(node_id)
                assert broker, 'Broker id %s not in current metadata' % (
                    node_id, )

                log.debug("Initiating connection to node %s at %s:%s", node_id,
                          broker.host, broker.port)
                host, port, afi = get_ip_port_afi(broker.host)
                cb = functools.partial(WeakMethod(self._conn_state_change),
                                       node_id)
                conn = BrokerConnection(host,
                                        broker.port,
                                        afi,
                                        state_change_callback=cb,
                                        node_id=node_id,
                                        **self.config)
                self._conns[node_id] = conn

            # Check if existing connection should be recreated because host/port changed
            elif self._should_recycle_connection(conn):
                self._conns.pop(node_id)
                return False

            elif conn.connected():
                return True

            conn.connect()
            return conn.connected()
コード例 #8
0
    def __init__(self, client, subscription, metrics, **configs):
        """Initialize the coordination manager.

        Keyword Arguments:
            group_id (str): name of the consumer group to join for dynamic
                partition assignment (if enabled), and to use for fetching and
                committing offsets. Default: 'kafka-python-default-group'
            enable_auto_commit (bool): If true the consumer's offset will be
                periodically committed in the background. Default: True.
            auto_commit_interval_ms (int): milliseconds between automatic
                offset commits, if enable_auto_commit is True. Default: 5000.
            default_offset_commit_callback (callable): called as
                callback(offsets, exception) response will be either an Exception
                or None. This callback can be used to trigger custom actions when
                a commit request completes.
            assignors (list): List of objects to use to distribute partition
                ownership amongst consumer instances when group management is
                used. Default: [RangePartitionAssignor, RoundRobinPartitionAssignor]
            heartbeat_interval_ms (int): The expected time in milliseconds
                between heartbeats to the consumer coordinator when using
                Kafka's group management feature. Heartbeats are used to ensure
                that the consumer's session stays active and to facilitate
                rebalancing when new consumers join or leave the group. The
                value must be set lower than session_timeout_ms, but typically
                should be set no higher than 1/3 of that value. It can be
                adjusted even lower to control the expected time for normal
                rebalances. Default: 3000
            session_timeout_ms (int): The timeout used to detect failures when
                using Kafka's group management facilities. Default: 30000
            retry_backoff_ms (int): Milliseconds to backoff when retrying on
                errors. Default: 100.
            exclude_internal_topics (bool): Whether records from internal topics
                (such as offsets) should be exposed to the consumer. If set to
                True the only way to receive records from an internal topic is
                subscribing to it. Requires 0.10+. Default: True
        """
        super(ConsumerCoordinator, self).__init__(client, metrics, **configs)

        self.config = copy.copy(self.DEFAULT_CONFIG)
        for key in self.config:
            if key in configs:
                self.config[key] = configs[key]

        self._subscription = subscription
        self._is_leader = False
        self._joined_subscription = set()
        self._metadata_snapshot = self._build_metadata_snapshot(
            subscription, client.cluster)
        self._assignment_snapshot = None
        self._cluster = client.cluster
        self.auto_commit_interval = self.config[
            "auto_commit_interval_ms"] / 1000
        self.next_auto_commit_deadline = None
        self.completed_offset_commits = collections.deque()

        if self.config["default_offset_commit_callback"] is None:
            self.config[
                "default_offset_commit_callback"] = self._default_offset_commit_callback

        if self.config["group_id"] is not None:
            if self.config["api_version"] >= (0, 9):
                if not self.config["assignors"]:
                    raise Errors.KafkaConfigurationError(
                        "Coordinator requires assignors")
            if self.config["api_version"] < (0, 10, 1):
                if (self.config["max_poll_interval_ms"] !=
                        self.config["session_timeout_ms"]):
                    raise Errors.KafkaConfigurationError(
                        "Broker version %s does not support "
                        "different values for max_poll_interval_ms "
                        "and session_timeout_ms")

        if self.config["enable_auto_commit"]:
            if self.config["api_version"] < (0, 8, 1):
                log.warning(
                    "Broker version (%s) does not support offset"
                    " commits; disabling auto-commit.",
                    self.config["api_version"],
                )
                self.config["enable_auto_commit"] = False
            elif self.config["group_id"] is None:
                log.warning("group_id is None: disabling auto-commit.")
                self.config["enable_auto_commit"] = False
            else:
                self.next_auto_commit_deadline = time.time(
                ) + self.auto_commit_interval

        self.consumer_sensors = ConsumerCoordinatorMetrics(
            metrics, self.config["metric_group_prefix"], self._subscription)

        self._cluster.request_update()
        self._cluster.add_listener(WeakMethod(self._handle_metadata_update))
コード例 #9
0
 def __del__(self):
     if hasattr(self, "_cluster") and self._cluster:
         self._cluster.remove_listener(
             WeakMethod(self._handle_metadata_update))
     super(ConsumerCoordinator, self).__del__()
コード例 #10
0
def test_init(client, coordinator):
    # metadata update on init 
    assert client.cluster._need_update is True
    assert WeakMethod(coordinator._handle_metadata_update) in client.cluster._listeners