在高并发的应用场景中,由于来不及同步处理请求,接收到的请求往往会发生阻塞。例如,大量的新增、更新请求同时到达数据库,这会导致行或表被锁住,最后会因为请求堆积过多,造成连接数过多(too many connections)的异常或者time out异常。因此,在高并发的应用场景中,需要一个缓冲机制,而消息队列则可以很好的充当这个角色,通过异步处理请求来削峰填谷,缓解系统的压力。
消息队列可以简单理解为:把要传输的数据放在队列中,拥有先进先出(FIFO)的特性。它主要用于不同进程或线程之间的通信,用来处理一系列的输入请求。消息队列采用异步通信机制。消息的发送者和接受者无须同时与消息队列进行数据交换,消息会一直保存在队列中,直至被接收者读取。
Kafka是由LinkedIn开发的一个分布式的消息系统,可独立部署在单台服务器上,也可部署在多台服务器上构成集群。它提供了发布与订阅功能。用户可以发送数据到Kafka集群中,也可以从Kafka集群中读取数据。Kafka使用Scala编写,它以可水平扩展和高吞吐率而被广泛使用。
目前越来越多的开源分布式处理系统如Storm,Spark,Flink都支持与Kafka集成。现在我们的数据实时处理平台也使用到了kafka。现在它已被多家不同类型的公司作为多种类型的数据管道和消息系统使用。
对比Kafka与Apache ActiveMQ和RabbitMQ,主要原因是kafka有更好的性能。Kafka性能好的主要原因包括:
Kafka使用了顺序IO(Sequential IO),并极力避免随机磁盘访问(Random Disk Access)。前者的写入速度比后者快了一个数量级,比如在一个由6块7200转SATA硬盘组成的磁盘阵列上,顺序写入的速度可以达到300MB/S,而随机写入速度只有50KB/S。
差距如此之大,难怪Kafka会快得飞起来。Kafka所采用的提交日志就是以追加的方式写入分区的,就是说单个分区的写入是可以保证顺序的,没有删除和更新操作,因此避免了随机写入。另外,从分区读取数据的时候也是按顺序读取的,避免了随机读取。
那么问题来了,就算顺序IO再快,也快不过内存,那么为什么Kafka不用内存来保存数据呢?第一个原因:内存虽快,但比硬盘要贵得多。Kafka作为一个大数据生态系统的一员,是为保存海量数据而生的,使用内存来保存海量数据显然是不现实的。
另外,Kafka的高可用是通过创建多个副本来实现的,一个消息可能会被复制三份五份,这无疑又增加了存储开销,使用内存来存储就更是天方夜谭。除此之外,Kafka运行在JVM上,如果内存堆中的对象太多,必然会在垃圾回收时造成严重的延迟,从而影响系统的整体性能。
内存映射文件将磁盘上的文件内容与内存映射起来,我们往内存里写入数据,操作系统会在稍后把数据冲刷到磁盘上。所以,在写入数据时几乎就是写入内存的速度,这是Kafka快到飞起的另一个原因。
Kafka使用了零拷贝技术,也就是直接将数据从内核空间的读缓冲区直接拷贝到内核空间的socket缓冲区,然后再写入到NIC缓冲区,避免了在内核空间和用户空间之间穿梭。
除了利用底层的技术外,Kafka还在应用程序层面提供了一些手段来提升性能。最明显的就是使用批次。在向Kafka写入数据时,可以启用批次写入,这样可以避免在网络上频繁传输单个消息带来的延迟和带宽开销。假设网络带宽为10MB/S,一次性传输10MB的消息比传输1KB的消息10000万次显然要快得多。
消息生产者,就是向kafka broker发消息的客户端;Producer作为消息的生产者,在生产完消息后需要将消息投送到指定的目的地(某个topic的某个partition)。Producer可以根据指定选择partition的算法或者是随机方式来选择发布消息到哪个partition。例如,通过获取消息记录主键(Key)的哈希值,然后使用该值对分区数取模运算,得到分区索引。# 计算topic 分区的索引值分区索引值 = 键的哈希值取绝对值 % 分区数int partition = Math.abs(key.hashCode()) % numPartitions;
消息消费者,向kafka broker拉取消息的客户端。
可以理解为一个队列。一个topic中通常放置一类消息,每个topic都有一个或者多个订阅者,也就是消息的消费者consumer;
消费者组,这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。一个topic可以有多个CG。topic的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个partion只会把消息发给该CG中的一个consumer。如果需要实现广播,只要每个consumer有一个独立的CG就可以了。
要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组,而不需要多次发送消息到不同的topic;
代理,一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic;
分区,为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)。
kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。每个partition有多个副本分布在不同的broker中。消息在partition分区内有序,整体不一定有序。如下图所示,一个topic分为了4个partition分区:
偏移量,kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka。
每个partition在存储层面是一个append log文件,发布到此partition的消息会追加到log文件的尾部,为顺序写入磁盘(顺序写磁盘比随机写内存的效率还要高)。每条消息在log文件中的位置成为offset(偏移量),offset为一个long型数字,唯一标记一条消息。每个消费者唯一保存的元数据是offset值,这个位置完全为消费者控制,因此消费者可以采用任何顺序来消费记录。
(1)Topic 与broker一个Broker上可以创建一个或者多个Topic。同一个topic可以在同一集群下的多个Broker中分布。
(2)消费者组、消息、分区第一,同一个消费者组里面不能有多个消费者去消费消息,只能有一个消费者去消费。第二,同一个消费者组里面是不会重复消费消息的。第三,同一个消费者组的一个消费者不是以一条一条数据为单元的,是以分区为单元,就相当于消费者和分区建立某种socket连接,进行传输数据,所以,一旦建立这个关系,这个分区的内容只能是由这个消费者消费。(3)为什么说kafka是分布式模型呢?首先,同一个kafka集群共同拥有一个topic,而同一个topic又拥有不同的分区,不同的分区可以分布在不同的borker上,也就是不同的机器上,所以,分区是分布式的,则数据也是分布式的,kafka就是分布式模式。对上述各组件了解之后,现在就应该可以很容易地理解Kafka的架构图。Kafka是一个分布式系统,用Zookeeper来管理、协调Kafka集群中的各个代理(Broker)节点。当Kafka集群中新添加一个代理节点,或者某一台代理节点出现故障时,Zookeeper服务将会通知生产者应用程序和消费者应用程序去其他的正常代理节点读写。
Kafka的常用业务场景有如下几个方面:
日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Elasticsearch等等。
消息系统:解耦生产者和消费者、缓存消息等。
更多Java技术笔记共享可以关注公众号:麒麟改bug获取!
用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
运营指标:Kafka也经常用来记录运营监控数据,包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。流式处理:Kafka 是一个流处理平台,所以在实际应用场景中也会和其他大数据套件结合使用,比如Spark Streaming、Storm、Flink等等。
事件源:事件源是一种应用程序的设计风格,其中状态更改会产生一条带有时间戳的记录,然后将这条以时间序列产生的记录进行保存。在面对非常大的状态更改需求时,可以使用这种方式来构建非常稳定可靠的后端应用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。