Kafka 深入浅出

深入浅出

  1. broker:每个Kafka实例叫一个broker,每个broker可以轻松处理数千个分区和每秒数百万的消息量
  2. 分区:一个分区属于一个broker,这个分区叫做主分区,同时分区可以分布在多个broker上面,这些分区叫做分区复制,如果主分区失效,其他分区接管主导权。分区复制只能发生在单个集群中。
  3. 保留策略:保留一段时间(比如7天),如果超时一定大小就删除旧消息,每个topic可以配置单独的保留策略
  4. 分组订阅:每条消息保证在分组中只会被消费一次
  5. 默认会自动创建topic的情况:1、生产者写入消息;2、消费者消费消息;3、客户端获取topic元信息
  6. Kafka客户端要自己负责把生产和消费请求发到正确的broker上面,否则会收到一个“非首领”错误,这是客户端会从任意一个broker获取并更新自己的元信息

服务器配置

  1. 使用1024以下端口,需要使用root权限
  2. 默认端口号9092
auto.create.topics.enable=false #关闭自动创建topic
log.retention.bytes=xxx #是设置每个分区最大保留大小,而不是topic维度

操作系统配置

# 页面缓存配置
vm.swappiness=1
vm.dirty_ratio=60~80
vm.dirty_background_ratio=5

# 网络配置
net.core.wmem_default=128K #socket写缓冲大小
net.core.rmem_default=128K #socket读缓冲大小
net.core.wmem_max=2M #socket写缓冲最大值
net.core.rmem_max=2M #socket读缓冲最大值
net.ipv4.tcp_wmem=4k 64K 2M #tcp socket配置
net.ipv4.tcp_rmem=4k 64K 2M

net.ipv4.tcp_window_scaling=1 #启动tcp时间窗口
net.ipv4.tcp_max_syn_backlog=1024 #设置并发连接数
net.core.netdev_max_backlog=1000 #设置最大排队等待数

生产者

发送消息的三种方式:

  1. 发送不管结果,直接返回
  2. 同步发送,send方法返回Future,调用get阻塞
  3. 异步发送,send方法指定回调函数

如果发送嫌慢,可以采用多线程

如何保证发送不丢失消息:
就是不能不管send的结果就直接返回,要通过get或者添加回调函数来处理失败的情况,失败后记录日志或保存到数据库

配置

client.id
acks=[0 | 1 | all] #0不等待直接返回,1首领节点成功就返回,all全部成功才返回
retries=n #收到服务器的错误,重试次数
retry.backoff.ms=n #重试时间间隔
buffer.memory=n #内存缓冲大小
compression.type=[snappy | gzip | lz4] #snappy占用较少cpu来提高可观的压缩比,gzip占用内存大压缩比高
batch.size=n #一批可发送的最大数量
linger.ms #发送前的等待时间,增加吞吐量,增加延迟
max.in.flight.requests.per.connection=n #生成者在收到服务器响应前可以发送多少条消息,设置为1可以保证顺序写入
max.block.ms #调用send和partitionsFor的最大阻塞时间
max.request.size #一批次发送消息的最大值
send.buffer.bytes=n #设置tcp缓冲大小,-1则使用操作系统的默认值

高性能-写入

Kafka的特性之一就是高吞吐率,但是Kafka的消息是保存或缓存在磁盘上的,一般在磁盘上读写数据性能是不高的,但kafka可以轻松支持每秒百万级的写入请求,主要是采用了顺序写入和MMFile(memory mapped files, 内存映射文件, 简称mmap)

顺序写

因硬盘寻址相对读写耗时, 每次读写都会寻址->写入, 随机I/O(RDB很多都是随机I/O)要比顺序I/O慢很多. 为了提高读写硬盘的速度, kafka就是使用顺序I/O. 这样省去了大量的内存开销(减少jvm垃圾回收)以及节省了IO寻址的时间.
但是单纯的使用顺序写入, kafka的写入性能也不可能和内存(寻址读写速度远远高于硬盘)进行对比, 因此Kafka的数据并不是实时的写入硬盘中

