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中消息会尽可能发送到服务端,如果事务成功了,会在这些消息后注明"成功提交"的标志。
序列化
所谓序列化,就是将对象以二进制方式,在网络之间传输或保存,并可以根据特定规则还原成对象
对象 --(序列化)-> 二进制 --(反序列化)-> 对象
序列化优势:
- 节省空间,提高网络传输效率
- 跨平台
- 跨语言
常用消息格式:
- 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的信息
浙公网安备 33010602011771号