kafka学习总结文档

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a1786223749/article/details/86138375

Kafka学习总结文档

Author xiuhong.chen

Date 2018.7.30

Desc 总结了kafka简介、原理、环境搭建、以及在java中的应用、kafka-manager管理topic等

一、Kafka简介

kafka is a distributed,partitioned,replicated commit logservice。它提供了类似于JMS的特性,但是在实现上完全不同,此外它并不是JMS规范的实现。kafka对消息保存时根据Topic进行归类,发送消息者成为Producer,消息接受者成为Consumer,此外kafka集群有多个kafka实例组成,每个实例()成为broker。无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。

二、基本架构图

image.png

三、基本概念解释

1)Broker

Kafka集群包含一个或多个服务器,这种服务器被称为broker。broker端不维护数据的消费状态,提升了性能。直接使用磁盘进行存储,线性读写,速度快:避免了数据在JVM内存和系统内存之间的复制,减少耗性能的创建对象和垃圾回收。

2)Producer

负责发布消息到Kafka broker;Producer将消息发布到指定的Topic中,同时Producer也能决定将此消息归属于哪个partition;比如基于"round-robin"方式或者通过其他的一些算法等.

3)Consumer

消息消费者,向Kafka broker读取消息的客户端,consumer从broker拉取(pull)数据并进行处理。

本质上kafka只支持Topic.每个consumer属于一个consumer group;反过来说,每个group中可以有多个consumer.发送到Topic的消息,只会被订阅此Topic的每个group中的一个consumer消费.

  • 如果所有的consumer都具有相同的group,这种情况和queue模式很像;消息将会在consumers之间负载均衡.
  • 如果所有的consumer都具有不同的group,那这就是"发布-订阅";消息将会广播给所有的消费者.

在kafka中,一个partition中的消息只会被group中的一个consumer消费;每个group中consumer消息消费互相独立;我们可以认为一个group是一个"订阅"者,一个Topic中的每个partions,只会被一个"订阅者"中的一个consumer消费,不过一个consumer可以消费多个partitions中的消息.kafka只能保证一个partition中的消息被某个consumer消费时,消息是顺序的.事实上,从Topic角度来说,消息仍不是有序的.

4)Topic

每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)

5)Partition

Parition是物理上的概念,每个topic将被分成多个partition(区),每个partition在存储层面是append log文件

6)Consumer Group

每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)

7)Topic & Partition

Topic在逻辑上可以被认为是一个queue,每条消费都必须指定它的Topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对应一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件。若创建topic1和topic2两个topic,且分别有13个和19个分区,则整个集群上会相应会生成共32个文件夹(本文所用集群共8个节点,此处topic1和topic2 replication-factor均为1)。

partitions的目的有多个.最根本原因是kafka基于文件存储.通过分区,可以将日志内容分散到多个上,来避免文件尺寸达到单机磁盘的上限,每个partiton都会被当前broker(kafka实例)保存;可以将一个topic切分多任意多个partitions,来提高消息保存/消费的效率.此外越多的partitions意味着可以容纳更多的consumer,有效提升并发消费的能力.

8)Distribution

一个Topic的多个partitions,被分布在kafka集群中的多个server上;每个server(kafka实例)负责partitions中消息的读写操作;此外kafka还可以配置partitions需要备份的个数(replicas),每个partition将会被备份到多台机器上,以提高可用性.

基于replicated方案,那么就意味着需要对多个备份进行调度;每个partition都有一个为"leader";leader负责所有的读写操作,如果leader失效,那么将会有其他follower来接管(成为新的leader);follower只是单调的和leader跟进,同步消息即可…由此可见作为leader的server承载了全部的请求压力,因此从集群的整体考虑,有多少个partitions就意味着有多少个"leader",kafka会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定.

四、适用场景

1)Messaging

​ 对于一些常规的消息系统,kafka是个不错的选择;partitons/replication和容错,可以使kafka具有良好的扩展性和性能优势.不过到目前为止,我们应该很清楚认识到,kafka并没有提供JMS中的"事务性"“消息传输担保(消息确认机制)”"消息分组"等企业级特性;kafka只能使用作为"常规"的消息系统,在一定程度上,尚未确保消息的发送与接收绝对可靠(比如,消息重发,消息发送丢失等)

2)Website activity tracking

​ kafka可以作为"网站活性跟踪"的最佳工具;可以将网页/用户操作等信息发送到kafka中.并实时监控,或者离线统计分析等

3)Metrics

