zoukankan      html  css  js  c++  java
  • 我们使用 Kafka 生产者在发消息的时候我们关注什么(Python 客户端 1.01 broker)

    之前使用 Kafka 的客户端消费者比较多一点,而且也是无脑订阅使用也没有深入了解过具体的参数。总的来说使用不够细节。

    这次公司项目活动期间暴露非常多的问题,于是有了这篇文章。

    首先我们来拆解一下 Kafka KafkaProducer 类给我们暴露的参数,我会依次介绍一下这些参数的功能以及效果,其中也包括比较重要的自定义 paritiition 的参数。

    1. bootstrap_servers

    bootstrap_servers: 'host[:port]' string (or list of 'host[:port]'
        strings) that the producer should contact to bootstrap initial
        cluster metadata. This does not have to be the full node list.
        It just needs to have at least one broker that will respond to a
        Metadata API Request. Default port is 9092. If no servers are
        specified, will default to localhost:9092.

    bootstrap_servers 接受单个字符串或者 list 参数,默认值是 'localhost',该值让你指定 kafka 的服务 server 格式是 host:port 。例如我们使用的集群,那么我们需要指定我们的生产者找到我们的集群地址我们会设置类似这样的 bootstrap_servers 地址

    kafka_conf = {
        'bootstrap_servers': ['10.171.97.1:9092', '10.163.13.219:9092', '10.170.249.122:9092']
    }
    self.kafka_producer = kafka.KafkaProducer(**kafka_conf)

    默认是 localhost。

    2. client_id

    client_id (str): a name for this client. This string is passed in
        each request to servers and can be used to identify specific
        server-side log entries that correspond to this client.
        Default: 'kafka-python-producer-#' (appended with a unique number
        per instance)

    设置了这个客户端 id 可以让你在与 server 通讯的时候标识这个生产者端。

    默认是 None 不设置。

    3. key_serializer | value_serializer 

     key_serializer (callable): used to convert user-supplied keys to bytes
         If not None, called as f(key), should return bytes. Default: None.
     value_serializer (callable): used to convert user-supplied message
         values to bytes. If not None, called as f(value), should return
         bytes. Default: None.

    key 和 value 的内容的序列化。可以指定自己的回调解析。

    默认参数是 None

    4. acks

    acks (0, 1, 'all'): The number of acknowledgments the producer requires
        the leader to have received before considering a request complete.
        This controls the durability of records that are sent. The
        following settings are common:
    
        0: Producer will not wait for any acknowledgment from the server.
            The message will immediately be added to the socket
            buffer and considered sent. No guarantee can be made that the
            server has received the record in this case, and the retries
            configuration will not take effect (as the client won't
            generally know of any failures). The offset given back for each
            record will always be set to -1.
        1: Wait for leader to write the record to its local log only.
            Broker will respond without awaiting full acknowledgement from
            all followers. In this case should the leader fail immediately
            after acknowledging the record but before the followers have
            replicated it then the record will be lost.
        all: Wait for the full set of in-sync replicas to write the record.
            This guarantees that the record will not be lost as long as at
            least one in-sync replica remains alive. This is the strongest
            available guarantee.
        If unset, defaults to acks=1.

    acks 是保障可靠性中非常重要的参数。

    在设置为 0 的情况下, kafka 会不管任何的事情,只要收到了消息就往 buffer 或者直接发送。是效率最高,但是完全不能保证消息是否发送成功,也不会确认 broker 的信息,更不会去重试属于典型的 fire and forget。

    该参数的默认值是 1,在参数被设置成 1 的情况下,会等待消费被 broker 端 leader 写入到日志中完成并且 broker leader 会返回 response ,但是不会等待其他 isr 写好副本的返回。在这种情况下如果 leader 挂了进行重新选举,可能会丢失消息。该参数比较好的兼容了吞吐量和可用性,只要 leader 不挂消息不会丢失,而且消息会慢慢被 拷贝到其他的 isr 上。

    在设置成 all 的情况下,会等待所有的 isr 都同步之后,才会返回,在消息完全不允许丢失的情况下启用该参数。他能保证只要还有一个可用的 isr 存活,消息都不会丢失。

    默认是 设置成 1.

    5. retries

    retries (int): Setting a value greater than zero will cause the client
        to resend any record whose send fails with a potentially transient
        error. Note that this retry is no different than if the client
        resent the record upon receiving the error. Allowing retries
        without setting max_in_flight_requests_per_connection to 1 will
        potentially change the ordering of records because if two batches
        are sent to a single partition, and the first fails and is retried
        but the second succeeds, then the records in the second batch may
        appear first.
        Default: 0.

    当我们设置 ack 大于 0 的情况下,该参数生效。如果我们发送失败设置了该参数会进行重试。默认情况下重试被关闭,如果需要保证数据不丢失活着高可用,可以将参数改为一个较大的值。

    试到成功为止,但是也可能因为这个引入一些 block 的问题,需要根据自己的环境进行评估和把握。

    默认是不开启重试。

    6. compression_type

    compression_type (str): The compression type for all data generated by
        the producer. Valid values are 'gzip', 'snappy', 'lz4', or None.
        Compression is of full batches of data, so the efficacy of batching
        will also impact the compression ratio (more batching means better
        compression). Default: None.

    指定一个压缩类型。这里注意我们是生产者,如果生产者生产了压缩的消息到达了 broker ,消费者可能同样需要指定相同的解压算法对数据进行解压。

    默认是不压缩。

    7. batch_size

    batch_size (int): Requests sent to brokers will contain multiple
        batches, one for each partition with data available to be sent.
        A small batch size will make batching less common and may reduce
        throughput (a batch size of zero will disable batching entirely).
        Default: 16384

    批处理大小 默认值 16kb,用于减少多次发送给 broker 给 broker 带来压力,发往 broker 同一个 partitions 的消息到达批处理大小后发送。我还没有调整过该参数,不太清楚具体影响。应该是在数据量和压力都特别大的情况下,有助于帮助网络减少繁忙状态。

    默认大小是 16kb。

    8. linger_ms

    linger_ms (int): The producer groups together any records that arrive
        in between request transmissions into a single batched request.
        Normally this occurs only under load when records arrive faster
        than they can be sent out. However in some circumstances the client
        may want to reduce the number of requests even under moderate load.
        This setting accomplishes this by adding a small amount of
        artificial delay; that is, rather than immediately sending out a
        record the producer will wait for up to the given delay to allow
        other records to be sent so that the sends can be batched together.
        This can be thought of as analogous to Nagle's algorithm in TCP.
        This setting gives the upper bound on the delay for batching: once
        we get batch_size worth of records for a partition it will be sent
        immediately regardless of this setting, however if we have fewer
        than this many bytes accumulated for this partition we will
        'linger' for the specified time waiting for more records to show
        up. This setting defaults to 0 (i.e. no delay). Setting linger_ms=5
        would have the effect of reducing the number of requests sent but
        would add up to 5ms of latency to records sent in the absense of
        load. Default: 0.

    这个参数通常情况下只有过载的情况下会触发,过载指生产者发送消息到 broker 的速度已经跟不上消息到达生产者的速度了。这个时候我们会在生产端 hold 一段时间 linger_ms 然后一并将内容发送到 broker ,以求减少到达 broker 的 requests 。实现这个功能是采取增加一段小的延迟来实现的。这有点像 tcp 上之前常用的 nagle 算法。

    同样这个参数我没有尝试过 - -不太清楚效果。如果遭遇大流量拥塞可以尝试开启。

    默认是不开启。

    9. partitioner

    partitioner (callable): Callable used to determine which partition
        each message is assigned to. Called (after key serialization):
        partitioner(key_bytes, all_partitions, available_partitions).
        The default partitioner implementation hashes each non-None key
        using the same murmur2 algorithm as the java client so that
        messages with the same key are assigned to the same partition.
        When a key is None, the message is delivered to a random partition
        (filtered to partitions with available leaders only, if possible).

    partitioner 是消息生产到指定 partitions 的计算函数,Python 端默认使用 DefaultPartitioner() 可以直接查看分组策略和使用参数 我这里贴一个

    class DefaultPartitioner(object):
        """Default partitioner.
    
        Hashes key to partition using murmur2 hashing (from java client)
        If key is None, selects partition randomly from available,
        or from all partitions if none are currently available
        """
        @classmethod
        def __call__(cls, key, all_partitions, available):
            """
            Get the partition corresponding to key
            :param key: partitioning key
            :param all_partitions: list of all partitions sorted by partition ID
            :param available: list of available partitions in no particular order
            :return: one of the values from all_partitions or available
            """
            if key is None:
                if available:
                    return random.choice(available)
                return random.choice(all_partitions)
    
            idx = murmur2(key)
            idx &= 0x7fffffff
            idx %= len(all_partitions)
            return all_partitions[idx]

    可以看到如果不指定 key 进行分组的话 使用了python 的 random choice 方法进行选择,如果使用 key 分组默认使用 murmu2 算法。

    如果不指定 key 出现数据有倾斜的问题,可以尝试提供新的的 partitions 算法。

    10. buffer_memory

    buffer_memory (int): The total bytes of memory the producer should use
        to buffer records waiting to be sent to the server. If records are
        sent faster than they can be delivered to the server the producer
        will block up to max_block_ms, raising an exception on timeout.
        In the current implementation, this setting is an approximation.
        Default: 33554432 (32MB)

    生产者端的一个发送 buffer ,最大提供了 32mb。如果 32mb 被填充满并且来不及全部发送给 broker ,将会触发 max_block_ms 并且 raise 出一个 timeout exception 。

    默认 32mb ,这个参数应该也需要在数据量非常大的情况下才会触发。但是我感觉该参数如果不是临时阻塞用上了 32mb 的缓存,应该都会触发超时。

    11. max_block_ms

    max_block_ms (int): Number of milliseconds to block during
        :meth:`~kafka.KafkaProducer.send` and
        :meth:`~kafka.KafkaProducer.partitions_for`. These methods can be
        blocked either because the buffer is full or metadata unavailable.
        Blocking in the user-supplied serializers or partitioner will not be
        counted against this timeout. Default: 60000.

    设置在调用 KafKaProducer.send 和 KafKaProducer.paritions_for 还有在 buffer 已经满的情况下的超时时间。

    例如我的 send 方法阻塞了 默认情况下 60s 会失效。但是感觉这个失效时间有点略长。如果 buffer 阻塞等 60s 可能后面的消息也已经排起长队了。

    12.max_request_size

    max_request_size (int): The maximum size of a request. This is also
        effectively a cap on the maximum record size. Note that the server
        has its own cap on record size which may be different from this.
        This setting will limit the number of record batches the producer
        will send in a single request to avoid sending huge requests.
        Default: 1048576.

    该参数会限制单批发送的最大请求大小,来避免请求发送过大。在服务器端应该也有个类似参数来控制消息避免发送过大参数。

    默认最大可以发送 1m 消息。

    13.retry_backoff_ms

    retry_backoff_ms (int): Milliseconds to backoff when retrying on
        errors. Default: 100.

    重试阻塞时间默认为 100ms

    14.metadata_max_age_ms

    metadata_max_age_ms (int): The period of time in milliseconds after
        which we force a refresh of metadata even if we haven't seen any
        partition leadership changes to proactively discover any new
        brokers or partitions. Default: 300000

    元数据最大刷新时间

    默认是 5分钟刷通过去 broker 刷新一次元数据。

    15.max_in_flight_requests_per_connection

    max_in_flight_requests_per_connection (int): Requests are pipelined
        to kafka brokers up to this number of maximum requests per
        broker connection. Note that if this setting is set to be greater
        than 1 and there are failed sends, there is a risk of message
        re-ordering due to retries (i.e., if retries are enabled).
        Default: 5.

    看了几篇文章感觉没有把这个参数说清楚,这个参数默认是 5 。说的是单个 connection 同时允许 5 个消息发送之后确认消息,如果设置成 1 发送一条将会对消息进行确认来保证顺序。

    怎么说呢,因为 producer 是 pipline 的是顺序发送的,只有重试的时候会引入顺序问题。比如我发了  1 2 3 4 5 然后开始确认了,确认的时候 2 没了 我需要确认 2 然后进行重发。顺序就乱了,因为 5 都接收了 我还要发送一次 2 。试想一下如果我们具有 idempotent ,那么我们 2 错了我们可以让 3 4 5都进行重传,这样又保证了顺序。

    如果我们没有幂等,我们就需要将 ack 设置为 all 并且将 max_in_flight_requests_per_connection 调整为 1 ,然后启用一个 partitions 来完全保障数据的顺序传输。

    还有不少参数包括安全协议传输的参数,我没有列举到这里。那些参数大部分时候都用不上,需要的时候再去看也行。

    当我们在发消息的时候通常不需要关注到 这么多参数,只是针对特定情况下我们需要调整一些参数来保障我们想要实现的语意。比较常见的一个情况是我们可能希望我们的消息是不丢失的,那我们应该如何配置呢?

    其实感觉 kafka 很多情况下都依赖消费端进行幂等,如果消费端幂等的话整个流程会非常健壮和快速也就是实现 At least Once 语意,而不是去保证 exactly once。

    1. 使用 producer.send 设置 ack 为 all 。

    2. 设置 retry 为较大值,重试避免消息丢失。

    3. 设置 unclean.leader.election.enable = false 阻止落后太多的非 irs 竞选 leader。

    4. 设置 topic 级别的 replication.factor >= 3 多备份冗余。

    5. 设置 min.insync.replicas > 1 控制提交数尽量多确认提交。

    6. 消费端放弃使用 autocommit 而使用 手动 commit 老保障消息的准确,如果我们使用 autocommit 也要保障消费端 idempotent。

    Reference:

    https://en.wikipedia.org/wiki/Nagle%27s_algorithm

    https://xinklabi.iteye.com/blog/2195092  MurmurHash算法(高运算性能,低碰撞率,hadoop、memcached等使用)

    https://www.iteblog.com/archives/2560.html  Kafka 是如何保证数据可靠性和一致性

    https://stackoverflow.com/questions/49802686/understanding-the-max-inflight-property-of-kafka-producer  Understanding the max.inflight property of kafka producer

    http://matt33.com/2018/10/24/kafka-idempotent/  Kafka 事务性之幂等性实现

  • 相关阅读:
    java 字节流与字符流的区别
    什么是缓冲区
    java流输入输出
    Apache安装配置
    Maven学习
    Redis
    数据结构与算法
    pig ERROR 2997: Encountered IOException. File or directory null does not exist.
    hadoop学习路线(转)
    86标准SQL与92标准SQL用法区别
  • 原文地址:https://www.cnblogs.com/piperck/p/11116129.html
Copyright © 2011-2022 走看看