MMAP

kafka充分利用了操作系统分页存储来利用内存提高I/O效率. mmap称为内存映射文件, 在64位操作系统中一般可以表示20G的数据文件
它的工作原理是直接利用操作系统的pagecache实现磁盘文件到物理内存的直接映射. 完成MMP映射后, 用户对内存的所有操作会被操作系统自动的刷新到磁盘上,极大地降低了IO使用率. (用户将不直接把数据写入磁盘, 而是写入内存(pagecache)中, pagecache数据会被系统定时(间隔由系统参数配置)刷写进磁盘)
直接写入硬盘, 速度慢, 但安全; 使用pagecache, 速度快, 但操作系统宕机(断电)会丢失数据(应用(用户态)挂掉不影响, pagecache是内核态, 数据依然会被操作系统刷入硬盘)

消费者

  1. 同一个消费组里面,消费者大于分区数会导致一些消费者被闲置,不能收到消息,因为一个分区只能由消费组中一个消费者消费,因此分区数量决定了并发消费的上限
  2. 当消费者增加和删除消费者时,会发生再均衡导致一小段时间不会消费消息(再均衡:在同一个消费组当中,分区所有权从一个消费者转移到另一个消费者)
  3. 订阅消息可以使用正则表达式匹配topic
  4. 唯一一次消费解决方案:使用数据库保存offset,处理逻辑和保存offset在同一个事物里面。offset通过ConsumerRebalanceListener监听和获取偏移量并设置要读取的开始偏移量
  5. consumer.wakeup()退出poll(),是consumer唯一一个线程安全的方法
Runtime.getRuntime().addShutdownHook(new Thread() {
    public void run() {
        consumer.wakeup();
    }
});
  1. 独立消费者:只有一个消费者消费数据,因为消费组有再均衡的问题,为了避免这个问题,可以使用如下API:
List<PartitionInfo> partitionInfoList = consumer.partitionsFor(ProducerTest.topic);
        List<TopicPartition> topicPartitionList = partitionInfoList.stream()
                .map(item -> new TopicPartition(item.topic(), item.partition()))
                .collect(Collectors.toList());
        consumer.assign(topicPartitionList);

        while(true) {
            ConsumerRecords<String, String> poll = consumer.poll(Duration.ofMillis(100));
            for (ConsumerRecord<String, String> record : poll) {
                System.out.printf("topic:%s,partition:%d,key:%s,content:%s\n",
                        record.topic(),
                        record.partition(),
                        record.key(),
                        record.value());
            }
            consumer.commitSync();
        }
  1. Kafka使用零复制技术,直接把消息从系统内核态发送到网络通道,而不需要经过内核态到用户态的拷贝
  2. 在消息还未复制完成时,消息者不会消费到这些消息,所以消费速度受到副本复制快慢的影响
  3. 如果中间有些消息想先不处理,可以调用pause保存到缓冲区,调用resume()开始从轮询里面获取新数据
  4. 基于键的分区,在之后调整分区数量是很困难,所以基于键的分区最好一开始就计算好分区数量

消费者配置

fetch.min.bytes=x #返回的数据大小至少xx,否则阻塞等待到有这么多数据
fetch.max.wait.ms=x #指定最大阻塞等待时间,默认500ms
max.partition.fetch.bytes=x #一次poll,从一个分区能拉取的最大大小
session.timeout.ms=x #会话过期时间,如果过期就被认定为消费者挂掉了
auto.offset.reset=[latest | earliest] #在没有偏移量的情况,指定消费其实位置(最前或者最后)
enable.auto.commit=true #是否自动提交偏移量
auto.commit.interval.ms=100 #设置自动提交偏移量的频率
max.poll.records=n #单次poll最大的返回最大记录数
receive.buffer.bytes=n #设置tcp缓冲大小,-1则使用操作系统的默认值

高性能-读取

kafka服务器在响应客户端读取时, 使用ZeroCopy技术, 直接将需要读取的数据从内核空间的磁盘中传递输出, 而无需将数据读取并拷贝到用户空间, 再进行传输