​ Kafka通常被用于可操作的监控数据。这包括从分布式应用程序来的聚合统计用来生产集中的运营数据提要。

4)Log Aggregation

kafka的特性决定它非常适合作为"日志收集中心";application可以将操作日志"批量""异步"的发送到kafka集群中,而不是保存在本地或者DB中;kafka可以批量提交消息/压缩消息等,这对producer端而言,几乎感觉不到性能的开支.此时consumer端可以使hadoop等其他系统化的存储和分析系统

五、设计原理

kafka的初衷是希望作为一个统一的信息收集平台,能够实时的收集反馈信息,并需要能够支撑较大的数据量,且具备良好的容错能力.

1、持久性

​ kafka使用文件存储消息,这就直接决定kafka在性能上严重依赖文件系统的本身特性.且无论任何OS下,对文件系统本身的优化几乎没有可能.文件缓存/直接内存映射等是常用的手段.因为kafka是对日志文件进行append操作,因此磁盘检索的开支是较小的;同时为了减少磁盘写入的次数,broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数.

2、性能

​ 需要考虑的影响性能点很多,除磁盘IO之外,我们还需要考虑网络IO,这直接关系到kafka的吞吐量问题.kafka并没有提供太多高超的技巧;对于producer端,可以将消息buffer起来,当消息的条数达到一定阀值时,批量发送给broker;对于consumer端也是一样,批量fetch多条消息.不过消息量的大小可以通过配置文件来指定.对于kafka broker端,似乎有个sendfile系统调用可以潜在的提升网络IO的性能:将文件的数据映射到系统内存中,socket直接读取相应的内存区域即可,而无需进程再次copy和交换. 其实对于producer/consumer/broker三者而言,CPU的开支应该都不大,因此启用消息压缩机制是一个良好的策略;压缩需要消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑.可以将任何在网络上传输的消息都经过压缩.kafka支持gzip/snappy等多种压缩方式.

3、生产者

负载均衡: producer将会和Topic下所有partition leader保持socket连接; 消息由producer直接通过socket发送到broker,中间不会经过任何"路由层".事实上,消息被路由到哪个partition上,有producer决定.比如可以采用"random"“key-hash”"轮询"等,如果一个topic中有多个partitions,那么在producer端实现"消息均衡分发"是必要的.

其中partition leader的位置(host:port)注册在zookeeper中,producer作为zookeeper client,已经注册了watch用来监听partition leader的变更事件.

异步发送:将多条消息暂且在客户端buffer起来,并将他们批量的发送到broker,小数据IO太多,会拖慢整体的网络延迟,批量延迟发送事实上提升了网络效率。不过这也有一定的隐患,比如说当producer失效时,那些尚未发送的消息将会丢失。

4、消费者

consumer端向broker发送"fetch"请求,并告知其获取消息的offset;此后consumer将会获得一定条数的消息;consumer端也可以重置offset来重新消费消息.

在JMS实现中,Topic模型基于push方式,即broker将消息推送给consumer端.不过在kafka中,采用了pull方式,即consumer在和broker建立连接之后,主动去pull(或者说fetch)消息;这中模式有些优点,首先consumer端可以根据自己的消费能力适时的去fetch消息并处理,且可以控制消息消费的进度(offset);此外,消费者可以良好的控制消息消费的数量,batch fetch.

其他JMS实现,消息消费的位置是有prodiver保留,以便避免重复发送消息或者将没有消费成功的消息重发等,同时还要控制消息的状态.这就要求JMS broker需要太多额外的工作.在kafka中,partition中的消息只有一个consumer在消费,且不存在消息状态的控制,也没有复杂的消息确认机制,可见kafka broker端是相当轻量级的.当消息被consumer接收之后,consumer可以在本地保存最后消息的offset,并间歇性的向zookeeper注册offset.由此可见,consumer也很轻量级.

5、消息传送机制

​ 对于JMS实现,消息传输担保非常直接:有且只有一次(exactly once).在kafka中稍有不同:

​ 1) at most once: 最多一次,这个和JMS中"非持久化"消息类似.发送一次,无论成败,将不会重发.

​ 2) at least once: 消息至少发送一次,如果消息未能接受成功,可能会重发,直到接收成功.

​ 3) exactly once: 消息只会发送一次.

at most once: 消费者fetch消息,然后保存offset,然后处理消息;当client保存offset之后,但是在消息处理过程中出现了异常,导致部分消息未能继续处理.那么此后"未处理"的消息将不能被fetch到,这就是"at most once".

