kafka基础和应用

前言

  • 会随着经验不断更新这篇博客,关于kafka的运用和实践。

1.什么是kafka

  • kafka是分布式消息队列,它是一种在分布式场景下适用的消息队列,message queue

2.它解决了什么问题

  • 它解决的核心问题就是专门负责不同系统之间的传递数据

1.异步解耦

  • 在前面秒杀优惠券场景下,我们通常的做法是通过lua脚本先去查询并扣除库存,然后将消息发送至Stream消息队列中,再由后台多开的线程进行读取消息,进行异步处理,进行落库,而kafka就可以替换redis中的stream消息队列,更加工程化,保证redis单一职责的架构模式

2.削峰填谷

  • kafka相当于一个超大容量的缓冲池,能够将流量削平,慢慢消费直到落库

3.持久化

  • kafka可以支持数据写磁盘,支持副本机制,宕机可恢复

4.分布式系统间的通信

  • kafka适用于分布式场景下,我们之前用的阻塞队是放在jvm里面的,倘若部署在不同的机器上面,jvm都会不同,所以无法进行不同机器之间的交流,而kafka是一个独立的服务器,所有服务器都能访问

5.大规模日志和数据流处理

  • 具体遇到我们再吸取经验,先大致了解一下

3.基本概念

  • offset 是 Kafka 每条消息在分区里的"编号"。

kafka中的结构

分区里面的结构

4.实现发送消息和接收消息的demo

  • 1.如果你是windows系统,下载好docker desktop,选择AMD64,确保能够启动docker服务,如果是linux,直接启动docker就行了,下载kafka
  • 2.书写docker-composer.yml
yml 复制代码
# 表示这是个容器
services:
  # zookeeper服务
  zookeeper:
    # 镜像选择 如果本地没有会自动pull
    image: confluentinc/cp-zookeeper:7.6.0
    # 环境变量
    environment:
      # zookeeper对外提供的客户端连接的端口是 2181
      ZOOKEEPER_CLIENT_PORT: 2181
      # zookeeper的心跳时间单位是 2000 ms -> 2s
      ZOOKEEPER_TICK_TIME: 2000
      # 宿主机的2181端口 映射到 容器的 2181端口
    ports:
      - "2181:2181"
  # kafka服务
  kafka:
    # 镜像选择
    image: confluentinc/cp-kafka:7.6.0
    # 启动顺序 在zookeeper之后 不保证zookeeper启动成功
    depends_on:
      - zookeeper
    # 将宿主机的9092端口 映射到 容器的 9092端口
    ports:
      - "9092:9092"
    # 环境变量
    environment:
      # kafka的节点brokerId
      KAFKA_BROKER_ID: 1
      # kafka要连接zookeeper的地址 zookeeper 是 compose 的 service 名(容器网络 DNS 可解析)
      KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"

      # 两套监听:宿主机 + 容器内部
      # 因为 Kafka 的客户端既可能来自宿主机,也可能来自容器内部网络,而不同网络环境下访问地址不同,
      # 所以必须配置多个监听器和对应的广播地址。
      # kafka在容器里的所有网卡都监听9092端口 在容器内部监听29092端口
      KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,PLAINTEXT_INTERNAL://0.0.0.0:29092"
      # kafka返回给客户端(对外)的节点地址是advised里面的地址在 本机9092端口 容器里的kafka服务的29092端口使用
      KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9092,PLAINTEXT_INTERNAL://kafka:29092"
      # kafka的监听器使用明文协议 容器内部也使用明文协议
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT,PLAINTEXT_INTERNAL:PLAINTEXT"
      # 集群内部的节点名字 解决数据同步通道问题
      KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT_INTERNAL"
      # 消费位移的副本数量 单机必须是 1
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      # kafka事务相关内部主题的副本数
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      # ISR = "正在同步的副本集合"(你可以先粗略理解为"可用副本") 单机只有一个
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      # consumer group 第一次 rebalance 时的等待时间 ms 重新分配分区的时间间隔
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
  • 3.cd进docker-composer目录,docker composer up -d启动

KafkaDemoConsumer

java 复制代码
package cn.fly.kafkademo.consumer;


import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

@Component
public class KafkaDemoConsumer {

    @KafkaListener(topics = "demo-topic", groupId = "demo-group")
    public void onMessage(String msg) {
        System.out.println("我监听到了:topic为:demo-topic groupId为:demo-group的kafka消息");
        System.out.println("✅ consumed: " + msg);
    }
}

