kafka实战

分布式安装

//kafka依赖zookeeper,要先启动zookeeper
mkdir etc  // etc 用于存放配置文件

// 将zookeeper配置文件复制到etc
cp config/zookeeper.properties etc

// 复制3份,伪分布式
cp config/server.properties etc/server-0.properties
cp config/server.properties etc/server-1.properties
cp config/server.properties etc/server-2.properties

//在每个server配置文件中修改以下内容
broker.id=?
listeners=PLAINTEXT://XXXX(端口号)
log.dirs=/tmp/kafka-logs-x(id)

//启动zookeeper,启动脚本都在bin目录下,因此要转到bin目录下进行以下操作
./zookeeper-server-start.sh ../etc/zookeeper.properties

//启动kafka实例,同样进入bin,分别在三个窗口执行以下三条命令
./kafka-server-start.sh ../etc/server-0.properties
./kafka-server-start.sh ../etc/server-1.properties
./kafka-server-start.sh ../etc/server-2.properties

//启动完后,新建窗口,进入bin
//创建主题
//--zookeeper 参数是必须的
//--topic 参数用于设置分区名,分区名设为test
//--partitions 参数用于设置分区数量
//--replication-factor 参数用于设置副本数量
./kafka-topics.sh --zookeeper localhost:2181 --create --topic test --partitions 3 --replication-factor 2

//创建成功后,--describe参数 用于查看分区状态
./kafka-topics.sh --zookeeper localhost:2181 --describe --topic test

//使用生产者-消费者进行消息的发送读取 
//新建窗口,bin目录下创建一个生产者,可以在生产者窗口发送消息
./kafka-console-producer.sh --broker-list localhost:9092 localhost:9093 localhost:9094 --topic test
//新建窗口,bin目录下创建一个消费者,消费者能收到生产者发送的消息
./kafka-console-consumer.sh --bootstrap-server localhost:9092 localhost:9093 localhost:9094 --topic test

监听器

是server. properties配置文件的配置项

用于指定broker启动时,本机的监听名称,端口

  • listeners

    • 指定broker启动时的本机监听端口,给服务器端使用
  • advertised.listeners(如果没有配置,采用listeners相同的配置)

    • 对外发布的访问IP和端口,注册到zookeeper中,给客户端使用

    • 如果外网不能访问内网IP,则需要把advertised.listeners 配置如下

      • inernal  
        http://kafka-0:9092  
        external  
        http://公网IP:9093
        
      • 客户端如果处于内网则通过主机名访问

      • 客户端如果处于外网通过公网IP来访问内网

docker部署kafka

搜索 docker hub kafka ,来获取bitnami的kafka镜像

复制docker-compose-cluster.yml,名字改成docker-compose保存到docker-kafka

这个文件中包括:zookeeper容器,kafka0 kafka1 kafka2 三个节点

//在文件包括的这几个里加入container_name和hostname
container_name: zookeeper / kafka-0 / 1 / 2
hostname: zookeeper / kafka-0 / 1 / 2
//每一个节点加入内外网的设置,kafka-0需要按照节点名修改
KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_CFG_LISTENERS=INTERNAL://:9092,EXTERNAL://0.0.0.0:9093
KAFKA_CFG_ADVERTISED_LISTENERS=INTERNAL://kafka-0:9092,EXTERNAL://localhost:9093
KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INTERNAL

//启动执行
docker-compose up -d
//查看状态
docker ps -a
//此时集群创建完成

消费模型及消费顺序

kafka中,分区是最小的并行单位,

  • 一个消费者可以消费多个分区
  • 一个分区可以被多个消费者组里的消费者消费
  • 一个分区不能同时同一个消费者组里的多个消费者消费
    • 出于性能和开销的考虑,同一个组里的消费者去同时消费同一个区的内容,就需要引入锁等

发布订阅模式

每个消费者都属于不同组

点对点

消费者都属于同一个组

分区与消费顺序

生产顺序
  • 同一生产者发送到同一分区的消息,按先后顺序消费(先发的offset更小)
  • 同一生产者发送到不同分区的消息,消费顺序无法保证
消费顺序
  • 按照消息存在分区里的顺序进行消费
  • 保证分区内的消息顺序不保证分区间顺序