at least once: 消费者fetch消息,然后处理消息,然后保存offset.如果消息处理成功之后,但是在保存offset阶段zookeeper异常导致保存操作未能执行成功,这就导致接下来再次fetch时可能获得上次已经处理过的消息,这就是"at least once",原因offset没有及时的提交给zookeeper,zookeeper恢复正常还是之前offset状态.

exactly once: kafka中并没有严格的去实现(基于2阶段提交,事务),我们认为这种策略在kafka中是没有必要的.

通常情况下"at-least-once"是我们搜选.(相比at most once而言,重复接收数据总比丢失数据要好).

6、复制备份

​ kafka将每个partition数据复制到多个server上,任何一个partition有一个leader和多个follower(可以没有);备份的个数可以通过broker配置文件来设定.leader处理所有的read-write请求,follower需要和leader保持同步.Follower和consumer一样,消费消息并保存在本地日志中;leader负责跟踪所有的follower状态,如果follower"落后"太多或者失效,leader将会把它从replicas同步列表中删除.当所有的follower都将一条消息保存成功,此消息才被认为是"committed",那么此时consumer才能消费它.即使只有一个replicas实例存活,仍然可以保证消息的正常发送和接收,只要zookeeper集群存活即可.(不同于其他分布式存储,比如hbase需要"多数派"存活才行)

​ 当leader失效时,需在followers中选取出新的leader,可能此时follower落后于leader,因此需要选择一个"up-to-date"的follower.选择follower时需要兼顾一个问题,就是新leader上所已经承载的partition leader的个数,如果一个server上有过多的partition leader,意味着此server将承受着更多的IO压力.在选举新leader,需要考虑到"负载均衡".

7、日志

​ 如果一个topic的名称为"my_topic",它有2个partitions,那么日志将会保存在my_topic_0和my_topic_1两个目录中;日志文件中保存了一序列"log entries"(日志条目),每个log entry格式为"4个字节的数字N表示消息的长度" + “N个字节的消息内容”;每个日志都有一个offset来唯一的标记一条消息,offset的值为8个字节的数字,表示此消息在此partition中所处的起始位置…每个partition在物理存储层面,有多个log file组成(称为segment).segmentfile的命名为"最小offset".kafka.例如"00000000000.kafka";其中"最小offset"表示此segment中起始消息的offset.其中每个partiton中所持有的segments列表信息会存储在zookeeper中.

当segment文件尺寸达到一定阀值时(可以通过配置文件设定,默认1G),将会创建一个新的文件;当buffer中消息的条数达到阀值时将会触发日志信息flush到日志文件中,同时如果"距离最近一次flush的时间差"达到阀值时,也会触发flush到日志文件.如果broker失效,极有可能会丢失那些尚未flush到文件的消息.因为意外实现,仍然会导致log文件格式的破坏(文件尾部),那么就要求当server启东是需要检测最后一个segment的文件结构是否合法并进行必要的修复.

获取消息时,需要指定offset和最大chunk尺寸,offset用来表示消息的起始位置,chunk size用来表示最大获取消息的总长度(间接的表示消息的条数).根据offset,可以找到此消息所在segment文件,然后根据segment的最小offset取差值,得到它在file中的相对位置,直接读取输出即可.

日志文件的删除策略非常简单:启动一个后台线程定期扫描log file列表,把保存时间超过阀值的文件直接删除(根据文件的创建时间).为了避免删除文件时仍然有read操作(consumer消费),采取copy-on-write方式.

8、分配

​ kafka使用zookeeper来存储一些meta信息,并使用了zookeeper watch机制来发现meta信息的变更并作出相应的动作(比如consumer失效,触发负载均衡等)

  1. Broker node registry: 当一个kafka broker启动后,首先会向zookeeper注册自己的节点信息(临时znode),同时当broker和zookeeper断开连接时,此znode也会被删除.

格式: /broker/ids/[0…N] -->host:port;其中[0…N]表示broker id,每个broker的配置文件中都需要指定一个数字类型的id(全局不可重复),znode的值为此broker的host:port信息.

  1. Broker Topic Registry: 当一个broker启动时,会向zookeeper注册自己持有的topic和partitions信息,仍然是一个临时znode.

​ 格式: /broker/topics/[topic]/[0…N] 其中[0…N]表示partition索引号.

  1. Consumer and Consumer group: 每个consumer被创建时,会向zookeeper注册自己的信息;此作用主要是为了"负载均衡".

