Kafka

模型图

Consumer group3
详细数据:单个分区保证有序
Consumer group2
详细数据:多个分区不保证顺序
Consumer group1
Consumer group0
Kafka单机
broker-0
Topic0
Kafka集群
broker-0
broker-1
Topic3 分区数:3,副本因子:3
Topic1 分区数:3,副本因子:3
Topic2 分区数:3,副本因子:3
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
默认hashkey%分区数,无key走其他分区策略,比如轮询策略,随机策略,每个版本的默认策略不一样
read
read
read
read
read
read
read
read
read
read
read
read offset=6
read offset=3
0
1
2
3
4
5
6
Consumer0
...
Consumer0
Consumer1
Consumer2
0
3
6
9
12
15
18
...
Consumer0
Consumer1
Consumer2
Consumer3
Consumer0
zookeeper
partition0
zookeeper
partition0::Leader
partition1:Follower
partition2:Follower
partition0:Follower
partition1:Follower
partition2:Leader
partition0:Leader
partition1:Follower
partition2:Follower
生产者

单机

安装JDK

https://blog.csdn.net/yuell102/article/details/119305484?spm=1001.2014.3001.5501

安装Docker

https://blog.csdn.net/yuell102/article/details/116572748

安装

docker 安装 zookeeper

docker pull wurstmeister/zookeeper

docker 安装 kafka

docker pull wurstmeister/kafka

单机方式启动zookeeper

docker run -d --name zookeeper --restart=always -p 2181:2181 -t wurstmeister/zookeeper

单机方式启动kafka

docker run -d --name kafka --restart=always -p 9092:9092 -e KAFKA_BROKER_ID=0 -e KAFKA_ZOOKEEPER_CONNECT=10.0.0.101:2181 -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://10.0.0.101:9092 -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 wurstmeister/kafka
//docker run -d --name kafka
//--restart=always 
//-p 9092:9092
//-e KAFKA_BROKER_ID=0 
//-e KAFKA_ZOOKEEPER_CONNECT=公网ip:2181
//-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://公网ip:9092 
//-e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 wurstmeister/kafka

Kafdrop

创建目录

sudo mkdir -p /home/kafdrop

创建并编辑docker-compose-kafdrop.yaml

sudo vi /home/kafdrop/docker-compose-kafdrop.yaml

写入内容

version: "3"
services:
  kafdrop:
    image: obsidiandynamics/kafdrop
    restart: "no"
    ports:
      - "9000:9000"
    environment:
      KAFKA_BROKERCONNECT: "外网ip:9092"

安装并启动

docker-compose -f /home/kafdrop/docker-compose-kafdrop.yaml up -d

访问 ip:9000
在这里插入图片描述

demo

Topic管理

TopicDetailed

创建as Topic 3个分区 1个复制因子 默认异步

adminClient.createTopics(Arrays.asList(new NewTopic("ac", 3, (short) 1)));

删除as Topic 默认异步

adminClient.deleteTopics(Arrays.asList("ac"));

返回 Topic列表名称 默认异步

ListTopicsResult listTopicsResult = adminClient.listTopics();
Set<String> names = listTopicsResult.names().get();

删除 Topic 默认异步

describeTopics(Arrays.asList("ac"));

使其同步,等待执行完回来,返回结果

xx.all().get();

简单 生产者/消费者

生产者

ProducerQuickStart

推送给分区消息 有key走的时hash

new ProducerRecord<>("ac", "key" + i, "value" + i);

推送消息 无key走的是其他策略(不一定是轮询 轮询策略)

new ProducerRecord<>("ac", "value" + i);

//推送消息 指定分区推送 指定1分区

new ProducerRecord<>("ac", 1, "key" + i, "value" + i);

消费者

ConsumerAssign 不配置消费组id 手动指定Topic分区 手动指定偏移量起始位值offset

1.assign手动指定消费分区 失去自动负载均衡
2.不能指定消费者组properties.put(ConsumerConfig.GROUP_ID_CONFIG, “c2”);

consumer.assign(topicPartitions);

3.指定消费偏移量 offset
指定消费位置 从头消费 从offset=0开始消费

consumer.seekToBeginning(topicPartitions);

指定消费位置 1分区 从offset=5开始消费

consumer.seek(new TopicPartition("ac", 1), 5);

从offset=当前最大 开始消费

consumer.seekToEnd(topicPartitions);
ConsumerSubscribe 配置消费组id 自动负载管理Topic分区 无需指定偏移量offset

1.指定消费者分组id

properties.put(ConsumerConfig.GROUP_ID_CONFIG, "c2");

2.订阅相关得Topics
一个topic多个分区时 一个消费组多个消费者时 subscribe订阅自动负载均衡

consumer.subscribe(Pattern.compile("^ac.*"));

自定义分区 partitioner

MyPartitioner

1.需要继承org.apache.kafka.clients.producer.Partitioner

2.重写或实现函数,可以参考org.apache.kafka.clients.producer.DefaultPartitioner