如果想保证所有消息的顺序,可以只设置一个分区失去性能拓展性

因此一般设置消息的key相同key的放到一个分区保证相同key的消息的顺序

消息传递语义

  • 至多一次——可能丢失不重复发送
  • 至少一次——不会丢失,但可能会重复
  • 精确一次——保证消息传递到服务端,且在服务端不重复

需要生产者和消费者共同保证:

  • 消费的消息由 信息和索引(消费位置) 组成

  • 事故发生条件:第二步骤失败

  • 至多一次——生产者发送消息失败,broker没有收到,且不进行重试

    • broker中有topic和特殊的topic(offset,存放每一个consumer的消费位置),如果先提交消费位置后读取broker的消费信息,此时消息处理失败了,但offset消息位置已经提交了,下次读取时就会读取下一个offset消息位置
  • 至少一次——生产者发送消息成功,broker成功将消息存放到服务端,但回执时失败,此时生产者重试

    • broker中有topic和特殊的topic(offset,存放每一个consumer的消费位置),如果先读取broker的消费信息,后提交消费位置,如果此时提交消费位置失败,那么下一次读取时,还是会读到上一个消费位置,如此,上一个消息会多次消费
  • 精确一次

    • 生产者实现:幂等参数(指在多次发送同样的消息,Kafka做到发送消息的不丢失和不重复。)

      • enable.idempotence=true
        //retries=Integer.MAX_VALUE  enable.idempotence=true时retries自动为最大值
        Acks=all  // acks必须为all,则意思是leader收到后,follow也同步
        
    • 消费者实现:

      • 通过offset来防止重复消费不是一个好的办法
      • 通常在消息中加入唯一ID(如订单ID),处理业务时,通过判断ID防止重复处理

事务

kafka事务的原子性:一批消息要么全成功,要么全失败(而redis事务,可以允许有失败,而不影响其他消息成功)

  • 但在kafka中,虽然当事务其中有消息失败时,此时一整批都提交失败,但也不影响消费者读取一部分消息
  • 这是因为,在默认隔离级别(脏读)下,kafka中消息会尽可能发送到服务端,如果事务成功了,会在这些消息后注明"成功提交"的标志。

序列化

所谓序列化,就是将对象二进制方式,在网络之间传输或保存,并可以根据特定规则还原成对象

对象 --(序列化)-> 二进制 --(反序列化)-> 对象

序列化优势:

  1. 节省空间,提高网络传输效率
  2. 跨平台
  3. 跨语言

常用消息格式:

  • CSV:适合简单消息
  • JSON:可读性好,但占空间大
  • 序列化消息
    • AVRO:Hadoop,Hive支持好(一般采用这个)
    • Protobuf:谷歌出品

Record Header

序列化时,理想情况是一个主题对应一个对象,判断主题名来确定需要序列化或反序列化(编解码)的对象

现实情况比较复杂:

  • 一个人进行3个主题的活动: 注册用户topic -> 购买商品topic -> 取消订单topic
  • 如果根据理想情况来编程,则设置三个类型的对象,分别为(注册,购买,取消)
  • 但kafka不能保证不同主题topic,不同分区之间的消费顺序,有可能导致逻辑错误
  • 为了保证顺序执行,要将所有事件放在同分区的同主题
    • 用户ID作为分区key,使他们位于相同分区
    • 但此时,同一主题中就包含不同类型的消息,序列化就会不适用
    • 于是就引出了schema registry:
      • 将kafka消息的结构传入schema registry,schema registry返回唯一的id,生产者将id和消息传给kafka。
      • 消费者获取id和消息后,根据id去schema registry中获取消息的结构,然后根据结构来解析消息
      • 缺点:数据解析强依赖schema registry(后续的组件想处理数据,都要将他们接入schema registry,这样违反了分布式的去中心化原则);加入的id破坏了数据本身
  • 如何将所有事件放在同分区的同主题中的同时,实现序列化,并解决上述问题
    • 引入了record header,通过在数据中加入header,通过header来标识数据类型
    • kafka提供了方法可以读到header的信息
posted @ 2024-07-13 11:30  落花流水sas  阅读(8)  评论(0)    收藏  举报