​ 一个group中的多个consumer可以交错的消费一个topic的所有partitions;简而言之,保证此topic的所有partitions都能被此group所消费,且消费时为了性能考虑,让partition相对均衡的分散到每个consumer上.

  1. Consumer id Registry: 每个consumer都有一个唯一的ID(host:uuid,可以通过配置文件指定,也可以由系统生成),此id用来标记消费者信息.

​ 格式:/consumers/[group_id]/ids/[consumer_id]

​ 仍然是一个临时的znode,此节点的值为{“topic_name”:#streams…},即表示此consumer目前所消费的topic + partitions列表.

  1. Consumer offset Tracking: 用来跟踪每个consumer目前所消费的partition中最大的offset.

​ 格式:/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]–>offset_value

​ 此znode为持久节点,可以看出offset跟group_id有关,以表明当group中一个消费者失效,其他consumer可以继续消费.

  1. Partition Owner registry: 用来标记partition被哪个consumer消费.临时znode

格式:/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]–>consumer_node_id当consumer启动时,所触发的操作:

​ A) 首先进行"Consumer id Registry";

​ B) 然后在"Consumer id Registry"节点下注册一个watch用来监听当前group中其他consumer的"leave"和"join";只要此znode path下节点列表变更,都会触发此group下consumer的负载均衡.(比如一个consumer失效,那么其他consumer接管partitions).

​ C) 在"Broker id registry"节点下,注册一个watch用来监听broker的存活情况;如果broker列表变更,将会触发所有的groups下的consumer重新balance.

9、生产者消费者通信图

  1. Producer端使用zookeeper用来"发现"broker列表,以及和Topic下每个partition leader建立socket连接并发送消息.

  2. Broker端使用zookeeper用来注册broker信息,以及监测partition leader存活性.

  3. Consumer端使用zookeeper用来注册consumer信息,其中包括consumer消费的partition列表等,同时也用来发现broker列表,并和partition leader建立socket连接,并获取消息.

六、主要配置

1.Broker配置

/usr/local/software/kafka/kafka_2.11-1.1.0/config/server.properties

#每一个boker都有一个唯一的id作为它们的名字。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况
broker.id=0  
#broker server服务端口
port=9092
#broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK
#host.name=
#broker处理消息的最大线程数,一般情况下数量为cpu核数
num.network.threads=3  
#处理IO的线程数
num.io.threads=8  
socket.send.buffer.bytes=102400  
socket.receive.buffer.bytes=102400  
socket.request.max.bytes=104857600 
#kafka数据的存放地址,多个地址的话用逗号分割,多个目录分布在不同磁盘上可以提高读写性能  /data/kafka-logs-1,/data/kafka-logs-2
log.dirs=/tmp/kafka-logs  
#默认分区数
num.partitions=2  
num.recovery.threads.per.data.dir=1

offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1


#控制一个log保留多长个小时
log.retention.hours=168  
#单一的log segment文件大小
log.segment.bytes=1073741824  
log.retention.check.interval.ms=300000  
#是否log cleaning
log.cleaner.enable=false  

#指定zookeeper连接字符串, 格式如hostname:port/chroot。chroot是一个namespace
zookeeper.connect=localhost:2181  
#连接zk的session超时时间
zookeeper.connection.timeout.ms=6000

group.initial.rebalance.delay.ms=0

2.消费者主要配置

bootstrap.servers=localhost:9092

#当前消费者的Group名称,需要指定
group.id=test-consumer-group

3.生产者的主要配置

bootstrap.servers=localhost:9092
compression.type=none

以上是关于kafka一些基础说明,在其中我们知道如果要kafka正常运行,必须配置zookeeper,否则无论是kafka集群还是的生存者和消费者都无法正常的工作的,以下是对zookeeper进行一些简单的介绍:

七、Zookeeper集群

zookeeper是一个为分布式应用提供一致性服务的软件,它是开源的Hadoop项目的一个子项目,并根据google发表的一篇论文来实现的。zookeeper为分布式系统提供了高效且易于使用的协同服务,它可以为分布式应用提供相当多的服务,诸如统一命名服务,配置管理,状态同步和组服务等。zookeeper接口简单,我们不必过多地纠结在分布式系统难于处理的同步和一致性问题上,你可以使用zookeeper提供的现成(off-the-shelf)服务来实现来实现分布式系统额配置管理,组管理,Leader选举等功能。

Zookeeper安装方式有三种,单机模式集群模式以及伪集群模式

■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境;
■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例;
■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble)