3.根据自己需求生产者配置,根据实现算法发送消息到期望的分区

4.配置自定义分区策略

properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, MyPartitioner.class.getName());

目前一直分区

认分区策略:
如果记录中指定了分区,则使用它
如果未指定分区但存在键,则根据键的散列选择一个分区
如果不存在分区或键,请选择在批处理已满时更改的粘性分区。 有关粘性分区的详细信息,请参阅 KIP-480DefaultPartitioner


“循环”分区器 当用户希望将写入平均分配到所有分区时,可以使用此分区策略。 这是与记录键哈希无关的行为
RoundRobinPartitioner

分区策略:
如果记录中指定了分区,则使用它
否则选择当批处理已满时更改的粘性分区。 注意:与 DefaultPartitioner 相比,记录键不用作此分区器中分区策略的一部分。 不保证具有相同键的记录被发送到同一个分区。 有关粘性分区的详细信息,请参阅 KIP-480UniformStickyPartitioner

序列化

MySerializer 序列化

1.实现接口org.apache.kafka.common.serialization.Serializer

2.生产者需要增加

//key传递序列化规则
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, MySerializer.class.getName());
//value传递序列化规则
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, MySerializer.class.getName());

MyDeserializer 反序列化

1.实现接口org.apache.kafka.common.serialization.Deserializer

2.消费者需要增加

//key传递反序列化规则
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, MyDeserializer.class.getName());
//value传递反序列化规则
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, MyDeserializer.class.getName());

拦截器

MyProducerInterceptor

1.需要实现接口:com.kafka.kafka_demo.interceptors.ProducerInterceptor

2.生产者需要增加

//自定义拦截器
properties.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, MyProducerInterceptor.class.getName());

offset自动控制

//默认 在消费者没有偏移量offset时生效
auto.offset.reset=latest
earliest:自动将偏移量重置为最早的偏移量
latest:自动将偏移量重置为最新的偏移量
none:如果未找到消费者组的先前偏移量,则消费者抛出异常

//默认 自动提交偏移量
enable.auto.commit=true

ConsumerSubscribeOffSet 手动提交偏移量

1.关闭提交自动 会导致偏移量一直停留在已消费过的消息上面

properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);

2.手动提交偏移量 偏移量一定要增加

 //消息的偏移量
long offset = next.offset();

//这里必须++  每次重启都会导致最后一个消息重复消费
long count = ++offset;

//手动提交offset
Map<TopicPartition, OffsetAndMetadata> offsets=new HashMap<TopicPartition, OffsetAndMetadata>();

offsets.put(new TopicPartition(next.topic(), partition),new OffsetAndMetadata(count));
consumer.commitAsync(offsets, new OffsetCommitCallback() {
    @Override
    public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
        System.out.println("完成:"+count+"提交!");
    }
});

ConsumerSubscribeOffSetDelay 延迟提交偏移量

1.自动提交offset 默认为true

properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);

2.自动提交时生效 offset 偏移量提交频次 10000毫秒提交一次 提交前,每次打开都会消费没提交的消息 10秒内重起会重复消费

properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 10000);

应答与重发(Acks&Retries)与幂等

ProducerAcks概括

//Kafka生成这在发送完一个消息之后,要求Broker在规定的时间Ack应答,如果规定时间内没有应答,Kafka生产这会尝试N次重新发送消息

//acks=1   只要Leader确认
//Leader 会将Record写到其本地日志中,但会在不等待所有Follower的完全确认情况下做出响应,在这种情况下,如果Leader在确认记录后立即失败,但Follower复制记录之前失败,则记录将丢失

//acks=0   不需要任何确认
//生产者根本不会等待服务器的任何确认。该记录将立即添加到套接字缓存区中并视为已发送。这种情况下,不能保证服务器已收到记录

//acks=all   acks=-1  需要Leader和至少一个副本确认
//Leader将等待全套同步副本确认记录。这保证只要至少一个同步副本仍处于活动那个状态,记录不会丢失,这是最有力的保证

//request.timeout.ms=30000 默认ack超时时间  如果30秒未收到确认重发
//retries=2147483647 默认重试次数

图画的有点问题,仅供参考acks=1

Topic
推送消息
Retries重发:推送消息
acks=1 不等待Follower复制完响应,会导致消息发送成功,记录丢失,消息重发
copy Leader 消息
copy Leader 消息
Leader
Follower
Follower
生产者

图画的有点问题,仅供参考acks=0

Topic
推送消息
Retries重发:推送消息
copy Leader 消息
copy Leader 消息
Leader
Follower
Follower
生产者,acks=0,不等待任何确认

图画的有点问题,仅供参考acks=all | acks=-1

Topic
推送消息
copy Leader 消息
copy Leader 消息
Leader
Follower
Follower
生产者,acks=-1,等待至少一个副本分区确认,记录不会丢失

ProducerAcks 生产者配置