传统IO操作

  1. 用户进程调用read等系统调用向操作系统发出IO请求,请求读取数据到自己的内存缓冲区中.自己进入阻塞状态
  2. 操作系统收到请求后, 进一步将IO请求发送磁盘
  3. 磁盘驱动器收到内核的IO请求,把数据从磁盘读取到驱动器的缓冲中.此时不占用CPU.当驱动器的缓冲区被读满后,向内核发起中断信号告知自己缓冲区已满
  4. 内核收到中断,使用CPU时间将磁盘驱动器的缓存中的数据拷贝到内核缓冲区中
  5. 如果内核缓冲区的数据少于用户申请的读的数据, 重复步骤3跟步骤4, 直到内核缓冲区的数据足够多为止
  6. 将数据从内核缓冲区拷贝到用户缓冲区, 同时从系统调用中返回. 完成任务

引入DMA(协处理器)后

  1. 用户进程调用read等系统调用向操作系统发出IO请求,请求读取数据到自己的内存缓冲区中。自己进入阻塞状态。
  2. 操作系统收到请求后,进一步将IO请求发送DMA。然后让CPU干别的活去。
  3. DMA进一步将IO请求发送给磁盘。
  4. 磁盘驱动器收到DMA的IO请求,把数据从磁盘读取到驱动器的缓冲中。当驱动器的缓冲区被读满后,向DMA发起中断信号告知自己缓冲区已满。
  5. DMA收到磁盘驱动器的信号,将磁盘驱动器的缓存中的数据拷贝到内核缓冲区中。此时不占用CPU。这个时候只要内核缓冲区的数据少于用户申请的读的数据,内核就会一直重复步骤3跟步骤4,直到内核缓冲区的数据足够多为止。
  6. 当DMA读取了足够多的数据,就会发送中断信号给CPU。
  7. CPU收到DMA的信号,知道数据已经准备好,于是将数据从内核拷贝到用户空间,系统调用返回。

zero copy

  1. 文件在磁盘中数据被copy到内核缓冲区
  2. 从内核缓冲区copy到内核与socket相关的缓冲区
  3. 数据从socket缓冲区copy到相关协议引擎发送出去

Kafka原理

分区

何为分区:就是一个topic的数据,分散存储在多个broker中,这样就可以避免一个topic把broker给占满的情况,同时分区可以提高吞吐量,比如一个客户端的消费速度是50M/S,要达到每秒1G/S的吞吐量,就需要1000/50=20个分区

分区策略

默认采用轮询broker(round-robin)的方式,先分主分区,再分副本分区;分区数=2为例: 第1条去0分区, 第2条去1分区, 第3条去0分区, 仅仅达到负载均衡

hash, 每条record根据key的hash值对分区数取模(去哪个分区= hash(record的key) % 分区数 ), 决定该record存放哪个分区

分段

  • 每个分区又分为多个片段,每个片段默认1G或者1周的数据量,每个片段是一个文件
  • 正在写入的片段叫活跃片段,永远不会被删除

数据管道Connect

  • 适用于以Kafka为中心,把Kafka的数据转存或者其他地方存到Kafka的场景,比如mysql发到Kafka,Kafka发到ES
  • 提供仅一次语义
  • 支持数据池Data Sink
  • Kafa支持数据加密(SASL)

管理Kafka

主题管理

创建分区

kafka-topics.sh --zookeeper zkStr --create --topic test --replication-factor 2 --partitions 8 #如果放置存在报错,可以加--if-not-exists

增加分区

只能增加分区,不能减少分区,因为减少分区会出现消息乱序,如果一定要减少分区,只能删除整个topic

kafka-topics.sh --zookeeper zkStr --alter --topic test --partition 16

删除topic

删除topic必须broker的delete.topic.enable=true,否则删除请求将被忽略

kafka-topics.sh --zookeeper zkStr --delete --topic test

列出所有topic(简要信息)

kafka-topics.sh --zookeeper zkStr --list