Zookeeper通过复制来实现高可用性,只要集合体中半数以上的机器处于可用状态,它就能够保证服务继续。为什么一定要超过半数呢?这跟Zookeeper的复制策略有关:zookeeper确保对znode 树的每一个修改都会被复制到集合体中超过半数的机器上。

关于Zookeeper的功能和工作原理可以参考:https://www.cnblogs.com/felixzh/p/5869212.html

1.单机模式搭建

wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/stable/zookeeper-3.4.12.tar.gz

tar -zxvf zookeeper-3.4.12.tar.gz #解压安装包

cd /usr/local/software/zookeeper/zookeeper-3.4.12/conf #进入配置文件所在的目录

mv zoo_sample.cfg zoo.cfg #创建一个配置文件zoo.cfg

vim zoo.cfg #配置zookeeper监听的端口号

#基本事件单元,这个时间是作为Zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,
#每隔tickTime时间就会发送一个心跳;最小 的session过期时间为2倍tickTime 
tickTime=2000
#允许follower连接并同步到Leader的初始化连接时间,以tickTime为单位。当初始化连接时间超过该值,则表示连接失败
initLimit=10
#Leader与Follower之间发送消息时,请求和应答时间长度。如果follower在设置时间内不能与leader通信,那么此follower将会被丢弃
syncLimit=5
#存储内存中数据库快照的位置,如果不设置参数,更新食物的日志将被存储到默认位置
dataDir=/tmp/zookeeper
#监听客户端连接的端口
clientPort=2181

#server.A=B:C:D 
#A:其中 A 是一个数字,表示这个是服务器的编号;
#B:是这个服务器的 ip 地址;
#C:第一个端口是master和slave之间的通信端口
#D:第二个端口是leader选举的端口,集群刚启动的时候选举或者leader挂掉之后进行新的选举的端口
#server.1=118.25.175.161:3181:4181

./zkServer.sh start #启动zookeeper

2.伪集群模式搭建

Zookeeper不但可以在单机上运行单机模式Zookeeper,而且可以在单机模拟集群模式 Zookeeper的运行,也就是将不同节点运行在同一台机器。我们知道伪分布模式下Hadoop的操作和分布式模式下有着很大的不同,但是在集群为分布 式模式下对Zookeeper的操作却和集群模式下没有本质的区别。显然,集群伪分布式模式为我们体验Zookeeper和做一些尝试性的实验提供了很大 的便利。比如,我们在实验的时候,可以先使用少量数据在集群伪分布模式下进行测试。当测试可行的时候,再将数据移植到集群模式进行真实的数据实验。这样不 但保证了它的可行性,同时大大提高了实验的效率。这种搭建方式,比较简便,成本比较低,适合测试和学习,如果你的手头机器不足,就可以在一台机器上部署3个server,监听不同的端口号。

在一台机器上部署了3个server,需要注意的是在集群为分布式模式下我们使用的每个配置文档模拟一台机器,也就是说单台机器及上运行多个Zookeeper实例。但是,必须保证每个配置文档的各个端口号不能冲突,除了clientPort不同之外,dataDir也不同。另外,还要在dataDir所对应的目录中创建myid文件来指定对应的Zookeeper服务器实例。

■ clientPort端口:如果在1台机器上部署多个server,那么每台机器都要不同的 clientPort,比如 server1是2181,server2是2182,server3是2183

■ dataDir和dataLogDir:dataDir和dataLogDir也需要区分下,将数据文件和日志文件分开存放,同时每个server的这两变量所对应的路径都是不同的

■ server.X和myid: server.X 这个数字就是对应,data/myid中的数字。在3个server的myid文件中分别写入了0,1,2,那么每个server中的zoo.cfg都配 server.0 server.2,server.3就行了。因为在同一台机器上,后面连着的2个端口,3个server都不要一样,否则端口冲突

下面是我所配置的集群伪分布模式,分别通过zoo1.cfg、zoo2.cfg、zoo3.cfg来模拟由三台机器的Zookeeper集群

zoo1.cfg如下:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zk/data_1
clientPort=2181
dataLogDir=/usr/local/zk/logs_1

server.0=localhost:2287:3387
server.1=localhost:2288:3388
server.2=localhost:2289:3389

zoo2.cfg如下:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zk/data_2
clientPort=2182
dataLogDir=/usr/local/zk/logs_2

server.0=localhost:2287:3387
server.1=localhost:2288:3388
server.2=localhost:2289:3389

zoo3.cfg如下:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zk/data_3
clientPort=2183
dataLogDir=/usr/local/zk/logs_3