1.设置应答模式 需要Leader和至少一个副本确认

properties.put(ProducerConfig.ACKS_CONFIG, "all");

2.重发次数3次 如果超过3次也失败,则系统放弃发送

properties.put(ProducerConfig.RETRIES_CONFIG, 3);

3.将检测超时的时间设置为1毫秒

properties.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 1);

ProducerAcksIdempotent 幂等 生产者配置 //设置应答模式 需要Leader和至少一个副本确认

1.必须配置为 all 保证Leader和至少一个副本确认都确认收到消息

properties.put(ProducerConfig.ACKS_CONFIG, "all");

2.必须设置重发次数 这里设置的是3

properties.put(ProducerConfig.RETRIES_CONFIG, 3);

3.开启Kafka的幂等性

properties.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);

4.必须设置为1 才能保证顺序 限制客户端在单个连接上能够发送的未响应请求的个数。设置此值是1表示kafka broker在响应请求之前client不能再向同一个broker发送请求。注意:设置此参数是为了避免消息乱序

properties.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 1);

事务

//Kafka的幂等,只能保证一条记录在分区发送的原子性
//多条记录多分区之间的完整性,需要开启kafka的事务操作

事务种类图

生产者事务Only
推送消息
copy 消息
copy 消息
生产者
分区0:Leader
分区1:Follower
分区2:Follower
Topic
flowchart LR
subgraph 消费者&生产者事务;
	subgraph Topic1;
		subgraph 分区3[分区0:Leader];
		end
		subgraph 分区4[分区1:Follower];
		end
		subgraph 分区5[分区2:Follower];
		end
	end
	分区3 -->|推送消息|消费者0;
	分区4 -->|推送消息|消费者0;
	分区5 -->|推送消息|消费者0;
	subgraph 业务;
		subgraph 消费者0;
		end
	end
	
	消费者0 -->|推送消息|分区6;
	消费者0 -->|推送消息|分区7;
	消费者0 -->|推送消息|分区8;
	subgraph Topic2;
		subgraph 分区6[分区0:Leader];
		end
		subgraph 分区7[分区1:Follower];
		end
		subgraph 分区8[分区2:Follower];
		end
	end
end  

事务隔离级别

isolation.level = read_uncommitted; //默认 事务读未提交   可以读到未提交事务的数据

isolation.level = read_committed; //事务读已提交   可以读到已提交事务的数据,未提交的读不到

生产者事务Only

读未提交ConsumerSubscribeReadUnCommitted

//消费事务的隔离级别read_committed 读未提交

properties.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_uncommitted");
读已提交ConsumerSubscribeReadCommitted

//消费事务的隔离级别read_committed 读已提交

properties.put(ConsumerConfig.ISOLATION_LEVEL_CONFIG, "read_committed");
生产者ProducerT
//设置事务id  id必须唯一
properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "transaction-id" + UUID.randomUUID().toString());
//配置Kafka批处理大小
properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 1024);
//等待5ms  如果batch中数据不足 1024大小
properties.put(ProducerConfig.LINGER_MS_CONFIG, 5);

//配置Kafka重试机制和幂等性
properties.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
properties.put(ProducerConfig.ACKS_CONFIG, "all");
properties.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, 20000);

消费者&生产者事务

//需要关闭第一个消费者的offset自动提交偏移量 手动+1维护偏移量

SpringBoot 集成

消费者

singleListenner 普通消费者
/**
     * 监听 aa Topic
     * @param value
     */
    @KafkaListeners(value = {@KafkaListener(topics = {"aa"})})
    public void singleListenner(ConsumerRecord<?, ?> value) {
        log.info(value.value().toString());
    }
listenner 接收 处理后 发送给其他Topic
/**
     * 接受 aa Topic 处理后发送给 bb Topic
     * @param value
     * @return
     */
    @KafkaListeners(value = {@KafkaListener(topics = {"aa"})})
    @SendTo(value = {"topic05"})
    public String listenner(ConsumerRecord<?, ?> value) {

        return value.value()+"我从aa处理后过来的!";
    }

生产者

非事务发送
    /**
     *  非事物发送
     *   配 transaction-id-prefix 会报错
     */
    @Test
    public void testNotTransaction(){
        kafkaTemplate.send(new ProducerRecord("aa","003","非事务消息"));
    }
事务发送
 /**
     *  kafka事物发送
     */
    @Test
    public void testKafkaTemplate(){
        kafkaTemplate.executeInTransaction(new KafkaOperations.OperationsCallback() {
            @Override
            public Object doInOperations(KafkaOperations kafkaOperations) {
                return kafkaOperations.send(new ProducerRecord("aa","002","发送Kafka事务"));
            }
        });
    }
利用Spring事物 发送kafka事物
@Resource
    private OrderService orderService;


    /**
     *  利用Spring事物 发送kafka事物
     */
    @Test
    public void testOrderService(){
        orderService.saveOrder("001","利用Spring事物 发送kafka事物");
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值