列出所有topic(详细信息)

kafka-topics.sh --zookeeper zkStr --describe
--topics-with-overrides #只列出有不同于默认配置的topic
--under-replicated-partitions #列出包含不同步副本分区
--unavailable-partitions #列出没有leader的分区

消费者群组

在旧版本中,消费者群组信息保存在zk上面,用–zookeeper指定;
在新版本中,消费者群组信息保存在broker上面,用–bootstrap-server指定;
在旧版本可以删除消费者群组和偏移量信息,新的不行。

列出所有消费者群组

kafka-consumer-groups.sh --zookeeper zkStr --list

列出某个消费者群组详细信息

kafka-consumer-groups.sh --zookeeper zkStr --describe --group testGroup
字段描述
group分组名字
topic分区名字
partition分区名字
current-offset这个群组读取的位置
log-end-offset高水位位置
logcurrent-offset和log-end-offset的差距
owner消费者id

动态配置

覆盖配置

kafka-configs.sh --zookeeper zkStr --alter --entity-type [topics | clients] --entity-name xxxx --add-config key=value[,key=value...]

列出被覆盖的配置

kafka-configs.sh --zookeeper zkStr --describe --entity-type [topics | clients] --entity-name xxxx

移除被覆盖的配置

kafka-configs.sh --zookeeper zkStr --alter --entity-type [topics | clients] --entity-name xxxx --delete-config key[,key...]

分区管理

首领选举

修改分区副本

修改复制系数

转储日志片段

副本验证

消费和生产

控制台消费者

kafka-console-consumer.sh --zookeeper zkStr --topic topicName [--from-begin] [--max-messages] [--partition] [--formatter] [--property]

控制台生产者

kafka-console-producer 
--broker-list ip:port[,ip:port] 
--topic topicName [--key-serializer] 
[--value-serializer] 
[--compression-codec [none | gzip | snappy | lz4]] #压缩方式
[--sync] #是否同步发送

Kafka Streams流式处理

什么是流式处理

请求与相应:一个请求对应一个响应,响应时长在亚秒和毫秒级

批处理:处理周期为分钟、小时、天、周、月、年等等

流式处理:流式处理范式介于上面两种范式中间,不要求在亚秒级响应,但也不能容忍第二天才返回结果。流式处理是持续的,输入的数据一直持续进行,返回的结果也是持续进行。有持续性和非阻塞特性。

流式处理概念

时间

事件时间:表示追踪时间的发生时间或者创建时间

日志追加时间:事件保存到broker的时间,这个时间一般和流式计算没有关系,除非事件没有记录事件时间,这时可以使用日志追加时间模糊代表事件时间

处理时间:指应用程序在收到事件之后开始对其进行处理的时间。因为从产生事件到处理事件中间时间不确定,所以这个时间更不可靠,尽量避免使用它

状态

本地状态:只有当前应用程序能访问,一般使用内嵌的数据库保存,优点访问速度快,不受内存大小限制

外部状态:使用外部的数据存储系统,一般是NoSQL,优势是没有容量限制,但是延迟高。大部分流式处理应该避免使用外部存储

流和表的二元性

表只关注于数据的当前状态,而流表示了数据的整个变化过程,比如,mysql的binlog就是一个事件流。大部分的数据库都提供了CDC方案(Change Data Capture)

时间窗口

窗口的大小:是统计5分钟还是10分钟内的数据

窗口的移动间隔:统计5分钟的数据,可以每1秒钟统计一次,也可以每1分钟统计一次。如果窗口大小和移动间隔相等叫做“滚动窗口”,如果窗口随每一条数据移动,这种叫做“滑动窗口”

窗口的可更新时长:有些数据可能由于网络或者服务器重启导致数据姗姗来迟,这些数据在允许的时间范围可以更新统计结果,否则忽略他们

Kafka Streams在流式计算领域应用的很少,主要在于只能处理Kafka的数据,对于其他数据源和Sink的数据只能干瞪眼。而且部署上也没有优势。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值