server.0=localhost:2287:3387
server.1=localhost:2288:3388
server.2=localhost:2289:3389

在集群为分布式下,我们只有一台机器,按时要运行三个Zookeeper实例。此时,如果在使用单机模式的启动命令是行不通的。此时,只要通过下面三条命令就能运行前面所配置的Zookeeper服务。如下所示:

zkServer.sh start zoo1.sh
zkServer.sh start zoo2.sh
zkServer.sh start zoo3.sh

3.服务器集群搭建

搭建要求:

(1) zk服务器集群规模不小于3个节点
(2) 要求各服务器之间系统时间要保持一致

在每台服务器上安装zookeeper,安装方式同单机模式搭建。

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zk/data_3
clientPort=2183
dataLogDir=/usr/local/zk/logs_3

server.0=hadoop:2888:3888
server.1=hadoop0:2888:3888
server.2=hadoop1:2888:3888

在三个节点上分别执行命令zkServer.sh status,从下面的图中我们会发现hadoop和hadoop1为Follower,hadoop0为Leader

image.png

八、Kafka环境搭建

1.安装kafka

wget https://mirrors.tuna.tsinghua.edu.cn/apache/kafka/1.1.0/kafka_2.11-1.1.0.tgz

tar -zxvf kafka_2.11-1.1.0.tgz #解压安装包

cd /usr/local/software/kafka/kafka_2.11-1.1.0/bin #进入kafka目录

vim …/config/server.properties #修改server.properties 设置zookeeper连接地址,和broker.id

broker.id=0
advertised.listeners=PLAINTEXT://118.25.175.161:9092
zookeeper.connect=118.25.175.161:2181,10.211.38.217:2181

./kafka-server-start.sh …/config/server.properties & #后台启动kafka

image.png

九、Kafka常用命令

1.启停kafka

其中">>/dev/null"表示将日志信息输出到"黑洞",其中"2>&1"表示将错误信息和前面的日志信息一样,也输出到"黑洞",末尾的"&"表示以后台方式启动kafka

./bin/kafka-server-start.sh config/server.properties >>/dev/null 2>&1 &
./bin/kafka-server-stop.sh config/server.properties >>/dev/null 2>&1 &

2.创建topic

–create表示创建,此处创建一个名为"test"的Topic,指定了zookeeper地址,备份数,分区数

./kafka-topics.sh --create --zookeeper 118.25.175.161:2181 --replication-factor 1 --partitions 1 --topic test

3.查看topic

./kafka-topics.sh --list --zookeeper 118.25.175.161:2181

4.修改topic

–alter表示修改,此处增加test的分区数为3

./kafka-topics.sh --zookeeper 118.25.175.161:2181 --alter --topic test --partitions 3

5.显示topic的详细信息

./kafka-topics.sh --list --zookeeper 118.25.175.161:2181

6.创建生产者

指定生产者发送消息时,往“test”这个topic中存放消息

./kafka-console-producer.sh --broker-list localhost:9092 --topic test  

7.创建消费者

从"test"这个Topic中消费消息,其中"–from-beginning"为可选参数,表示要从头消费消息:

./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning 

测试结果如下:开启了两个窗口,一个生产消息,消费者这边会自动显示消息

image.png

十、kafka在java中的应用

此时测试单机kafka生成消费消息。

1.引入Kafka依赖

	<!--kafka-->
    <dependency>
      <groupId>org.apache.kafka</groupId>
      <artifactId>kafka_2.10</artifactId>
      <version>0.10.0.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.kafka</groupId>
      <artifactId>kafka-clients</artifactId>
      <version>0.10.0.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.kafka</groupId>
      <artifactId>kafka-streams</artifactId>
      <version>0.10.0.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.kafka</groupId>
      <artifactId>spring-kafka</artifactId>
      <version>2.1.8.RELEASE</version>
    </dependency>
    <!---->

2.常量配置

首先将常用的ip信息等设置在配置文件中

package testKafka;

/**
 * @author: Chen Xiuhong
 * @date: 2018/7/30 10:11
 * @description:
 */
public class KafkaProperties {
    public static final String BOOTSTRAP_SERVERS = "10.211.38.217:9092";
    public static final String ZK_SERVER = "118.25.175.161:2181";
    public static final String  GROUP_ID= "test02";
    public static final String  BROKER_LIST= "118.25.175.161:9092,10.211.38.217:9092";

}

3.Producer

生产者往“topic1”生产消息,消息生产之后在控制台打印消息

package testKafka;