KafkaDemoController

java 复制代码
package cn.fly.kafkademo.controller;

import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.*;
import lombok.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/kafka")
public class KafkaDemoController {

    private final KafkaTemplate<String, String> kafkaTemplate;

    @PostMapping("/send")
    public String send(@RequestParam String msg) {
        System.out.println(msg);
        kafkaTemplate.send("demo-topic", msg);
        return "sent: " + msg;
    }
}

application.yml

yml 复制代码
spring:
  kafka:
  # kafka集群的入口
    bootstrap-servers: localhost:9092
    # 当前消费者属于哪个消费者组
    consumer:
      group-id: demo-group
      # 当offset不存在时怎样读消息 earliest最早 latest最新 none报错
      auto-offset-reset: earliest
    producer:
    # 生产者发送消息后多少节点确认才算成功
      acks: all

pom.xml

xml 复制代码
   <dependencies>
<!--        kafka依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-kafka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Web(做接口测试Kafka用) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

5.常见问题

1.你的maven是否配置正确

  • 如果你的maven配置正确,还是启动不了,还是有依赖没导入,打开maven的生命周期,clean一遍,再compile一遍,重新加载maven,就可以了

2.为什么要双监听

  • listeners 决定 Kafka 实际监听的端口;advertised.listeners 决定 Kafka 返回给客户端的可达地址。单机时两者常可一样,但在 Docker/内外网/NAT 场景下必须区分,否则客户端会拿到不可达的 broker 地址导致连接失败。比如 0.0.0.0:9092 表示容器里所有网卡都监听 9092。

3.什么是网卡

  • 有一个误区,网卡和ip地址难道不一样吗?

  • 不一样,网卡是一个网络接口,一个连通网络的实际通道,而ip地址就是这个通道的地址,网卡就是实际的房子,ip就是门牌号,同一个网卡可以有多个ip,而每个ip只能对应一个网卡

  • 比如:0.0.0.0:9092 表示所有网卡的所有ip都可以监听9092端口,192.168.1.10:9092 表示只有这个ip对应的那个网卡才能监听9092端口

4.那既然listener已经能监听到kafka了 那为啥还要告诉客户端用哪个地址来连接我?

  • listeners 决定"我在哪接电话"
    advertised.listeners 决定"我告诉别人我的电话是多少",在分布式场景下,我的kafka假设我部署在公司私网地址里面,那客户端来访问kafka监听的端口,这个时候advertisedListener返回客户端你应该访问的地址,如果这个地址还是公司私网,那客户端根本连接不了,这说明,advertisedListener解决了kafka监听的地址,可能和kafka告诉客户端应该访问的地址不一致,导致客户端无法访问的问题

5.但是假设我的kafka就只部署在一台机器上呢?

  • 如果kafka只部署在一台机器上,那么就可以一样,监听地址 = 客户端可达地址
相关推荐
予枫的编程笔记2 小时前
【Kafka基础篇】面试高频题:Rebalance触发条件、执行阶段,一篇讲透不踩坑
kafka·rebalance·参数调优·分布式中间件·重平衡机制·面试考点·分布式开发
无心水10 小时前
【任务调度:数据库锁 + 线程池实战】3、 从 SELECT 到 UPDATE:深入理解 SKIP LOCKED 的锁机制与隔离级别
java·分布式·科技·spring·架构
百锦再11 小时前
Java中的char、String、StringBuilder与StringBuffer 深度详解
java·开发语言·python·struts·kafka·tomcat·maven
何中应16 小时前
RabbitMQ安装及简单使用
分布式·后端·消息队列
何中应16 小时前
SpringAMQP消息转化器
分布式·后端·消息队列
indexsunny19 小时前
互联网大厂Java求职面试实战:基于电商场景的技术问答及解析
java·spring boot·redis·kafka·security·microservices·面试指导
百锦再21 小时前
Java中的日期时间API详解:从Date、Calendar到现代时间体系
java·开发语言·spring boot·struts·spring cloud·junit·kafka
Coder_Boy_1 天前
Java高级_资深_架构岗 核心知识点——高并发模块(底层+实践+最佳实践)
java·开发语言·人工智能·spring boot·分布式·微服务·架构
百锦再1 天前
Java IO详解:File、FileInputStream与FileOutputStream
java·开发语言·jvm·spring boot·spring cloud·kafka·maven