import org.apache.kafka.clients.producer.*;
import java.util.Properties;
import static testKafka.KafkaProperties.BROKER_LIST;

/**
 * @author: Chen Xiuhong
 * @date: 2018/7/18 20:29
 * @description:
 */
public class TestKafkaProducer {
    public static void main(String[] args) {
        produceMessage();
    }

    private static void produceMessage(){
        Properties producerProps = new Properties();
        /*
        bootstrap.servers 只是代表kafka的连接入口,只需要指定集群中的某一broker
        集群是通过配置bootstrap.servers指定一个或多个broker。不用指定全部的broker,它将自动发现集群中的其余的borker(最好指定多个,万一有服务器故障)。
        */
        producerProps.put("bootstrap.servers","118.25.175.161:9092");

        //key.serializer和value.serializer指示如何将用户用ProducerRecord提供的键和值对象转换为字节。
        producerProps.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
        producerProps.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        Producer<String, String> producer = new KafkaProducer<String, String>(producerProps);

        try {
            for(int i=0; i<10; i++){
                //"topic1"为topic    生产消息,标记topic为topic1
                producer.send(new ProducerRecord<String, String>("topic1", "2018.7.30生产消息"), new Callback() {
                    @Override
                    public void onCompletion(RecordMetadata metadata, Exception e) {
                        if (e != null) {
                            e.printStackTrace();
                        } else {
                            System.out.println("metadata:"+metadata.toString()+"    offset:"+metadata.offset());
                        }
                    }
                });
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            producer.close();
        }
    }

}

4.Consumer

消费者消费“topic1”中的消息,打印消费的消息详细信息

package testKafka;

import org.apache.kafka.clients.consumer.ConsumerRebalanceListener;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;

import java.util.*;

import static testKafka.KafkaProperties.BROKER_LIST;
import static testKafka.KafkaProperties.GROUP_ID;

/**
 * @author: Chen Xiuhong
 * @date: 2018/7/18 20:37
 * @description:
 */
public class TestKafkaConsumer {
    public static void main(String[] args) throws Exception {
        //一旦consumer和kakfa集群建立连接,consumer会以心跳的方式来高速集群自己还活着,如果session.timeout.ms 内心跳未到达服务器,服务器认为心跳丢失,会做rebalence。
        Properties props = new Properties();
        /*
        bootstrap.servers 只是代表kafka的连接入口,只需要指定集群中的某一broker
        集群是通过配置bootstrap.servers指定一个或多个broker。不用指定全部的broker,它将自动发现集群中的其余的borker(最好指定多个,万一有服务器故障)。
        */
        props.put("bootstrap.servers", "118.25.175.161:9092");
        //消费者的组id,这里是test02
        props.put("group.id", GROUP_ID);
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        //从poll(拉)的回话处理时长
        props.put("session.timeout.ms", "30000");
        //处理字节消息
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

        final KafkaConsumer<String, String> consumer = new KafkaConsumer<String,String>(props);
        consumer.subscribe(Arrays.asList("topic1"),new ConsumerRebalanceListener() {
            @Override
            public void onPartitionsRevoked(Collection<TopicPartition> collection) {
            }
            @Override
            public void onPartitionsAssigned(Collection<TopicPartition> collection) {
                //将偏移设置到最开始
                consumer.seekToBeginning(collection);
            }
        });
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("topic=%s partition=%d offset = %d, key = %s, value = %s%n",record.topic(),record.partition(), record.offset(), record.key(), record.value());
            }
        }
    }
}

启动生产类报错:观察日志发现是不能正确解析主机名造成的。

image.png

添加window解析在本机的hosts 文件增加 118.25.175.161 VM_0_3_centos,用cmd ping bogon 试试如果可以ping通即可。

或者在kafka的server.properties文件中添加 advertised.listeners=PLAINTEXT://118.25.175.161:9092 (主机名:监听端口)

image.png

分别启动生产者消费者,打印日志如下:

image.png
image.png

十一、Kafka集群部署

由于测试时服务器资源优先,此处使用两太虚拟机搭建

1.环境要求

  • linux一台或多台,大于等于2
  • 已经搭建好的zookeeper集群
  • 软件版本kafka_2.11-0.9.0.1.tgz

2.安装Kafka

在每台虚拟机上安装kafka,安装过程同第八节的单机环境搭建一样,两个kafka-server的zookeeper地址一样,表示在同一个集群里,不同的是broker.id不一样。如下:

server0:

broker.id=0
advertised.listeners=PLAINTEXT://118.25.175.161:9092
zookeeper.connect=118.25.175.161:2181,10.211.38.217:2181

server1:

broker.id=1
advertised.listeners=PLAINTEXT://118.25.175.161:9092
zookeeper.connect=118.25.175.161:2181,10.211.38.217:2181

3.启动kafka集群

分别启动两台kafka-server,其实就是启动kafka集群,然后在server0中创建topic,在server1中可以看到刚才的topic,说明集群搭建成功

image.png

image.png

十二、Kafka-Manager集群管理工具

为了简化开发者和服务工程师维护Kafka集群的工作,yahoo构建了一个叫做Kafka管理器的基于Web工具,叫做 Kafka Manager。这个管理工具可以很容易地发现分布在集群中的哪些topic分布不均匀,或者是分区在整个集群分布不均匀的的情况。它支持管理多个集群、选择副本、副本重新分配以及创建Topic。同时,这个管理工具也是一个非常好的可以快速浏览这个集群的工具,有如下功能:

1.管理多个kafka集群
2.便捷的检查kafka集群状态(topics,brokers,备份分布情况,分区分布情况)
3.选择你要运行的副本
4.基于当前分区状况进行
5.可以选择topic配置并创建topic(0.8.1.1和0.8.2的配置不同)
6.删除topic(只支持0.8.2以上的版本并且要在broker配置中设置delete.topic.enable=true)
7.Topic list会指明哪些topic被删除(在0.8.2以上版本适用)
8.为已存在的topic增加分区
9.为已存在的topic更新配置
10.在多个topic上批量重分区
11.在多个topic上批量重分区(可选partition broker位置)

1.环境要求

  • Java 8+
  • zookeeper 2+
  • 本次实验所用的环境是kafka_2.11-1.1.0 , jdk-1.8.0_60 , zookeeper-3.4.12,下载解压安装运行

2.安装sbt

3.下载安装kafka-manager

#前提是服务器上安装有git工具,或者在win下载好之后,然后传输到服务器上,再解压缩unzip kafka-manager-master.zip
git clone https://github.com/yahoo/kafka-manager.git  

cd kafka-manager-master

#备注:使用sbt编译打包的时候时间可能会比较长
#可以将project/plugins.sbt 中的logLevel参数修改为logLevel := Level.Debug(默认为Warn)
vim project/plugins.sbt 

#因为要编译。所以下面这步操作要等很久(巨慢,还经常没反应,多执行几次,耐心等待)
sbt clean distcd target/ 

#解压并修改配置文件
unzip kafka-manager-1.3.0.4.zip
vim kafka-manager-1.3.0.4/conf/application.conf 

#将application.conf中的kafka-manager.zkhosts的值设置为你的zk地址 如下
#若搭建的是kafka集群,用逗号隔开,kafka-manager.zkhosts="10.0.0.50:12181,10.0.0.60:12181,10.0.0.70:12181"
kafka-manager.zkhosts="118.25.175.161:2181"
pinned-dispatcher.type="PinnedDispatcher"
pinned-dispatcher.executor="thread-pool-executor"


#启动kafka-manager,默认端口是9000
#启动完毕后可以查看端口是否启动,由于启动过程需要一段时间,端口起来的时间可能会延后。
cd kafka-manager-1.3.0.4/
nohup /usr/local/software/kafka-manager-1.3.0.4/bin/kafka-manager -Dconfig.file=/usr/local/software/kafka-manager-1.3.0.4/conf/application.conf -Dhttp.port 9000 &
或者/usr/local/software/kafka-manager-1.3.0.4/bin/kafka-manager &

访问http://118.25.175.161:9000/ 主界面如下:

image.png

4.使用kafka-manager

点击【Cluster】>【Add Cluster】打开如下添加集群的配置界面:

输入集群的名字(如test-Cluster)和 Zookeeper 服务器地址(如118.25.175.161:2181),选择最接近的Kafka版本(如0.8.1.1

注意:如果没有在 Kafka 中配置过 JMX_PORT,千万不要选择第一个复选框。
Enable JMX Polling 如果选择了该复选框,Kafka-manager 可能会无法启动。

image.png

然后可以自己在界面上查看broker、Topic,然后自己创建topic,设置partition数量等;点击topic名字还可以查看topic的详细信息

image.png

5.问题

当生成很多消息时,界面就会报超时错误

image.png

解决方案:把application.conf中的kafka-manager.api-timeout-millis调大

猜你喜欢

转载自blog.csdn.net/a1786223749/article/details/86138375
今日推荐