关于kafka:Kafka-Producer-异步发送消息居然也会阻塞

Kafka 始终以来都以高吞吐量的个性而妇孺皆知,就在上周,在一个性能监控我的项目中,须要应用到 Kafka 传输海量音讯,在这过程中遇到了一个 Kafka Producer 异步发送音讯会被阻塞的问题,导致生产端发送耗时很大。 是的,你没听错,Kafka Producer 异步发送音讯也会产生阻塞景象,那到底是怎么回事呢? 在新版的 Kafka Producer 中,设计了一个音讯缓冲池,客户端发送的音讯都会被存储到缓冲池中,同时 Producer 启动后还会开启一个 Sender 线程,一直地从缓冲池获取音讯并将其发送到 Broker,如下图所示: 这么看来,Kafka 的所有发送,都能够看作是异步发送了,因而在新版的 Kafka Producer 中废除掉异步发送的办法了,仅保留了一个 send 办法,同时返回一个 Futrue 对象,须要同步期待发送后果,就应用 Futrue#get 办法阻塞获取发送后果。而我在我的项目中间接调用 send 办法,为何还会发送阻塞呢? 咱们在构建 Kafka Producer 时,会有一个自定义缓冲池大小的参数 buffer.memory,默认大小为 32M,因而缓冲池的大小是有限度的,咱们无妨想一下,缓冲池内存资源耗尽了会怎么样? Kafka 源码的正文是十分具体的,RecordAccumulator 类是 Kafka Producer 缓冲池的外围类,而 RecordAccumulator 类就有那么一段正文: The accumulator uses a bounded amount of memory and append calls will block when that memory is exhausted, unless this behavior is explicitly disabled.大略的意思是: ...

September 13, 2020 · 2 min · jiezi

关于kafka:Kafka详细学习资料

Kafka一. 消息中间件的益处1.解耦 容许你独立的扩大或批改两边的处理过程,只有确保它们恪守同样的接口束缚。如果为以能解决这类峰值拜访为规范来投入资源随时待命无疑是微小的节约。应用音讯队列可能使要害组件顶住突发的拜访压力,而不会因为突发的超负荷的申请而齐全解体。 2.异步 很多时候,用户不想也不须要立刻解决音讯。音讯队列提供了异步解决机制,容许用户把一个音讯放入队列,但并不立刻解决它。想向队列中放入多少音讯就放多少,而后在须要的时候再去解决它们。 3.灵活性/削峰 在访问量剧增的状况下,利用依然须要持续发挥作用,然而这样的突发流量并不常见。如果为以能解决这类峰值拜访为规范来投入资源随时待命无疑是微小的节约。应用音讯队列可能使要害组件顶住突发的拜访压力,而不会因为突发的超负荷的申请而齐全解体。 4.可恢复性 零碎的一部分组件生效时,不会影响到整个零碎。音讯队列升高了过程间的耦合度,所以即便一个解决音讯的过程挂掉,退出队列中的音讯依然能够在零碎复原后被解决。 5.缓冲 有助于管制和优化数据流通过零碎的速度,解决生产音讯和生产音讯的处理速度不统一的状况。 二. 音讯队列通信的模式1.点对点模式(一对一,消费者被动拉取数据,音讯收到后音讯革除) 2.公布订阅模式(一对多,消费者生产数据之后不会革除音讯)kafka个别应用的是生产方拉取,会始终轮询,浪费资源。(能够设置一个等待时间,在没有拉取到信息的时候,会期待设置的工夫) 三. KafkaKafka是由Apache软件基金会开发的一个开源流解决平台,由Scala(一品种java语言)和Java编写。Kafka是一种高吞吐量的分布式的基于公布/订阅模式的音讯队列,次要利用于大数据实时处理畛域(spark实时剖析框架)。 1.Kafka的个性高吞吐量、低提早:kafka每秒能够解决几十万条音讯,它的提早最低只有几毫秒,每个topic能够分多个partition, consumer group 对partition进行consume操作。可扩展性:kafka集群反对热扩大持久性、可靠性:音讯被长久化到本地磁盘,并且反对数据备份避免数据失落容错性:容许集群中节点失败(若正本数量为n,则容许n-1个节点失败)高并发:反对数千个客户端同时读写2.Kafka的根本架构 1)Producer :音讯生产者,就是向 kafka broker 发消息的客户端; 2)Consumer :音讯消费者,向 kafka broker 取音讯的客户端; 3)Consumer Group (CG):消费者组,由多个 consumer 组成。消费者组内每个消费者负责生产不同分区的数据,一个分区只能由一个组内消费者生产;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。 4)Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker能够包容多个 topic。 5)Topic :能够了解为一个队列,生产者和消费者面向的都是一个 topic; 6)Partition:为了实现扩展性,一个十分大的 topic 能够散布到多个 broker(即服务器)上,一个 topic 能够分为多个 partition,每个 partition 是一个有序的队列; 7)Replica:正本,为保障集群中的某个节点产生故障时,该节点上的 partition 数据不失落,且 kafka 依然可能持续工作,kafka 提供了正本机制,一个 topic 的每个分区都有若干个正本,一个 leader 和若干个 follower。 8)leader:每个分区多个正本的“主”,生产者发送数据的对象,以及消费者生产数据的对象都是 leader。 9)follower:每个分区多个正本中的“从”,实时从 leader 中同步数据,放弃和 leader 数据的同步。leader 产生故障时,某个 follower 会成为新的 follower ...

September 1, 2020 · 10 min · jiezi

关于kafka:Kafka和RocketMQ底层存储之那些你不知道的事

大家好,我是yes。 咱们都晓得 RocketMQ 和 Kafka 音讯都是存在磁盘中的,那为什么音讯存磁盘读写还能够这么快?有没有做了什么优化?都是存磁盘它们两者的实现之间有什么区别么?各自有什么优缺点? 明天咱们就来一探到底。 存储介质-磁盘一般而言消息中间件的音讯都存储在本地文件中,因为从效率来看间接放本地文件是最快的,并且稳定性最高。毕竟要是放相似数据库等第三方存储中的话,就多一个依赖少一份平安,并且还有网络的开销。 那对于将音讯存入磁盘文件来说一个流程的瓶颈就是磁盘的写入和读取。咱们晓得磁盘相对而言读写速度较慢,那通过磁盘作为存储介质如何实现高吞吐呢? 程序读写答案就是程序读写。 首先理解一下页缓存,页缓存是操作系统用来作为磁盘的一种缓存,缩小磁盘的I/O操作。 在写入磁盘的时候其实是写入页缓存中,使得对磁盘的写入变成对内存的写入。写入的页变成脏页,而后操作系统会在适合的时候将脏页写入磁盘中。 在读取的时候如果页缓存命中则间接返回,如果页缓存 miss 则产生缺页中断,从磁盘加载数据至页缓存中,而后返回数据。 并且在读的时候会预读,依据局部性原理当读取的时候会把相邻的磁盘块读入页缓存中。在写入的时候会后写,写入的也是页缓存,这样存着能够将一些小的写入操作合并成大的写入,而后再刷盘。 而且依据磁盘的结构,程序 I/O 的时候,磁头简直不必换道,或者换道的工夫很短。 依据网上的一些测试后果,程序写盘的速度比随机写内存还要快。 当然这样的写入存在数据失落的危险,例如机器忽然断电,那些还未刷盘的脏页就失落了。不过能够调用 fsync 强制刷盘,然而这样对于性能的损耗较大。 因而个别倡议通过多正本机制来保障音讯的牢靠,而不是同步刷盘。 能够看到程序 I/O 适应磁盘的结构,并且还有预读和后写。 RocketMQ 和 Kafka 都是程序写入和近似程序读取。它们都采纳文件追加的形式来写入音讯,只能在日志文件尾部写入新的音讯,老的音讯无奈更改。 mmap-文件内存映射从下面可知拜访磁盘文件会将数据加载到页缓存中,然而页缓存属于内核空间,用户空间拜访不了,因而数据还须要拷贝到用户空间缓冲区。 能够看到数据须要从页缓存再通过一次拷贝程序能力拜访的到,因而还能够通过mmap来做一波优化,利用内存映射文件来防止拷贝。 简略的说文件映射就是将程序虚构页面间接映射到页缓存上,这样就无需有内核态再往用户态的拷贝,而且也防止了反复数据的产生。并且也不用再通过调用read或write办法对文件进行读写,能够通过映射地址加偏移量的形式间接操作。 sendfile-零拷贝既然音讯是存在磁盘中的,那消费者来拉音讯的时候就得从磁盘拿。咱们先来看看个别发送文件的流程是如何的。 简略说下DMA是什么,全称 Direct Memory Access ,它能够独立地间接读写零碎内存,不须要 CPU 染指,像显卡、网卡之类都会用DMA。 能够看到数据其实是冗余的,那咱们来看看mmap之后的发送文件流程是怎么的。 能够看到上下文切换的次数没有变动,然而数据少拷贝一份,这和咱们上文提到的mmap能达到的成果是一样的。 然而数据还是冗余了一份,这不是能够间接把数据从页缓存拷贝到网卡不就好了嘛?sendfile就有这个效用。咱们先来看看Linux2.1版本中的sendfile。 因为就一个零碎调用就满足了发送的需要,相比 read + write 或者 mmap + write 上下文切换必定是少了的,然而如同数据还是有冗余啊。是的,因而 Linux2.4 版本的 sendfile + 带 「扩散-收集(Scatter-gather)」的DMA。实现了真正的无冗余。 ...

August 26, 2020 · 2 min · jiezi

关于kafka:kafka集群安装部署

3、 kafka集群装置部署3.1、具体部署过程1、下载安装包(http://kafka.apache.org) kafka_2.11-1.1.0.tgz2、布局装置目录 /wangyq/install3、上传安装包到node01服务器,并解压 # 通过FTP工具上传安装包到node01服务器的/wangyq/soft门路下,而后进行解压cd /wangyq/soft/tar -zxf kafka_2.11-1.1.0.tgz -C /wangyq/install/4、批改配置文件 在node01上批改kafak对应的配置文件 server.properties 进入到kafka装置目录下有一个config目录,批改配置文件 cd /wangyq/install/kafka_2.11-1.1.0/config vim server.properties#指定kafka对应的broker id ,惟一broker.id=0#指定数据寄存的目录log.dirs=/wangyq/install/kafka_2.11-1.1.0/logs#指定zk地址zookeeper.connect=node01:2181,node02:2181,node03:2181#指定是否能够删除topic ,默认是false 示意不能够删除delete.topic.enable=true#指定broker主机名host.name=node01```5、node01执行以下命令散发kafka装置目录到其余节点 # 由node01节点同步其余正本节点中cd /wangyq/install/scp -r kafka_2.11-1.1.0/ node02:$PWDscp -r kafka_2.11-1.1.0/ node03:$PWD6、批改node02和node03上的配置 node02执行以下命令进行批改配置 cd /wangyq/install/kafka_2.11-1.1.0/config/vi server.properties#指定kafka对应的broker id ,惟一broker.id=1#指定数据寄存的目录log.dirs=/wangyq/install/kafka_2.11-1.1.0/logs#指定zk地址zookeeper.connect=node01:2181,node02:2181,node03:2181#指定是否能够删除topic ,默认是false 示意不能够删除delete.topic.enable=true#指定broker主机名host.name=node02node03执行以下命令进行批改配置 cd /wangyq/install/kafka_2.11-1.1.0/config/vi server.properties#指定kafka对应的broker id ,惟一broker.id=2#指定数据寄存的目录log.dirs=/wangyq/install/kafka_2.11-1.1.0/logs#指定zk地址zookeeper.connect=node01:2181,node02:2181,node03:2181#指定是否能够删除topic ,默认是false 示意不能够删除delete.topic.enable=true#指定broker主机名host.name=node033.1、 kafka集群启动和进行3.1.1、 启动先启动zk集群而后在所有节点执行脚本 cd /wangyq/install/kafka_2.11-1.1.0/nohup bin/kafka-server-start.sh config/server.properties 2>&1 & 一键启动kafka start_kafka.sh #!/bin/shfor host in node01 node02 node03do ssh $host "source /etc/profile;nohup /wangyq/install/kafka_2.11-1.1.0/bin/kafka-server-start.sh /wangyq/install/kafka_2.11-1.1.0/config/server.properties >/dev/null 2>&1 &" echo "$host kafka is running"done3.2.1、 进行所有节点执行敞开kafka脚本 ...

August 26, 2020 · 1 min · jiezi

关于kafka:kafka全面理解

kafka全面了解什么是音讯队列,它的益处是什么?解藕将音讯写入音讯队列,须要音讯的零碎本人从音讯队列中订阅,从而上游零碎不须要做任何批改 例如有上游零碎a,它有3个上游零碎b,c,d,为了使b,c,d能拿到a的数据,a须要在代码中去调用这3个零碎。如果有一天,b不再应用a的数据了或b的接口产生了变动,a还须要批改代码。而应用音讯队列,就只管往队列里发送数据,须要的上游本人去队列里取数据即可。异步不必同步期待上游将数据处理完,将音讯发到音讯队列中即可返回,不妨碍主流程。 例如上游零碎a是主业务,b,c,d是非次要业务,没有必要同步期待3个上游都返回主业务才持续。应用音讯队列能够实现异步,进步吞吐量。削峰上游数据有突发流量,上游可能扛不住,kafka在两头能够起到一个缓冲的作用,把音讯暂存在kafka中,上游服务就能够依照本人的节奏缓缓解决。 kafka概念brokerbroker是kafka实例。 replication每一个partition有多正本,当主节点产生故障时,会抉择一个正本作为主节点。kafka是主写主读的。 topictopic是音讯的分类,一个topic能够供任意多个生产组生产。 partitiontopic的分区,每个topic的数据能够被分成多个partition,能够不在一个机器上,由此来实现kafka的伸缩性。各个partition的数据是不反复的,雷同partition的数据是依照发送程序有序的。任何partition只有一个leader,只有leader是对外提供服务的。leader接管到数据后,follower会不停给他发送申请尝试去拉取最新的数据,拉取到本人本地后,写入磁盘中。每个partition都有多个正本,雷同partition的各个正本散布在不同的broker上。 consumer group/consumer一个consumer group是一个topic的订阅者,一个topic能够被多个consumer group订阅,各个consumer group是互相独立的。一个consumer group外部能够有多个consumer,多个consumer不会生产雷同的partition的音讯。最多无效的consumer数与partition数雷同,如果consumer数多于partition数,那么多进去的consumer不会生产到任何音讯。 rebalance生产组内某个消费者挂掉后,其余消费者主动重新分配订阅topic的partition的过程。rebalance是消费者端实现高可用的重要伎俩。 kafka的个性高吞吐、低提早:kakfa 最大的特点就是收发音讯十分快,kafka 每秒能够解决几十万条音讯,它的最低提早只有几毫秒。高伸缩性: 每个主题(topic) 蕴含多个分区(partition),主题中的分区能够散布在不同的主机(broker)中。持久性、可靠性: Kafka 可能容许数据的长久化存储,音讯被长久化到磁盘,并反对数据备份避免数据失落,Kafka 底层的数据存储是基于 Zookeeper 存储的,Zookeeper 咱们晓得它的数据可能长久存储。容错性: 容许集群中的节点失败,某个节点宕机,Kafka 集群可能失常工作高并发: 反对数千个客户端同时读写kafka为何快页缓存+程序写入kafka 写数据的时候,十分要害的一点,它是以磁盘程序写的形式来写的。仅仅将数据追加到文件的开端,不是在文件的随机地位来批改数据。写入磁盘文件的时候,能够间接写入这个 OS Cache 里,也就是仅仅写入内存中,接下来由操作系统本人决定什么时候把 OS Cache 里的数据真的刷入磁盘文件中。 零拷贝如果Kafka从磁盘中读取数据发送给上游的消费者,大略过程是: 先看看要读的数据在不在os cache中,如果不在的话就从磁盘文件里读取数据后放入os cache从操作系统的os cache 里拷贝数据到应用程序过程的缓存里从应用程序过程的缓存里拷贝数据到操作系统层面的Socket缓存里从Soket缓存里提取数据后发送到网卡,最初发送进来给上游消费者整个过程有两次没必要的拷贝 从操作系统的cache里拷贝到利用过程的缓存里从应用程序缓存里拷贝回操作系统的Socket缓存里。为了进行这两次拷贝,两头还产生了好几次上下文切换,一会儿是应用程序在执行,一会儿上下文切换到操作系统来执行。 所以这种形式来读取数据是比拟耗费性能的 零拷贝 让操作系统的cache中的数据发送到网卡网卡传出给上游的消费者两头跳过了两次拷贝数据的步骤,Socket缓存中仅仅会拷贝一个描述符过来,不会拷贝数据到Socket缓存 另外:从磁盘读数据的时候,会先看看os cache内存中是否有,如果有的话,其实读数据都是间接读内存的。 如果kafka集群通过良好的调优,大家会发现大量的数据都是间接写入os cache中,而后读数据的时候也是从os cache中读。 相当于是Kafka齐全基于内存提供数据的写和读了,所以这个整体性能会极其的高 消息压缩批量发送参考文章https://juejin.im/post/684490...https://juejin.im/post/684490...https://zhuanlan.zhihu.com/p/...

August 25, 2020 · 1 min · jiezi

关于kafka:Linux-Page-Cache调优在Kafka中的应用

本文首发于 vivo互联网技术 微信公众号  链接: https://mp.weixin.qq.com/s/MaeXn-kmgLUah78brglFkg 作者:Yang Yijun本文次要形容Linux Page Cache优化的背景、Page Cache的基本概念、列举之前针对Kafka的 IO 性能瓶颈采取的一些解决方案、如何进行Page Cache相干参数调整以及性能优化前后成果比照。 一、优化背景当业务快速增长,每天须要解决万亿记录级数据量时。在读写数据方面,Kafka 集群的压力将变得微小,而磁盘 IO 成为了 Kafka 集群最大的性能瓶颈。 当呈现入流量突增或者出流量突增状况,磁盘 IO 继续处于被打满状态,导致无奈解决新的读写申请,甚至造成局部broker节点雪崩而影响集群的稳固。 如下图所示,磁盘 IO 被继续打满: 这重大的影响了集群的稳固,从而影响业务的稳固运行。对此,咱们做出了一些针对性的优化计划: 对Linux操作系统的Page Cache参数进行优化;【本文次要解说内容】对kafka集群用户的出入流量进行限度,防止出入流量突增给磁盘IO带来的压力;【本文对此计划不做解说】按业务对集群进行资源组隔离(集群broker的物理隔离),防止不同业务间因为共享磁盘IO相互影响;【本文对此计划不做解说】对Kafka集群broker节点服务参数进行优化;【本文对此计划不做解说】革新Kafka正本迁徙源码,实现增量并发正本迁徙,缩小正本迁徙给集群broker节点磁盘IO带来的压力;【本文对此计划不做解说】开发一套Kafka集群主动负载平衡服务,定期对集群进行负载平衡;【本文对此计划不做解说】采纳IO性能更好的SSD固态硬盘替换一般的机械硬盘;进行磁盘RAID让broker外部多块磁盘间IO负载更加平衡【本文对此计划不做解说】革新Kafka源码,对Kafka集群单个broker及单个topic进行出入流量限度,实现流量对最细粒度管制;当单个broker流量突增时能够对其进行下限限度,防止节点被异样流量打挂;【本文对此计划不做解说】革新Kafka源码,修复正本迁徙工作启动后不可手动终止的缺点,实现当因迁徙导致负载过高却无奈进行的问题;【本文对此计划不做解说】机房网络带宽的竞争也将间接的影响到follower同步leader的数据,最终将导致follower同步拉取历史数据而减少IO负载,因而须要对网络带宽进行优先级打标,当有竞争时进步Kafka集群的优先级,防止kafka集群的broker和其余大量耗费网络带宽的业务共用机房交换机。【本文对此计划不做解说】以上只是列举了几点次要的优化计划,还有一些其余的内容这里不再赘述。本文咱们次要来解说一下 Linux操作系统的Page Cache参数调优。 二、基本概念1、什么是Page Cache?Page Cache是针对文件系统的缓存,通过将磁盘中的文件数据缓存到内存中,从而缩小磁盘I/O操作进步性能。 对磁盘的数据进行缓存从而进步性能次要是基于两个因素: 磁盘拜访的速度比内存慢好几个数量级(毫秒和纳秒的差距);被拜访过的数据,有很大概率会被再次拜访。文件读写流程如下所示: 2、读Cache当内核发动一个读申请时(例如过程发动read()申请),首先会查看申请的数据是否缓存到了Page Cache中。 如果有,那么间接从内存中读取,不须要拜访磁盘,这被称为cache命中(cache hit); 如果cache中没有申请的数据,即cache未命中(cache miss),就必须从磁盘中读取数据。而后内核将读取的数据缓存到cache中,这样后续的读申请就能够命中cache了。 page能够只缓存一个文件局部的内容,不须要把整个文件都缓存进来。 3、写Cache当内核发动一个写申请时(例如过程发动write()申请),同样是间接往cache中写入,后备存储中的内容不会间接更新(当服务器呈现断电关机时,存在数据失落危险)。 内核会将被写入的page标记为dirty,并将其退出dirty list中。内核会周期性地将dirty list中的page写回到磁盘上,从而使磁盘上的数据和内存中缓存的数据统一。 当满足以下两个条件之一将触发脏数据刷新到磁盘操作: 数据存在的工夫超过了dirty_expire_centisecs(默认300厘秒,即30秒)工夫;脏数据所占内存 > dirty_background_ratio,也就是说当脏数据所占用的内存占总内存的比例超过dirty_background_ratio(默认10,即零碎内存的10%)的时候会触发pdflush刷新脏数据。4、Page Cache缓存查看工具咱们如何查看缓存命中率呢?在这里咱们能够借助一个缓存命中率查看工具 cachestat。 (1)下载安装mkdir /opt/bigdata/app/cachestatcd /opt/bigdata/app/cachestatgit clone --depth 1 https://github.com/brendangregg/perf-tools(2)启动执行 (3)输入内容阐明 5、如何回收Page Cache执行脚本:echo 1 > /proc/sys/vm/drop_caches 这里可能须要期待一会,因为有应用程序正在写数据。 ...

August 24, 2020 · 1 min · jiezi

关于kafka:kafka-系列-41消费者基本介绍

1、消费者食用DEMOProperties prop = new Properties();prop.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());prop.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());prop.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka:9092");prop.put(ConsumerConfig.GROUP_ID_CONFIG, "testConsumer");prop.put(ConsumerConfig.CLIENT_ID_CONFIG, "consumerDemo");KafkaConsumer<String, String> consumer = new KafkaConsumer<>(prop);consumer.subscribe(Collections.singleton("test"));while (true) { ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000)); for (ConsumerRecord<String, String> record : records) { String key = record.key(); String value = record.value(); System.err.println(record.toString()); }}2、消费者基本概念kafka 消费者是以 组为根本单位 进行生产的。生产的模型如下 1 个 topic 容许被多个 生产组 生产。再次强调,kafka 生产是以组为单位。 prop.put(ConsumerConfig.GROUP_ID_CONFIG, "testConsumer");以上这行代码设置了生产组。 2.1、partition 调配topic 为逻辑上的概念,partition 才是物理上的概念。那么看完这个以上的生产模型图。你可能会很纳闷。当一个组下有多个消费者时,每个消费者是如何生产的? 先阐明:partition 的调配为平均分配 假如一:topic1 上面有 3 个分区。别离如下:p1 - p3。那么 groupA 下的三个消费者生产的对应 partition 为如下 ...

August 20, 2020 · 3 min · jiezi

关于kafka:Kafka260发布性能大幅提升

近日Kafka2.6版本公布,间隔2.5.0公布只过来了不到四个月的工夫。 Kafka 2.6.0蕴含许多重要的新性能。以下是一些重要更改的摘要: 默认状况下,已为Java 11或更高版本启用TLSv1.3性能显着进步,尤其是当代理具备大量分区时扩大Kafka Streams的应用程序更便捷Kafka Streams反对更改时收回新的metrics可提供更好的经营洞察力配置为进行连贯时,Kafka Connect能够主动创立Topic改良了Kafka Connect中接收器连接器的错误报告选项Kafka Connect中的新过滤器和有条件地利用SMT“ client.dns.lookup”配置的默认值当初为“ use_all_dns_ips”。将Zookeeper降级到3.5.8新性能增加KStream#repartition操作使SSL上下文/引擎配置可扩大默认状况下启用TLSv1.3,并禁用某些较旧的协定有条件地利用SMT向流指标增加工作级流动过程比率重构主循环以一次解决一个工作的多个记录改善加强了TransformerSupplier / ProcessorSupplier清理工作治理将“ onAssignment”流与“ partitionsAssigned”工作创立合并公开磁盘读写指标容许消费者明确触发从新均衡将gradle更新为6.0+反对Java 14将默认版本切换到Scala 2.13-改良“ matchingAcls”的性能控制台生产者反对client.id的设置降级指南:如果要从2.1.x之前的版本升级,请参阅以下正文,以理解用于存储使用者偏移量的架构的更改。将inter.broker.protocol.version更改为最新版本后,将无奈降级到2.1之前的版本。 对于滚动降级: 在所有代理上更新server.properties并增加以下属性。CURRENT_KAFKA_VERSION指的是您要降级的版本。CURRENT_MESSAGE_FORMAT_VERSION是指以后应用的音讯格局版本。如果以前笼罩了音讯格局版本,则应保留其以后值。或者,如果要从0.11.0.x之前的版本升级,则应将CURRENT_MESSAGE_FORMAT_VERSION设置为与CURRENT_KAFKA_VERSION相匹配。 inter.broker.protocol.version = CURRENT_KAFKA_VERSION(例如2.5,2.4等)log.message.format.version = CURRENT_MESSAGE_FORMAT_VERSION如果要从0.11.0.x或更高版本升级,并且尚未笼罩音讯格局,则只须要笼罩代理间协定版本。 inter.broker.protocol.version = CURRENT_KAFKA_VERSION(例如2.5,2.4等)一次降级一个代理:敞开代理,更新代码,而后重新启动。实现此操作后,代理将运行最新版本,并且您能够验证集群的行为和性能是否合乎预期。如果有任何问题,此时依然能够降级。验证集群的行为和性能后,请通过编辑协定版本inter.broker.protocol.version并将其设置为来更改协定版本 2.6。逐个重新启动代理,以使新协定版本失效。代理开始应用最新的协定版本后,将无奈再将群集降级到较旧的版本。如果您已依照上述阐明笼罩了音讯格局版本,则须要再次滚动重启以将其降级到最新版本。一旦所有(或大多数)使用者都降级到0.11.0或更高版本,则在每个代理上将log.message.format.version更改为2.6,而后逐个重新启动它们。请留神,不再保护的较旧的Scala客户端不反对0.11中引入的音讯格局,为防止转换老本必须应用较新的Java客户端。2.6.0留神点Kafka Streams增加了一种新的解决模式(须要Broker 2.5或更高版本),该模式应用齐全一次的保障进步了应用程序的可伸缩性。 缺省状况下,Java 11或更高版本已启用TLSv1.3。如果客户端和服务器均反对TLSv1.3,则将协商该协定,否则将回退至TLSv1.2。 缺省状况下,Java 11或更高版本已启用TLSv1.3。如果客户端和服务器均反对TLSv1.3,则将协商该协定,否则将回退至TLSv1.2。 NotLeaderForPartitionException已弃用,并已替换为NotLeaderOrFollowerException。如果代理不是正本,则获取申请和仅用于领导者或跟随者的其余申请将返回NOT_LEADER_OR_FOLLOWER(6)而不是REPLICA_NOT_AVAILABLE(9),以确保重新分配期间的此临时谬误由所有客户端作为可重试的异样进行解决。 更多Flink,Kafka,Spark等相干技术博文,科技资讯,欢送关注实时流式计算 公众号后盾回复 “电子书” 下载300页Flink实战电子书

August 11, 2020 · 1 min · jiezi

关于kafka:消息队列的消费幂等性如何保证

什么是幂等?任意屡次执行所产生的影响均与一次执行的影响雷同就能够称为幂等 什么是音讯幂等?当呈现消费者对某条音讯反复生产的状况时,反复生产的后果与生产一次的后果是雷同的,并且屡次生产并未对业务零碎产生任何负面影响 为什么咱们要保障幂等性,不保障幂等性,会不会有问题?这个问题其实没法精确答复。答复这个问题的本源得从业务场景上进行剖析。比方失常业务状况下,咱们是不容许同个订单反复领取,这种业务场景咱们就须要确保幂等性。再比方日志记录,这种业务场景,咱们可能就不须要做幂等判断。 因而是否要保障幂等性,得基于业务进行考量 音讯队列的生产幂等性如何保障?没法保障。后面说了要保障幂等性,得基于业务场景进行考量。音讯队列他自身就不是给你用来做业务幂等性用的。如果你要实现业务幂等性,靠音讯队列是没法帮你实现的,你本人得依据本身业务场景,来实现幂等。 罕用的业务幂等性保障办法1、利用数据库的惟一束缚实现幂等比方将订单表中的订单编号设置为惟一索引,创立订单时,依据订单编号就能够保障幂等 2、去重表这个计划实质也是依据数据库的唯一性束缚来实现。其实现大体思路是:首先在去重表上建惟一索引,其次操作时把业务表和去重表放在同个本地事务中,如果呈现重现反复生产,数据库会抛惟一束缚异样,操作就会回滚 3、利用redis的原子性每次操作都间接set到redis外面,而后将redis数据定时同步到数据库中 4、多版本(乐观锁)管制此计划多用于更新的场景下。其实现的大体思路是:给业务数据减少一个版本号属性,每次更新数据前,比拟以后数据的版本号是否和音讯中的版本统一,如果不统一则回绝更新数据,更新数据的同时将版本号+1 5、状态机机制此计划多用于更新且业务场景存在多种状态流转的场景 6、token机制生产者发送每条数据的时候,减少一个全局惟一的id,这个id通常是业务的惟一标识,比方订单编号。在生产端生产时,则验证该id是否被生产过,如果还没生产过,则进行业务解决。解决完结后,在把该id存入redis,同时设置状态为已生产。如果曾经生产过了,则不进行解决。 演示例子应用springboot2加kafka来演示一下应用token机制如何实现生产端幂等 1、application.ymlspring: redis: host: localhost port: 6379 # 连贯超时工夫(毫秒) timeout: 10000 jedis: pool: # 连接池中的最大闲暇连贯 max-idle: 8 # 连接池中的最小闲暇连贯 min-idle: 10 # 连接池最大连接数(应用负值示意没有限度) max-active: 100 # 连接池最大阻塞等待时间(应用负值示意没有限度) max-wait: -1 password: kafka: # 以逗号分隔的地址列表,用于建设与Kafka集群的初始连贯(kafka 默认的端口号为9092) bootstrap-servers: localhost:9092 producer: # 产生谬误后,音讯重发的次数。 retries: 0 #当有多个音讯须要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次能够应用的内存大小,依照字节数计算。 batch-size: 16384 # 设置生产者内存缓冲区的大小。 buffer-memory: 33554432 # 键的序列化形式 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 值的序列化形式 value-serializer: com.github.lybgeek.kafka.serialization.ObjectSerializer # acks=0 : 生产者在胜利写入音讯之前不会期待任何来自服务器的响应。 # acks=1 : 只有集群的领袖节点收到音讯,生产者就会收到一个来自服务器胜利响应。 # acks=all :只有当所有参加复制的节点全副收到音讯时,生产者才会收到一个来自服务器的胜利响应。 acks: 1 consumer: # 主动提交的工夫距离 在spring boot 2.X 版本中这里采纳的是值的类型为Duration 须要合乎特定的格局,如1S,1M,2H,5D auto-commit-interval: 1S # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量有效的状况下该作何解决: # latest(默认值)在偏移量有效的状况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录) # earliest :在偏移量有效的状况下,消费者将从起始地位读取分区的记录 auto-offset-reset: earliest # 是否主动提交偏移量,默认值是true,为了避免出现反复数据和数据失落,能够把它设置为false,而后手动提交偏移量 enable-auto-commit: false # 键的反序列化形式 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer # 值的反序列化形式 value-deserializer: com.github.lybgeek.kafka.serialization.ObjectDeserializer listener: # 在侦听器容器中运行的线程数。 concurrency: 1 #listner负责ack,每调用一次,就立刻commit ack-mode: manual_immediate2、实现kafka的自定义序列和反序列注:kakfa默认的序列化和反序列形式是StringSerializer和StringDeserializer。咱们要革新成反对对象的序列化和反序列化 ...

August 9, 2020 · 2 min · jiezi

关于kafka:消息队列的消费幂等性如何保证

什么是幂等?任意屡次执行所产生的影响均与一次执行的影响雷同就能够称为幂等 什么是音讯幂等?当呈现消费者对某条音讯反复生产的状况时,反复生产的后果与生产一次的后果是雷同的,并且屡次生产并未对业务零碎产生任何负面影响 为什么咱们要保障幂等性,不保障幂等性,会不会有问题?这个问题其实没法精确答复。答复这个问题的本源得从业务场景上进行剖析。比方失常业务状况下,咱们是不容许同个订单反复领取,这种业务场景咱们就须要确保幂等性。再比方日志记录,这种业务场景,咱们可能就不须要做幂等判断。 因而是否要保障幂等性,得基于业务进行考量 音讯队列的生产幂等性如何保障?没法保障。后面说了要保障幂等性,得基于业务场景进行考量。音讯队列他自身就不是给你用来做业务幂等性用的。如果你要实现业务幂等性,靠音讯队列是没法帮你实现的,你本人得依据本身业务场景,来实现幂等。 罕用的业务幂等性保障办法1、利用数据库的惟一束缚实现幂等比方将订单表中的订单编号设置为惟一索引,创立订单时,依据订单编号就能够保障幂等 2、去重表这个计划实质也是依据数据库的唯一性束缚来实现。其实现大体思路是:首先在去重表上建惟一索引,其次操作时把业务表和去重表放在同个本地事务中,如果呈现重现反复生产,数据库会抛惟一束缚异样,操作就会回滚 3、利用redis的原子性每次操作都间接set到redis外面,而后将redis数据定时同步到数据库中 4、多版本(乐观锁)管制此计划多用于更新的场景下。其实现的大体思路是:给业务数据减少一个版本号属性,每次更新数据前,比拟以后数据的版本号是否和音讯中的版本统一,如果不统一则回绝更新数据,更新数据的同时将版本号+1 5、状态机机制此计划多用于更新且业务场景存在多种状态流转的场景 6、token机制生产者发送每条数据的时候,减少一个全局惟一的id,这个id通常是业务的惟一标识,比方订单编号。在生产端生产时,则验证该id是否被生产过,如果还没生产过,则进行业务解决。解决完结后,在把该id存入redis,同时设置状态为已生产。如果曾经生产过了,则不进行解决。 演示例子应用springboot2加kafka来演示一下应用token机制如何实现生产端幂等 1、application.ymlspring: redis: host: localhost port: 6379 # 连贯超时工夫(毫秒) timeout: 10000 jedis: pool: # 连接池中的最大闲暇连贯 max-idle: 8 # 连接池中的最小闲暇连贯 min-idle: 10 # 连接池最大连接数(应用负值示意没有限度) max-active: 100 # 连接池最大阻塞等待时间(应用负值示意没有限度) max-wait: -1 password: kafka: # 以逗号分隔的地址列表,用于建设与Kafka集群的初始连贯(kafka 默认的端口号为9092) bootstrap-servers: localhost:9092 producer: # 产生谬误后,音讯重发的次数。 retries: 0 #当有多个音讯须要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次能够应用的内存大小,依照字节数计算。 batch-size: 16384 # 设置生产者内存缓冲区的大小。 buffer-memory: 33554432 # 键的序列化形式 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 值的序列化形式 value-serializer: com.github.lybgeek.kafka.serialization.ObjectSerializer # acks=0 : 生产者在胜利写入音讯之前不会期待任何来自服务器的响应。 # acks=1 : 只有集群的领袖节点收到音讯,生产者就会收到一个来自服务器胜利响应。 # acks=all :只有当所有参加复制的节点全副收到音讯时,生产者才会收到一个来自服务器的胜利响应。 acks: 1 consumer: # 主动提交的工夫距离 在spring boot 2.X 版本中这里采纳的是值的类型为Duration 须要合乎特定的格局,如1S,1M,2H,5D auto-commit-interval: 1S # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量有效的状况下该作何解决: # latest(默认值)在偏移量有效的状况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录) # earliest :在偏移量有效的状况下,消费者将从起始地位读取分区的记录 auto-offset-reset: earliest # 是否主动提交偏移量,默认值是true,为了避免出现反复数据和数据失落,能够把它设置为false,而后手动提交偏移量 enable-auto-commit: false # 键的反序列化形式 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer # 值的反序列化形式 value-deserializer: com.github.lybgeek.kafka.serialization.ObjectDeserializer listener: # 在侦听器容器中运行的线程数。 concurrency: 1 #listner负责ack,每调用一次,就立刻commit ack-mode: manual_immediate2、实现kafka的自定义序列和反序列注:kakfa默认的序列化和反序列形式是StringSerializer和StringDeserializer。咱们要革新成反对对象的序列化和反序列化 ...

August 9, 2020 · 2 min · jiezi

关于kafka:消息队列的消费幂等性如何保证

什么是幂等?任意屡次执行所产生的影响均与一次执行的影响雷同就能够称为幂等 什么是音讯幂等?当呈现消费者对某条音讯反复生产的状况时,反复生产的后果与生产一次的后果是雷同的,并且屡次生产并未对业务零碎产生任何负面影响 为什么咱们要保障幂等性,不保障幂等性,会不会有问题?这个问题其实没法精确答复。答复这个问题的本源得从业务场景上进行剖析。比方失常业务状况下,咱们是不容许同个订单反复领取,这种业务场景咱们就须要确保幂等性。再比方日志记录,这种业务场景,咱们可能就不须要做幂等判断。 因而是否要保障幂等性,得基于业务进行考量 音讯队列的生产幂等性如何保障?没法保障。后面说了要保障幂等性,得基于业务场景进行考量。音讯队列他自身就不是给你用来做业务幂等性用的。如果你要实现业务幂等性,靠音讯队列是没法帮你实现的,你本人得依据本身业务场景,来实现幂等。 罕用的业务幂等性保障办法1、利用数据库的惟一束缚实现幂等比方将订单表中的订单编号设置为惟一索引,创立订单时,依据订单编号就能够保障幂等 2、去重表这个计划实质也是依据数据库的唯一性束缚来实现。其实现大体思路是:首先在去重表上建惟一索引,其次操作时把业务表和去重表放在同个本地事务中,如果呈现重现反复生产,数据库会抛惟一束缚异样,操作就会回滚 3、利用redis的原子性每次操作都间接set到redis外面,而后将redis数据定时同步到数据库中 4、多版本(乐观锁)管制此计划多用于更新的场景下。其实现的大体思路是:给业务数据减少一个版本号属性,每次更新数据前,比拟以后数据的版本号是否和音讯中的版本统一,如果不统一则回绝更新数据,更新数据的同时将版本号+1 5、状态机机制此计划多用于更新且业务场景存在多种状态流转的场景 6、token机制生产者发送每条数据的时候,减少一个全局惟一的id,这个id通常是业务的惟一标识,比方订单编号。在生产端生产时,则验证该id是否被生产过,如果还没生产过,则进行业务解决。解决完结后,在把该id存入redis,同时设置状态为已生产。如果曾经生产过了,则不进行解决。 演示例子应用springboot2加kafka来演示一下应用token机制如何实现生产端幂等 1、application.ymlspring: redis: host: localhost port: 6379 # 连贯超时工夫(毫秒) timeout: 10000 jedis: pool: # 连接池中的最大闲暇连贯 max-idle: 8 # 连接池中的最小闲暇连贯 min-idle: 10 # 连接池最大连接数(应用负值示意没有限度) max-active: 100 # 连接池最大阻塞等待时间(应用负值示意没有限度) max-wait: -1 password: kafka: # 以逗号分隔的地址列表,用于建设与Kafka集群的初始连贯(kafka 默认的端口号为9092) bootstrap-servers: localhost:9092 producer: # 产生谬误后,音讯重发的次数。 retries: 0 #当有多个音讯须要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次能够应用的内存大小,依照字节数计算。 batch-size: 16384 # 设置生产者内存缓冲区的大小。 buffer-memory: 33554432 # 键的序列化形式 key-serializer: org.apache.kafka.common.serialization.StringSerializer # 值的序列化形式 value-serializer: com.github.lybgeek.kafka.serialization.ObjectSerializer # acks=0 : 生产者在胜利写入音讯之前不会期待任何来自服务器的响应。 # acks=1 : 只有集群的领袖节点收到音讯,生产者就会收到一个来自服务器胜利响应。 # acks=all :只有当所有参加复制的节点全副收到音讯时,生产者才会收到一个来自服务器的胜利响应。 acks: 1 consumer: # 主动提交的工夫距离 在spring boot 2.X 版本中这里采纳的是值的类型为Duration 须要合乎特定的格局,如1S,1M,2H,5D auto-commit-interval: 1S # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量有效的状况下该作何解决: # latest(默认值)在偏移量有效的状况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录) # earliest :在偏移量有效的状况下,消费者将从起始地位读取分区的记录 auto-offset-reset: earliest # 是否主动提交偏移量,默认值是true,为了避免出现反复数据和数据失落,能够把它设置为false,而后手动提交偏移量 enable-auto-commit: false # 键的反序列化形式 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer # 值的反序列化形式 value-deserializer: com.github.lybgeek.kafka.serialization.ObjectDeserializer listener: # 在侦听器容器中运行的线程数。 concurrency: 1 #listner负责ack,每调用一次,就立刻commit ack-mode: manual_immediate2、实现kafka的自定义序列和反序列注:kakfa默认的序列化和反序列形式是StringSerializer和StringDeserializer。咱们要革新成反对对象的序列化和反序列化 ...

August 9, 2020 · 2 min · jiezi

关于kafka:你真的了解Flink-Kafka-source吗

Flink 提供了专门的 Kafka 连接器,向 Kafka topic 中读取或者写入数据。Flink Kafka Consumer 集成了 Flink 的 Checkpoint 机制,可提供 exactly-once 的解决语义。为此,Flink 并不齐全依赖于跟踪 Kafka 生产组的偏移量,而是在外部跟踪和查看偏移量。 引言当咱们在应用Spark Streaming、Flink等计算框架进行数据实时处理时,应用Kafka作为一款公布与订阅的音讯零碎成为了标配。Spark Streaming与Flink都提供了绝对应的Kafka Consumer,应用起来十分的不便,只须要设置一下Kafka的参数,而后增加kafka的source就高枕无忧了。如果你真的感觉事件就是如此的so easy,感觉妈妈再也不必放心你的学习了,那就真的是too young too simple sometimes naive了。本文以Flink 的Kafka Source为探讨对象,首先从根本的应用动手,而后深刻源码逐个分析,一并为你拨开Flink Kafka connector的神秘面纱。值得注意的是,本文假设读者具备了Kafka的相干常识,对于Kafka的相干细节问题,不在本文的探讨范畴之内。 Flink Kafka Consumer介绍Flink Kafka Connector有很多个版本,能够依据你的kafka和Flink的版本抉择相应的包(maven artifact id)和类名。本文所波及的Flink版本为1.10,Kafka的版本为2.3.4。Flink所提供的Maven依赖于类名如下表所示: Demo示例增加Maven依赖<!--本文应用的是通用型的connector--><dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-kafka_2.11</artifactId> <version>1.10.0</version></dependency>简略代码案例public class KafkaConnector { public static void main(String[] args) throws Exception { StreamExecutionEnvironment senv = StreamExecutionEnvironment.getExecutionEnvironment(); // 开启checkpoint,工夫距离为毫秒 senv.enableCheckpointing(5000L); // 抉择状态后端 senv.setStateBackend((StateBackend) new FsStateBackend("file:///E://checkpoint")); //senv.setStateBackend((StateBackend) new FsStateBackend("hdfs://kms-1:8020/checkpoint")); Properties props = new Properties(); // kafka broker地址 props.put("bootstrap.servers", "kms-2:9092,kms-3:9092,kms-4:9092"); // 仅kafka0.8版本须要配置 props.put("zookeeper.connect", "kms-2:2181,kms-3:2181,kms-4:2181"); // 消费者组 props.put("group.id", "test"); // 主动偏移量提交 props.put("enable.auto.commit", true); // 偏移量提交的工夫距离,毫秒 props.put("auto.commit.interval.ms", 5000); // kafka 音讯的key序列化器 props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); // kafka 音讯的value序列化器 props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); // 指定kafka的消费者从哪里开始生产数据 // 共有三种形式, // #earliest // 当各分区下有已提交的offset时,从提交的offset开始生产; // 无提交的offset时,从头开始生产 // #latest // 当各分区下有已提交的offset时,从提交的offset开始生产; // 无提交的offset时,生产新产生的该分区下的数据 // #none // topic各分区都存在已提交的offset时, // 从offset后开始生产; // 只有有一个分区不存在已提交的offset,则抛出异样 props.put("auto.offset.reset", "latest"); FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>( "qfbap_ods.code_city", new SimpleStringSchema(), props); //设置checkpoint后在提交offset,即oncheckpoint模式 // 该值默认为true, consumer.setCommitOffsetsOnCheckpoints(true); // 最早的数据开始生产 // 该模式下,Kafka 中的 committed offset 将被疏忽,不会用作起始地位。 //consumer.setStartFromEarliest(); // 消费者组最近一次提交的偏移量,默认。 // 如果找不到分区的偏移量,那么将会应用配置中的 auto.offset.reset 设置 //consumer.setStartFromGroupOffsets(); // 最新的数据开始生产 // 该模式下,Kafka 中的 committed offset 将被疏忽,不会用作起始地位。 //consumer.setStartFromLatest(); // 指定具体的偏移量工夫戳,毫秒 // 对于每个分区,其工夫戳大于或等于指定工夫戳的记录将用作起始地位。 // 如果一个分区的最新记录早于指定的工夫戳,则只从最新记录读取该分区数据。 // 在这种模式下,Kafka 中的已提交 offset 将被疏忽,不会用作起始地位。 //consumer.setStartFromTimestamp(1585047859000L); // 为每个分区指定偏移量 /*Map<KafkaTopicPartition, Long> specificStartOffsets = new HashMap<>(); specificStartOffsets.put(new KafkaTopicPartition("qfbap_ods.code_city", 0), 23L); specificStartOffsets.put(new KafkaTopicPartition("qfbap_ods.code_city", 1), 31L); specificStartOffsets.put(new KafkaTopicPartition("qfbap_ods.code_city", 2), 43L); consumer1.setStartFromSpecificOffsets(specificStartOffsets);*/ /** * * 请留神:当 Job 从故障中主动复原或应用 savepoint 手动复原时, * 这些起始地位配置办法不会影响生产的起始地位。 * 在复原时,每个 Kafka 分区的起始地位由存储在 savepoint 或 checkpoint 中的 offset 确定 * */ DataStreamSource<String> source = senv.addSource(consumer); // TODO source.print(); senv.execute("test kafka connector"); }}参数配置解读在Demo示例中,给出了具体的配置信息,上面将对下面的参数配置进行逐个剖析。 ...

August 8, 2020 · 9 min · jiezi

关于kafka:Kafka的Controller-Broker是什么

控制器组件(Controller),是 Apache Kafka 的外围组件。它的次要作用是在 Apache ZooKeeper 的帮忙下治理和协调整个 Kafka 集群。集群中任意一台 Broker 都能充当控制器的角色,然而,在运行过程中,只能有一个 Broker 成为控制器,行使其治理和协调的职责。接下来,咱们将探讨Controller原理和外部运行机制。通过本文你能够理解到: 什么是Controller BrokerController Broker是怎么被选举的Controller Broker次要作用是什么Kafka是如何解决脑裂的在分布式系统中,通常须要有一个协调者,该协调者会在分布式系统产生异样时施展非凡的作用。在Kafka中该协调者称之为控制器(Controller),其实该控制器并没有什么非凡之处,它自身也是一个一般的Broker,只不过须要负责一些额定的工作(追踪集群中的其余Broker,并在适合的时候解决新退出的和失败的Broker节点、Rebalance分区、调配新的leader分区等)。值得注意的是:Kafka集群中始终只有一个Controller Broker。 Controller Broker是如何被选出来的上一大节解释了什么是Controller Broker,并且每台 Broker 都有充当控制器的可能性。那么,控制器是如何被选出来的呢?当集群启动后,Kafka 怎么确认控制器位于哪台 Broker 呢? 实际上,Broker 在启动时,会尝试去 ZooKeeper 中创立 /controller 节点。Kafka 以后选举控制器的规定是:第一个胜利创立 /controller 节点的 Broker 会被指定为控制器。 Controller Broker的具体作用是什么Controller Broker的主要职责有很多,次要是一些治理行为,次要包含以下几个方面: 创立、删除主题,减少分区并调配leader分区集群Broker治理(新增 Broker、Broker 被动敞开、Broker 故障)preferred leader选举分区重调配解决集群中下线的Broker当某个Broker节点因为故障来到Kafka群集时,则存在于该Broker的leader分区将不可用(因为客户端仅对leader分区进行读写操作)。为了最大水平地缩小停机工夫,须要疾速找到代替的leader分区。 Controller Broker能够对失败的Broker做出响应,Controller Broker能够从Zookeeper监听(zookeeper watch)中获取告诉信息,ZooKeeper 赋予客户端监控 znode 变更的能力,即所谓的 Watch 告诉性能。一旦 znode 节点被创立、删除,子节点数量发生变化,抑或是 znode 所存的数据自身变更,ZooKeeper 会通过节点变更监听器 (ChangeHandler) 的形式显式告诉客户端。 每个 Broker 启动后,会在zookeeper的 /Brokers/ids 下创立一个长期 znode。当 Broker 宕机或被动敞开后,该 Broker 与 ZooKeeper 的会话完结,这个 znode 会被主动删除。同理,ZooKeeper 的 Watch 机制将这一变更推送给控制器,这样控制器就能晓得有 Broker 敞开或宕机了,从而进行后续的协调操作。 ...

August 8, 2020 · 1 min · jiezi

关于kafka:Kafka生产者ack机制剖析

Kafka有两个很重要的配置参数,acks与min.insync.replicas .其中acks是producer的配置参数,min.insync.replicas是Broker端的配置参数,这两个参数对于生产者不失落数据起到了很大的作用.接下来,本文会以图示的形式解说这两个参数的含意和应用形式。通过本文,你能够理解到: Kafka的分区正本什么是同步正本(In-sync replicas)什么是acks确认机制什么是最小同步正本ack=all与最小同步正本是如何发挥作用的分区正本Kafka的topic是能够分区的,并且能够为分区配置多个正本,改配置能够通过replication.factor参数实现. Kafka中的分区正本包含两种类型:领导者正本(Leader Replica)和追随者正本(Follower Replica),每个分区在创立时都要选举一个正本作为领导者正本,其余的正本主动变为追随者正本. 在 Kafka 中,追随者正本是不对外提供服务的,也就是说,任何一个追随者正本都不能响应消费者和生产者的读写申请. 所有的申请都必须由领导者副原本解决. 换句话说,所有的读写申请都必须发往领导者正本所在的 Broker,由该 Broker 负责解决. 追随者正本不解决客户端申请,它惟一的工作就是从领导者正本异步拉取音讯,并写入到本人的提交日志中,从而实现与领导者正本的同步. Kafka默认的正本因子是3,即每个分区只有1个leader正本和2个follower正本.具体如下图所示: 下面提到生产者客户端仅写入Leader broker,跟随者异步复制数据。因为Kafka是一个分布式系统,必然会存在与 Leader 不能实时同步的危险,所以须要一种办法来判断这些追随者是否跟上了领导者的步调, 即追随者是否同步了最新的数据.换句话说,Kafka 要明确地通知咱们,追随者正本到底在什么条件下才算与 Leader 同步?这就是上面所要说的ISR同步正本机制. 同步正本(In-sync replicas)In-sync replica(ISR)称之为同步正本,ISR中的正本都是与Leader进行同步的正本,所以不在该列表的follower会被认为与Leader是不同步的. 那么,ISR中存在是什么正本呢?首先能够明确的是:Leader正本总是存在于ISR中. 而follower正本是否在ISR中,取决于该follower正本是否与Leader正本放弃了“同步”. 尖叫提醒:对于"follower正本是否与Leader正本放弃了同步"的了解如下:(1)下面所说的同步不是指齐全的同步,即并不是说一旦follower正本同步滞后与Leader正本,就会被踢出ISR列表. (2)Kafka的broker端有一个参数replica.lag.time.max.ms, 该参数示意follower正本滞后与Leader正本的最长工夫距离,默认是10秒. 这就意味着,只有follower正本落后于leader正本的工夫距离不超过10秒,就能够认为该follower正本与leader正本是同步的,所以哪怕以后follower正本落后于Leader正本几条音讯,只有在10秒之内赶上Leader正本,就不会被踢出出局. (3)如果follower正本被踢出ISR列表,等到该正本追上了Leader正本的进度,该正本会被再次退出到ISR列表中,所以ISR是一个动静列表,并不是动态不变的。 如上图所示:Broker3上的partition1正本超过了规定工夫,未与Leader正本同步,所以被踢出ISR列表,此时的ISR为[1,3]. acks确认机制acks参数指定了必须要有多少个分区正本收到音讯,生产者才认为该音讯是写入胜利的,这个参数对于音讯是否失落起着重要作用,该参数的配置具体如下: acks=0,示意生产者在胜利写入音讯之前不会期待任何来自服务器的响应. 换句话说,一旦呈现了问题导致服务器没有收到音讯,那么生产者就无从得悉,音讯也就失落了. 改配置因为不须要等到服务器的响应,所以能够以网络反对的最大速度发送音讯,从而达到很高的吞吐量。 acks=1,示意只有集群的leader分区正本接管到了音讯,就会向生产者发送一个胜利响应的ack,此时生产者接管到ack之后就能够认为该音讯是写入胜利的. 一旦音讯无奈写入leader分区正本(比方网络起因、leader节点解体),生产者会收到一个谬误响应,当生产者接管到该谬误响应之后,为了防止数据失落,会从新发送数据.这种形式的吞吐量取决于应用的是异步发送还是同步发送. 尖叫提醒:如果生产者收到了谬误响应,即使是从新发消息,还是会有可能呈现丢数据的景象. 比方,如果一个没有收到音讯的节点成为了新的Leader,音讯就会失落. acks =all,示意只有所有参加复制的节点(ISR列表的正本)全副收到音讯时,生产者才会接管到来自服务器的响应. 这种模式是最高级别的,也是最平安的,能够确保不止一个Broker接管到了音讯. 该模式的提早会很高. 最小同步正本下面提到,当acks=all时,须要所有的正本都同步了才会发送胜利响应到生产者. 其实这外面存在一个问题:如果Leader正本是惟一的同步正本时会产生什么呢?此时相当于acks=1.所以是不平安的. Kafka的Broker端提供了一个参数min.insync.replicas,该参数管制的是音讯至多被写入到多少个正本才算是"真正写入",该值默认值为1,生产环境设定为一个大于1的值能够晋升音讯的持久性. 因为如果同步正本的数量低于该配置值,则生产者会收到谬误响应,从而确保音讯不失落. Case 1如下图,当min.insync.replicas=2且acks=all时,如果此时ISR列表只有[1,2],3被踢出ISR列表,只须要保障两个正本同步了,生产者就会收到胜利响应. Case 2如下图,当min.insync.replicas=2,如果此时ISR列表只有[1],2和3被踢出ISR列表,那么当acks=all时,则不能胜利写入数;当acks=0或者acks=1能够胜利写入数据. Case 3这种状况是很容易引起误会的,如果acks=all且min.insync.replicas=2,此时ISR列表为[1,2,3],那么还是会等到所有的同步正本都同步了音讯,才会向生产者发送胜利响应的ack.因为min.insync.replicas=2只是一个最低限度,即同步正本少于该配置值,则会抛异样,而acks=all,是须要保障所有的ISR列表的正本都同步了才能够发送胜利响应. 如下图所示: 总结acks=0,生产者在胜利写入音讯之前不会期待任何来自服务器的响应. acks=1,只有集群的leader分区正本接管到了音讯,就会向生产者发送一个胜利响应的ack. ...

August 8, 2020 · 1 min · jiezi

关于kafka:Kafka权威指南

Kafka权威指南 下载地址: https://pan.baidu.com/s/1auSKYE5MlVI2yLWpJ3f1fA 扫码上面二维码关注公众号回复100022 获取分享码 本书目录构造如下: 序 xiii 前言 xv 第 1 章 初识Kafka 1 1.1 公布与订阅音讯零碎 1 1.1.1 如何开始 2 1.1.2 独立的队列零碎 3 1.2 Kafka退场 4 1.2.1 音讯和批次 4 1.2.2 模式 4 1.2.3 主题和分区 5 1.2.4 生产者和消费者 5 1.2.5 broker和集群 6 1.2.6 多集群 7 1.3 为什么抉择Kafka 8 1.3.1 多个生产者 8 1.3.2 多个消费者 8 1.3.3 基于磁盘的数据存储 9 1.3.4 伸缩性 9 1.3.5 高性能 9 1.4 数据生态系统 9 1.5 起源故事 11 1.5.1 LinkedIn的问题 11 1.5.2 Kafka的诞生 12 1.5.3 走向开源 12 1.5.4 命名 13 1.6 开始Kafka之旅 13 第 2 章 装置Kafka 14 2.1 要事后行 14 2.1.1 抉择操作系统 14 2.1.2 装置Java 14 2.1.3 装置Zookeeper 15 2.2 装置Kafka Broker 17 2.3 broker配置 18 2.3.1 惯例配置 18 2.3.2 主题的默认配置 19 2.4 硬件的抉择 23 2.4.1 磁盘吞吐量 23 2.4.2 磁盘容量 23 2.4.3 内存 23 2.4.4 网络 24 2.4.5 CPU 24 2.5 云端的Kafka 24 2.6 Kafka集群 24 2.6.1 须要多少个broker 25 2.6.2 broker 配置 25 2.6.3 操作系统调优 26 2.7 生产环境的注意事项 28 2.7.1 垃圾回收器选项 28 2.7.2 数据中心布局 29 2.7.3 共享Zookeeper 29 2.8 总结 30 第 3 章 Kafka生产者——向Kafka写入数据 31 3.1 生产者概览 32 3.2 创立Kafka生产者 33 3.3 发送音讯到Kafka 34 3.3.1 同步发送音讯 35 3.3.2 异步发送音讯 35 3.4 生产者的配置 36 3.5 序列化器 39 3.5.1 自定义序列化器 39 3.5.2 应用Avro序列化 41 3.5.3 在Kafka里应用Avro 42 3.6 分区 45 3.7 旧版的生产者API 46 3.8 总结 47 第 4 章 Kafka消费者——从Kafka读取数据 48 4.1 KafkaConsumer概念 48 4.1.1 消费者和消费者群组 48 4.1.2 消费者群组和分区再平衡 51 4.2 创立Kafka消费者 52 4.3 订阅主题 53 4.4 轮询 53 4.5 消费者的配置 55 4.6 提交和偏移量 57 4.6.1 主动提交 58 4.6.2 提交以后偏移量 59 4.6.3 异步提交 59 4.6.4 同步和异步组合提交 61 4.6.5 提交特定的偏移量 61 4.7 再平衡监听器 62 4.8 从特定偏移量处开始解决记录 64 4.9 如何退出 66 4.10 反序列化器 67 4.11 独立消费者——为什么以及怎么应用没有群组的消费者 71 4.12 旧版的消费者API 71 4.13 总结 72 第 5 章 深刻Kafka 73 5.1 集群成员关系 73 5.2 控制器 74 5.3 复制 74 5.4 解决申请 76 5.4.1 生产申请 78 5.4.2 获取申请 78 5.4.3 其余申请 80 5.5 物理存储 81 5.5.1 分区调配 81 5.5.2 文件治理 82 5.5.3 文件格式 83 5.5.4 索引 84 5.5.5 清理 84 5.5.6 清理的工作原理 84 5.5.7 被删除的事件 86 5.5.8 何时会清理主题 86 5.9 总结 86 第 6 章 牢靠的数据传递 87 6.1 可靠性保障 87 6.2 复制 88 6.3 broker配置 89 6.3.1 复制系数 89 6.3.2 不齐全的领袖选举 90 6.3.3 起码同步正本 91 6.4 在牢靠的零碎里应用生产者 92 6.4.1 发送确认 92 6.4.2 配置生产者的重试参数 93 6.4.3 额定的错误处理 94 6.5 在牢靠的零碎里应用消费者 94 6.5.1 消费者的可靠性配置 95 6.5.2 显式提交偏移量 95 6.6 验证系统可靠性 97 6.6.1 配置验证 98 6.6.2 利用程序验证 98 6.6.3 在生产环境监控可靠性 99 6.7 总结 100 第 7 章 构建数据管道 101 7.1 构建数据管道时须要思考的问题 102 7.1.1 及时性 102 7.1.2 可靠性 102 7.1.3 高吞吐量和动静吞吐量 103 7.1.4 数据格式 103 7.1.5 转换 104 7.1.6 安全性 104 7.1.7 故障解决能力 104 7.1.8 耦合性和灵活性 105 7.2 如何在Connect API和客户端API之间作出抉择 105 7.3 Kafka Connect 106 7.3.1 运行Connect 106 7.3.2 连接器示例——文件数据源和文件数据池 107 7.3.3 连接器示例——从MySQL到ElasticSearch 109 7.3.4 深刻了解Connect 114 7.4 Connect之外的抉择 116 7.4.1 用于其余数据存储的摄入框架 116 7.4.2 基于图形界面的ETL工具 117 7.4.3 流式解决框架 117 7.5 总结 117 第 8 章 跨集群数据镜像 118 8.1 跨集群镜像的应用场景 118 8.2 多集群架构 119 8.2.1 跨数据中心通信的一些现实情况 119 8.2.2 Hub和Spoke架构 120 8.2.3 双活架构 121 8.2.4 主备架构 123 8.2.5 延展集群 127 8.3 Kafka的MirrorMaker 128 8.3.1 如何配置 129 8.3.2 在生产环境部署MirrorMaker 130 8.3.3 MirrorMaker调优 132 8.4 其余跨集群镜像计划 134 8.4.1 优步的uReplicator 134 8.4.2 Confluent的Replicator 135 8.5 总结 135 第 9 章 治理Kafka 136 9.1 主题操作 136 9.1.1 创立主题 137 9.1.2 减少分区 138 9.1.3 删除主题 138 9.1.4 列出集群里的所有主题 139 9.1.5 列出主题详细信息 139 9.2 消费者群组 140 9.2.1 列出并形容群组 140 9.2.2 删除群组 142 9.2.3 偏移量治理 142 9.3 动静配置变更 143 9.3.1 笼罩主题的默认配置 143 9.3.2 笼罩客户端的默认配置 145 9.3.3 列出被笼罩的配置 145 9.3.4 移除被笼罩的配置 146 9.4 分区治理 146 9.4.1 首选的领袖选举 146 9.4.2 批改分区正本 147 9.4.3 批改复制系数 150 9.4.4 转储日志片段 151 9.4.5 正本验证 152 9.5 生产和生产 153 9.5.1 控制台消费者 153 9.5.2 控制台生产者 155 9.6 客户端ACL 157 9.7 不平安的操作 157 9.7.1 挪动集群控制器 157 9.7.2 勾销分区重调配 157 9.7.3 移除待删除的主题 158 9.7.4 手动删除主题 158 9.8 总结 159 第 10 章 监控Kafka 160 10.1 度量指标根底 160 10.1.1 度量指标在哪里 160 10.1.2 外部或内部度量 161 10.1.3 应用程序衰弱检测 161 10.1.4 度量指标的覆盖面 161 10.2 broker的度量指标 162 10.2.1 非同步分区 162 10.2.2 broker度量指标 166 10.2.3 主题和分区的度量指标 173 10.2.4 Java虚拟机监控 174 10.2.5 操作系统监控 175 10.2.6 日志 176 10.3 客户端监控 177 10.3.1 生产者度量指标 177 10.3.2 消费者度量指标 179 10.3.3 配额 181 10.4 延时监控 182 10.5 端到端监控 183 10.6 总结 183 第 11 章 流式解决 184 11.1 什么是流式解决 185 11.2 流式解决的一些概念 186 11.2.1 工夫 187 11.2.2 状态 188 11.2.3 流和表的二元性 188 11.2.4 工夫窗口 189 11.3 流式解决的设计模式 190 11.3.1 单个事件处理 191 11.3.2 应用本地状态 191 11.3.3 多阶段解决和重分区 193 11.3.4 应用内部查找——流和表的连贯 193 11.3.5 流与流的连贯 195 11.3.6 乱序的事件 195 11.3.7 重新处理 196 11.4 Streams示例 197 11.4.1 字数统计 197 11.4.2 股票市场统计 199 11.4.3 填充点击事件流 201 11.5 Kafka Streams的架构概览 202 11.5.1 构建拓扑 202 11.5.2 对拓扑进行伸缩 203 11.5.3 从故障中存活下来 205 11.6 流式解决应用场景 205 11.7 如何抉择流式解决框架 206 11.8 总结 208 附录A 在其余操作系统上装置Kafka 209 作者介绍 214 封面介绍 214 ...

August 8, 2020 · 4 min · jiezi

关于kafka:Kafka权威指南

Kafka权威指南 下载地址: https://pan.baidu.com/s/1auSKYE5MlVI2yLWpJ3f1fA 扫码上面二维码关注公众号回复100022 获取分享码 本书目录构造如下: 序 xiii 前言 xv 第 1 章 初识Kafka 1 1.1 公布与订阅音讯零碎 1 1.1.1 如何开始 2 1.1.2 独立的队列零碎 3 1.2 Kafka退场 4 1.2.1 音讯和批次 4 1.2.2 模式 4 1.2.3 主题和分区 5 1.2.4 生产者和消费者 5 1.2.5 broker和集群 6 1.2.6 多集群 7 1.3 为什么抉择Kafka 8 1.3.1 多个生产者 8 1.3.2 多个消费者 8 1.3.3 基于磁盘的数据存储 9 1.3.4 伸缩性 9 1.3.5 高性能 9 1.4 数据生态系统 9 1.5 起源故事 11 1.5.1 LinkedIn的问题 11 1.5.2 Kafka的诞生 12 1.5.3 走向开源 12 1.5.4 命名 13 1.6 开始Kafka之旅 13 第 2 章 装置Kafka 14 2.1 要事后行 14 2.1.1 抉择操作系统 14 2.1.2 装置Java 14 2.1.3 装置Zookeeper 15 2.2 装置Kafka Broker 17 2.3 broker配置 18 2.3.1 惯例配置 18 2.3.2 主题的默认配置 19 2.4 硬件的抉择 23 2.4.1 磁盘吞吐量 23 2.4.2 磁盘容量 23 2.4.3 内存 23 2.4.4 网络 24 2.4.5 CPU 24 2.5 云端的Kafka 24 2.6 Kafka集群 24 2.6.1 须要多少个broker 25 2.6.2 broker 配置 25 2.6.3 操作系统调优 26 2.7 生产环境的注意事项 28 2.7.1 垃圾回收器选项 28 2.7.2 数据中心布局 29 2.7.3 共享Zookeeper 29 2.8 总结 30 第 3 章 Kafka生产者——向Kafka写入数据 31 3.1 生产者概览 32 3.2 创立Kafka生产者 33 3.3 发送音讯到Kafka 34 3.3.1 同步发送音讯 35 3.3.2 异步发送音讯 35 3.4 生产者的配置 36 3.5 序列化器 39 3.5.1 自定义序列化器 39 3.5.2 应用Avro序列化 41 3.5.3 在Kafka里应用Avro 42 3.6 分区 45 3.7 旧版的生产者API 46 3.8 总结 47 第 4 章 Kafka消费者——从Kafka读取数据 48 4.1 KafkaConsumer概念 48 4.1.1 消费者和消费者群组 48 4.1.2 消费者群组和分区再平衡 51 4.2 创立Kafka消费者 52 4.3 订阅主题 53 4.4 轮询 53 4.5 消费者的配置 55 4.6 提交和偏移量 57 4.6.1 主动提交 58 4.6.2 提交以后偏移量 59 4.6.3 异步提交 59 4.6.4 同步和异步组合提交 61 4.6.5 提交特定的偏移量 61 4.7 再平衡监听器 62 4.8 从特定偏移量处开始解决记录 64 4.9 如何退出 66 4.10 反序列化器 67 4.11 独立消费者——为什么以及怎么应用没有群组的消费者 71 4.12 旧版的消费者API 71 4.13 总结 72 第 5 章 深刻Kafka 73 5.1 集群成员关系 73 5.2 控制器 74 5.3 复制 74 5.4 解决申请 76 5.4.1 生产申请 78 5.4.2 获取申请 78 5.4.3 其余申请 80 5.5 物理存储 81 5.5.1 分区调配 81 5.5.2 文件治理 82 5.5.3 文件格式 83 5.5.4 索引 84 5.5.5 清理 84 5.5.6 清理的工作原理 84 5.5.7 被删除的事件 86 5.5.8 何时会清理主题 86 5.9 总结 86 第 6 章 牢靠的数据传递 87 6.1 可靠性保障 87 6.2 复制 88 6.3 broker配置 89 6.3.1 复制系数 89 6.3.2 不齐全的领袖选举 90 6.3.3 起码同步正本 91 6.4 在牢靠的零碎里应用生产者 92 6.4.1 发送确认 92 6.4.2 配置生产者的重试参数 93 6.4.3 额定的错误处理 94 6.5 在牢靠的零碎里应用消费者 94 6.5.1 消费者的可靠性配置 95 6.5.2 显式提交偏移量 95 6.6 验证系统可靠性 97 6.6.1 配置验证 98 6.6.2 利用程序验证 98 6.6.3 在生产环境监控可靠性 99 6.7 总结 100 第 7 章 构建数据管道 101 7.1 构建数据管道时须要思考的问题 102 7.1.1 及时性 102 7.1.2 可靠性 102 7.1.3 高吞吐量和动静吞吐量 103 7.1.4 数据格式 103 7.1.5 转换 104 7.1.6 安全性 104 7.1.7 故障解决能力 104 7.1.8 耦合性和灵活性 105 7.2 如何在Connect API和客户端API之间作出抉择 105 7.3 Kafka Connect 106 7.3.1 运行Connect 106 7.3.2 连接器示例——文件数据源和文件数据池 107 7.3.3 连接器示例——从MySQL到ElasticSearch 109 7.3.4 深刻了解Connect 114 7.4 Connect之外的抉择 116 7.4.1 用于其余数据存储的摄入框架 116 7.4.2 基于图形界面的ETL工具 117 7.4.3 流式解决框架 117 7.5 总结 117 第 8 章 跨集群数据镜像 118 8.1 跨集群镜像的应用场景 118 8.2 多集群架构 119 8.2.1 跨数据中心通信的一些现实情况 119 8.2.2 Hub和Spoke架构 120 8.2.3 双活架构 121 8.2.4 主备架构 123 8.2.5 延展集群 127 8.3 Kafka的MirrorMaker 128 8.3.1 如何配置 129 8.3.2 在生产环境部署MirrorMaker 130 8.3.3 MirrorMaker调优 132 8.4 其余跨集群镜像计划 134 8.4.1 优步的uReplicator 134 8.4.2 Confluent的Replicator 135 8.5 总结 135 第 9 章 治理Kafka 136 9.1 主题操作 136 9.1.1 创立主题 137 9.1.2 减少分区 138 9.1.3 删除主题 138 9.1.4 列出集群里的所有主题 139 9.1.5 列出主题详细信息 139 9.2 消费者群组 140 9.2.1 列出并形容群组 140 9.2.2 删除群组 142 9.2.3 偏移量治理 142 9.3 动静配置变更 143 9.3.1 笼罩主题的默认配置 143 9.3.2 笼罩客户端的默认配置 145 9.3.3 列出被笼罩的配置 145 9.3.4 移除被笼罩的配置 146 9.4 分区治理 146 9.4.1 首选的领袖选举 146 9.4.2 批改分区正本 147 9.4.3 批改复制系数 150 9.4.4 转储日志片段 151 9.4.5 正本验证 152 9.5 生产和生产 153 9.5.1 控制台消费者 153 9.5.2 控制台生产者 155 9.6 客户端ACL 157 9.7 不平安的操作 157 9.7.1 挪动集群控制器 157 9.7.2 勾销分区重调配 157 9.7.3 移除待删除的主题 158 9.7.4 手动删除主题 158 9.8 总结 159 第 10 章 监控Kafka 160 10.1 度量指标根底 160 10.1.1 度量指标在哪里 160 10.1.2 外部或内部度量 161 10.1.3 应用程序衰弱检测 161 10.1.4 度量指标的覆盖面 161 10.2 broker的度量指标 162 10.2.1 非同步分区 162 10.2.2 broker度量指标 166 10.2.3 主题和分区的度量指标 173 10.2.4 Java虚拟机监控 174 10.2.5 操作系统监控 175 10.2.6 日志 176 10.3 客户端监控 177 10.3.1 生产者度量指标 177 10.3.2 消费者度量指标 179 10.3.3 配额 181 10.4 延时监控 182 10.5 端到端监控 183 10.6 总结 183 第 11 章 流式解决 184 11.1 什么是流式解决 185 11.2 流式解决的一些概念 186 11.2.1 工夫 187 11.2.2 状态 188 11.2.3 流和表的二元性 188 11.2.4 工夫窗口 189 11.3 流式解决的设计模式 190 11.3.1 单个事件处理 191 11.3.2 应用本地状态 191 11.3.3 多阶段解决和重分区 193 11.3.4 应用内部查找——流和表的连贯 193 11.3.5 流与流的连贯 195 11.3.6 乱序的事件 195 11.3.7 重新处理 196 11.4 Streams示例 197 11.4.1 字数统计 197 11.4.2 股票市场统计 199 11.4.3 填充点击事件流 201 11.5 Kafka Streams的架构概览 202 11.5.1 构建拓扑 202 11.5.2 对拓扑进行伸缩 203 11.5.3 从故障中存活下来 205 11.6 流式解决应用场景 205 11.7 如何抉择流式解决框架 206 11.8 总结 208 附录A 在其余操作系统上装置Kafka 209 作者介绍 214 封面介绍 214 ...

August 8, 2020 · 4 min · jiezi

关于kafka:Kafka-Python的生产者和消费者

Kafka Python的生产者和消费者在本教程中,咱们将应用Python构建Kafka Producer和Consumer。除此之外,咱们还将学习如何在Kafka中设置配置以及如何应用组和偏移量概念。 建设对于本教程,咱们应该在计算机上安装python。另外,咱们须要拜访在咱们的设施或某些服务器上运行的Apache Kafka。您能够查看如何在Windows上装置Apache Kafka。除此之外,咱们须要python的_kafka_ 库来运行咱们的代码。要解决此问题,请在零碎上运行以下命令 pip install kafka 卡夫卡生产者=== 让咱们开始创立本人的Kafka Producer。咱们必须从kafka库导入KafkaProducer。咱们还须要将Kafka服务器的代理列表提供给Producer,以便它能够连贯到Kafka服务器。咱们还须要提供要向其公布音讯的主题名称。这是创立生产者所需的最小配置。 from kafka import KafkaProducerbootstrap_servers = ['localhost:9092']topicName = 'myTopic'producer = KafkaProducer(bootstrap_servers = bootstrap_servers)producer = KafkaProducer()咱们能够应用以下代码开始向该主题发送音讯。 ack = producer.send(topicName, b'Hello World!!!!!!!!')metadata = ack.get()print(metadata.topic)print(metadata.partition)下面的代码将音讯发送到Kafka服务器中名为“ myTopic”的主题。然而,如果该主题尚未呈现在Kafka服务器中怎么办?在这种状况下,Kafka会应用该名称创立一个新主题并向其公布音讯。不便吗?然而您应该记住要查看主题名称中是否存在拼写错误。 如果要为Producer设置更多属性或更改其序列化格局,则能够应用以下代码行。 producer = KafkaProducer(bootstrap_servers = bootstrap_servers, retries = 5,value_serializer=lambda m: json.dumps(m).encode('ascii'))卡夫卡消费者实现创立Producer的工作后,当初让咱们开始应用python构建Consumer,看看这是否同样容易。导入KafkaConsumer后,咱们须要设置提供疏导服务器ID和主题名称,以与Kafka服务器建设连贯。 from kafka import KafkaConsumerimport sysbootstrap_servers = ['localhost:9092']topicName = 'myTopic'consumer = KafkaConsumer (topicName, group_id = 'group1',bootstrap_servers = bootstrap_servers,auto_offset_reset = 'earliest')如咱们所见,咱们须要设置哪个组消费者属于。另外,咱们须要指定偏移量,此使用者应该从该偏移量读取主题中的音讯。在上述情况下,咱们最早指定了auto_offset_reset,这意味着此使用者将从主题的结尾开始读取音讯。 之后,咱们能够开始浏览主题中的音讯。与每条音讯一起,咱们还取得了一些其余信息,例如音讯所属的分区,在该分区中的偏移量和键。 ...

August 4, 2020 · 1 min · jiezi

关于kafka:Kafka的原理介绍及实践

文|孙超 网易智慧企业资深后端开发工程师 官网定义 依据官网的介绍,kafka是一个提供对立的、高吞吐、低提早的,用来解决实时数据的流式平台,它具备以下三个性: 流式记录的公布和订阅:相似于音讯零碎。存储:在一个分布式、容错的集群中平安长久化地存储流式数据。解决:编写流解决应用程序,对实时事件进行响应。kafka个别用在两大类利用中: 建设实时流数据管道,在零碎或利用之间实时地传输数据。构建对数据流进行转换和解决的实时流应用程序。在邮箱服务中,咱们次要将kafka作为音讯零碎,用于零碎内部消息的传输。为什么要采纳kafka呢?让咱们先从kafka的设计原理说起。 概念与存储机制 kafka中是以Topic机制来对音讯进行分类的,同一类音讯属于同一个Topic,你能够将每个Topic看成是一个音讯队列。生产者将音讯发送到相应的Topic,而消费者通过从Topic拉取音讯来生产,没错,在kafka中是要求消费者被动拉取音讯生产的,它并不会被动推送音讯,这是它的一个特点,为什么会这样设计呢?咱们前面再说,先来看一下Topic的构造: Partition分区,每个topic能够有多个分区,这是kafka为了进步并发量而设计的一种机制:一个topic下的多个分区能够并发接管音讯,同样的也能供消费者并发拉取音讯,即分区之间互不烦扰,这样的话,有多少个分区就能够有多大的并发量。所以,如果要更精确的打比方,一个分区就是一个音讯队列,只不过这些音讯队列同属于一种音讯分类。 在kafka服务器,分区是以目录模式存在的,每个分区目录中,kafka会按配置大小或配置周期将分区拆分成多个段文件(LogSegment), 每个段由三局部组成: 磁盘文件:*.log位移索引文件:*.index工夫索引文件:*.timeindex其中*.log用于存储音讯自身的数据内容,*.index存储音讯在文件中的地位(包含音讯的逻辑offset和物理存储offset),*.timeindex存储音讯创立工夫和对应逻辑地址的映射关系。 段文件结构图如下 : 将分区拆分成多个段是为了管制存储的文件大小,如果整个分区只保留为一个文件,那随着分区里音讯的增多,文件也将越来越大,最初不可管制。而如果每个音讯都保留为一个文件,那文件数量又将变得微小,同样容易失去管制。所以kafka采纳段这种形式,管制了每个文件的大小,也不便管制所有文件的数量。同时,这些文件因为大小适中,能够很不便地通过操作系统mmap机制映射到内存中,进步写入和读取效率。这个设计的另一个益处是:当零碎要革除过期数据时,能够间接将过期的段文件删除,十分简洁。 然而这里也会有一个问题:如果每个音讯都要在index文件中保留地位信息,那么index文件也很容易变得很大,这样又会削弱上文所说的益处。所以在kafka中,index设计为稠密索引来升高index的文件大小,这样,index文件存储的理论内容为:该段音讯在音讯队列中的绝对offset和在log文件中的物理偏移量映射的稠密记录。 那么多少条音讯会在index中保留一条记录呢?这个能够通过系统配置来进行设置。索引记录固定为8个字节大小,别离为4个字节的绝对offset(音讯在partition中全局offset减去该segment的起始offset),4个字节的音讯具体存储文件的物理偏移量。 index文件结构图如下: Kafka不会在消费者拉取完音讯后马上就清理音讯,而是会保留段文件一段时间,直到其过期再标记为可清理,由后台程序定期进行清理。这种机制使得消费者能够反复生产音讯,满足更灵便的需要。 查问机制 下面说过,kafka尽管作为音讯零碎,然而生产音讯并不是通过推送而是通过拉取来生产的,client须要通过offset和size参数被动去查问音讯。 kafka收到客户端申请后,对音讯的寻址会通过上面几个步骤: 查找具体的Log Segment,kafka将段信息缓存在跳跃表中,所以这个步骤将从跳跃表中获取段信息。依据offset在index文件中进行定位,找到匹配范畴的偏移量position,此时失去的是一个近似起始文件偏移量。从Log文件的position地位处开始往后寻找,直到找到offset处的音讯。kafka读取示意图: RabbitMQ vs kafka 介绍了kafka的实现原理,咱们再来比照一下同样作为音讯队列服务的RabbitMQ。MQ的利用也很宽泛,性能多而全,那么和MQ相比,kafka有哪些劣势呢?为什么咱们会应用kafka而摈弃了RabbitMQ呢? RabbitMQ流程图: RabbitMQ消费者只能从队列头部按序进行生产,音讯一旦被生产,就会被打上删除标记,紧接着生产下一条音讯,没方法进行回溯操作,这样的话一个消费者生产完音讯,另一个消费者就别想再生产了。而Kafka提供动静指定生产位点,可能灵便地进行回溯生产操作,只有该音讯还在生命周期内能够反复拉取,并且不同消费者能够互不烦扰的生产同一个音讯队列,这就比RabbitMQ灵便多了。 kafka生产位点示意图: RabbitMQ如果要满足多个消费者生产同一个音讯队列,也能够借助exchange路由能力,然而这样会将音讯复制到多个队列,每个消费者须要绑定一个本人的队列进行生产。如果有几百个消费者,那么队列复制几百倍,引起mq的音讯水位猛涨,容易失控。而kafka就没这个问题,不论多少个消费者都只须要一个队列就能满足,每个消费者都能够残缺地不互相烦扰地生产队列中的所有音讯。 当然,RabbitMQ也有其长处,它提供的exchange,binding, queue等形象实体,提供弱小的路由关系(rounte key and bindkey)和音讯过滤能力。作为传统音讯零碎提供了细粒度的音讯控制能力。而Kafka次要是面向高流量,大吞吐的批处理零碎,在路由形象方面化繁为简,重点关注零碎的高吞吐,所以应用上更为简洁。 kafka还有传统解决方案无奈满足的高伸缩能力等劣势,这里就不一一介绍了。 Kafka在邮件系统data bus中的使用 正因为kafka有着以上介绍的能力和劣势,咱们的邮箱服务中采纳了它作为音讯零碎,其中一个利用就是邮件系统的data bus。 data bus介绍 邮件系统用户收发信流程随同着大量的业务逻辑和子系统调用,如果将这些流程都强附丽在骨干枝上,将会对系统造成较大的压力,整个业务流程也将变得复杂而迟缓。所以通过数据总线将主次流程进行解耦,加重收发信主流程的复杂度,使其能够以更快的速度实现,放慢零碎响应工夫。主流程产生事件源,通过kafka的传输,触发多个主要流程,主要流程能够并发在零碎后盾实现,并且能够轻易的扩大多种多样的主要流程。 下图以简化后的信流程为例: Kafka在data bus中的使用 邮件系统在实现收发信流程后,会生成当次流程相干的零碎事件,比方新邮件事件。data bus将这些事件写入到kafka集群的相应topic中,上游的一系列子系统对topic进行生产。 每个不同的流程会对应不必的topic,以辨别不同类别的事件,比方新进邮件,邮件已读,邮件删除等。每个topic能够依据各自的音讯吞吐量和并发需要划分成多个partition,比方新进邮件量大能够划分成256个分区,邮件删除量小则能够划分32个分区。每个事件按什么机制来调配到相应的分区呢?一般来说能够按邮筒来划分,同一个邮筒的事件进入同一个partition,这样就保障了同一邮筒产生的事件的程序。不同事件的时效性可能有不同,所以其须要保留的工夫也能够不同,能够依据业务的需要来设置topic的保留时长。因为事件全副写入到kafka中,后台任务能够任意生产,所以能够灵便地减少不同的业务流程。如下图所示,利用生产能力能借助Kafka集群实现弹性扩容 总    结 kafka在邮件系统中的利用给咱们带来的益处: ...

July 24, 2020 · 1 min · jiezi

关于kafka:Kafka的原理介绍及实践

文|孙超 网易智慧企业资深后端开发工程师 官网定义 依据官网的介绍,kafka是一个提供对立的、高吞吐、低提早的,用来解决实时数据的流式平台,它具备以下三个性: 流式记录的公布和订阅:相似于音讯零碎。存储:在一个分布式、容错的集群中平安长久化地存储流式数据。解决:编写流解决应用程序,对实时事件进行响应。kafka个别用在两大类利用中: 建设实时流数据管道,在零碎或利用之间实时地传输数据。构建对数据流进行转换和解决的实时流应用程序。在邮箱服务中,咱们次要将kafka作为音讯零碎,用于零碎内部消息的传输。为什么要采纳kafka呢?让咱们先从kafka的设计原理说起。 概念与存储机制 kafka中是以Topic机制来对音讯进行分类的,同一类音讯属于同一个Topic,你能够将每个Topic看成是一个音讯队列。生产者将音讯发送到相应的Topic,而消费者通过从Topic拉取音讯来生产,没错,在kafka中是要求消费者被动拉取音讯生产的,它并不会被动推送音讯,这是它的一个特点,为什么会这样设计呢?咱们前面再说,先来看一下Topic的构造: Partition分区,每个topic能够有多个分区,这是kafka为了进步并发量而设计的一种机制:一个topic下的多个分区能够并发接管音讯,同样的也能供消费者并发拉取音讯,即分区之间互不烦扰,这样的话,有多少个分区就能够有多大的并发量。所以,如果要更精确的打比方,一个分区就是一个音讯队列,只不过这些音讯队列同属于一种音讯分类。 在kafka服务器,分区是以目录模式存在的,每个分区目录中,kafka会按配置大小或配置周期将分区拆分成多个段文件(LogSegment), 每个段由三局部组成: 磁盘文件:*.log位移索引文件:*.index工夫索引文件:*.timeindex其中*.log用于存储音讯自身的数据内容,*.index存储音讯在文件中的地位(包含音讯的逻辑offset和物理存储offset),*.timeindex存储音讯创立工夫和对应逻辑地址的映射关系。 段文件结构图如下 : 将分区拆分成多个段是为了管制存储的文件大小,如果整个分区只保留为一个文件,那随着分区里音讯的增多,文件也将越来越大,最初不可管制。而如果每个音讯都保留为一个文件,那文件数量又将变得微小,同样容易失去管制。所以kafka采纳段这种形式,管制了每个文件的大小,也不便管制所有文件的数量。同时,这些文件因为大小适中,能够很不便地通过操作系统mmap机制映射到内存中,进步写入和读取效率。这个设计的另一个益处是:当零碎要革除过期数据时,能够间接将过期的段文件删除,十分简洁。 然而这里也会有一个问题:如果每个音讯都要在index文件中保留地位信息,那么index文件也很容易变得很大,这样又会削弱上文所说的益处。所以在kafka中,index设计为稠密索引来升高index的文件大小,这样,index文件存储的理论内容为:该段音讯在音讯队列中的绝对offset和在log文件中的物理偏移量映射的稠密记录。 那么多少条音讯会在index中保留一条记录呢?这个能够通过系统配置来进行设置。索引记录固定为8个字节大小,别离为4个字节的绝对offset(音讯在partition中全局offset减去该segment的起始offset),4个字节的音讯具体存储文件的物理偏移量。 index文件结构图如下: Kafka不会在消费者拉取完音讯后马上就清理音讯,而是会保留段文件一段时间,直到其过期再标记为可清理,由后台程序定期进行清理。这种机制使得消费者能够反复生产音讯,满足更灵便的需要。 查问机制 下面说过,kafka尽管作为音讯零碎,然而生产音讯并不是通过推送而是通过拉取来生产的,client须要通过offset和size参数被动去查问音讯。 kafka收到客户端申请后,对音讯的寻址会通过上面几个步骤: 查找具体的Log Segment,kafka将段信息缓存在跳跃表中,所以这个步骤将从跳跃表中获取段信息。依据offset在index文件中进行定位,找到匹配范畴的偏移量position,此时失去的是一个近似起始文件偏移量。从Log文件的position地位处开始往后寻找,直到找到offset处的音讯。kafka读取示意图: RabbitMQ vs kafka 介绍了kafka的实现原理,咱们再来比照一下同样作为音讯队列服务的RabbitMQ。MQ的利用也很宽泛,性能多而全,那么和MQ相比,kafka有哪些劣势呢?为什么咱们会应用kafka而摈弃了RabbitMQ呢? RabbitMQ流程图: RabbitMQ消费者只能从队列头部按序进行生产,音讯一旦被生产,就会被打上删除标记,紧接着生产下一条音讯,没方法进行回溯操作,这样的话一个消费者生产完音讯,另一个消费者就别想再生产了。而Kafka提供动静指定生产位点,可能灵便地进行回溯生产操作,只有该音讯还在生命周期内能够反复拉取,并且不同消费者能够互不烦扰的生产同一个音讯队列,这就比RabbitMQ灵便多了。 kafka生产位点示意图: RabbitMQ如果要满足多个消费者生产同一个音讯队列,也能够借助exchange路由能力,然而这样会将音讯复制到多个队列,每个消费者须要绑定一个本人的队列进行生产。如果有几百个消费者,那么队列复制几百倍,引起mq的音讯水位猛涨,容易失控。而kafka就没这个问题,不论多少个消费者都只须要一个队列就能满足,每个消费者都能够残缺地不互相烦扰地生产队列中的所有音讯。 当然,RabbitMQ也有其长处,它提供的exchange,binding, queue等形象实体,提供弱小的路由关系(rounte key and bindkey)和音讯过滤能力。作为传统音讯零碎提供了细粒度的音讯控制能力。而Kafka次要是面向高流量,大吞吐的批处理零碎,在路由形象方面化繁为简,重点关注零碎的高吞吐,所以应用上更为简洁。 kafka还有传统解决方案无奈满足的高伸缩能力等劣势,这里就不一一介绍了。 Kafka在邮件系统data bus中的使用 正因为kafka有着以上介绍的能力和劣势,咱们的邮箱服务中采纳了它作为音讯零碎,其中一个利用就是邮件系统的data bus。 data bus介绍 邮件系统用户收发信流程随同着大量的业务逻辑和子系统调用,如果将这些流程都强附丽在骨干枝上,将会对系统造成较大的压力,整个业务流程也将变得复杂而迟缓。所以通过数据总线将主次流程进行解耦,加重收发信主流程的复杂度,使其能够以更快的速度实现,放慢零碎响应工夫。主流程产生事件源,通过kafka的传输,触发多个主要流程,主要流程能够并发在零碎后盾实现,并且能够轻易的扩大多种多样的主要流程。 下图以简化后的信流程为例: Kafka在data bus中的使用 邮件系统在实现收发信流程后,会生成当次流程相干的零碎事件,比方新邮件事件。data bus将这些事件写入到kafka集群的相应topic中,上游的一系列子系统对topic进行生产。 每个不同的流程会对应不必的topic,以辨别不同类别的事件,比方新进邮件,邮件已读,邮件删除等。每个topic能够依据各自的音讯吞吐量和并发需要划分成多个partition,比方新进邮件量大能够划分成256个分区,邮件删除量小则能够划分32个分区。每个事件按什么机制来调配到相应的分区呢?一般来说能够按邮筒来划分,同一个邮筒的事件进入同一个partition,这样就保障了同一邮筒产生的事件的程序。不同事件的时效性可能有不同,所以其须要保留的工夫也能够不同,能够依据业务的需要来设置topic的保留时长。因为事件全副写入到kafka中,后台任务能够任意生产,所以能够灵便地减少不同的业务流程。如下图所示,利用生产能力能借助Kafka集群实现弹性扩容 总    结 kafka在邮件系统中的利用给咱们带来的益处: ...

July 24, 2020 · 1 min · jiezi

关于kafka:快速搭建kafka集群3台

默认三台机器曾经装置java,设置好ssh免密连贯。1.下载kafkahttp://kafka.apache.org/downl...2.发送到集群解压到指定目录/usr/local下 tar -zxvf kafka_2.11-0.10.0.1.tar.gz -C /usr/local3.进入kafka_2.11-0.10.0.1目录,创立文件夹zk_kfk_data(自取),并在该目录下创立myid文件,内容在三个集群中不同,别离是1,2,3 cd kafka_2.11-0.10.0.1mkdir zk_kfk_datavi myid4.创立目录 mkdir logsmkdir kafka-logs-15.批改/config/zookeeper.properties文件 cd configvi zookeeper.properties6.批改server.properties vi server.properties7.把kafka整个文件夹分发给两个子节点 scp -r /usr/local/kafka_2.11-0.10.1.1 hadoop@centos2:/usr/localscp -r /usr/local/kafka_2.11-0.10.1.1 hadoop@centos3:/usr/local8.批改centos2和centos3的myid ssh centos2cd /usr/local/kafka_2.11-0.10.1.1/zk_kfk_datavi myid ssh centos3cd /usr/local/kafka_2.11-0.10.1.1/zk_kfk_datavi myid9.批改centos2和centos3的server.properties ssh centos2cd /usr/local/kafka_2.11-0.10.1.1/configvi server.properties ssh centos3cd /usr/local/kafka_2.11-0.10.1.1/configvi server.properties装置实现!运行测试:(默认在/usr/local/kafka_2.11-0.10.1.1目录下执行)10.三台集群别离启动zk: ./bin/zookeeper-server-start.sh config/zookeeper.properties & 11.启动kafka集群 nohup ./bin/kafka-server-start.sh config/server.properties &>> kafka.log &12.创立topic: ./bin/kafka-topics.sh --create --zookeeper centos1:2181,centos2:2181,centos3:2181  --replication-factor 1 --partitions 1 --topic test13.查看topic: ./bin/kafka-topics.sh --list --zookeeper localhost:218114.发送数据: ./bin/kafka-console-producer.sh --broker-list centos1:9092,centos2:9092,centos3:9092 --topic test15.生产: ...

July 22, 2020 · 1 min · jiezi

关于kafka:查看kafka基本信息命令

将miner-profit topic正本由一个减少到3个replication.json { "version": 1, "partitions": [ { "topic": "miner-profit", "partition": 0, "replicas": [ 1, 2, 3 ] }, { "topic": "miner-profit", "partition": 1, "replicas": [ 1, 2, 3 ] }, { "topic": "miner-profit", "partition": 2, "replicas": [ 1, 2, 3 ] } ]}执行命令 ../kafka1/bin/kafka-reassign-partitions.sh --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --reassignment-json-file replication.json --execute查看所有topic./kafka1/bin/kafka-topics.sh --list --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183创立topic./kafka1/bin/kafka-topics.sh --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --create --replication-factor 2 --partitions 2 --topic user-login删除topic./kafka1/bin/kafka-topics.sh --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --delete --topic user-regisger减少topic分区数目./kafka1/bin/kafka-topics.sh --alter --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --topic miner-profit --partitions 3 查看topic分区信息./kafka1/bin/kafka-topics.sh --describe --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --topic miner-profit查看一个topic中的所有音讯./kafka1/bin/kafka-console-consumer.sh --bootstrap-server dev1.xiayu.com:9092,dev2.xiayu.com:9093,dev3.xiayu.com:9094 --topic miner-profit --from-beginning查看所有消费者组./kafka1/bin/kafka-consumer-groups.sh --list --bootstrap-server dev1.xiayu.com:9092,dev2.xiayu.com:9093,dev3.xiayu.com:9094查看消费者组生产状况./kafka1/bin/kafka-consumer-groups.sh --bootstrap-server dev1.xiayu.com:9092,dev2.xiayu.com:9093,dev3.xiayu.com:9094 --group xiayu-consumer-group --describecmak 启动./cmak-3.0.0.5/bin/cmak -Dconfig.file=./cmak-3.0.0.5/conf/application.conf -Dhttp.port=8333

July 21, 2020 · 1 min · jiezi

关于kafka:kafka-系列-2搭建与实践

前言入手实际往往比看看更重要???? 单机版 Docker 搭建version: '2' services: zookeeper: image: wurstmeister/zookeeper ports: - "2181:2181" kafka: image: wurstmeister/kafka depends_on: [ zookeeper ] ports: - "9092:9092" environment: KAFKA_ADVERTISED_HOST_NAME: kafka KAFKA_ADVERTISED_PORT: 9092 KAFKA_CREATE_TOPICS: "test:1:1" KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"注意事项: 如果想要 java 客户端可能失常连贯上 kafka, 须要配置宿主机的 hostsudo vim /etc/hosts172.20.10.6 kafka如何应用 kafka 自带的 kafka-console-producer 测试发送音讯?kafka-console-producer.sh --bootstrap-server kafka:9092 --topic test集群版 + kafka managerkafka 集群 docker-composeversion: '2'services: zookeeper: image: wurstmeister/zookeeper ports: - "2181:2181" kafka1: restart: always image: wurstmeister/kafka depends_on: [ zookeeper ] ports: - "9092:9092" environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181" KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka1:9092" KAFKA_LISTENERS: "PLAINTEXT://kafka1:9092" KAFKA_PORT: 9092 kafka2: restart: always image: wurstmeister/kafka depends_on: [ zookeeper ] ports: - "9093:9093" environment: KAFKA_BROKER_ID: 2 KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181" KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka2:9093" KAFKA_LISTENERS: "PLAINTEXT://kafka2:9093" KAFKA_PORT: 9093 kafka3: restart: always image: wurstmeister/kafka depends_on: [ zookeeper ] ports: - "9094:9094" environment: KAFKA_BROKER_ID: 3 KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181" KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka3:9094" KAFKA_LISTENERS: "PLAINTEXT://kafka3:9094" KAFKA_PORT: 9094注意事项: ...

July 19, 2020 · 2 min · jiezi

关于kafka:Kafka扩容之分区扩容

分区扩容举例,主题“user_order”目前是1个分区,这里将该主题分区减少到6个,察看批改后果kafka-topics.sh --partitions 6 --alter --zookeeper dn1:2181,dn2:2181,dn3:2181 --topic user_order这里只是批改分区数,然而数据还没有迁徙过来 应用kafka提供的工具kafka-reassign-partitions.sh来迁徙数据。迁徙数据须要分三步做: 生成迁徙打算先手动生成一个topic.json,内容如下。这里topic能够是一个列 {"topics": [{ "topic": "user_order"}],"version": 1}执行如下语句 --topics-to-move-json-file ./bin/kafka-reassign-partitions.sh --zookeeper dn1:2181,dn2:2181,dn3:2181 --topics-to-move-json-file topic.json --broker-list "0,1,2,3,4" --generate这句命令的意思是,将topic.json里的topic迁徙到broker-list列表里列的broker上,会失去一个迁徙执行打算 Current partition replica assignment { "version": 1, "partitions": [....]}Proposed partition reassignment configuration { "version": 1, "partitions": [.....]}新建一个文件reassignment.json,保留上边这些信息。其中Current partition replica assignment指以后的分区状况,Proposed partition reassignment configuration是打算的分区状况 数据迁徙执行如下命令 ./bin/kafka-reassign-partitions.sh --zookeeper dn1:2181,dn2:2181,dn3:2181 --reassignment-json-file reassignment.json --execute验证 ./bin/kafka-reassign-partitions.sh --zookeeper dn1:2181,dn2:2181,dn3:2181 --reassignment-json-file reassignment.json --verify

July 18, 2020 · 1 min · jiezi

基础科普一文带你了争Kafka基本原理

起源:阿凡卢,www.cnblogs.com/luxiaoxun/p/5492646.html简介 Apache Kafka是分布式公布-订阅音讯零碎。它最后由LinkedIn公司开发,之后成为Apache我的项目的一部分。Kafka是一种疾速、可扩大的、设计外在就是分布式的,分区的和可复制的提交日志服务。 Kafka架构 它的架构包含以下组件: 话题(Topic):是特定类型的音讯流。音讯是字节的无效负载(Payload),话题是音讯的分类名或种子(Feed)名。生产者(Producer):是可能公布音讯到话题的任何对象。服务代理(Broker):已公布的音讯保留在一组服务器中,它们被称为代理(Broker)或Kafka集群。消费者(Consumer):能够订阅一个或多个话题,并从Broker拉数据,从而生产这些已公布的音讯。 Kafka存储策略 1)kafka以topic来进行音讯治理,每个topic蕴含多个partition,每个partition对应一个逻辑log,有多个segment组成。 2)每个segment中存储多条音讯(见下图),音讯id由其逻辑地位决定,即从音讯id可间接定位到音讯的存储地位,防止id到地位的额定映射。 3)每个part在内存中对应一个index,记录每个segment中的第一条音讯偏移。 4)发布者发到某个topic的音讯会被平均的散布到多个partition上(或依据用户指定的路由规定进行散布),broker收到公布音讯往对应partition的最初一个segment上增加该音讯,当某个segment上的音讯条数达到配置值或音讯公布工夫超过阈值时,segment上的音讯会被flush到磁盘,只有flush到磁盘上的音讯订阅者能力订阅到,segment达到肯定的大小后将不会再往该segment写数据,broker会创立新的segment。 Kafka删除策略 1)N天前的删除。 2)保留最近的MGB数据。 Kafka broker 与其它音讯零碎不同,Kafka broker是无状态的。这意味着消费者必须保护已生产的状态信息。这些信息由消费者本人保护,broker齐全不论(有offset managerbroker治理)。 从代理删除音讯变得很辣手,因为代理并不知道消费者是否曾经应用了该音讯。Kafka创新性地解决了这个问题,它将一个简略的基于工夫的SLA利用于保留策略。当音讯在代理中超过肯定工夫后,将会被主动删除。这种翻新设计有很大的益处,消费者能够成心倒回到老的偏移量再次生产数据。这违反了队列的常见约定,但被证实是许多消费者的基本特征。以下摘抄自kafka官网文档: Kafka Design 指标 1) 高吞吐量来反对高容量的事件流解决 2) 反对从离线零碎加载数据 3) 低提早的音讯零碎 长久化 1) 依赖文件系统,长久化到本地 2) 数据长久化到log 效率 1) 解决”small IO problem“: 应用”message set“组合音讯。 server应用”chunks of messages“写到log。 consumer一次获取大的音讯块。 2)解决”byte copying“: 在producer、broker和consumer之间应用对立的binary message format。 应用零碎的pagecache。 应用sendfile传输log,防止拷贝。 端到端的批量压缩(End-to-end Batch Compression) Kafka反对GZIP和Snappy压缩协定。 The Producer 负载平衡 1)producer能够自定义发送到哪个partition的路由规定。默认路由规定:hash(key)%numPartitions,如果key为null则随机抉择一个partition。 2)自定义路由:如果key是一个user id,能够把同一个user的音讯发送到同一个partition,这时consumer就能够从同一个partition读取同一个user的音讯。 异步批量发送 批量发送:配置不多于固定音讯数目一起发送并且等待时间小于一个固定提早的数据。 ...

July 13, 2020 · 1 min · jiezi

kafka-系列-1基本概念

前言思考的过程往往比间接失去论断更加重要 kafka 利用场景利用监控网站用户行为追踪流数据持久性日志基本概念在说基本概念前,先看一下 kafka 的零碎架构 Broker 一般而言,一台机器就是一个 broker,当然 1 台机器上能够部署多个 brokerProducer 音讯的生产者Consumer 音讯的消费者Consumer Group 消费者组,组内能够有多个消费者,共享同一个 groupid。生产组内的消费者,个别状况下为同一个消费者部署多个实例。Topic topic 在 kafka 中是一个逻辑上的概念,用于将 partition 分类。1 个 topic 有多个 partition。生产者将音讯发送到指定的 topic 中,消费者从指定的 topic 进行生产。partition 一个可追加的日志存储文件。kafka 的分区能够散布在不同的 broker 上多正本机制 kafka 为 partition 引入了多正本机制,能够通过减少正本数量来晋升容灾能力。同一分区中的不同正本保留雷同的音讯。正本之间个别是 一主多从。如下图,每个分区有 3 个正本 AR 集群中的正本,统称 AR(Assigned Replication),AR = ISR + OSRISR 与 leader 正本放弃肯定同步的正本,称为 ISR(in-sync-replication)。音讯需先发送到 leader 正本,follower 能力从 leader 正本中拉取音讯OSR 与 leader 正本同步滞后过多的正本,称为 OSR(out-sync-replication)。leader 正本 leader 正本负责保护 follower 正本的状态,当 ISR 正本中滞后 leader 正本过多,会被移除到 OSR 正本中。当 OSR 正本跟上了 leader 正本,会被挪动到 ISR 正本。只有在 ISR 汇合的正本,才有机会选举 leaderHW、LEO HW(Hight Watermark)俗称高水位,标识一个特定的偏移量,消费者只能拉取该偏移量之前的音讯 LEO(Low End Offset)日志最初偏移量。标识日志文件中待写入音讯的偏移量关系图图 2: HW、LEO 的关系图 3 - 图4: leader 正本、follower、HW、LEO 关系 音讯写入之后,LEO 变成5,follower,会从 leader 中拉取音讯,进行同步 当 ISR 汇合的正本都写入 3 后,HW 就会变成 4,示意 0-3 的音讯为可生产 当 ISR 汇合都写入 3 、4 之后,HW、LEO 值都变成 5总结 kafka 音讯的写入,是可靠性和可用性的衡量。当 ISR 正本均写入音讯时,不必期待 OSR 正本也写入音讯。这里防止了 OSR 滞后太多,从而导致不可用性,且音讯被写入到多个正本,也保障了音讯的可靠性。 当 leader 写入完音讯立刻挂掉后,ISR 正本因未能同步到音讯,从而导致音讯失落。与 RocketMQ 区别partition 在 RocketMQ 中为 队列。生产模型也简直统一,基于生产组进行生产RocketMQ 有自带的注册核心,无需 zookeeper。kafka partition 有多正本机制,RocketMQ 队列没有多正本机制kafka 多正本机制有丢音讯问题,RocketMQ 则没有从设计上来看,RocketMQ 与 kafka 的解决的利用场景不一样。RocketMQ 重视音讯的可靠性,而 kafka 在这一方面比拟弱,kafka 更重视零碎吞吐量。因而 kafka 不适宜要求音讯不能丢的场景。

July 12, 2020 · 1 min · jiezi

kafka真实环境部署规划转载

kafka真实环境部署规划1. 操作系统选型因为kafka服务端代码是Scala语言开发的,因此属于JVM系的大数据框架,目前部署最多的3类操作系统主要由Linux ,OS X 和Windows,但是部署在Linux数量最多,为什么呢?因为I/O模型的使用和数据网络传输效率两点。 第一:Kafka新版本的Clients在设计底层网络库时采用了Java的Select模型,而在Linux实现机制是epoll,感兴趣的读者可以查询一下epoll和select的区别,明确一点就是:kafka跑在Linux上效率更高,因为epoll取消了轮询机制,换成了回调机制,当底层连接socket数较多时,可以避免CPU的时间浪费。第二:网络传输效率上。kafka需要通过网络和磁盘进行数据传输,而大部分操作系统都是通过Java的FileChannel.transferTo方法实现,而Linux操作系统则会调用sendFile系统调用,也即零拷贝(Zero Copy 技术),避免了数据在内核地址空间和用户程序空间进行重复拷贝。2. 磁盘类型规划机械磁盘(HDD) 一般机械磁盘寻道时间是毫秒级的,若有大量随机I/O,则将会出现指数级的延迟,但是kafka是顺序读写的,因此对于机械磁盘的性能也是不弱的,所以,基于成本问题可以考虑。固态硬盘(SSD) 读写速度可观,没有成本问题可以考虑。JBOD (Just Bunch Of Disks ) 经济实惠的方案,对数据安全级别不是非常非常高的情况下可以采用,建议用户在Broker服务器上设置多个日志路径,每个路径挂载在不同磁盘上,可以极大提升并发的日志写入速度。RAID 磁盘阵列 常见的RAID是RAID10,或者称为(RAID 1+0) 这种磁盘阵列结合了磁盘镜像和磁盘带化技术来保护数据,因为使用了磁盘镜像技术,使用率只有50%,注意,LinkedIn公司采用的就是RAID作为存储来提供服务的。那么弊端在什么地方呢?如果Kafka副本数量设置为3,那么实际上数据将存在6倍的冗余数据,利用率实在太低。因此,LinkedIn正在计划更改方案为JBOD.3. 磁盘容量规划我们公司物联网平台每天大约能够产生一亿条消息,假设副本replica设置为2 (其实我们设置为3),数据留存时间为1周,平均每条上报事件消息为1K左右,那么每天产生的消息总量为:1亿 乘 2 乘 1K 除以 1000 除以 1000 =200G磁盘。预留10%的磁盘空间,为210G。一周大约为1.5T。采用压缩,平均压缩比为0.5,整体磁盘容量为0.75T。 关联因素主要有: 新增消息数副本数是否启用压缩消息大小消息保留时间4. 内存容量规划kafka对于内存的使用,并不过多依赖JVM 内存,而是更多的依赖操作系统的页缓存,consumer若命中页缓存,则不用消耗物理I/O操作。一般情况下,java堆内存的使用属于朝生夕灭的,很快会被GC,一般情况下,不会超过6G,对于16G内存的机器,文件系统page cache 可以达到10-14GB。 怎么设计page cache,可以设置为单个日志段文件大小,若日志段为10G,那么页缓存应该至少设计为10G以上。堆内存最好不要超过6G。5. CPU选择规划kafka不属于计算密集型系统,因此CPU核数够多就可以,而不必追求时钟频率,因此核数选择最好大于8。 6. 网络带宽决定Broker数量带宽主要有1Gb/s 和10 Gb/s 。我们可以称为千兆位网络和万兆位网络。举例如下: 我们的物联网系统一天每小时都要处理1Tb的数据,我们选择1Gb/b带宽,那么需要选择多少机器呢? 假设网络带宽kafka专用,且分配给kafka服务器70%带宽,那么单台Borker带宽就是710Mb/s,但是万一出现突发流量问题,很容易把网卡打满,因此在降低1/3,也即240Mb/s。因为1小时处理1TTB数据,每秒需要处理292MB,1MB=8Mb,也就是2336Mb数据,那么一小时处理1TB数据至少需要2336/240=10台Broker数据。冗余设计,最终可以定为20台机器。作者:凯新的技术社区 链接:https://juejin.im/post/5bd464... 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 1. kafka生产者吞吐量测试指标kafka-producer-perf-test :是kafka提供的测试Producer性能脚本,通过脚本,可以计算出Producer在一段时间内的平均延时和吞吐量。 1.1 kafka-producer-perf-test在kafka安装目录下面执行如下命令,生产环境中尽量让脚本运行较长的时间,才会有意义: bin/kafka-producer-perf-test.sh --topic test --num-records 500000 --record-size 200 --througthput -1 --producer-props bootstrap.servers=bd-master:9092,bd-slave1=9092,bd-slave3=9092 acks=1 ...

July 3, 2020 · 2 min · jiezi

监测kafka-lag值的shell脚本

自己手写了一个监测kafka lag值的shell脚本。之前是用python写的,感觉比较麻烦,这里写了一个shell版的,大家可以直接拿来使用。cd /usr/share/kafka/kafka_2.11-2.4.1/ || exit 1lag=$(./bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group my_group 2>/dev/null|grep -v GROUP|awk 'NR>1{num+=$6}END{print num}')echo "$lag"if [ "$lag" -gt 10 ];then echo "lag值过大" #或者mail或者send_ding_msg,自行设置fi下面是执行情况。 最后可以将这个脚本添加到crontab定时任务,我目前是每10分钟执行一次,还没有遇到消息堆积的情况。

June 28, 2020 · 1 min · jiezi

Kafka控制器选举原理

1. Kafka控制器介绍 在Kafka集群中会有一个或多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。当某个分区的leader副本(一个分区会有多个副本,其中只有leader副本对外提供读写服务)出现故障时,由控制器负责为该分区选举新的leader副本。当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。当为某个Topic增加分区数量时,由控制器负责分区的重新分配。 分区集合介绍:分区中的所有副本统称为AR(Assigned Replicas)。所有与leader副本保持一定程度同步的副本(包括leader副本在内)组成ISR(In-Sync Replicas)。与leader副本同步滞后过多的副本(不包括leader副本)组成OSR(Out-of-Sync Replicas)。 leader副本负责维护和跟踪ISR集合中所有follower副本的滞后状态,当follower副本落后太多或失效时,leader副本会把它从ISR集合中剔除。当OSR集合中有follower副本“追上”了leader副本,那么leader副本会把它从OSR集合转移至ISR集合。默认情况下,当leader副本发生故障时,只有在ISR集合中的副本才有资格被选举为新的leader。 2. Kafka控制器选举原理 Kafka中的控制器选举工作依赖于Zookeeper,成功竞选成为控制器的broker会在Zookeeper中创建/controller临时(Ephemeral)节点,此临时节点的内容参考如下: {"version":1,"brokerid":0,"timestamp":"1593330804078"} 其中version与Kafka版本相关,对同一个Kafka版本来说为固定值。brokerid表示成为控制器的broker的id编号,timestamp表示竞选成为控制器时的时间戳(精确到毫秒)。 在任意时刻,集群中有且只有一个控制器。每个broker启动的时候会去尝试读取/controller节点的brokerid的值,如果读取到的brokerid的值不为-1,表示已经有其他broker节点成功竞选为控制器,所以当前broker就会放弃竞选;如果Zookeeper中不存在/controller节点,或者这个节点的数据异常,那么就会尝试去创建/controller节点。当前broker去创建节点的时候,也有可能有其他broker同时去尝试创建这个节点,只有创建成功的那个broker才会成为控制器。每个broker都会在内存中保存当前控制器的brokerid值,这个值可以标识为activeControllerId。 Zookeeper中还有一个与控制器有关的/controller_epoch节点,这个节点是持久(Persistent)节点,节点中存放的是一个整型的controller_epoch值。controller_epoch值用于记录控制器发生变更的次数,即记录当前的控制器是第几代控制器,我们也可以称之为“控制器纪元”。 controller_epoch的初始值为1,即集群中的第一个控制器的纪元为1,当控制器发生变更时,每选出一个新的控制器就将该字段值加1。每个和控制器交互的请求都会携带controller_epoch这个字段,如果请求的controller_epoch值小于内存中的controller_epoch值,则认为这个请求是向已经过期的控制器发送的请求,那么这个请求会被认定为无效的请求。如果请求的controller_epoch值大于内存中的controller_epoch值,那么说明已经有新的控制器当选了(也就是说接收到这种请求的broker已经不再是控制器了)。由此可见,Kafka通过controller_epoch来保证控制器的唯一性,进而保证相关操作的一致性。 具备控制器身份的broker需要比其他普通的broker多一份职责,具体细节如下: 监听分区的变化。监听主题的变化。监听broker相关的变化。从Zookeeper中读取获取当前所有与主题、分区及broker有关的信息并进行相应的管理。启动并管理分区状态机和副本状态机。更新集群的元数据信息。 当/controller节点的数据发生变化时,每个broker都会更新自身内存中保存的activeControllerId。如果broker在数据变更前是控制器,在数据变更后自身的brokerid值与新的activeControllerId值不一致,那么就需要“退位”,关闭相应的资源,比如关闭状态机、注销相应的监听器等。有可能控制器由于异常而下线,造成/controller这个临时节点被自动删除;也有可能是其他原因将此节点删除了。 当/controller节点被删除时,每个broker都会进行选举,如果broker在节点被删除前是控制器,那么在选举前还需要有一个“退位”的动作。如果有特殊需要,则可以手动删除/controller节点来触发新一轮的选举。当然关闭控制器所对应的broker,以及手动向/controller节点写入新的brokerid的所对应的数据,同样可以触发新一轮的选举。 3. 总结 Kafka控制器选择的流程并不复杂,但是考虑的各种边界条件还是比较周到的,程序的健壮性比较好。有时候我们需要根据业务场景去设计或者调整某种分布式集群,Kafka控制器的选举也可以是一个很好的借鉴,尤其是/controller_epoch的设计,考虑到了控制器可能会因为某种原因过时,保证了控制器的唯一性。 4. 参考文献《深入理解Kafka:核心设计与实践原理》

June 28, 2020 · 1 min · jiezi

快速搭建-Kafka-集群

版本JDK 14ZookeeperKafka安装 Zookeeper 和 KafkaKafka 依赖 Zookeeper,所以我们需要在安装 Kafka 之前先拥有 Zookeeper。准备如下的 docker-compose.yaml 文件,将文件中的主机地址 192.168.1.100 替换成你自己的环境中的主机地址即可。 version: "3"services: zookeeper: image: zookeeper build: context: ./ container_name: zookeeper ports: - 2181:2181 volumes: - ./data/zookeeper/data:/data - ./data/zookeeper/datalog:/datalog - ./data/zookeeper/logs:/logs restart: always kafka_node_0: depends_on: - zookeeper build: context: ./ container_name: kafka-node-0 image: wurstmeister/kafka environment: KAFKA_BROKER_ID: 0 KAFKA_ZOOKEEPER_CONNECT: 192.168.1.100:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.1.100:9092 KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092 KAFKA_NUM_PARTITIONS: 3 KAFKA_DEFAULT_REPLICATION_FACTOR: 2 ports: - 9092:9092 volumes: - ./data/kafka/node_0:/kafka restart: unless-stopped kafka_node_1: depends_on: - kafka_node_0 build: context: ./ container_name: kafka-node-1 image: wurstmeister/kafka environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: 192.168.1.100:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.1.100:9093 KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9093 KAFKA_NUM_PARTITIONS: 3 KAFKA_DEFAULT_REPLICATION_FACTOR: 2 ports: - 9093:9093 volumes: - ./data/kafka/node_1:/kafka restart: unless-stopped kafka_node_2: depends_on: - kafka_node_1 build: context: ./ container_name: kafka-node-2 image: wurstmeister/kafka environment: KAFKA_BROKER_ID: 2 KAFKA_ZOOKEEPER_CONNECT: 192.168.1.100:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.1.100:9094 KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9094 KAFKA_NUM_PARTITIONS: 3 KAFKA_DEFAULT_REPLICATION_FACTOR: 2 ports: - 9094:9094 volumes: - ./data/kafka/node_2:/kafka restart: unless-stopped输入 docker-compose up -d 运行脚本文件进行集群构建。等待一会儿,得到如下结果即为成功。 ...

June 21, 2020 · 2 min · jiezi

不要被kafka的异步模式欺骗了

啥是异步模式kafka的生产者可以选择使用异步方式发送数据,所谓异步方式,就是我们调用 send() 方法,并指定一个回调函数, 服务器在返回响应时调用该函数。 kafka在客户端里暴露了两个send方法,我们可以自己选择同步或者异步模式。我们来看一个kafka的生产者发送示例,有个直观的感受。这个示例是一个同步的模式。 ProducerRecord<String, String> record = new ProducerRecord<>(“Kafka”, “Kafka_Products”, “测试”);//Topic Key Valuetry{Future future = producer.send(record);future.get();//获取执行结果} catch(Exception e) {e.printStackTrace();}我们从源码层面来继续看下。 首先kafka定义了一个接口, 然后KafkaProducer实现了这两个方法,我们看下异步方法的实现逻辑。 可以看到最终是调用doSend方法,调用的时候传入一个回调。这个回调就是监听方法的执行结果的。 异步模式也会阻塞的很多人会认为,既然是异步模式,不管结果是成功还是失败,肯定方法调用会马上返回的。那我只能告诉你,不好意思,不一定是这样。我自己就曾经踩过这个坑。 我们当时有个业务流程需要在执行完成后发送kakfa消息给某个业务方,为了尽量减少影响我这个主流程的执行时间,采用了异步方式发送kafka消息。在使用中,因为配错了kafka的TOPIC信息,发现流程阻塞发送消息这里长达6秒(kafka默认的发送超时时间)。 究竟为啥异步方式还会阻塞呢?我们继续看源码。 不管是同步模式还是异步模式,最终都会调用到doSend方法,注意看上图中的waitOnMetadata方法,我上面说的阻塞的情况就是阻塞在这个方法里。那我们继续看这个方法。 通过代码中的注释我们大概能了解这个方法的功能,不过我这里还是要解释下。(防止有人看不懂英文,哈哈) waitOnMetadata获取当前的集群元数据信息,如果缓存有,并且分区没有超过指定分区范围则缓存返回,否则触发更新,等待新的metadata。这个等待的操作在下面这行代码: metadata.awaitUpdate(version, remainingWaitMs);然后就继续跟喽, 这个方法很好理解,就是一直在等一个条件,这个条件达到了就返回,否则一直等待超时退出。而这个条件就是当前的版本号要大于上个版本号。 那么谁来更新版本号呢?就是我们前面提到的sender线程。当我们的topic配置错误的时候导致metadata一直无法更新,然后一直等到超时。 破案了! 总结kafka的异步模式可以让我们在业务场景中发送消息时即刻返回,不必等待发送的结果。但是当metadata取不到时,发送的过程还是需要等待一直超时的。 程序员是一个尤其需要不断学习的工种,平时养成阅读源码的习惯,不光能避免踩一些坑,还能在遇到问题是快递定位到问题的根源。

June 13, 2020 · 1 min · jiezi

数据源管理-Kafka集群环境搭建消息存储机制详解

本文源码:GitHub·点这里 || GitEE·点这里 一、Kafka集群环境1、环境版本版本:kafka2.11,zookeeper3.4注意:这里zookeeper3.4也是基于集群模式部署。 2、解压重命名tar -zxvf kafka_2.11-0.11.0.0.tgzmv kafka_2.11-0.11.0.0 kafka2.11创建日志目录 [root@en-master kafka2.11]# mkdir logs注意:以上操作需要同步到集群下其他服务上。 3、添加环境变量vim /etc/profileexport KAFKA_HOME=/opt/kafka2.11export PATH=$PATH:$KAFKA_HOME/binsource /etc/profile4、修改核心配置[root@en-master /opt/kafka2.11/config]# vim server.properties-- 核心修改如下# 唯一编号broker.id=0# 开启topic删除delete.topic.enable=true# 日志地址log.dirs=/opt/kafka2.11/logs# zk集群zookeeper.connect=zk01:2181,zk02:2181,zk03:2181注意:broker.id安装集群服务个数编排即可,集群下不能重复。 5、启动kafka集群# 启动命令[root@node02 kafka2.11]# bin/kafka-server-start.sh -daemon config/server.properties# 停止命令[root@node02 kafka2.11]# bin/kafka-server-stop.sh# 进程查看[root@node02 kafka2.11]# jps注意:这里默认启动了zookeeper集群服务,并且集群下的kafka分别启动。 6、基础管理命令创建topic bin/kafka-topics.sh --zookeeper zk01:2181 \--create --replication-factor 3 --partitions 1 --topic one-topic参数说明: replication-factor 定义副本个数partitions 定义分区个数topic:定义topic名称查看topic列表 bin/kafka-topics.sh --zookeeper zk01:2181 --list修改topic分区 bin/kafka-topics.sh --zookeeper zk01:2181 --alter --topic one-topic --partitions 5查看topic bin/kafka-topics.sh --zookeeper zk01:2181 \--describe --topic one-topic发送消息 ...

June 11, 2020 · 2 min · jiezi

译为什么-Kafka-这么快

博客原文https://taohuawu.club/why-kaf... 为什么 Kafka 如此地快探究是哪些精妙的设计决策使得 Kafka 成为了现如今的性能强者。软件体系结构在过去的几年间发生了巨大的变化。单体应用程序或甚至几个粗粒度的服务共享一个公共数据存储的理念,在全世界的软件从业者的头脑中早已不复存在了。自主微服务、事件驱动架构和职责分离 (CQRS) 模式是构建以业务为中心的现代应用程序的主要工具。除此之外,设备连接物联网、移动和可穿戴设备的普及,正在对系统在接近实时的情况下必须处理的事件数量造成越来越大的压力。 我们首先要接受一个共识:术语『快』是一个多义的、复杂的甚至是模糊不清的词。延迟、吞吐量和抖动,这些指标会影响人们对这个术语的理解。它还具有内在的上下文关系:行业和应用领域本身就设置了关于性能的规范和期望。某个东西是否『快』很大程度上取决于一个人的参照系。 Apache Kafka 以延迟和抖动为代价对吞吐量进行了优化,同时保留了其他必须的功能特性,比如持久化、严格的日志记录顺序和至少交付一次的语义。当有人说 "Kafka 很快",并且假定他们至少是有资格说这话的,那么我们可以认为他们指的是 Kafka 在短时间内安全地积累和分发大量日志记录的能力。 从历史上看,Kafka 诞生于 LinkedIn 的业务需求:高效地移动大量的消息,每小时的数据量达数 TB 。因为时间的可变性,单个消息的传播延迟被认为是次要的。毕竟,LinkedIn 不是从事高频交易的金融机构,也不是需要在确定的时限内完成指定操作的工业控制系统。Kafka 可用于实现近实时(或称为软实时)的系统。 注意:对于不熟悉这个术语的人,这里必须说明一下,实时并不等同于快速,它仅仅意味着 "可预测"。具体点说,实时意味着完成一个指定操作所需的硬性时间上限,或称为截止时间。如果系统作为一个整体不能每次都满足这个时限(内完成操作),它就不能被归类为实时。能够在具有小概率超时容错性的时限范围内完成操作的系统被称为近实时系统。就吞吐量而言,实时系统通常比近实时或非实时的系统要慢。Kafka 的高性能主要得益于两个要素,这两个要素需要分开来讨论。第一个与客户端 (Client) 和 代理 (Broker) 实现上的底层效率有关。第二个则来自于流数据处理的机会性并行。 Broker 性能日志结构的持久性Kafka 利用了一种分段式的、只追加 (Append-Only) 的日志,基本上把自身的读写操作限制为顺序 I/O,也就使得它在各种存储介质上能有很快的速度。一直以来,有一种广泛的误解认为磁盘很慢。实际上,存储介质 (特别是旋转式的机械硬盘) 的性能很大程度依赖于访问模式。在一个 7200 转/分钟的 SATA 机械硬盘上,随机 I/O 的性能比顺序 I/O 低了大概 3 到 4 个数量级。此外,一般来说现代的操作系统都会提供预读和延迟写技术:以大数据块的倍数预先载入数据,以及合并多个小的逻辑写操作成一个大的物理写操作。正因为如此,顺序 I/O 和随机 I/O 之间的性能差距在 flash 和其他固态非易失性存储介质中仍然很明显,尽管它远没有旋转式的存储介质那么明显。 日志记录批处理顺序 I/O 在大多数的存储介质上都非常快,几乎可以和网络 I/O 的峰值性能相媲美。在实践中,这意味着一个设计良好的日志结构的持久层将可以紧随网络流量的速度。事实上,Kafka 的瓶颈通常是网络而非磁盘。因此,除了由操作系统提供的底层批处理能力之外,Kafka 的 Clients 和 Brokers 会把多条读写的日志记录合并成一个批次,然后才通过网络发送出去。日志记录的批处理通过使用更大的包以及提高带宽效率来摊薄网络往返的开销。 ...

June 9, 2020 · 3 min · jiezi

聊一聊高并发高可用那些事-Kafka篇

目录 为什么需要消息队列1.异步 :一个下单流程,你需要扣积分,扣优惠卷,发短信等,有些耗时又不需要立即处理的事,可以丢到队列里异步处理。 2.削峰 :按平常的流量,服务器刚好可以正常负载。偶尔推出一个优惠活动时,请求量极速上升。由于服务器 Redis,MySQL 承受能力不一样,如果请求全部接收,服务器负载不了会导致宕机。加机器嘛,需要去调整配置,活动结束后用不到了,即麻烦又浪费。这时可以将请求放到队列里,按照服务器的能力去消费。 3.解耦 :一个订单流程,需要扣积分,优惠券,发短信等调用多个接口,出现问题时不好排查。像发短信有很多地方需要用到, 如果哪天修改了短信接口参数,用到的地方都得修改。这时可以将要发送的内容放到队列里,起一个服务去消费, 统一发送短信。 高吞吐、高可用 MQ 对比分析看了几个招聘网站,提到较多的消息队列有:RabbitMQ、RocketMQ、Kafka 以及 Redis 的消息队列和发布订阅模式。 Redis 队列是用 List 数据结构模拟的,指定一端 Push,另一端 Pop,一条消息只能被一个程序所消费。如果要一对多消费的,可以用 Redis 的发布订阅模式。Redis 发布订阅是实时消费的,服务端不会保存生产的消息,也不会记录客户端消费到哪一条。在消费的时候如果客户端宕机了,消息就会丢失。这时就需要用到高级的消息队列,如 RocketMQ、Kafka 等。 ZeroMQ 只有点对点模式和 Redis 发布订阅模式差不多,如果不是对性能要求极高,我会用其它队列代替,毕竟关解决开发环境所需的依赖库就够折腾的。 RabbitMQ 多语言支持比较完善,特性的支持也比较齐全,但是吞吐量相对小些,而且基于 Erlang 语言开发,不利于二次开发和维护。 RocketMQ 和 Kafka 性能差不多,基于 Topic 的订阅模式。RocketMQ 支持分布式事务,但在集群下主从不能自动切换,导致了一些小问题。RocketMQ 使用的集群是 Master-Slave ,在 Master 没有宕机时,Slave 作为灾备,空闲着机器。而 Kafka 采用的是 Leader-Slave 无状态集群,每台服务器既是 Master 也是 Slave。 Kafka 相关概念在高可用环境中,Kafka 需要部署多台,避免 Kafka 宕机后,服务无法访问。Kafka集群中每一台 Kafka 机器就是一个 Broker。Kafka 主题名称和 Leader 的选举等操作需要依赖 ZooKeeper。 ...

June 7, 2020 · 2 min · jiezi

为什么使用消息队列消息队列有什么优点和缺点

面试题为什么使用消息队列?消息队列有什么优点和缺点?Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么区别,以及适合哪些场景?面试官心理分析其实面试官主要是想看看: 第一,你知不知道你们系统里为什么要用消息队列这个东西?不少候选人,说自己项目里用了 Redis、MQ,但是其实他并不知道自己为什么要用这个东西。其实说白了,就是为了用而用,或者是别人设计的架构,他从头到尾都没思考过。没有对自己的架构问过为什么的人,一定是平时没有思考的人,面试官对这类候选人印象通常很不好。因为面试官担心你进了团队之后只会木头木脑的干呆活儿,不会自己思考。 第二,你既然用了消息队列这个东西,你知不知道用了有什么好处&坏处?你要是没考虑过这个,那你盲目弄个 MQ 进系统里,后面出了问题你是不是就自己溜了给公司留坑?你要是没考虑过引入一个技术可能存在的弊端和风险,面试官把这类候选人招进来了,基本可能就是挖坑型选手。就怕你干 1 年挖一堆坑,自己跳槽了,给公司留下无穷后患。 第三,既然你用了 MQ,可能是某一种 MQ,那么你当时做没做过调研?你别傻乎乎的自己拍脑袋看个人喜好就瞎用了一个 MQ,比如 Kafka,甚至都从没调研过业界流行的 MQ 到底有哪几种。每一个 MQ 的优点和缺点是什么。每一个 MQ 没有绝对的好坏,但是就是看用在哪个场景可以扬长避短,利用其优势,规避其劣势。如果是一个不考虑技术选型的候选人招进了团队,leader 交给他一个任务,去设计个什么系统,他在里面用一些技术,可能都没考虑过选型,最后选的技术可能并不一定合适,一样是留坑。 面试题剖析为什么使用消息队列其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么? 面试官问你这个问题,期望的一个回答是说,你们公司有个什么业务场景,这个业务场景有个什么技术挑战,如果不用 MQ 可能会很麻烦,但是你现在用了 MQ 之后带给了你很多的好处。 先说一下消息队列常见的使用场景吧,其实场景有很多,但是比较核心的有 3 个:解耦、异步、削峰。 解耦看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃...... 在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊! 如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。 总结:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。 面试技巧:你需要去考虑一下你负责的系统中是否有类似的场景,就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦,也是可以的,你就需要去考虑在你的项目里,是不是可以运用这个 MQ 去进行系统的解耦。在简历中体现出来这块东西,用 MQ 作解耦。 ...

June 4, 2020 · 2 min · jiezi

Kafka-Streams未来可期

核心知识预热TIPS1.资料来源说明书以及内部构造2.学习技术就是不断解惑的过程,就kafka stream自问:是个什么技术,能干什么,怎么使用..Kafka Streams是一个数据输入和数据输出都保存在kafka集群的程序和微服务构建的客户端类库,那么就不需要专门去搭建计算集群,方便快捷;Kafka Streams提供两种方法来定义流处理拓扑。Kafka Streams DSL提供了最通用的可直接使用的数据转换操作(比如map);低阶的处理器API则允许开发者定义和连接到自定义的处理器或者和state store进行交互。也就是说前者是高阶API,封装好了的,通用场景使用且能快速开发;后者是低阶API,更接近底层,开发难度大但是能更好地适配程序和业务。Kafka Streams同样支持状态统计、窗口函数、eventTime和exactly-once语义等实时场景;前置概念conceptdescstream processing application多个处理器形成的拓扑结构,包含有一定处理逻辑的应用程序processor topology流处理器拓扑,是processor+...+processor的形式,source和sink是特殊的processorSource Processor源头处理器,即上游没有其他的流处理器,从kafka的topic中消费数据产生数据流输送到下游Sink Processor结果处理器,即下游没有其他的流处理器,将上游的数据输送到指定的kafka topicTime联想flink的时间语义,例如某某time1手机端购买某商品,产生了日志数据,然后time2这个日志数据被实时采集到Kafka持久化到topic,然后进入流式处理框架,在time3正式被计算,那么time123分别称为:event time,ingestion time,processing timestates保存和查询数据状态的功能,可以定义流处理应用外的程序进行只读访问processing guarantees消费是否丢失和是否重复的级别,比如exactly-once,at-least-once,at-most-once拓扑kafka stream的拓扑其实就是一个个processor连接起来的流程图,其中source和sink是比较特殊的processor,分别没有上游和下游处理器。拓扑创建方式是在创建下游processor的时候指定上游的processor名称进行连接 // DSL转换算子生成新KStream是调用void addGraphNode(final StreamsGraphNode parent,final StreamsGraphNode child) {}// 直接通过builder添加processorpublic synchronized Topology addProcessor(final String name,final ProcessorSupplier supplier,final String... parentNames) {} 使用使用上核心都是四个步骤: 创建流处理应用配置参数;构造流处理拓扑结构;创建流处理客户端实例;开始执行流处理程序;使用DSL编写单词统计测试代码 /* 1.props */ Properties props = new Properties(); props.put(StreamsConfig.APPLICATION_ID_CONFIG, "streams-wordcount");//可作为consumer的group id props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");//kafka的地址,多个逗号分隔 props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());// 序列化和反序列化,在读取和写出流的时候、在读取和写出state的时候都会用到 props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass()); /* 2.topology */ final StreamsBuilder builder = new StreamsBuilder(); KStream<String, String> source = builder.stream("streams-plaintext-input");//source processor,传入参数可定义key,value的序列化方式,以及时间提取器等 source.flatMapValues(value -> Arrays.asList(value.toLowerCase(Locale.getDefault()).split(" ")))//KString<String,String> .groupBy((key, value) -> value)// KGroupedStream<String,String> .count(Materialized.<String, Long, KeyValueStore<Bytes, byte[]>>as("counts-store"))//KTable<String,String> .toStream()//KStream<String,Long> .to("streams-wordcount-output", Produced.with(Serdes.String(), Serdes.Long()));//sink processor,指定输出key,value的数据类型 final Topology topology = builder.build(); /* 3.KafkaStreams实例 */ final KafkaStreams streams = new KafkaStreams(topology, props); // CountDownLatch用await()阻塞当前线程,countDown()记录完成线程的数量 // 当getCount()=0的时候继续执行await后续的代码 final CountDownLatch latch = new CountDownLatch(1); System.out.println(topology.describe());// 打印流处理拓扑 // 钩子函数 Runtime.getRuntime().addShutdownHook(new Thread("streams-shutdown-hook") { @Override public void run() { streams.close(); latch.countDown(); } }); try { // 4.执行 streams.start(); latch.await(); } catch (Throwable e) { System.exit(1); } System.exit(0);测试数据# 生产者打印生产数据langjiang@langs-MacBook-Pro kafka_2.11-2.1.0 % bin/kafka-console-producer.sh --broker-list localhost:9092 --topic streams-plaintext-input>hello hello hello hello>kafka kafka kafka kafka# 消费者打印消费数据langjiang@langs-MacBook-Pro kafka_2.11-2.1.0 % bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \--topic streams-wordcount-output \--from-beginning \--formatter kafka.tools.DefaultMessageFormatter \--property print.key=true \--property print.value=true \--property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer \--property value.deserializer=org.apache.kafka.common.serialization.LongDeserializerhello 4kafka 4打印拓扑这里可以看到有点类似于宽依赖的时候,拓扑会划分,中间会生成streams-wordcount-counts-store-repartition主题保存中间结果。 ...

June 3, 2020 · 3 min · jiezi

Kafka内核高水位与Leader-Epoch

前言你可能听说过高水位,但不一定听说过Leader Epoch。前者是Kafka中非常重要的概念。而后者是0.11版本中新推出的。主要是为了弥补前者水位机制的一些缺陷。 1.高水位1.1 什么是高水位Kafka的水位不是时间戳,更与时间无关。它是和位置信息绑定的,具体来说,它是用消息位移来表征的。1.2 高水位的作用定义消息可见性,用来标识分区下的哪些消息是可以被消费者消费的帮助kafka完成副本同步1.3 已提交消息和未提交消息 在分区高水位以下的消息就被认为是已提交消息,反之就是未提交消息消费者只能消费已提交消息,即位移值小于8的消息。这里不存在kafka的事务,因为事务机制会影响消息者所能看到的消息的范围,他不只是简单依赖高水位来判断,是依赖于一个名为LSO的位移值来判断事务性消费者的可见性位移值等于高水位的消息也属于为未提交消息。即高水位的消息也是不能被消费者消费的LEO表示副本写入下一条消息的位移值。同一个副本对象,起高水位值不会超过LEO1.4 高水位更新机制Kafka中所有副本对象都保存一组高水位值和LEO值,但Leader副本中还保留着其他Follower副本的LEO值。 Kafka副本机制在运行过程中,会更新Broker1上Follower副本的高水位和LEO值,同时也会更新Broker0上Leader副本的高水位和LEO以及Follow副本的LEO,但不会更新其HW。Leader副本处理生产者请求得逻辑如下: 1.写入消息到本次磁盘2.更新分区高水位 获取Leader副本所在Broker端保存得所有远程副本LEO值(LEO-1,LEO-2,......,LEO-n)获取Leader副本高水位值:currentHW更新currentHW=max{currentHW,min(LEO-1,LEO-2,.....,LEO-n)}处理Follower副本拉取消息的逻辑如下:1.读取磁盘(或页缓存)中的数据。2.使用Follower副本发送请求中位移值更新远程副本LEO的值。3.更新分区高水位值(具体步骤与处理生产者请求的步骤相同) Follower副本从Leader拉取消息的处理逻辑如下: 1.写入消息到本地磁盘2.更新LEO值3.更新高水位 获取Leader发送的高水位值:currentHW获取步骤2中更新的LEO值:currentLEO更新高水位为min(currnetHW,currentLEO)1.5 副本同步机制解析当生产者发送一条消息时,Leader和Follower副本对应的高水位是怎么被更新的呢? 首先是初始状态,remoteLEO指的是远程副本的LEO值。在初始状态时,所有值都是0. 当生产者给主题分区发送一条消息后,状态变更为: 此时,Leader副本成功讲消息写入了本地磁盘,故LEO值被更新为1 Follower再次尝试从Leader拉取消息。有消息拉去后,状态进一步变更: 这时,Follower副本也成功地更新LEO为1.此时,Leader和Follower副本的LEO都是1,但各自的高水位依然是0,还没有被更新。他们需要在下一轮的拉取中被更新 在新一轮的拉去请求中,由于位移值是0的消息已经拉取成功,因此Follower副本这次请求拉去的位移值为1的消息。Leader副本接收此请求后,更新远程副本LEO为1,然后更新Leader高水位为1,然后才会将更新过的高水位值1发送给Follower副本。Follower副本接收到以后,也将自己的高水位值更新为1.至此,一个完整的消息同步周期就结束了。 2. Leader Epoch依托高水位,Kafka既界定了消息的对外可见性,又实现了异步的副本同步机制。 但,在上文分析过程中,Follower副本的高水位更新是需要额外一轮的拉取请求才能实现的。若有多个副本的情况下,则需要多轮的拉取请求。也就是说,Leader副本高水位更新和Follower副本高水位更新在时间上是存在错配的。而这种错配往往是数据丢失,数据不一致问题现象的根源。因此kafka社区在0.11版本中引入了Leader Epoch。 2.1 Leader Epoch的组成Epoch。一个单调递增的版本号。每当副本领导权发生变更时,都会增加该版本号。小版本号的Leader被认为是过期的Leader,不能再行使Leader的权力。起始位移(Start Offset)。Leader副本在该Epoch上写入的首条消息的位移。Leader Epoch<0,0>和<1,100>。第一个Epoch指的是0版本,位移0开始保存消息,一共保存100条消息。之后Leader发生了变更,版本号增加到1,新版本起始位移为100.Kafka Broker会在内存中为每个分区都缓存Leader Epoch数据,同时它还会定期的将这信息持久化一个checkpoint文件中。当Leader副本写入消息到磁盘时,Broker会尝试更新这部分缓存,如果该Leader是首次写入消息,那么Broker会向缓存中增加一个Leader Epoch条目,否则就不做更新。 2.2 Leader Epoch使用Leader Epoch是怎样防止数据丢失的呢? 单纯依赖高水位是怎么造成数据丢失的。开始时,副本A和副本B都处于正常状态,A是Leader副本,B是Follower副本。当生产者使用ack=1(默认)往Leader副本A中发送两条消息。且A全部写入成功,此时Kafka会通知生产者说这两条消息写入成功。现在假设A,B都写入了这两条消息,而且Leader副本的高水位也已经更新了,但Follower副本高水位还未更新。因为Follower端高水位的更新与Leader端有时间错配。假如现在副本B所在Broker宕机了,那么当它重启回来后,副本B就会执行日志截断操作,将LEO值调整为之前的高水位值,也就是1.所以副本B当中位移值为1的消息就丢失了。副本B中只保留了位移值0的消息。 当执行完截断操作之后,副本B开始从A中拉取消息,执行正常的消息同步。假如此时副本A所在的Broker也宕机了。那么kafka只能让副本B成为新的Leader,然后副本A重启回来之后,也需要执行日志截断操作,即调整高水位为与B相同的值,也就是1。这样操作之后,位移值为1的那条消息就永远丢失了。 Leader Epoch机制如何规避这种数据丢失现象呢? 延续上文场景,引用了Leader Epoch机制之后,Follower副本B重启回来后,需要向A发送一个特殊的请求去获取Leader的LEO值,该例子中为2。当知道Leader LEO为2时,B发现该LEO值不必自己的LEO值小,而且缓存中也没有保存任何起始位移值>2的Epoch条目,因此B无需执行日志截断操作。这是对高水位机制的一次明显改进,即不是依赖于高水位判断是否进行日志截断操作。现在,副本A宕机了,B成立新Leader。同样的,在A重启回来后,执行与B逻辑相同的判断,也不需要执行日志截断操作,所以位移值为1的那条消息就全部得以保存。后面当生产者程序向 B 写入新消息时,副本 B 所在的 Broker 缓存中,会生成新的 Leader Epoch 条目:[Epoch=1, Offset=2]。之后,副本 B 会使用这个条目帮助判断后续是否执行日志截断操作。这样,kafka就规避掉了数据丢失的场景。

June 3, 2020 · 1 min · jiezi

一文读懂Kafka副本机制

前言副本机制就是备份机制,指的是在分布式集群机器中保存着相同的数据备份。 那么副本机制的好处的是什么呢? 提供数据冗余(主要作用)提供高伸缩性改善数据局部性总之: 副本机制是kafka确保系统高可用和高持久的重要基石。 1.副本所谓副本,本质上就是一个只能追加写消息的提交日志。这些日志被相同的分散保存在不同的Broker上。在实际生产上,每台Broker都可能保存有各个主题下不同分区的不同副本。因此单个Broker上存有成百上千个副本现象是非常正常的。 1.1 副本角色既然多个Broker中保存分区下的多个副本,那么是如何保证副本当中的数据都是一致的呢? 针对这个问题,kafka的解决方案就是领导者副本机制 领导者的副本机制工作原理 在kafka中,副本分成两类:领导者副本和追随者副本。每个分区在创建时都要选举一个副本,成为领导者副本,其余的副本自动称为追随者副本。kafka中,追随者副本是不会对外提供服务的,所有的请求都必须由领导者副本来处理。它唯一的任务就是从领导者副本异步拉去消息,并写入到自己提交日志中,从而实现与领导者副本的同步。当领导者副本挂掉了,或者说所在Broker宕机了,kafka可以通过Zookeeper提供的监控功能能够实时感知到,并开启新一轮领导者选举,从追随者副本中选一个作为新的领导者。老Leader副本重启回来后,只能作为追随者副本加入到集群中。一定注意上面第二点,追随者副本是不会对外提供服务的。这也是kafka没能提供读操作横向扩展的根本原因,而且它也不像mysql副本一样有”抗读“的作用,帮助领导者减轻压力。那么这种副本机制设计究竟有什么好处呢?1.2 副本机制的好处1.方便实现“Read-your-writes” 顾名思义,就是当你使用生产者api向kafka成功写入消息后,就马上使用消费者api去读取刚才的消息。举个例子,就是你刚发完一条微博,肯定是希望立马能够看到的。这就是Read-your-writes场景了。如果追随者副本对外提供服务的话,由于副本同步是异步的,因此有可能发生追随者副本还没有及时从领导者副本中拉取最新消息,从而使客户端看不到最新的消息。2.方便实现单调读 什么是单调读。单调读就是消费者在多次读消息时候,不会看到一条消息一会儿存在一会儿不存在。例如:如果允许追随者副本提供读服务,那么假设当前有两个追随者副本F1,F2。生产者往领导者中发送了消息后,F1,F2开始异步拉取消息。若F1拉取成功了,而F2还未拉取成功。此时消费者第一次消费F1副本获取最新消息,第二次消费的时候消费到了F2副本。就获取不到该条消息了。这就不是单调读一致性。所以都由Leader副本来处理请求的话,就能实现单调读。1.3 In-sync Replicas(ISR)上文提及到的追随者副本不对外提供服务,只是定期的异步拉取消息。既然是异步的,那么就存在着不可能与Leader实时同步的风险。所以kafka应该告诉我们,追随者副本到底在什么条件之下才算与Leader同步。 基于这个想法,kafka引入了ISR,副本集合。ISR中的副本都是与Leader同步的副本,相反,不在ISR中的追随者副本被认为是与Leader不同步的。那么进入ISR到底需要满足什么条件才能进入呢。 首先需要明确一点。ISR不只是追随者副本集合,它必然包括Leader副本。甚至在某些情况下,ISR只有Leader这一个副本。 图中有3个副本:1个领导者副本,2个追随者副本。领导者副本写入了10条消息,F1同步了6条,F3同步了3条。那么哪个追随者副本与Leader不同步呢?事实上,这两个副本都有可能与Leader副本不同步,但也可能同步。它实际上不是依靠与消息条数来进行判断的。而是根据Broker端参数replica.lag.time.max.ms参数值。这个参数的含义就是Follower副本能够落后Leader副本的最长时间间隔,当前默认值是10秒。这就是说,只要一个 Follower 副本落后 Leader 副本的时间不连续超过 10 秒,那么 Kafka 就认为该 Follower 副本与 Leader 是同步的,即使此时 Follower 副本中保存的消息明显少于 Leader 副本中的消息。若是同步过程的速度持续慢于Leadr副本的写入速度,那么在replica.lag.time.max.ms时间后,kafka就会自动收缩ISR集合,将改副本提出集合。值得注意的是,若改副本后面慢慢追上了Leader的进度。那么它是可以被重新放入ISR集合中的。这也表明ISR是一个动态调整的集合,而非静态不变的。 Unclean 领导者选举既然ISR可以动态调整,那么就会出现ISR为空的情况。ISR为空的情况就代表Leader副本也挂掉了。那么kafka就需要重新选举新的Leader。那么该怎么选举Leader呢? kafka把所有不在ISR的存活副本都成为非同步副本。通常来说,非同步副本落后Leader太多,因此,如果选择这些副本为新的Leader,就可能出现数据的丢失。在kafka,选举Leader这种过程被成为Unclean。由Broker端参数unclean.leader.election.enable控制是否允许Unclean领导者选举。开启 Unclean 领导者选举可能会造成数据丢失,但好处是,它使得分区 Leader 副本一直存在,不至于停止对外提供服务,因此提升了高可用性。反之,禁止 Unclean 领导者选举的好处在于维护了数据的一致性,避免了消息丢失,但牺牲了高可用性。可以根据你的实际业务场景决定是否开启 Unclean 领导者选举。不过并不建议开启它,毕竟我们还可以通过其他的方式来提升高可用性。如果为了这点儿高可用性的改善,牺牲了数据一致性,那就非常不值当了。

June 2, 2020 · 1 min · jiezi

kafka学习笔记

一、什么是Kafka?Apache kafka 是一个开源的分布式消息队列,由Scala写成。是由Apache软件基金会开发的一个开源消息系统项目,该项目的目的是为处理实时数据提供一个高吞吐量、低等待的平台。 二、kafka架构 1)Producer:消息生产者,向kafka broker发消息的客户端 2)Consumer:消息消费者,向kafka broker取消息的客户端 3)Topic:对消息进行归类 4)Consumer Group(GC):这是kafka 用来实现一个topic 消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。一个topic 可以有多个CG。topic 的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个partion 只会把消息发给该CG 中的一个consumer。如果需要实现广播,只要每个consumer 有一个独立的CG 就可以了。要实现单播只要所有的consumer 在同一个CG。用CG 还可以将consumer 进行自由的分组而不需要多次发送消息到不同的topic 5)Broker:一台kafka服务器就是一个Broker,一个集群由多个Broker组成。一个Broker可以容纳多个Topic 6)Partition:为了实现扩展性,一个非常大的topic 可以分布到多个broker(即服务器)上,一个topic 可以分为多个partition,每个partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的id(offset)。kafka 只保证按一个partition 中的顺序将消息发给 consumer,不保证一个topic 的整体(多个partition 间)的顺序 7)Offset:消息偏移量,方便查找。 三、Producer向Kafka写消息的流程 1)producer 先从zookeeper 的 "/brokers/.../state"节点找到该partition的leader 2)producer 将消息发送给该leader 3)leader 将消息写入本地log 4)followers 从leader pull消息,写入本地log 后向leader发送ACK 5)leader收到所有ISR中的replication 的ACK后,增加HW(high watermark,最后commit的offset)并向producer 发送ACK 四、kafka的分区数和消费者个数的关系topic下的一个分区只能被同一个consumer group下的一个consumer线程来消费,但反之并不成立,即一个consumer线程可以消费多个分区的数据,比如Kafka提供的ConsoleConsumer,默认就只是一个线程来消费所有分区的数据。即分区数决定了同组消费者个数的上限,所以,如果你的分区数是N,那么最好线程数也保持为N,这样通常能够达到最大的吞吐量。超过N的配置只是浪费系统资源,因为多出的线程不会被分配到任何分区。 Kafka提供的两种Consumer消费Partition的分配策略: range和roundrobin,由参数partition.assignment.strategy指定,默认是range策略。具体参考:https://www.jianshu.com/p/dbbca800f607 五、kafka如何实现高可用?Kafka 0.8 以后,提供了 HA 机制,就是 replica(复制品) 副本机制。每个 partition 的数据都会同步到其它机器上,形成自己的多个 replica 副本。所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上的数据即可。 ...

May 28, 2020 · 2 min · jiezi

安装Zookeeper和Kafka集群

配置/etc/hosts文件vi /etc/hosts# 添加192.168.200.110 master192.168.200.111 slave1192.168.200.112 slave2安装Zookeeper集群下载Zookeeper安装包下载地址:https://www.apache.org/dyn/cl... 创建对应的ZK数据和日志目录# 创建ZK的数据目录,同时需要创建myid指定这个节点的IDmkdir -p /software/zookeeper/zkdata/vi /software/zookeeper/zkdata/myid# 创建ZK的日志目录mkdir /software/zookeeper/zklogs/修改zoo.cfg文件先复制一份: cp zoo_sample.cfg zoo.cfg开始修改: cat zoo.cfg# The number of milliseconds of each ticktickTime=2000# The number of ticks that the initial # synchronization phase can takeinitLimit=10# The number of ticks that can pass between # sending a request and getting an acknowledgementsyncLimit=5# the directory where the snapshot is stored.# do not use /tmp for storage, /tmp here is just # example sakes.#dataDir=/tmp/zookeeper# the port at which the clients will connectclientPort=2181# the maximum number of client connections.# increase this if you need to handle more clients#maxClientCnxns=60## Be sure to read the maintenance section of the # administrator guide before turning on autopurge.## http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance## The number of snapshots to retain in dataDir#autopurge.snapRetainCount=3# Purge task interval in hours# Set to "0" to disable auto purge feature#autopurge.purgeInterval=1# ZK的数据目录,myid也在里面dataDir=/software/zookeeper/zkdata# ZK的日志目录dataLogDir=/software/zookeeper/zklogs# 集群模式的各个节点,如果是自身的话,需要配置为0.0.0.0,而不是master/IP地址,不然可能会出现zk之间无法连接通信的情况。(如果您提供了公共IP,则侦听器将无法连接到端口,您必须为当前节点指定0.0.0.0)server.1=0.0.0.0:2888:3888server.2=slave1:2888:3888server.3=slave2:2888:3888启动/停止ZK/查看状态/重启zkServer.sh startzkServer.sh stopzkServer.sh statuszkServer.sh restartZK的相关命令操作# 连接ZK[root@master conf]# zkCli.sh -server 127.0.0.1:2181# 显示根目录下文件[zk: 127.0.0.1:2181(CONNECTED) 0] ls /[admin, brokers, cluster, config, consumers, controller_epoch, isr_change_notification, latest_producer_id_block, log_dir_event_notification, zookeeper]# 显示根目录下文件,并能看到更新次数等数据[zk: 127.0.0.1:2181(CONNECTED) 1] ls2 /'ls2' has been deprecated. Please use 'ls [-s] path' instead.[cluster, controller_epoch, brokers, zookeeper, admin, isr_change_notification, consumers, log_dir_event_notification, latest_producer_id_block, config]cZxid = 0x0ctime = Thu Jan 01 08:00:00 CST 1970mZxid = 0x0mtime = Thu Jan 01 08:00:00 CST 1970pZxid = 0x400000003cversion = 10dataVersion = 0aclVersion = 0ephemeralOwner = 0x0dataLength = 0numChildren = 10# 创建文件,并设置初始化内容[zk: 127.0.0.1:2181(CONNECTED) 3] create /lzhpo-test1 "hello,lzhpo-test1~"Created /lzhpo-test1# 查看文件内容[zk: 127.0.0.1:2181(CONNECTED) 6] get /lzhpo-test1hello,lzhpo-test1~# 修改文件内容[zk: 127.0.0.1:2181(CONNECTED) 7] set /lzhpo-test1 "test1"[zk: 127.0.0.1:2181(CONNECTED) 8] get /lzhpo-test1test1# 删除文件[zk: 127.0.0.1:2181(CONNECTED) 9] delete /lzhpo-test1[zk: 127.0.0.1:2181(CONNECTED) 10] ls /[admin, brokers, cluster, config, consumers, controller_epoch, isr_change_notification, latest_producer_id_block, log_dir_event_notification, zookeeper]剩下的,请看官方文档:https://zookeeper.apache.org/... ...

May 27, 2020 · 7 min · jiezi

如何将-Redis-用于微服务通信的事件存储

来源:Redislabs 作者:Martin Forstner 翻译:Kevin (公众号:中间件小哥) 以我的经验,将某些应用拆分成更小的、松耦合的、可协同工作的独立逻辑业务服务会更易于构建和维护。这些服务(也被称为微服务)各自管理自己的技术栈,因此很容易独立于其他服务进行开发和部署。前人已经总结了很多关于使用这种架构设计的好处,在此我就不再赘述了。关于这种设计,有一个方面我一直在重点关注,因为如果没有它,将会导致一些有趣的挑战。虽然构建松耦合的微服务是一个非常轻量级和快速的开发过程,但是这些服务之间共享状态、事件以及数据的通信模型却不那么简单。我使用过的最简单的通信模型就是服务间直接通信,但是这种模型被 Fernando Dogio 明确地证明一旦服务规模扩大就会失效,会导致服务崩溃、重载逻辑以及负载增加等问题,从而可能引起的巨大麻烦,因此应该尽量避免使用这种模型。还有一些其他通信模型,比如通用的发布/订阅模型、复杂的 kafka 事件流模型等,但是最近我在使用 Redis 构建微服务间的通信模型。 拯救者 Redis! 微服务通过网络边界发布状态,为了跟踪这种状态,事件通常需要被保存在事件存储中。由于事件通常是一种异步写入操作的不可变流的记录(又被称为事务日志),因此适用于以下场景: 1. 顺序很重要(时间序列数据) 2. 丢失一个事件会导致错误状态 3. 回放状态在任何给定时间点都是已知的 4. 写操作简单且快捷 5. 读操作需要更多的时间,以至于需要缓存 6. 需要高可扩展性,服务之间都是解耦的,没有关联 使用 Redis,我始终可以轻松实现发布-订阅模式。但现在,Redis 5.0 提供了新的Streams 数据类型,我们可以以一种更加抽象的方式对日志数据结构进行建模-使之成为时间序列数据的理想用例(例如最多一次或最少一次传递语义的事务日志)。基于双主功能,轻松简单的部署以及内存中的超快速处理能力,Redis 流成为一种管理大规模微服务通信的必备工具。基本的模型被称为命令查询职责分离(CQRS),它将命令和查询分开执行,命令使用 HTTP 协议,而查询采用 RESP(Redis 序列化协议)。让我们使用一个例子来说明如何使用 Redis 作为事件存储。 OrderShop简单应用概述 我创建了一个简单但是通用的电子商务应用作为例子。当创建/删除客户、库存物品或订单时,使用 RESP 将事件异步传递到 CRM 服务,以管理 OrderShop 与当前和潜在客户的互动。像许多常见应用程序的需求一样,CRM 服务可以在运行时启动和停止,而不会影响其他微服务。这需要捕获在其停机期间发送给它的所有消息以进行后续处理。 下图展示了 9 个解耦的微服务的互连性,这些微服务使用由 Redis 流构建的事件存储进行服务间通信。他们通过侦听事件存储(即 Redis 实例)中特定事件流上的任何新创建的事件来执行此操作。 图1. OrderShop 架构 我们的 OrderShop 应用程序的域模型由以下 5 个实体组成: 顾客产品库存订单账单通过侦听域事件并保持实体缓存为最新状态,事件存储的聚合功能仅需调用一次或在响应时调用。 图2. OrderShop 域模型 安装并运行OrderShop 按照如下步骤安装并运行 OrderShop 应用: ...

October 16, 2019 · 1 min · jiezi

Kafka入门介绍

应用背景:目前 Kafka 在我所在部门做的是数据同步,如沈阳生成的数据要同步到北京集群,就需要这样的分布式消息队列。也有用Kafka做数据缓存同步完数据之后就可以用Stream、Storm、FLink等处理流式数据Kafka 特点高吞吐率是第一需求、低延迟(实时性),每秒处理几十万消息,延迟最低几毫秒可扩展性,支持动态扩展节点数据持久性与可靠性,数据被持久化磁盘,支持数据多副本防止数据丢失高容错,允许节点失败高并发,支持上千个客户端同时读写Kafka 架构kafka 运行在一群 broker 上,这里 broker 指的是各个节点/服务器每个 broker 上,消息的组织是按照 topic 来的,有 topic1, topic2, ..., topicm每个 topic 对应着 partition1,partition2,...,partitionn,把数据分片的目的:为了并行计算时增加并行度,计算时并行处理各个分片Kafka 的容错机制,是因为它会有消息副本(存放在其他位置),在使用 Kafka 备份时,要指定分区、指定备份因子(备几份)Kafka 基于消息订阅消费Producer 生产者身份,制作生产数据Consumer 消费者身份,消费即读数据。Consumer 有 Consumer Group 的概念,也就是一个 topic 的 n 个分片会被 Consumer Group 的 consumer1,consumer2,...,cnsumerk 消费需要注意的是:同一 Group 的 consumer 不可重复消费,每个Consumer 不同重复消费数据,这是为了保障并行计算的效率。那么每次消费,就需要记录消费到哪儿了,这里使用的是 Offset,用来记录读到哪个位置Offset(即位置信息) 保存在 zooKeeper 集群上,以目录的形式存储,如 broker1/topic1/consumer1/47/。这种机制类似 Spark 的 Checkpoint 检查点文件,用于做信息回溯。Kafka 的整体架构和消费机制如下图: 列出各个broker上的topic: ./kafka-topics.sh --zookeeper ip1:port1,ip2:port2,ip3:port3 --list创建topic: ./kafka-topics.sh --zookeeper 192.168.152.122:2181,192.168.152.222:2181,192.168.152.131:2181,192.168.152.231:2181,192.168.152.241:2181 --create --topic sunjingru --partitions 2 --replication-factor 2console生产者输入数据: ...

October 15, 2019 · 1 min · jiezi

该如何选择消息队列

在高并发业务场景下,消息队列在流量削峰、解耦上有不可替代的作用。当前使用较多的消息队列有 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、Pulsar 等。 消息队列这么多,到底该选择哪款消息队列呢? 选择消息队列的基本标准虽然这些消息队列在功能和特性方面各有优劣,但我们在选择的时候要有一个基本标准。 首先,必须是开源的产品。开源意味着,如果有一天你使用的消息队列遇到了一个影响你系统业务的 Bug,至少还有机会通过修改源代码来迅速修复或规避这个 Bug,解决你的系统的问题,而不是等待开发者发布的下一个版本来解决。 其次,这个产品必须是近年来比较流行并且有一定社区活跃度的产品。流行的好处是,只要使用场景不太冷门,遇到 Bug 的概率会非常低,因为大部分遇到的 Bug,其他人早就遇到并且修复了。在使用过程中遇到的一些问题,也比较容易在网上搜索到类似的问题,然后很快的找到解决方案。还有一个优势就是,流行的产品与周边生态系统会有一个比较好的集成和兼容。 最后,作为一款及格的消息队列,必须具备的几个特性包括: 消息的可靠传递:确保不丢消息;Cluster:支持集群,确保不会因为某个节点宕机导致服务不可用,当然也不能丢消息;性能:具备足够好的性能,能满足绝大多数场景的性能要求。接下来看一下有哪些符合上面这些条件,可供选择的开源消息队列。 RabbitMQ 首先,我们来看下消息队列 RabbitMQ。RabbitMQ 于 2007 年发布,是使用 Erlang 编程语言编写的,最早是为电信行业系统之间的可靠通信设计的,也是少数几个支持 AMQP 协议的消息队列之一。 RabbitMQ:轻量级、迅捷,它的宣传口号,也很明确地表明了 RabbitMQ 的特点:Messaging that just works,开箱即用的消息队列。也就是说,RabbitMQ 是一个相当轻量级的消息队列,非常容易部署和使用。 RabbitMQ 一个比较有特色的功能是支持非常灵活的路由配置,和其他消息队列不同的是,它在生产者(Producer)和队列(Queue)之间增加了一个 Exchange 模块,可以理解为交换机。 Exchange 模块的作用和交换机非常相似,根据配置的路由规则将生产者发出的消息分发到不同的队列中。路由的规则也非常灵活,甚至可以自己来实现路由规则。如果正好需要这个功能,RabbitMQ 是个不错的选择。 RabbitMQ 的客户端支持的编程语言大概是所有消息队列中最多的。 接下来说下 RabbitMQ 的几个问题: RabbitMQ 对消息堆积的支持并不好,当大量消息积压的时候,会导致 RabbitMQ 的性能急剧下降。RabbitMQ 的性能是这几个消息队列中最差的,大概每秒钟可以处理几万到十几万条消息。如果应用对消息队列的性能要求非常高,那不要选择 RabbitMQ。RabbitMQ 使用的编程语言 Erlang,扩展和二次开发成本高。RocketMQ RocketMQ 是阿里巴巴在 2012 年开源的消息队列产品,用 Java 语言实现,在设计时参考了 Kafka,并做出了自己的一些改进,后来捐赠给 Apache 软件基金会,2017 正式毕业,成为 Apache 的顶级项目。RocketMQ 在阿里内部被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,Binglog 分发等场景。经历过多次双十一考验,它的性能、稳定性和可靠性都是值得信赖的。 RocketMQ 有着不错的性能,稳定性和可靠性,具备一个现代的消息队列应该有的几乎全部功能和特性,并且它还在持续的成长中。 ...

October 15, 2019 · 1 min · jiezi

dacker安装kafka

docker安装使用kafka收集整理而来,学习kafka时使用使用时请注意开放相关端口,将脚本中IP改成自己的使用Kafka需要安装zk镜像如需定制请在镜像的docker hub官网上参考使用文档docker pull wurstmeister/zookeeperdocker pull wurstmeister/kafka启动docker run -d --name zookeeper \ -p 2181:2181 \ -t wurstmeister/zookeeperdocker run -d --name kafka \ -p 9092:9092 \ -e KAFKA_BROKER_ID=0 \ -e KAFKA_ZOOKEEPER_CONNECT=127.0.0.1:2181 \ -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092 \ -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 \ -t wurstmeister/kafka创建topic/opt/kafka/bin/kafka-topics.sh --create --zookeeper 127.0.0.1:2181 --replication-factor 1 --partitions 1 --topic mykafka创建生产者/opt/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic mykafka创建消费者另起一个ssh窗口进入容器/opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic mykafka --from-beginning测试在生产者窗口输入数据在消费者窗口查看消费的数据

October 9, 2019 · 1 min · jiezi

Kafka-异步消息也会阻塞记一次-Dubbo-频繁超时排查过程

线上某服务 A 调用服务 B 接口完成一次交易,一次晚上的生产变更之后,系统监控发现服务 B 接口频繁超时,后续甚至返回线程池耗尽错误 Thread pool is EXHAUSTED。因为服务 B 依赖外部接口,刚开始误以为外部接口延时导致,所以临时增加服务 B dubbo 线程池线程数量。配置变更之后,重启服务,服务恢复正常。一段时间之后,服务 B 再次返回线程池耗尽错误。这次深入排查问题之后,才发现 Kafka 异步发送消息阻塞了 dubbo 线程,从而导致调用超时。 一、问题分析Dubbo 2.6.5,Kafak maven 0.8.0-beta1服务 A 调用服务 B,收到如下错误: 2019-08-30 09:14:52,311 WARN method [%f [DUBBO] Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-xxxx, Pool Size: 1000 (active: 1000, core: 1000, max: 1000, largest: 1000), Task: 6491 (completed: 5491), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://xxxx!, dubbo version: 2.6.0, current host: 127.0.0.1可以看到当前 dubbo 线程池已经满载运行,不能再接受新的调用。正常情况下 dubbo 线程可以很快完成任务,然后归还到线程池中。由于线程执行的任务发生阻塞,消费者端调用超时。而服务提供者端由于已有线程被阻塞,线程池必须不断创建新线程处理任务,直到线程数量达到最大数量,系统返回 Thread pool is EXHAUSTED。 ...

October 8, 2019 · 4 min · jiezi

kafka1简介

kafka 简介定义kafka 是一个分布式的数据流平台 数据流平台有3个关键特性: 发布订阅记录流(streams of records),类似于消息队列或企业级的消息传递系统以容错的持久化方式存储记录流当记录流产生时,进行相应处理kafka 通常用于两大类应用: 构建用于系统和应用间可靠的获取数据的实时数据流管道构建转换或响应数据流的实时流应用程序简单的说 可以作为一个可靠的数据流管道用于处理实时数据流的应用程序总的来说 kafka是一个提供(实时)读写数据流的分布式中间件,并提供对记录流的持久化功能 基础概念kafka 作为集群可运行在一台或多台服务器上,可跨多个数据中心kafka 使用topic对存储的记录流进行分类一条记录(消息)由key,value 和 timestamp(时间戳)组成kafka集群中的每个节点称之为broker特性高吞吐,低延迟可扩展,可动态增加节点,可重新分配分区(热扩展对运行中程序的影响待确认)持久性,可靠性:支持数据持久化到硬盘,并支持副本机制,容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)高并发:支持数千个客户端同时读写应用场景消息队列(Messaging) 可代替传统消息队列:解耦,缓存消息 低延迟强大的持久化保证网站活动跟踪(Website Activity Tracking) 用户网站的活动(page views, searches, other)根据类型被发布到不同的topic,这些被发布到topics中的数据可以作为数据源被应用在不同的场景:实时处理程序,实时监控,被加载至 Hadoop 或离线数据仓库进行数据的离线处理和报告等。 因为活动信息量非常大,主要用到以下特性 高吞吐量度量(Metrics)kafka通常用于监控一些可操作的数据(operational monitoring data)。涉及从分布式应用程序中汇总统计信息,然后生成集中的数据源 日志聚合(Log Aggregation)许多人使用kafka来替代日志聚合解决方案。日志收集系统通常从服务器收集物理日志文件,并将其载入一个中心系统(文件服务器或HDFS)进行处理。与Scribe(Facebook开源的日志收集系统),Flume(Flume最早是Cloudera提供的日志收集系统,目前是Apache下的一个孵化项目,Flume支持在日志系统中定制各类数据发送方,用于收集数据)等以日志为中心的系统相比,kafka 具备同样出色的性能,更强的耐用性(副本机制)和更低的端到端延迟 流处理(Stream Processing)处理由多个阶段组成的处理管道中的数据。从一个topic中消费,然后汇总,转换为新的topic供后续处理。如新闻推荐:抓取数据发布到topic1->读取数据,格式化,去重->topic2->读取数据推荐给用户 事件朔源(Event Sourcing)Event Sourcing就是基于时间记录一个对象的所有事件,进而根据一系列事件来得到其状态(记录事件而非状态,有点像binlog)。Kafka 可以存储非常多的日志数据,为基于 event sourcing 的应用程序提供强有力的支持 提交日志kafka 可以从外部为分布式系统提供日志提交功能。该日志有助于在节点之间复制数据,并充当故障节点恢复其数据的重新同步机制,kafka的日志压缩(不是压缩算法,而是日志的清理,合并机制)特性支持这一用法。

October 8, 2019 · 1 min · jiezi

kafka2集群部署

kakfa 集群部署准备工作jre 安装zookeeper 单机集群搭建假设当前目录为 /home/zk.配置3节点伪集群. 下载wget http://mirror.bit.edu.cn/apache/zookeeper/stable/apache-zookeeper-3.5.5-bin.tar.gzzxf apache-zookeeper-3.5.5-bin.tar.gzmv apache-zookeeper-3.5.5-bin zookeeper创建zookeeper data目录mkdir /home/zk/tmp/node-0/data -pmkdir /home/zk/tmp/node-0/datalog -pmkdir /home/zk/tmp/node-1/data -pmkdir /home/zk/tmp/node-1/datalog -pmkdir /home/zk/tmp/node-2/data -pmkdir /home/zk/tmp/node-2/datalog -p修改配置文件cp zookeeper/conf/zoo_sample.cfg zookeeper/conf/zoo-node-0.cfg# 修改节点0配置vi zookeeper/conf/zoo-node-0.cfgdataDir=/home/zk/tmp/node-0/datadataLogDir=/home/zk/tmp/node-0/datalog# server.index = ip:port-原子广播:port-选举server.0=127.0.0.1:2887:3887server.1=127.0.0.1:2888:3888server.2=127.0.0.1:2889:3889# 拷贝节点0配置cp zookeeper/conf/zoo-node-0.cfg zookeeper/conf/zoo-node-1.cfgcp zookeeper/conf/zoo-node-0.cfg zookeeper/conf/zoo-node-2.cfg# 修改节点1配置vi zookeeper/conf/zoo-node-1.cfgdataDir=/home/zk/tmp/node-1/datadataLogDir=/home/zk/tmp/node-1/datalogclientPort=2182# 修改节点2配置vi zookeeper/conf/zoo-node-2.cfgdataDir=/home/zk/tmp/node-2/datadataLogDir=/home/zk/tmp/node-2/datalogclientPort=2183# 指定节点编号echo 0 >> tmp/node-0/data/myidecho 1 >> tmp/node-1/data/myidecho 2 >> tmp/node-2/data/myid启动cd zookeeperbin/zkServer.sh start conf/zoo-node-0.cfgbin/zkServer.sh start conf/zoo-node-1.cfgbin/zkServer.sh start conf/zoo-node-2.cfg# 查看状态bin/zkServer.sh status conf/zoo-node-0.cfgbin/zkServer.sh status conf/zoo-node-1.cfgbin/zkServer.sh status conf/zoo-node-2.cfgkafka 单机集群搭建假设当前目录为 /home/kfk 下载wget http://mirror.bit.edu.cn/apache/kafka/2.3.0/kafka_2.12-2.3.0.tgztar zxf kafka_2.12-2.3.0.tgz# 或者设置软链接mv kafka_2.12-2.3.0 kafka创建日志目录cd kafkamkdir tmp/kafka-logs-0 -pmkdir tmp/kafka-logs-1 -pmkdir tmp/kafka-logs-2 -p修改配置文件cp config/server.properties config/server-0.propertiesvi config/server-0.propertiesbroker.id=0listeners=PLAINTEXT://:9092log.dirs=/home/kfk/kafka/tmp/kafka-logs-0zookeeper.connect=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183# end vicp config/server-0.properties config/server-1.propertiescp config/server-0.properties config/server-2.propertiesvi config/server-1.propertiesbroker.id=1listeners=PLAINTEXT://:9093log.dirs=/home/kfk/kafka/tmp/kafka-logs-1# end vivi config/server-2.propertiesbroker.id=2listeners=PLAINTEXT://:9094log.dirs=/home/kfk/kafka/tmp/kafka-logs-2# end vi启动nohup bin/kafka-server-start.sh config/server-0.properties 1>/dev/null 2>&1 &nohup bin/kafka-server-start.sh config/server-1.properties 1>/dev/null 2>&1 &nohup bin/kafka-server-start.sh config/server-2.properties 1>/dev/null 2>&1 &常用配置说明broker 配置https://kafka.apache.org/docu... ...

October 8, 2019 · 2 min · jiezi

kafka3部分原理

kafka 部分设计及原理topic & partition & segment定义topic :主题名,用于对消息进行分类,是一个逻辑上的概念 partition :是物理上的一个概念,一个topic可以对应多个partition,消息实际存储在partition partition 是一个有序,不可变的记录序列。partition中的每一条消息都有一个序列号,称之为offset,offset 在一个partition内唯一,用于区别消息 segment :partition被分为多个segment文件进行存储 partition 的物理存储结构 创建topic:test.show.log, 副本数2,分区数3,segment文件大小512byte # 创建topicbin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 2 --partitions 3 --topic test.show.log --config segment.bytes=512# 查看topic 状态bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic test.show.log Topic:test.show.log PartitionCount:3 ReplicationFactor:2 Configs:segment.bytes=512 Topic: test.show.log Partition: 0 Leader: 1 Replicas: 1,0 Isr: 1,0 Topic: test.show.log Partition: 1 Leader: 0 Replicas: 0,2 Isr: 0,2 Topic: test.show.log Partition: 2 Leader: 2 Replicas: 2,1 Isr: 2,1可以看到broker.0 服务持有partition 0,1的副本,并且为1 partition的leader ...

October 8, 2019 · 3 min · jiezi

kafka5常用命令

kafka 常用命令创建topic bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 3 --partitions 1 --topic my-replicated-topic --config message.timestamp.type=CreateTime--config 可以指定topic的配置,message.timestamp.type 指定topic的时间戳使用方式,其他配置见 https://kafka.apache.org/docu... 查看topic状态 bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic my-replicated-topic生产者 bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic消费者 bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic my-replicated-topic查看segment文件信息 kafka-run-class.sh kafka.tools.DumpLogSegments --print-data-log --files xxxxxxxxxxxx.log

October 8, 2019 · 1 min · jiezi

我太难了kafka常用命令

背景即使了解了kafka的概念,也无法阻挡测试同事的不断追问。例如kafka消息这么多,我怎么测试啊;消息到底有没有发到kafka,怎么看;kafka磁盘写满了,怎么办;我太难了。。???????????? kafka的常用命令-- zookeeper 服务端地址-- replication-factor 副本数-- partitions 分区数-- topic 主题名-- describe 创建主题(4个分区,2个副本)kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 2 --partitions 4 --topic test 查询集群描述kafka-topics.sh --describe --zookeeper 删除主题kafka-topics.sh --zookeeper node3:2181,node4:2181,node5:2181 --delete --topic kfk 更新特定主题的分区bin/kafka-topics.sh –zookeeper 127.0.0.1:2181 –alter –partitions 20 –topic testKJ1 新消费者列表查询(支持0.9版本+)kafka-consumer-groups.sh --new-consumer --bootstrap-server localhost:9092 --list 设置偏移量到最新kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group you_consumer_group_name --topic you_topic_name --execute --reset-offsets --to-latest 显示某个消费组的消费详情(支持0.9版本+)kafka-consumer-groups.sh --new-consumer --bootstrap-server localhost:9092 --describe --group test-consumer-group 生产者操作bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test 新生产者(支持0.9版本+)bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test --producer.config config/producer.properties ...

October 3, 2019 · 1 min · jiezi

Kafka分区原理简图

Kafka分区原理简图最近在用kafka作为消费队列,看的网上对分区的工作原理都是文字描述比较多,所以特地画了简图分享出来Kafka写入数据是随机写入某一个分区,每个分区内的数据是按照顺序的,如下图,每个分区的入库的数量并不一致Kafka消费时,如果只有一个进程,那么只能消费某一个分区的数据(随机监听某一个),当前分区消费完成后才会通过reblance过程,选择其他分区去消费。(如果当前分区一直有数据,那么其他分区的数据就会延迟消费)为了减少延迟消费和提升消费效率,建议消费的进程数和分区数保持一致,这样就不会有reblance过程,数据不管落到哪一个分区,都能被立即消费到

September 19, 2019 · 1 min · jiezi

Kafka低延迟原理

刚才午睡了一会,做了一个梦,Kafka为什么"这么快",于是乎,便写了这篇探讨的博客文章,网上很多关于Kafka测试的文章,测试结果通常都是Kafka延迟吊打其他MQ,那么我们学习一个新东西的时候,首先会想到它会在我们的业务场景中有一个什么样的作用,为什么要用它而不用其他类似的组件,Kafka学习中,也会有这样的疑问。顺着这个逻辑,我们可能会有两个思路,第一,Kafka到底是什么?第二,Kafka的优点?有了这两个概念,我们就可以解决为什么要用它而不用其他组件的问题。 阅读Kafka官方文档,寻找这两个问题的答案~ Kafka到底是什么?ApacheKafka是一个分布式流媒体平台。流媒体平台有三个关键功能: 发布和订阅记录流,类似于消息队列或企业消息传递系统。以容错的持久方式存储记录流。记录发生时处理流。Kafka通常用于两大类应用: 构建可在系统或应用程序之间可靠获取数据的实时流数据管道。构建转换或响应数据流的实时流应用程序。Kafka的优点?高吞吐,低延迟持久性、可靠性容错性高并发可扩展性初步了解了Kafka之后,我们现在来一起探讨下Kafka低延迟的原理。 Kafka低延迟的原理在上面的了解中,知道了Kafka有低延迟的优势,它是怎么实现的呢?下面就来一起探讨一下。生产者(producer)写入数据:Kafka在收到消息的时候,会把消息持久化存储到硬盘中,为了优化写入速度,Kafka是这样设计的,顺序写入和运用页缓存技术。 首先说一下,为什么顺序写入比随机写入快:先介绍一下它的存储原理。机械硬盘的结构你可以想象成一个唱片机,它有一个旋转的盘片和一个能沿半径方向移动的磁头。处理读取和写入请求时,首先可以根据请求的开始地址算出要处理的数据在磁盘上的位置,之后要进行以下几步工作:1、磁头沿半径方向移动,直至移动到数据所在的柱面(相同半径的磁道组成的环面)2、盘片高速旋转,使磁头到达数据的起始位置3、磁头沿磁道从磁盘读取或写入数据。当一次读取的数据量很少的时候,1、2步骤带来的开销是无法忽略的,这使得随机写相对于顺序写会有巨大的性能劣势。因为在顺序写的时候,1、2步骤只需要执行一次,剩下的全是数据传输所需要的固有开销;而每次随机写的时候,前两个步骤都需要执行,带来了极大的额外开销。其次再说一下页缓存:即便是顺序写入硬盘,硬盘的访问速度还是不可能追上内存。所以Kafka的数据并 不是实时的写入硬盘 ,它充分利用了现代操作系统 分页存储 来利用内存提高I/O效率。Memory Mapped Files也被翻译成 内存映射文件 ,在64位操作系统中一般可以表示20G的数据文件,它的工作原理是直接利用操作系统的Page来实现文件到物理内存的直接映射。完成映射之后你对物理内存的操作会被同步到硬盘上(操作系统在适当的时候),也就是说,将复杂的IO操作交给了操作系统。消费者(consumer)读取数据:Kafka在消费数据的时候,为了提高读取速度,运用了零拷贝技术。 零拷贝:考虑这样一种常用的情形:你需要将静态内容(类似图片、文件)展示给用户。那么这个情形就意味着你需要先将静态内容从磁盘中拷贝出来放到一个内存buf中,然后将这个buf通过socket传输给用户,进而用户或者静态内容的展示。这看起来再正常不过了,但是实际上这是很低效的流程,我们把上面的这种情形抽象成下面的过程: 1.首先,调用read时,文件A拷贝到了kernel模式; 2.之后,CPU控制将kernel模式数据copy到user模式下; 3.调用write时,先将user模式下的内容copy到kernel模式下的socket的buffer中; 4.最后将kernel模式下的socket buffer的数据copy到网卡设备中传送; 幸运的是,你可以用一种叫做Zero-Copy的技术来去掉这些无谓的copy。应用程序用Zero-Copy来请求kernel直接把disk的data传输给socket,而不是通过应用程序传输。Zero-Copy大大提高了应用程序的性能,并且减少了kernel和user模式上下文的切换。

September 8, 2019 · 1 min · jiezi

Flink快速入门安装与示例运行

flink是一款开源的大数据流式处理框架,他可以同时批处理和流处理,具有容错性、高吞吐、低延迟等优势,本文简述flink在windows和linux中安装步骤,和示例程序的运行。 首先要想运行Flink,我们需要下载并解压Flink的二进制包,下载地址如下:https://flink.apache.org/down... 我们可以选择Flink与Scala结合版本,这里我们选择最新的1.9版本Apache Flink 1.9.0 for Scala 2.12进行下载。 下载成功后,在windows系统中可以通过Windows的bat文件或者Cygwin来运行Flink。 在linux系统中分为单机,集群和Hadoop等多种情况。 通过Windows的bat文件运行首先启动cmd命令行窗口,进入flink文件夹,运行bin目录下的start-cluster.bat 注意:运行flink需要java环境,请确保系统已经配置java环境变量。 $ cd flink$ cd bin$ start-cluster.batStarting a local cluster with one JobManager process and one TaskManager process.You can terminate the processes via CTRL-C in the spawned shell windows.Web interface by default on http://localhost:8081/.显示启动成功后,我们在浏览器访问 http://localhost:8081/可以看到flink的管理页面。 通过Cygwin运行Cygwin是一个在windows平台上运行的类UNIX模拟环境,官网下载:http://cygwin.com/install.html 安装成功后,启动Cygwin终端,运行start-cluster.sh脚本。 $ cd flink$ bin/start-cluster.shStarting cluster.显示启动成功后,我们在浏览器访问 http://localhost:8081/可以看到flink的管理页面。 图 ui Linux系统上安装flink单节点安装在Linux上单节点安装方式与cygwin一样,下载Apache Flink 1.9.0 for Scala 2.12,然后解压后只需要启动start-cluster.sh。 集群安装集群安装分为以下几步: ...

August 28, 2019 · 1 min · jiezi

kafkaconsumer

-- coding:utf-8 --"""Author:YjxTime:2019-8-17 14:22"""from kafka import KafkaProducerfrom kafka import KafkaConsumerfrom kafka.errors import KafkaErrorfrom threading import Threadimport time class kafkaConsumer(object): def __init__(self, kafkatopic, kafkahost, kafkaport): self.kafkatopic = kafkatopic self.kafkahost = kafkahost self.kafkaport = kafkaport bootstrap_server = [] for host in self.kafkahost: bootstrap_server.append('{kafka_host}:{kafka_port}'.format(kafka_host=host, kafka_port=self.kafkaport)) self.consumer = KafkaConsumer(self.kafkatopic, bootstrap_servers=bootstrap_server, auto_offset_reset='latest', consumer_timeout_ms=20000)def getMsg(self): try: for msg in self.consumer: # yield msg print('get_key_>>>>', msg.key, 'get_message_>>>>', msg.value) print('get_message_>>>>', msg) # print('****done****') except KafkaError as e: print('KafkaError:', e)if name == '__main__': ...

August 19, 2019 · 1 min · jiezi

Kafka-原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平台、移动浏览器、推荐广告和大数据、人工智能等相关开发和架构。目前在vivo智能平台中心从事 AI中台建设以及广告推荐业务。擅长各种业务形态的业务架构、平台化以及各种业务解决方案。博客地址:http://arganzheng.life。背景最近要把原来做的那套集中式日志监控系统进行迁移,原来的实现方案是: Log Agent => Log Server => ElasticSearch => Kibana,其中Log Agent和Log Server之间走的是Thrift RPC,自己实现了一个简单的负载均衡(WRB)。 原来的方案其实运行的挺好的,异步化Agent对应用性能基本没有影响。支持我们这个每天几千万PV的应用一点压力都没有。不过有个缺点就是如果错误日志暴增,Log Server这块处理不过来,会导致消息丢失。当然我们量级没有达到这个程度,而且也是可以通过引入队列缓冲一下处理。不过现在综合考虑,其实直接使用消息队列会更简单。PRC,负载均衡,负载缓冲都内建实现了。另一种方式是直接读取日志,类似于logstash或者flume的方式。不过考虑到灵活性还是决定使用消息队列的方式,反正我们已经部署了Zookeeper。调研了一下,Kafka是最适合做这个数据中转和缓冲的。于是,打算把方案改成: Log Agent => Kafka => ElasticSearch => Kibana。 Kafka介绍一、Kafka基本概念Broker:Kafka集群包含一个或多个服务器,这种服务器被称为broker。Topic:每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。Message 消息是Kafka通讯的基本单位,有一个固定长度的消息头和一个可变长度的消息体(payload)构成。在Java客户端中又称之为记录(Record)。消息结构各部分说明如下: CRC32: CRC32校验和,4个字节。magic: Kafka服务程序协议版本号,用于做兼容。1个字节。attributes: 该字段占1字节,其中低两位用来表示压缩方式,第三位表示时间戳类型(0表示LogCreateTime,1表示LogAppendTime),高四位为预留位置,暂无实际意义。timestamp: 消息时间戳,当 magic > 0 时消息头必须包含该字段。8个字节。key-length: 消息key长度,4个字节。key: 消息key实际数据。payload-length: 消息实际数据长度,4个字节。payload: 消息实际数据在实际存储一条消息还包括12字节的额外开销(LogOverhead): 消息的偏移量: 8字节,类似于消息的Id。消息的总长度: 4字节Partition: Partition(分区)是物理上的概念,每个Topic包含一个或多个Partition。每个分区由一系列有序的不可变的消息组成,是一个有序队列。每个分区在物理上对应为一个文件夹,分区的命名规则为${topicName}-{partitionId},如__consumer_offsets-0。分区目录下存储的是该分区的日志段,包括日志数据文件和两个索引文件。每条消息被追加到相应的分区中,是顺序写磁盘,因此效率非常高,这也是Kafka高吞吐率的一个重要保证。kafka只能保证一个分区内的消息的有序性,并不能保证跨分区消息的有序性。LogSegment: 日志文件按照大小或者时间滚动切分成一个或者多个日志段(LogSegment),其中日志段大小由配置项log.segment.bytes指定,默认是1GB。时间长度则是根据log.roll.ms或者log.roll.hours配置项设置;当前活跃的日志段称之为活跃段(activeSegment)。不同于普通的日志文件,Kafka的日志段除了有一个具体的日志文件之外,还有两个辅助的索引文件: 数据文件 数据文件是以 .log 为文件后缀名的消息集文件(FileMessageSet),用于保存消息实际数据命名规则为:由数据文件的第一条消息偏移量,也称之为基准偏移量(BaseOffset),左补0构成20位数字字符组成每个数据文件的基准偏移量就是上一个数据文件的LEO+1(第一个数据文件为0)偏移量索引文件 文件名与数据文件相同,但是以.index为后缀名。它的目的是为了快速根据偏移量定位到消息所在的位置。首先Kafka将每个日志段以BaseOffset为key保存到一个ConcurrentSkipListMap跳跃表中,这样在查找指定偏移量的消息时,用二分查找法就能快速定位到消息所在的数据文件和索引文件然后在索引文件中通过二分查找,查找值小于等于指定偏移量的最大偏移量,最后从查找出的最大偏移量处开始顺序扫描数据文件,直到在数据文件中查询到偏移量与指定偏移量相等的消息需要注意的是并不是每条消息都对应有索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引,我们可以通过index.interval.bytes设置索引跨度。时间戳索引文件 Kafka从0.10.1.1版本开始引入了一个基于时间戳的索引文件,文件名与数据文件相同,但是以.timeindex作为后缀。它的作用则是为了解决根据时间戳快速定位消息所在位置。Kafka API提供了一个 offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch)方法,该方法会返回时间戳大于等于待查询时间的第一条消息对应的偏移量和时间戳。这个功能其实挺好用的,假设我们希望从某个时间段开始消费,就可以用offsetsForTimes()方法定位到离这个时间最近的第一条消息的偏移量,然后调用seek(TopicPartition, long offset)方法将消费者偏移量移动过去,然后调用poll()方法长轮询拉取消息。Producer: 负责发布消息到Kafka broker。生产者的一些重要的配置项: request.required.acks: Kafka为生产者提供了三种消息确认机制(ACK),用于配置broker接到消息后向生产者发送确认信息,以便生产者根据ACK进行相应的处理,该机制通过属性request.required.acks设置,取值可以为0, -1, 1,默认是1。 acks=0: 生产者不需要等待broker返回确认消息,而连续发送消息。acks=1: 生产者需要等待Leader副本已经成功将消息写入日志文件中。这种方式在一定程度上降低了数据丢失的可能性,但仍无法保证数据一定不会丢失。因为没有等待follower副本同步完成。acks=-1: Leader副本和所有的ISR列表中的副本都完成数据存储时才会向生产者发送确认消息。为了保证数据不丢失,需要保证同步的副本至少大于1,通过参数min.insync.replicas设置,当同步副本数不足次配置项时,生产者会抛出异常。但是这种方式同时也影响了生产者发送消息的速度以及吞吐率。message.send.max.retries: 生产者在放弃该消息前进行重试的次数,默认是3次。retry.backoff.ms: 每次重试之前等待的时间,单位是ms,默认是100。queue.buffering.max.ms: 在异步模式下,消息被缓存的最长时间,当到达该时间后消息被开始批量发送;若在异步模式下同时配置了缓存数据的最大值batch.num.messages,则达到这两个阈值的任何一个就会触发消息批量发送。默认是1000ms。queue.buffering.max.messages: 在异步模式下,可以被缓存到队列中的未发送的最大消息条数。默认是10000。queue.enqueue.timeout.ms: ...

August 19, 2019 · 6 min · jiezi

kafka之跑不通

-- coding:utf-8 --"""Author:YjxTime:2019-8-17 16:26"""from kafka import KafkaProducerfrom kafka import KafkaConsumerfrom threading import Threadimport time class kafkaProducer(object): def __init__(self, kafkatopic, kafkahost, kafkaport): self.kafkatopic = kafkatopic self.kafkahost = kafkahost self.kafkaport = kafkaport bootstrap_server = [] for host in self.kafkahost: bootstrap_server.append('{kafka_host}:{kafka_port}'.format(kafka_host=host, kafka_port=self.kafkaport)) self.producer = KafkaProducer(bootstrap_servers=bootstrap_server)def sendMsg(self): try: for i in range(10): parmas_message = 'send_message_' + str(i+1) self.producer.send(self.kafkatopic, parmas_message.encode('utf-8')) self.producer.flush() print(parmas_message) self.producer.close() print('****over****') except KafkaError as e: print('KafkaError:', e)class kafkaConsumer(object): ...

August 17, 2019 · 1 min · jiezi

Kafka之啥也没写

1 Kafka概念一个分布式消息队列,具有高性能、持久化、多副本备份、横向扩展能力。生产者往队列里写消息,消费者从队列里取消息进行业务逻辑。1.1 kafka读、发消息的方式kafka对外使用topic的概念,生产者往topic里写消息,消费者从读消息。为了做到水平扩展,一个topic实际是由多个partition组成的,遇到瓶颈时,可以通过增加partition的数量来进行横向扩容。单个parition内是保证消息有序。 1.2 topic,分区,grouptopic标签实际就是队列; 1.3 KafkaProducer 1.4 kafka节点之间如何复制备份的?1.5 kafka消息是否会丢失?为什么?1.6 kafka最合理的配置是什么?1.7 kafka的leader选举机制是什么?1.8 kafka对硬件的配置有什么要求?1.9 kafka的消息保证有几种方式?1.10kafka为什么会丢消息?

August 17, 2019 · 1 min · jiezi

深入理解Apache-Kafka

一、介绍Kafka在世界享有盛名,大部分互联网公司都在使用它,那么它到底是什么呢? Kafka由LinkedIn公司于2011年推出,自那时起功能逐步迭代,目前演变成一个完整的平台级产品,它允许您冗余地存储巨大的数据量,拥有一个具有巨大吞吐量(数百万/秒)的消息总线,并且支持实时流任务处理。总的来说,Kafka是一个分布式,可水平扩展,容错的日志提交系统 这些描述都非常抽象,让我们一个接一个地理解它们的意思,随后深入探讨其工作原理 二、分布式分布式系统意味着不同机器上的服务实例一起工作成一个整体为用户提供完整的服务,Kafka的分布式体现在存储、接收信息、发送信息在不同的节点上,它带来的好处是可扩展性和容错性 三、水平可扩展我们先给垂直可扩展下一个定义,比如说,你的传统数据库服务开始变得超负载,可以通过简单地扩充该服务器资源(CPURAMSSD)缓存这个问题,这就叫垂直扩展-单点增加资源,不过有两大致命的缺点:底层硬件资源有限、需要停机操作。反之,水平扩展通过增加更多的机器部署服务解决类似问题 四、容错分布式系统被设计成可容许一定程序的错误,不像单点部署发生异常时整体服务都将不可用,有五个节点的Kafka实例,即使有2个节点宕机了仍能继续工作 五、commit日志一个commit日记类似预写式日记(WAL)和事务日记,它是可追加的有序的持久化数据,无法进行修改或者删除 这种结构是Kafka的核心,它具备排序功能,而排序则可以保证确定性的处理,这两者都是分布式系统中的重要问题 Kafka通常会将消息持久化到磁盘上,它充分利用磁盘的有序读取特性,读写的时间复杂度都为O(1),这是相当了不起的,另外读取和写入操作不会相互影响,写入不会加锁阻塞读取操作 六、如何工作的生产者发到消息至Kafka Node节点,存储在主题Topic中,消费者订阅主题以接收消息,这是一个生产订阅模式。为了使一个节点Topic的数据量不至过大,Kafka引入分区的概念,从而具备更好的性能和伸缩性。Kafka保证分区内的所有消息都按照到达顺序排序,区分消息的方式是通过其偏移量offset,你可以将其理解为普通数组的下标索引 Kafka中Broker服务节点是愚蠢的,消费者是聪明的,Kafka不会记录消费者读取的操作和删除消息,相反,数据被存储一段时间或者达到一定的大小阈值,消费者可以自由调整偏移量offset以重复获取他们想要的消息或者舍弃 值得注意的是为了避免进程两次读取相同的消息,Kafka引入了消费者组的概念,其中包含一个或者多个消息者实例,约定每个组只能同时有一个实例消费分区的消息。不过这引来了一个麻烦,连社区也无力解决,也就是Kafka中的重平衡Rebalance问题,它本质是一种协议,规定一个消费者组下的所有消费者实例如何达成一致,来分配订阅主题的每个分区,当组成员数发生变更、订阅主题数发生变更、订阅主题的分区数发生变更时都会触发Rebalance,从而达到最公平的分配策略,不过他和GC的STW类似,在Rebalance期间,所有的消费者实例都会停止消费,然后重新分配连接。我们应该尽量避免这种情况的发生,尽量让消费实例数等于分区数 七、持久化至磁盘正如前面提及的,Kafk将消息存储至磁盘而不是内存RAM,你或许会惊讶它是如何做出这种选择的,背后应该有许多优化使其可行,没错,事实上优化点包括: 1、Kafka的通信协议支持消息合并,减少网络流量传输,Broker节点一次持续存储大量数据,消费者可以一次获取大量的消息 2、操作系统通过提前读入(read-ahead)和write-behind缓存技术,使得磁盘上的线性读写速度快,现代磁盘速度慢的结论是基于需要磁盘搜索的场景 3、现代操作系统引入页面缓存(Page cache)技术,页缓冲由多个磁盘块构造,在linux读写文件时,它用于缓存文件的逻辑内容,从而加块对磁盘映射和数据的访问 4、Kafka存储消息使用的是不可变的标准二进制格式,可以充分利用零拷贝技术(zero-copy),将数据从页缓存直接复制到socket通道中 八、数据分布式和复制我们来谈谈Kafka如何实现容错以及如何在节点间分配数据 Kafka将分区数据拷贝复制到多个Brokers节点上,避免某个Broker死亡导致数据不可达。每时每刻,一个Broker节点”拥有”一个分区,并且是应用程序从该分区读取写入的节点,这称为分区leader,它将收到的数据复制到其他N个Broker节点上,它们称为follower,并准备好在leader节点死亡时被选举为leader。这种模式使得消息不易丢失,你可以根据消息的重要程序合理调整replication factor参数,下图是4个Broker节点,拥有3个复制副本的示例 你或许会有疑问,生产者或者消费者是如何正确得知分区的leader是哪个节点的?事实上,Kafka将这些信息保存到Zookeeper服务中 九、Zookeeper服务Zookeeper是一个分布式KV对目录存储系统,特点是可靠性高、读取性能高,但是写入性能差,常被用于存储元数据和保存集群状态,包括心跳、配置等等 Kafka将以下消息保存至Zookeeper中: 1、消费者组的每个分区的偏移量,不过后来Kafka将其保存至内部主题__consumer_offsets中 2、访问权限列表 3、生产者和消费者速率限定额度 4、分区leader信息和它们的健康状态 十、Controller控制器一个分布式系统肯定是可协调的,当事件发生时,节点必须以某种方式做出反应,控制器负责决定集群如何做出反应并指示节点做某事,它是功能不能过于复杂的Broker节点,最主要的职责是负责节点下线和重新加入时重平衡和分配新的分区leader 控制器从ZooKeeper Watch事件中可以得知某个Broker节点实例下线(或者节点过期,一般发生于Broker长时间繁忙导致心跳异常)的情况,然后做出反应,决定哪些节点应成为受影响分区的新leader,然后通知每个相关的follower通过leaderAndlsr请求开始从新的leader复制数据 从上面可以得知,原本作为分区leader的Broker节点实例重启后,它将不再担任任何分区的leader,消费者也不会从这个节点上读取消息,这导致了资源的浪费,幸运的是,Kafka有一个被称为优先副本(preferred leader replica)的概念-你可以理解成原先为该分区leader节点(通过broker id区分)的副本,如果该副本可用,Kafka会将集群恢复成之前状态,通过设置auto.leader.rebalance.enabled=true可以使得这个过程自动触发,默认值为true Broker节点下线通常都是短暂的,这意味着一段时间后会恢复,这就是为什么当一个节点离开集群时,与之关联的元数据不会被删除,如果它是一个分区的跟随者,系统也不会为此分区重新分配新的跟随者 但是需要注意的是,恢复加入的节点不能立即拿回其上次的leader地位,它还没有资格 十一、ISR副本同步队列ISR(in-sync replicas),它是由leader维护的,follower从leader同步数据是有延迟的,任意一个超过阈值都会被剔除出ISR列表, 存入OSR(Outof-Sync Replicas)列表中,新加入的follower也会先存放在OSR中 一个follower想被选举成leader,它必须在ISR队列中才有资格,不过,在没有同步副本存在并且已有leader都下线的边缘情况下,可以选择可用性而不是一致性 ISR列表维护标准如下: 1、它在过去的X秒内有完整同步leader消息,通过replica.lag.time.max.ms配置约定 2、它在过去的X秒内向Zookeeper发送了一个心跳,通过zookeeper.session.timeout.ms配置约定 十二、生产者acks设置明显,存在一系列意外事件会导致leader下线,假如leader节点接收到生产者的消息,在存储并且响应ack后节点崩溃了,此时Kafka会从ISR列表中选举一个新的leader,但是由于生产者ack配置默认为1,意思是只考虑leader接收情况不考虑follower同步情况,最终导致部分消息丢失了,所以我们应该在生产者端设置acks=all,要求每条数据必须是写入所有副本之后,才能认为是写成功,另外一层意思是起码有一个leader和一个follower。不过这种设置影响集群性能,降低了吞吐量,使得生产者需要在发送下一批消息之前等待更多时间 十三、水位通过ack=all约定了leader节点在消息没有同步到所有的ISR列表前不会有任何返回,另外,节点会跟踪所有同步副本具有的最大偏移量,也就是高水位偏移量HW(high watermark offset),consumer无法消费分区下leader副本中偏移量大于分区HW的任何消息。当某个副本成为leader副本时、broker出现崩溃导致副本被踢出ISR时、producer向leader写入消息后、leader处理follower fetch请求时,都会尝试更新分区HW,从而保证了数据一致性和正常消费时不会出现读取到旧值 十四、脑裂想象一下,当正常存活的controller控制器由于长时间GC-STW导致不可用,然后Zookeeper会认为/controller节点(节点3)已经过期随即删除并发送通知到其他broker节点,其他每个broker节点都尝试升级为控制器节点,假设节点2从竞争中胜出成功新的控制器节点并在ZK中创建/controller节点 然后其他节点接收到通知,了解到节点2成为了新的控制器节点,除了还在GC暂停的节点3,或者通知压根没到达的节点3,也就是说节点3不知道leadership已经发生了变化,它还以为自己是控制器节点。此时,同时存在两个控制器,并行发出可能存在冲突的命令,导致严重的后果 幸运的是,Kafka提供了epoch number的方式可以轻松区分出真实的控制器,它是自增长的序列号,信息存储在ZooKeeper中,显然序列号最大的那个节点才是真实的 十五、什么时候应该使用Kafka从上面几点可知,Kafka可以成为事件驱动架构的中心部分,使你可以真正将应用程序彼此分离 ...

July 14, 2019 · 1 min · jiezi

深入理解Apache-Kafka

文字图片过多,欢迎前行http://www.liangsonghua.me/ar... 文章来源:http://www.liangsonghua.me作者介绍:京东资深工程师-梁松华,长期关注稳定性保障、敏捷开发、JAVA高级、微服务架构

July 12, 2019 · 1 min · jiezi

SpringBoot-KafKa集群的集成

简介本文主要讲在springboot2中,如何通过自定义的配置来集成,并可以比较好的扩展性,同时集成多个kafka集群 引入依赖引入kafka的依赖 <!-- kafka --> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency>配置文件添加配置文件,默认添加一个kafka的集群, topinfo: # kafka集群配置 ,bootstrap-servers 是必须的 kafka: # 生产者的kafka集群地址 bootstrap-servers: 192.168.90.225:9092,192.168.90.226:9092,192.168.90.227:9092 producer: topic-name: topinfo-01 consumer: group-id: ci-data 如果多个,则配置多个kafka的集群配置即可 添加属性配置类添加对应的属性配置类,如果是多个kafka集群,则可以填多个即可,注意对应的@ConfigurationProperties。 package com.topinfo.ci.dataex.config;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;import com.topinfo.ci.dataex.bean.Consumer;import com.topinfo.ci.dataex.bean.Producer;/** * @Description: kafka 属性配置 * @Author:杨攀 * @Since:2019年7月10日上午10:35:18 */@ConfigurationProperties(prefix = "topinfo.kafka")@Componentpublic class KafKaConfiguration { /** * @Fields bootstrapServer : 集群的地址 */ private String bootstrapServers; private Producer producer; private Consumer consumer; public String getBootstrapServers() { return bootstrapServers; } public void setBootstrapServers(String bootstrapServers) { this.bootstrapServers = bootstrapServers; } public Producer getProducer() { return producer; } public void setProducer(Producer producer) { this.producer = producer; } public Consumer getConsumer() { return consumer; } public void setConsumer(Consumer consumer) { this.consumer = consumer; }}添加kafka配置类kafka的配置类中, 主要注意的方法: ...

July 11, 2019 · 4 min · jiezi

KafKa集群部署手册

1 简介1.1 简介Kafka is a distributed,partitioned,replicated commit logservice。它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现。kafka对消息保存时根据Topic进行归类,发送消息者成为Producer,消息接受者成为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)成为broker。无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。 官网: http://kafka.apache.org/中文网: https://www.orchome.com/5 在其中我们知道如果要kafka正常运行,必须配置zookeeper,否则无论是kafka集群还是客户端的生存者和消费者都无法正常的工作的,使用需要先安装zookeeper集群: Zookeeper下载地址:http://zookeeper.apache.org/r... 1.2 参考资料http://kafka.apache.org/intro...https://www.cnblogs.com/likeh... 2 集群规划2.1 机器规划192.168.90.225 : zookeeper + kafka 192.168.90.226 : zookeeper + kafka 192.168.90.227 : zookeeper + kafka 2.2 目录规划mkdir /usr/local/tmp #临时目录,用于上传压缩包mkdir /topinfo # 安装目录3 zookeeper集群3.1 部署环境操作系统:CentOS7 +三台服务器:192.168.90.225/226/227 安装包: zookeeper-3.4.14.tar.gz 前置条件: jdk1.8+ 3.2 安装zookeeper3.2.1 上传zookeeper安装包在其中一台服务器上,上传安装包: cd /usr/local/tmp/ # 跳转到临时目录rz #通过rz 上传zookeeper-3.4.14.tar.gz3.2.2 安装1、解压 tar -zxvf zookeeper-3.4.14.tar.gz # 解压mv zookeeper-3.4.14 zookeeper #重命名mv zookeeper/ /topinfo/ # 将解压包拷贝到指定目录2、进入zookeeper的目录 ...

July 11, 2019 · 2 min · jiezi

大数据学习路线

一、大数据处理流程上图是一个简化的大数据处理流程图,大数据处理的主要流程包括数据收集、数据存储、数据处理、数据应用等主要环节。下面我们逐一对各个环节所需要的技术栈进行讲解: 1.1 数据收集大数据处理的第一步是数据的收集。现在的中大型项目通常采用微服务架构进行分布式部署,所以数据的采集需要在多台服务器上进行,且采集过程不能影响正常业务的开展。基于这种需求,就衍生了多种日志收集工具,如 Flume 、Logstash、Kibana 等,它们都能通过简单的配置完成复杂的数据收集和数据聚合。 1.2 数据存储收集到数据后,下一个问题就是:数据该如何进行存储?通常大家最为熟知是 MySQL、Oracle 等传统的关系型数据库,它们的优点是能够快速存储结构化的数据,并支持随机访问。但大数据的数据结构通常是半结构化(如日志数据)、甚至是非结构化的(如视频、音频数据),为了解决海量半结构化和非结构化数据的存储,衍生了 Hadoop HDFS 、KFS、GFS 等分布式文件系统,它们都能够支持结构化、半结构和非结构化数据的存储,并可以通过增加机器进行横向扩展。 分布式文件系统完美地解决了海量数据存储的问题,但是一个优秀的数据存储系统需要同时考虑数据存储和访问两方面的问题,比如你希望能够对数据进行随机访问,这是传统的关系型数据库所擅长的,但却不是分布式文件系统所擅长的,那么有没有一种存储方案能够同时兼具分布式文件系统和关系型数据库的优点,基于这种需求,就产生了 HBase、MongoDB。 1.3 数据分析大数据处理最重要的环节就是数据分析,数据分析通常分为两种:批处理和流处理。 批处理:对一段时间内海量的离线数据进行统一的处理,对应的处理框架有 Hadoop MapReduce、Spark、Flink 等;流处理:对运动中的数据进行处理,即在接收数据的同时就对其进行处理,对应的处理框架有 Storm、Spark Streaming、Flink Streaming等。批处理和流处理各有其适用的场景,时间不敏感或者硬件资源有限,可以采用批处理;时间敏感和及时性要求高就可以采用流处理。随着服务器硬件的价格越来越低和大家对及时性的要求越来越高,流处理越来越普遍,如股票价格预测和电商运营数据分析等。 上面的框架都是需要通过编程来进行数据分析,那么如果你不是一个后台工程师,是不是就不能进行数据的分析了?当然不是,大数据是一个非常完善的生态圈,有需求就有解决方案。为了能够让熟悉 SQL 的人员也能够进行数据的分析,查询分析框架应运而生,常用的有 Hive 、Spark SQL 、Flink SQL、 Pig、Phoenix 等。这些框架都能够使用标准的 SQL 或者 类SQL 语法灵活地进行数据的查询分析。这些 SQL 经过解析优化后转换为对应的作业程序来运行,如 Hive 本质上就是将 SQL 转换为 MapReduce 作业,Spark SQL 将 SQL 转换为一系列的 RDDs 和转换关系(transformations),Phoenix 将 SQL 查询转换为一个或多个HBase Scan。 1.4 数据应用数据分析完成后,接下来就是数据应用的范畴,这取决于你实际的业务需求。比如你可以将数据进行可视化展现,或者将数据用于优化你的推荐算法,这种运用现在很普遍,比如短视频个性化推荐、电商商品推荐、头条新闻推荐等。当然你也可以将数据用于训练你的机器学习模型,这些都属于其他领域的范畴,都有着对应的框架和技术栈进行处理,这里就不一一赘述。 1.5 其他框架上面是一个标准的大数据处理流程所用到的技术框架。但是实际的大数据处理流程比上面复杂很多,针对大数据处理中的各种复杂问题分别衍生了各类框架: 单机的处理能力都是存在瓶颈的,所以大数据框架都是采用集群模式进行部署,为了更方便的进行集群的部署、监控和管理,衍生了 Ambari、Cloudera Manager 等集群管理工具;想要保证集群高可用,需要用到 ZooKeeper ,ZooKeeper 是最常用的分布式协调服务,它能够解决大多数集群问题,包括首领选举、失败恢复、元数据存储及其一致性保证。同时针对集群资源管理的需求,又衍生了 Hadoop YARN ;复杂大数据处理的另外一个显著的问题是,如何调度多个复杂的并且彼此之间存在依赖关系的作业?基于这种需求,产生了 Azkaban 和 Oozie 等工作流调度框架;大数据流处理中使用的比较多的另外一个框架是 Kafka,它可以用于消峰,避免在秒杀等场景下并发数据对流处理程序造成冲击;另一个常用的框架是 Sqoop ,主要是解决了数据迁移的问题,它能够通过简单的命令将关系型数据库中的数据导入到 HDFS 、Hive 或 HBase 中,或者从 HDFS 、Hive 导出到关系型数据库上。二、学习路线介绍完大数据框架,接着就可以介绍其对应的学习路线了,主要分为以下几个方面: ...

July 4, 2019 · 2 min · jiezi

Kafka集群部署指南

一、前言1、Kafka简介Kafka是一个开源的分布式消息引擎/消息中间件,同时Kafka也是一个流处理平台。Kakfa支持以发布/订阅的方式在应用间传递消息,同时并基于消息功能添加了Kafka Connect、Kafka Streams以支持连接其他系统的数据(Elasticsearch、Hadoop等) Kafka最核心的最成熟的还是他的消息引擎,所以Kafka大部分应用场景还是用来作为消息队列削峰平谷。另外,Kafka也是目前性能最好的消息中间件。 2、Kafka架构 在Kafka集群(Cluster)中,一个Kafka节点就是一个Broker,消息由Topic来承载,可以存储在1个或多个Partition中。发布消息的应用为Producer、消费消息的应用为Consumer,多个Consumer可以促成Consumer Group共同消费一个Topic中的消息。 概念/对象简单说明BrokerKafka节点Topic主题,用来承载消息Partition分区,用于主题分片存储Producer生产者,向主题发布消息的应用Consumer消费者,从主题订阅消息的应用Consumer Group消费者组,由多个消费者组成3、准备工作1、Kafka服务器准备3台CentOS服务器,并配置好静态IP、主机名 服务器名IP说明kafka01192.168.88.51Kafka节点1kafka02192.168.88.52Kafka节点2kafka03192.168.88.53Kafka节点3软件版本说明 项说明Linux ServerCentOS 7Kafka2.3.02、ZooKeeper集群Kakfa集群需要依赖ZooKeeper存储Broker、Topic等信息,这里我们部署三台ZK 服务器名IP说明zk01192.168.88.21ZooKeeper节点zk02192.168.88.22ZooKeeper节点zk03192.168.88.23ZooKeeper节点部署过程参考:https://ken.io/note/zookeeper... 二、部署过程1、应用&数据目录#创建应用目录mkdir /usr/kafka#创建Kafka数据目录mkdir /kafkamkdir /kafka/logschmod 777 -R /kafka2、下载&解压Kafka官方下载地址:https://kafka.apache.org/down...这次我下载的是2.3.0版本 #创建并进入下载目录mkdir /home/downloadscd /home/downloads#下载安装包wget http://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.3.0/kafka_2.12-2.3.0.tgz #解压到应用目录tar -zvxf kafka_2.12-2.3.0.tgz -C /usr/kafkakafka_2.12-2.3.0.tgz 其中2.12是Scala编译器的版本,2.3.0才是Kafka的版本3、Kafka节点配置#进入应用目录cd /usr/kafka/kafka_2.12-2.3.0/#修改配置文件vi config/server.properties通用配置配置日志目录、指定ZooKeeper服务器 # A comma separated list of directories under which to store log fileslog.dirs=/kafka/logs# root directory for all kafka znodes.zookeeper.connect=192.168.88.21:2181,192.168.88.22:2181,192.168.88.23:2181分节点配置Kafka01broker.id=0#listeners=PLAINTEXT://:9092listeners=PLAINTEXT://192.168.88.51:9092Kafka02broker.id=1#listeners=PLAINTEXT://:9092listeners=PLAINTEXT://192.168.88.52:9092Kafka03broker.id=2#listeners=PLAINTEXT://:9092listeners=PLAINTEXT://192.168.88.53:90924、防火墙配置#开放端口firewall-cmd --add-port=9092/tcp --permanent#重新加载防火墙配置firewall-cmd --reload5、启动Kafka#进入kafka根目录cd /usr/kafka/kafka_2.12-2.3.0/#启动/bin/kafka-server-start.sh config/server.properties &#启动成功输出示例(最后几行)[2019-06-26 21:48:57,183] INFO Kafka commitId: fc1aaa116b661c8a (org.apache.kafka.common.utils.AppInfoParser)[2019-06-26 21:48:57,183] INFO Kafka startTimeMs: 1561531737175 (org.apache.kafka.common.utils.AppInfoParser)[2019-06-26 21:48:57,185] INFO [KafkaServer id=0] started (kafka.server.KafkaServer)三、Kafka测试1、创建Topic在kafka01(Broker)上创建测试Tpoic:test-ken-io,这里我们指定了3个副本、1个分区 ...

July 3, 2019 · 1 min · jiezi

kafka常用命令

./kafka-server-stop.sh直接执行,无需加任何参数,当前机器中的所有broker都会被停止。

June 22, 2019 · 1 min · jiezi

多维度对比5款主流分布式MQ消息队列妈妈再也不担心我的技术选型了

1、引言对于即时通讯系统(包括IM、消息推送系统等)来说,MQ消息中件间是非常常见的基础软件,但市面上种类众多、各有所长的MQ消息中件间产品,该怎么去选择?这是个问题! 对于很多经验不足的开发者来说,一个公司内部用的IM聊天系统,总用户量也不过百十来人,动辄就是Kafka、MongoDB,美其名曰为了高性能和可扩展性,真是大炮打蚊子。而对于中大型的即时通讯场景来说,有的开发者确为了贪图使用简单、资料全面,反而使用臃肿不堪的ActiveMQ,这就有点失去章法了。 唧唧歪歪这么多,那什么样的场景到底该用哪种MQ消息中件间产品合适?读完本文您或许就有了答案。 本文将从17个维度综合对比Kafka、RabbitMQ、ZeroMQ、RocketMQ、ActiveMQ这5款当前最主流的MQ消息中间件产品,希望能为您的下一次产品的架构设计和MQ消息中间件选型提供参考依据。 学习交流: 即时通讯/推送技术开发交流4群:101279154[推荐]移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》(本文同步发布于:http://www.52im.net/thread-26...) 2、相关资料官网地址: Kafka:http://kafka.apache.org/ RabbitMQ:https://www.rabbitmq.com/ ZeroMQ:http://zeromq.org/ RocketMQ:http://rocketmq.apache.org/ ActiveMQ:http://activemq.apache.org/ 相关文章: 《IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列》 《IM系统的MQ消息中间件选型:Kafka还是RabbitMQ?》 3、维度1:资料文档1)Kafka:资料数量中等。有Kafka作者自己写的书,网上资料也有一些。 2)RabbitMQ:资料数量多。有一些不错的书,网上资料多。 3)ZeroMQ:资料数量少。专门写ZeroMQ的书较少,网上的资料多是一些代码的实现和简单介绍。 4)RocketMQ:资料数量少。专门写RocketMQ的书目前有了两本;网上的资料良莠不齐,官方文档很简洁,但是对技术细节没有过多的描述。 5)ActiveMQ:资料数量多。没有专门写ActiveMQ的书,网上资料多。 4、维度2:开发语言1)Kafka:Scala 2)RabbitMQ:Erlang 3)ZeroMQ:C语言 4)RocketMQ:Java 5)ActiveMQ:Java 5、维度3:支持的协议1)Kafka:自己定义的一套…(基于TCP) 2)RabbitMQ:AMQP 3)ZeroMQ:TCP、UDP 4)RocketMQ:自己定义的一套… 5)ActiveMQ:OpenWire、STOMP、REST、XMPP、AMQP 6、维度4:消息存储1)Kafka: 内存、磁盘、数据库。支持大量堆积。 Kafka的最小存储单元是分区,一个topic包含多个分区,Kafka创建主题时,这些分区会被分配在多个服务器上,通常一个broker一台服务器。 分区首领会均匀地分布在不同的服务器上,分区副本也会均匀的分布在不同的服务器上,确保负载均衡和高可用性,当新的broker加入集群的时候,部分副本会被移动到新的broker上。 根据配置文件中的目录清单,Kafka会把新的分区分配给目录清单里分区数最少的目录。 默认情况下,分区器使用轮询算法把消息均衡地分布在同一个主题的不同分区中,对于发送时指定了key的情况,会根据key的hashcode取模后的值存到对应的分区中。 2)RabbitMQ: 内存、磁盘。支持少量堆积。 RabbitMQ的消息分为持久化的消息和非持久化消息,不管是持久化的消息还是非持久化的消息都可以写入到磁盘。 持久化的消息在到达队列时就写入到磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,这样可以提高一定的性能,当内存吃紧的时候会从内存中清除。 非持久化的消息一般只存在于内存中,在内存吃紧的时候会被换入到磁盘中,以节省内存。 引入镜像队列机制,可将重要队列“复制”到集群中的其他broker上,保证这些队列的消息不会丢失。 配置镜像的队列,都包含一个主节点master和多个从节点slave,如果master失效,加入时间最长的slave会被提升为新的master,除发送消息外的所有动作都向master发送,然后由master将命令执行结果广播给各个slave,RabbitMQ会让master均匀地分布在不同的服务器上,而同一个队列的slave也会均匀地分布在不同的服务器上,保证负载均衡和高可用性。 3)ZeroMQ: 消息发送端的内存或者磁盘中。不支持持久化。 4)RocketMQ: 磁盘。支持大量堆积。 commitLog文件存放实际的消息数据,每个commitLog上限是1G,满了之后会自动新建一个commitLog文件保存数据。 ConsumeQueue队列只存放offset、size、tagcode,非常小,分布在多个broker上。 ConsumeQueue相当于CommitLog的索引文件,消费者消费时会从consumeQueue中查找消息在commitLog中的offset,再去commitLog中查找元数据。 ConsumeQueue存储格式的特性,保证了写过程的顺序写盘(写CommitLog文件),大量数据IO都在顺序写同一个commitLog,满1G了再写新的。 加上RocketMQ是累计4K才强制从PageCache中刷到磁盘(缓存),所以高并发写性能突出。 5)ActiveMQ: 内存、磁盘、数据库。支持少量堆积。 7、维度5:消息事务1)Kafka:支持 2)RabbitMQ:支持。客户端将信道设置为事务模式,只有当消息被RabbitMQ接收,事务才能提交成功,否则在捕获异常后进行回滚。使用事务会使得性能有所下降 3)ZeroMQ:不支持 4)RocketMQ:支持 5)ActiveMQ:支持 8、维度6:负载均衡8.1 Kafka支持负载均衡。 1)一个broker通常就是一台服务器节点。 对于同一个Topic的不同分区,Kafka会尽力将这些分区分布到不同的Broker服务器上,zookeeper保存了broker、主题和分区的元数据信息。 分区首领会处理来自客户端的生产请求,Kafka分区首领会被分配到不同的broker服务器上,让不同的broker服务器共同分担任务。 每一个broker都缓存了元数据信息,客户端可以从任意一个broker获取元数据信息并缓存起来,根据元数据信息知道要往哪里发送请求。 2)Kafka的消费者组订阅同一个topic,会尽可能地使得每一个消费者分配到相同数量的分区,分摊负载。 ...

June 21, 2019 · 3 min · jiezi

大数据系列kafka学习笔记

1. 大数据领域数据类型1.1 有界数据 一般批处理(一个文件 或者一批文件),不管文件多大,都是可以度量 mapreduce hive sparkcore sparksql 1.2 无界数据 源源不断的流水一样 (流数据) Storm SparkStreaming 2. 消息队列(Message Queue)消息 Message 网络中的两台计算机或者两个通讯设备之间传递的数据,例如说:文本、音乐、视频等内容队列 Queue 一种特殊的线性表(数据元素首尾相接),特殊之处在于只允许在首部移除元素和在尾部追加元素。入队、出队。消息队列 MQ 消息+队列保存消息的队列消息的传输过程中的容器主要提供生产、消费接口供外部调用做数据的存储和获取3. 消息队列的分类3.1 点对点(P2P)一个生产者生产的消息只能被一个消费者消费3.2 发布订阅(Pub/Sub)消息队列(Queue)、主题(Topic)、发布者(Publisher)、订阅者(Subscriber) 消息的发布者消息的订阅者每个消息可以有多个消费者,彼此互不影响。比如我发布一个微博:关注我的人都能够看到。 4. Kafka的简介在大数据领域呢,为了满足日益增长的数据量,也有一款可以满足百万级别消息的生成和消费,分布式、持久稳定的产品——KafkaKafka是分布式的发布—订阅消息系统(基于PS的一个消息队列)它最初由LinkedIn(领英)公司发布,使用Scala语言编写 Kafka是一个高吞吐量的、持久性的、分布式发布订阅消息系统它主要用于处理活跃的数据(登录、浏览、点击、分享、喜欢等用户行为产生的数据5. Kafka的特点高吞吐量 可以满足每秒百万级别消息的生产和消费(生产消费 )持久性 有一套完善的消息存储机制,确保数据的高效安全的持久化 (数据的存储)分布式 基于分布式的扩展和容错机制;Kafka的数据都会复制到几台服务器上。当某一台故障失效时,生产者和消费者转而使用其它的机器——整体健壮性6. Kafka的组件一个消息队列需要哪些部分? 生产消费消息类别存储等等Topic(主题) Kafka处理的消息的不同分类Broker (消息代理) Kafka集群中的一个kafka服务节点称为一个broker,主要存储消息数据,存在硬盘中。每个topic都是有分区的Partition (物理上的分区) 一个topic在broker中被分为1个或者多个partition,分区在创建topic的时候指定Message (消息) 消息,是通信的基本单位,每个消息都属于一个partition7. Kafka的服务Producer : 消息和数据的生产者,向Kafka的一个topic发布消息Consumer :消息和数据的消费者,定于topic并处理其发布的消息Zookeeper :协调kafka的正常运行8. Kafka的安装8.1 单机版的安装准备kafka kafka_2.10-0.10.0.1.tgz解压kafka tar -zxvf kafka_2.10-0.10.0.1.tgz -C /opt/重命名 mv kafka_2.10-0.10.0.1.tgz kafka配置环境变量 export KAFKA_HOME=/opt/kafkaexport PATH=$PATH:$KAFKA_HOME/bin编辑server.properties ...

June 14, 2019 · 2 min · jiezi

kafka

该博客迁移到github:https://github.com/dackh/blog基础架构及术语 主题和分区topic是发布记录的类别或订阅源名称。 Kafka的topic总是多用户; 也就是说,一个主题可以有零个,一个或多个消费者订阅写入它的数据。 对于每个主题,Kafka群集都维护一个如下所示的分区日志: 每个分区都是一个有序的,不可变的记录序列,不断附加到结构化的提交日志中。 分区中的记录每个都分配了一个称为offset的顺序ID号,它唯一地标识分区中的每个记录。 Kafka集群持久保存所有已发布的记录 - 无论是否已使用 - 使用可配置的保留期。 例如,如果保留策略设置为两天,则在发布记录后的两天内,它可供使用,之后将被丢弃以释放空间。 Kafka的性能在数据大小方面实际上是恒定的,因此长时间存储数据不是问题。 实际上,基于每个消费者保留的唯一元数据是该消费者在日志中的偏移或位置。 这种偏移由消费者控制:通常消费者在读取记录时会线性地提高其偏移量,但事实上,由于该位置由消费者控制,因此它可以按照自己喜欢的任何顺序消费记录。 例如,消费者可以重置为较旧的偏移量来重新处理过去的数据,或者跳到最近的记录并从“现在”开始消费。(offset由comsumer决定,comsumer可以决定offset的数据)。 生产者和消费者生产者将数据发布到他们选择的topic。 生产者负责选择分配给topic中哪个partition的记录。 这可以以round-robin方式完成,仅仅是为了balance load,或者可以根据一些语义分区功能(例如基于记录中的某些键)来完成。 消费者使用消费者组名称标记自己,并且发布到主题的每个记录被传递到每个订阅消费者组中的一个消费者实例。 消费者实例可以在不同的进程中,也可以在不同的机器。 如果所有使用者实例具有相同的使用者组,则记录将有效地在使用者实例上进行负载平衡。 如果所有消费者实例具有不同的消费者组,则每个记录将广播到所有消费者进程。 broker和集群一个独立的Kafka服务器被称为broker,broker是集群的组成部分,每个集群都有一个broker充当了集群控制者的角色(自动从集群活跃成员中选举出来),控制者负责管理工作,包括将分区分配给broker和监控broker。 容错日志的分区分布在Kafka集群中的服务器上,每个服务器处理数据并请求分区的共享。 每个分区都在可配置数量的服务器上进行复制,以实现容错。(多个服务器保存partition数据,实现容错) 每个分区都有一个服务器充当“领导者”,零个或多个服务器充当“追随者”。 领导者处理分区的所有读取和写入请求,而关注者被动地复制领导者。 如果领导者失败,其中一个粉丝将自动成为新的领导者。 每个服务器都充当其某些分区的领导者和其他服务器的追随者,因此负载在群集中得到很好的平衡。(高性能实现) 再均衡在kafka中,当消费者发生崩溃或者有新的消费者加入时,将会触发再均衡。 消费者会像叫做_consumer_offset的特殊主题发送消息,消息内包含灭个分区的偏移量。 再均衡之后,消费者可能分配到新的分区,为了能够继续之前的工作,消费者需要读取每个分区最后一次提交的offset,但如果提交的offset小于客户端处理的最后一个offset,那么消息将会被重复处理。 KafkaConsumer API提供了很多种方式来提交偏移量。 自动提交最简单的提交方式,将enable.auto.commit设为true,提交时间为auto.commit.interval.ms设置的值,默认为5s。每过5s,消费者会自动把从poll()方法接收的最大offset提交上去。 这种方式虽然简单,但是并没有避免重复处理消息的问题。(在5s内发生再均衡) 提交当前offset消费者API提供了另一种提交偏移量的方式,开发者可以在必要的时候提交当前的偏移量而不是基于时间间隔。通过调用commitSync()或者commitAsync()方法进行提交。 commitSync()在broker回应之前一直阻塞,限制了应用程序的吞吐量。commitAsync()是异步提交的方式,但是commitAsync()无法保证一定成功,commitSync()在成功提交或者遇到无法恢复的错误之前会一直重试,但是commitAsync()不会。之所以不进行重试是因为它收到响应之前可能另一个更大的offset提交成功了。commitAsync同时也支持回调。集群成员之间的关系kafka通过zookeeper来维护集群成员之间的关系,每个broker都有一个唯一标识符,这个标识符可以配置里指定,也可以自动生成。在broker启动的时候,通过创建zookeeper的临时节点把自己的ID注册到zookeeper。kafka组件订阅zookeeper的/brokers/ids路径,当有broker加入集群或者退出集群时,这个组件就会获得通知。 在broker停机、出现网络分区或长时间垃圾回收停顿时,broker会从zookeeper上断开连接,此时broker在启动时创建的临时节点会自动从zookeeper移除,监听broker列表的kafka组件会被告知该broker已移除。 在broker关闭之后,它的节点会消息,但是ID会继续存在于其他数据结构中,在之后如果使用相同ID启动一个全新的broker,它会立刻加入集群并且拥有与旧broker相同的分区和主题。 控制器控制器也是broker,同时还负责分区首领的选举。集群中第一个启动的broker通过在zookeeper里创建一个临时节点/controller让自己成为控制器。其他节点在该节点上创建watch对象,通过这种方式确保集群里只有一个控制器存在。 如果控制器断开连接,其他监听的节点收到变更通知之后将会尝试创建controller节点成为控制器,只有第一个创建成功的才能成为控制器。 分区复制kafka中的每个分区都有多个副本,这些副本保存在broker中,副本可以分为: 首领副本(leader):所有生产者消费者请求都会经过这个副本。跟随者副本(follower):从首领复制消息,保持与首领一致。leader通过查看每个follower请求的最新offset了解follower进度,如果follower在10s(通过replica.lag.time.max.ms配值)内没有请求最新消息,那么它被认为不同步。只有同步的follower才能在当前leader宕机时被选定为新的leader。 每个分区都会有一个首选leader——创建主题时选定的首领就是分区的首选首领,默认情况下,kafka的auto.leader.rebalance.enable被设为true,它会检查首选首领是不是当前首领,如果不是,并且该副本同步,那么就会触发首领选举,让首选首领成为当前首领。 幂等性为了实现Producer的幂等性,kafka引入了Producer ID(即PID)和Sequence Number PID每个producer在初始化的时候都会被分配一个唯一的PID,这个PID对应用透明,完全没有暴露给用户,对于一个给定的PID,sequence number将会从0开始自增,每个topic-partition都会有一个独立的sequence number,producer在发送数据时,将会给msg标识一个sequence number,Server也就是通过这个验证数据是否重复,这里的PID是全局唯一,producer故障后重新启动后会被分配一个新的PID,这也是幂等性无法做到夸会话的一个原因。 Sequence Number有PID之后,在PID+topic-partition级别上添加一个sequence numbers信息,就可以实现producer的幂等性。 幂等性前后对比前 后 持久化kafka很大程度上依赖文件系统来存储和缓存消息,有一普遍的认识:磁盘很慢。但是其实顺序的磁盘读写比任意内存读写都快。 基于JVM内存有一下缺点: 对象的内存开销很大,通常会让存储数据的大小加倍随着堆内数据的增加,GC的速度越来越慢,而且可能导致错误基于OS的文件系统设计有一下好处: 可以通过os的pagecache来有效利用主内存空间,由于数据紧凑,可以cache大量数据,并且没有gc的压力即使服务重启,缓存中的数据也是热的(不需要预热)。而基于进程的缓存,需要程序进行预热,而且会消耗很长的时间。(10G大概需要10分钟)大大简化了代码。因为在缓存和文件系统之间保持一致性的所有逻辑都在OS中。以上建议和设计使得代码实现起来十分简单,不需要尽力想办法去维护内存中的数据,数据会立即写入磁盘。数据持久化发现线性的访问磁盘(即:按顺序的访问磁盘),很多时候比随机的内存访问快得多,而且有利于持久化传统的使用内存做为磁盘的缓存Kafka直接将数据写入到日志文件中,以追加的形式写入日志数据持久化写操作:通过将数据追加到文件中实现读操作:读的时候从文件中读就好了优势读操作不会阻塞写操作和其他操作(因为读和写都是追加的形式,都是顺序的,不会乱,所以不会发生阻塞),数据大小不对性能产生影响;没有容量限制(相对于内存来说)的硬盘空间建立消息系统;线性访问磁盘,速度快,可以保存任意一段时间!kafka为什么这么快为什么kafka那么快什么是Zero-Copy?Kafka副本同步机制理解Kafka深度解析参考kafka权威指南http://kafka.apache.org/introKafka的内部机制深入(持久化,分布式,通讯协议)

June 13, 2019 · 1 min · jiezi

开源一个kafka增强okmq100

本工具的核心思想就是:赌。只有两个基础组件同时死亡,才会受到严重影响。哦,断电除外。mq是个好东西,我们都在用。这也决定了mq应该是高高高可用的。某团就因为这个组件,出了好几次生产事故,呵呵。 大部分业务系统,要求的消息语义都是at least once,即都会有重复消息,但保证不会丢。即使这样,依然有很多问题: 一、mq可用性无法保证。 mq的意外死亡,造成生产端发送失败。很多消息要通过扒取日志进行回放,成本高耗时长。 二、mq阻塞业务正常进行。 mq卡顿或者网络问题,会造成业务线程卡在mq的发送方法上,正常业务进行不下去,造成灾难性的后果。 三、消息延迟。 mq死了就用不着说了,消息还没投胎就已死亡。消息延迟主要是客户端消费能力不强,或者是消费通道单一造成的。 使用组合存储来保证消息的可靠投递,就是okmq。 注意:okmq注重的是可靠性。对于顺序性、事务等其他要素,不予考虑。当然,速度是必须的。设计想法我即使用两套redis来模拟一些mq操作,都会比现有的一些解决方案要强。但这肯定不是我们需要的,因为redis的堆积能力太有限,内存占用率直线上升的感觉并不太好。 但我们可以用redis来作为额外的发送确认机制。这个想法,在《使用多线程增加kafka消费能力》一文中曾经提到过,现在到了实现的时候了。 首先看下使用ApiOkmqKafkaProducer producer = new ProducerBuilder().defaultSerializer().eanbleHa("redis").any("okmq.redis.mode", "single").any("okmq.redis.endpoint", "127.0.0.1:6379").any("okmq.redis.poolConfig.maxTotal", 100).servers("localhost:9092").clientID("okMQProducerTest").build();Packet packet = new Packet();packet.setTopic("okmq-test-topic");packet.setContent("i will send you a msg");producer.sendAsync(packet, null);producer.shutdown();以redis为例我们按照数字标号来介绍: 1、 在消息发送到kafka之前,首先入库redis。由于后续回调需要用到一个唯一表示,我们在packet包里添加了一个uuid。 2、 调用底层的api,进行真正的消息投递。 3、 通过监听kafka的回调,删除redis中对应的key。在这里可以得到某条消息确切的的ack时间。那么长时间没有删除的,就算是投递失败的消息。 4、 后台会有一个线程进行这些失败消息的遍历和重新投递。我们叫做recovery。最复杂的也就是这一部分。对于redis来说,会首先争抢一个持续5min的锁,然后遍历相关hashkey。 所以,对于以上代码,redis发出以下命令: 1559206423.395597 [0 127.0.0.1:62858] "HEXISTS" "okmq:indexhash" "okmq:5197354"1559206423.396670 [0 127.0.0.1:62858] "HSET" "okmq:indexhash" "okmq:5197354" ""1559206423.397300 [0 127.0.0.1:62858] "HSET" "okmq:5197354" "okmq::2b9b33fd-95fd-4cd6-8815-4c572f13f76e" "{\"content\":\"i will send you a msg104736623015238\",\"topic\":\"okmq-test-topic\",\"identify\":\"2b9b33fd-95fd-4cd6-8815-4c572f13f76e\",\"timestamp\":1559206423318}"1559206423.676212 [0 127.0.0.1:62858] "HDEL" "okmq:5197354" "okmq::2b9b33fd-95fd-4cd6-8815-4c572f13f76e"1559206428.327788 [0 127.0.0.1:62861] "SET" "okmq:recovery:lock" "01fb85a9-0670-40c3-8386-b2b7178d4faf" "px" "300000"1559206428.337930 [0 127.0.0.1:62858] "HGETALL" "okmq:indexhash"1559206428.341365 [0 127.0.0.1:62858] "HSCAN" "okmq:5197354" "0"1559206428.342446 [0 127.0.0.1:62858] "HDEL" "okmq:indexhash" "okmq:5197354"1559206428.342788 [0 127.0.0.1:62861] "GET" "okmq:recovery:lock"1559206428.343119 [0 127.0.0.1:62861] "DEL" "okmq:recovery:lock"以上问题解答所以对于以上的三个问题,回答如下:一、mq可用性无法保证。 ...

June 5, 2019 · 1 min · jiezi

极客时间Kafka核心技术与实战返现-送学习笔记

关注有课学微信公众号,回复暗号 kafka 获取购买《Kafka核心技术与实战》极客时间专栏地址,购买成功后提交购买截图即可获得返现,另外送 《Kafka核心技术与实战》专栏学习笔记,待课程更新完成送统一通过微信公众号发放。

June 3, 2019 · 1 min · jiezi

python3连接kafka模块pykafka生产者简单封装

1.1安装模块pip install pykafka1.2基本使用 # -* coding:utf8 *- from pykafka import KafkaClient host = 'IP:9092, IP:9092, IP:9092' client = KafkaClient(hosts = host) # 生产者 topicdocu = client.topics['my-topic'] producer = topicdocu.get_producer() for i in range(100): print i producer.produce('test message ' + str(i ** 2)) producer.stop()1.3简单封装 class KafkaProduct(): def __init__(self,hosts,topic): """ 初始化实例 :param hosts: 连接地址 :param topic: """ self.__client = KafkaClient(hosts=hosts) self.__topic = self.__client.topics[topic.encode()] def __set_topic(self, topic): self.__topic = self.__client.topics[topic.encode()] def set_topic(self, topic): """ 设置topic :param topic: :return: """ self.__set_topic(topic) def get_topics(self): """ 获取当前所有topic :return: """ return self.__client.topics def get_topic(self): """ 获取当前topic :return: """ return self.__topic def Producer(self): """ 生产者对象 :return: """ with self.__topic.get_producer(delivery_reports=True) as producer: next_data = '' while True: if next_data: producer.produce(str(next_data).encode()) next_data = yield True def send_data(self,datas): """ 发送数据 :param datas:需要传入的可迭代对象 :return: """ c = self.Producer() next(c) for i in datas: c.send(i)if __name__ == '__main__': hosts = "1.2.3.4:9999,2.3.4.5:9090" #连接hosts topic = "test_523" K = KafkaProduct(hosts=hosts, topic=topic) # #K.set_topic("test") #切换设置新的topic K.get_topic() #获取当前设置的topic #K.get_topics() #获取所有topic data = range(10000) #要发送的可迭代对象 K.send_data(data)1.4引用来源博客园:Python测试Kafka集群(pykafka) ...

May 28, 2019 · 1 min · jiezi

Kafka压缩详解初稿

Kafka压缩概括需要理解kafka压缩则需要理解Kafka的存储格式. Kafka存储格式RecordBatch baseOffset: int64batchLength: int32partitionLeaderEpoch: int32magic: int8 (current magic value is 2)crc: int32attributes: int16 bit 0~2: 0: no compression 1: gzip 2: snappy 3: lz4 4: zstd bit 3: timestampType bit 4: isTransactional (0 means not transactional) bit 5: isControlBatch (0 means not a control batch) bit 6~15: unusedlastOffsetDelta: int32firstTimestamp: int64maxTimestamp: int64producerId: int64producerEpoch: int16baseSequence: int32records: [Record]Record length: varintattributes: int8 bit 0~7: unusedtimestampDelta: varintoffsetDelta: varintkeyLength: varintkey: byte[]valueLen: varintvalue: byte[]Headers => [Header]Record Header ...

May 21, 2019 · 2 min · jiezi

Kafka凭什么速度那么快

本文来源 | www.cnblogs.com/binyue/p/10308754.html 作者 | 邴越 Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafka的特性之一就是高吞吐率。 即使是普通的服务器,Kafka也可以轻松支持每秒百万级的写入请求,超过了大部分的消息中间件,这种特性也使得Kafka在日志处理等海量数据场景广泛应用。 针对Kafka的基准测试可以参考,Apache Kafka基准测试:每秒写入2百万(在三台廉价机器上)。下面从数据写入和读取两方面分析,为什么Kafka速度这么快。 01写入数据 Kafka会把收到的消息都写入到硬盘中,它绝对不会丢失数据。为了优化写入速度Kafka采用了两个技术, 顺序写入和MMFile 。 1、顺序写入 磁盘读写的快慢取决于你怎么使用它,也就是顺序读写或者随机读写。在顺序读写的情况下,磁盘的顺序读写速度和内存持平。 因为硬盘是机械结构,每次读写都会寻址->写入,其中寻址是一个“机械动作”,它是最耗时的。所以硬盘最讨厌随机I/O,最喜欢顺序I/O。为了提高读写硬盘的速度,Kafka就是使用顺序I/O。 而且Linux对于磁盘的读写优化也比较多,包括read-ahead和write-behind,磁盘缓存等。如果在内存做这些操作的时候,一个是JAVA对象的内存开销很大,另一个是随着堆内存数据的增多,JAVA的GC时间会变得很长,使用磁盘操作有以下几个好处: 磁盘顺序读写速度超过内存随机读写 JVM的GC效率低,内存占用大。使用磁盘可以避免这一问题 系统冷启动后,磁盘缓存依然可用。 下图就展示了Kafka是如何写入数据的, 每一个Partition其实都是一个文件 ,收到消息后Kafka会把数据插入到文件末尾(虚框部分): 这种方法有一个缺陷——没有办法删除数据 ,所以Kafka是不会删除数据的,它会把所有的数据都保留下来,每个消费者(Consumer)对每个Topic都有一个offset用来表示读取到了第几条数据 。 两个消费者: Consumer1有两个offset分别对应Partition0、Partition1(假设每一个Topic一个Partition); Consumer2有一个offset对应Partition2。 这个offset是由客户端SDK负责保存的,Kafka的Broker完全无视这个东西的存在;一般情况下SDK会把它保存到Zookeeper里面,所以需要给Consumer提供zookeeper的地址。 如果不删除硬盘肯定会被撑满,所以Kakfa提供了两种策略来删除数据: 一是基于时间; 二是基于partition文件大小。 具体配置可以参看它的配置文档。 2、Memory Mapped Files 即便是顺序写入硬盘,硬盘的访问速度还是不可能追上内存。所以Kafka的数据并不是实时的写入硬盘 ,它充分利用了现代操作系统分页存储来利用内存提高I/O效率。 Memory Mapped Files(后面简称mmap)也被翻译成 内存映射文件 ,在64位操作系统中一般可以表示20G的数据文件,它的工作原理是直接利用操作系统的Page来实现文件到物理内存的直接映射。 完成映射之后你对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。 通过mmap,进程像读写硬盘一样读写内存(当然是虚拟机内存),也不必关心内存的大小有虚拟内存为我们兜底。 使用这种方式可以获取很大的I/O提升,省去了用户空间到内核空间复制的开销(调用文件的read会把数据先放到内核空间的内存中,然后再复制到用户空间的内存中。) 但也有一个很明显的缺陷——不可靠,写到mmap中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用flush的时候才把数据真正的写到硬盘。 Kafka提供了一个参数——producer.type来控制是不是主动flush,如果Kafka写入到mmap之后就立即flush然后再返回Producer叫 同步 (sync);写入mmap之后立即返回Producer不调用flush叫异步 (async)。 02读取数据 Kafka在读取磁盘时做了哪些优化? 2、基于sendfile实现Zero Copy 传统模式下,当需要对一个文件进行传输的时候,其具体流程细节如下: 调用read函数,文件数据被copy到内核缓冲区 read函数返回,文件数据从内核缓冲区copy到用户缓冲区 write函数调用,将文件数据从用户缓冲区copy到内核与socket相关的缓冲区 数据从socket缓冲区copy到相关协议引擎 以上细节是传统read/write方式进行网络文件传输的方式,我们可以看到,在这个过程当中,文件数据实际上是经过了四次copy操作: 硬盘—>内核buf—>用户buf—>socket相关缓冲区—>协议引擎 而sendfile系统调用则提供了一种减少以上多次copy,提升文件传输性能的方法。 在内核版本2.1中,引入了sendfile系统调用,以简化网络上和两个本地文件之间的数据传输。sendfile的引入不仅减少了数据复制,还减少了上下文切换。 sendfile(socket, file, len); ...

May 21, 2019 · 1 min · jiezi

Kafka两级调度实现分布式协调微服务任务分配Golang版

背景基于Kafka消息队列的两级协调调度架构 Kafka内部为了协调内部的consumer和kafka connector的工作实现了一个复制协议, 主要工作分为两个步骤: 通过worker(consumer或connect)获取自身的topic offset等元数据信息,交给kafka的broker完成Leader/Follower选举worker Leader节点获取到kafka存储的partation和member信息,来进行二级分配,实现结合具体业务的负载均衡分配从功能实现上两级调度,一级调度负责将Leader选举,二级调度则是worker节点完成每个成员的任务的分配 主要是学习这种架构设计思想,虽然这种方案场景非常有限 基于消息队列实现分布式协调设计 一级协调器设计:一级协调器主要是指的Coordinator部分,通过记录成员的元数据信息,来进行Leader选举,比如根据offset的大小来决定谁是Leader二级协调器设计:二级协调器主要是指的Leader任务分配部分, worker节点获取到所有的任务和节点信息,就可以根据合适的算法来进行任务的分配,最终广播到消息队列 值得我们学习的地方, 通常在kafka这种场景下,如果要针对不同的业务实现统一调度,还是蛮麻烦的, 所以比如将具体任务的分配工作从架构中迁移出去, 在broker端只负责通用层的Leader选举即可, 将具体业务的分配工作,从主业务架构分离出去,由具体业务去实现 代码实现核心设计 根据设计,我们抽象出: MemoryQueue、Worker、 Coordinator、GroupRequest、GroupResponse、Task、Assignment集合核心组件 MemoryQueue: 模拟消息队列实现消息的分发,充当kafka broker角色Worker: 任务执行和具体业务二级协调算法Coordinator: 位于消息队列内部的一个协调器,用于Leader/Follower选举 Task: 任务Assignment: Coordnator根据任务信息和节点信息构建的任务分配结果GroupRequest: 加入集群请求GroupResponse: 响应信息 MemoryQueue核心数据结构// MemoryQueue 内存消息队列type MemoryQueue struct { done chan struct{} queue chan interface{} wg sync.WaitGroup coordinator map[string]*Coordinator worker map[string]*Worker}其中coordinator用于标识每个Group组的协调器,为每个组都建立一个分配器 节点加入集群请求处理 MemoryQueue 接收事件类型,然后根据事件类型进行分发,如果是GroupRequest事件,则分发给handleGroupRequest进行处理handleGroupRequest内部先获取对应group的coordinator,然后根据当前信息buildGroupResponse发回消息队列 事件分发处理func (mq *MemoryQueue) handleEvent(event interface{}) { switch event.(type) { case GroupRequest: request := event.(GroupRequest) mq.handleGroupRequest(&request) case Task: task := event.(Task) mq.handleTask(&task) default: mq.Notify(event) } mq.wg.Done()}加入Group组请求处理 ...

May 21, 2019 · 3 min · jiezi

Kafka消息过长详解

Kafka发送消息大小问题⚠️ 本文实验的Kafka版本为2.11版本. 消息概述kafka中的消息指的就是一条ProducerRecord,里面除了携带发送的数据之外,还包含: topic 发往的Topicpartition 发往的分区headers 头信息key 数据value 数据timestamp-long 时间戳Producer生产消息过长在生产者发送消息的时候,并不是上面所有的信息都算在发送的消息大小.详情见下面代码. 上面的代码会将value序列化成字节数组,参与序列化的有topic,headers,key. 用来验证value是否超出长度的是ensureValidRecordSize(serializedSize);方法. ensureValidRecordSize从两个方面验证,一个是maxRequestSize(max.request.size),另一个是totalMemorySize(buffer.memory), 只有当value的长度同时小于时,消息才可以正常发送. private void ensureValidRecordSize(int size) { if (size > this.maxRequestSize) throw new RecordTooLargeException("The message is " + size + " bytes when serialized which is larger than the maximum request size you have configured with the " + ProducerConfig.MAX_REQUEST_SIZE_CONFIG + " configuration."); if (size > this.totalMemorySize) throw new RecordTooLargeException("The message is " + size + " bytes when serialized which is larger than the total memory buffer you have configured with the " + ProducerConfig.BUFFER_MEMORY_CONFIG + " configuration.");}单条消息过长或产生如下错误. ...

May 19, 2019 · 2 min · jiezi

kafka-入门详解

KafkaKafka 核心概念什么是 KafkaKafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。该项目的目标是为处理实时数据提供一个统一、高吞吐、低延迟的平台。其持久化层本质上是一个“按照分布式事务日志架构的大规模发布/订阅消息队列”,这使它作为企业级基础设施来处理流式数据非常有价值。此外,Kafka可以通过Kafka Connect连接到外部系统(用于数据输入/输出),并提供了Kafka Streams——一个Java流式处理库。该设计受事务日志的影响较大。 基本概念Kafka是一个分布式数据流平台,可以运行在单台服务器上,也可以在多台服务器上部署形成集群。它提供了发布和订阅功能,使用者可以发送数据到Kafka中,也可以从Kafka中读取数据(以便进行后续的处理)。Kafka具有高吞吐、低延迟、高容错等特点。下面介绍一下Kafka中常用的基本概念: Broker消息队列中常用的概念,在Kafka中指部署了Kafka实例的服务器节点。 Topic用来区分不同类型信息的主题。比如应用程序A订阅了主题t1,应用程序B订阅了主题t2而没有订阅t1,那么发送到主题t1中的数据将只能被应用程序A读到,而不会被应用程序B读到。 Partition每个topic可以有一个或多个partition(分区)。分区是在物理层面上的,不同的分区对应着不同的数据文件。Kafka使用分区支持物理上的并发写入和读取,从而大大提高了吞吐量。 Record实际写入Kafka中并可以被读取的消息记录。每个record包含了key、value和timestamp。 Producer生产者,用来向Kafka中发送数据(record)。 Consumer消费者,用来读取Kafka中的数据(record)。 Consumer Group一个消费者组可以包含一个或多个消费者。使用多分区+多消费者方式可以极大提高数据下游的处理速度。 kafka 核心名词解释Topic(主题): 每一条发送到kafka集群的消息都可以有一个类别,这个类别叫做topic,不同的消息会进行分开存储,如果topic很大,可以分布到多个broker上,也可以这样理解:topic被认为是一个队列,每一条消息都必须指定它的topic,可以说我们需要明确把消息放入哪一个队列。对于传统的message queue而言,一般会删除已经被消费的消息,而Kafka集群会保留所有的消息,无论其被消费与否。当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此Kafka提供两种策略删除旧数据。一是基于时间,二是基于Partition文件大小。Broker(代理): 一台kafka服务器就可以称之为broker.一个集群由多个broker组成,一个broker可以有多个topicPartition(分区): 为了使得kafka吞吐量线性提高,物理上把topic分成一个或者多个分区,每一个分区是一个有序的队列。且每一个分区在物理上都对应着一个文件夹,该文件夹下存储这个分区所有消息和索引文件。分区的表示: topic名字-分区的id每个日志文件都是一个Log Entry序列,每个Log Entry包含一个4字节整型数值(值为M+5),1个字节的"magic value",4个字节的CRC校验码,然后跟M个字节的消息这个log entries并非由一个文件构成,而是分成多个segment,每个segment以该segment第一条消息的offset命名并以“.kafka”为后缀。另外会有一个索引文件,它标明了每个segment下包含的log entry的offset范围分区中每条消息都有一个当前Partition下唯一的64字节的offset,它指明了这条消息的起始位置,Kafka只保证一个分区的数据顺序发送给消费者,而不保证整个topic里多个分区之间的顺序 Replicas(副本): 试想:一旦某一个Broker宕机,则其上所有的Partition数据都不可被消费,所以需要对分区备份。其中一个宕机后其它Replica必须要能继续服务并且即不能造成数据重复也不能造成数据丢失。如果没有一个Leader,所有Replica都可同时读/写数据,那就需要保证多个Replica之间互相(N×N条通路)同步数据,数据的一致性和有序性非常难保证,大大增加了Replication实现的复杂性,同时也增加了出现异常的几率。而引入Leader后,只有Leader负责数据读写,Follower只向Leader顺序Fetch数据(N条通路),系统更加简单且高效。每一个分区,根据复制因子N,会有N个副本,比如在broker1上有一个topic,分区为topic-1, 复制因子为2,那么在两个broker的数据目录里,就都有一个topic-1,其中一个是leader,一个replicas同一个Partition可能会有多个Replica,而这时需要在这些Replication之间选出一个Leader,Producer和Consumer只与这个Leader交互,其它Replica作为Follower从Leader中复制数据 Producer: Producer将消息发布到指定的topic中,同时,producer还需要指定该消息属于哪个partitionConsumer: 本质上kafka只支持topic,每一个consumer属于一个consumer group,每个consumer group可以包含多个consumer。发送到topic的消息只会被订阅该topic的每个group中的一个consumer消费。如果所有的consumer都具有相同的group,这种情况和queue很相似,消息将会在consumer之间均衡分配;如果所有的consumer都在不同的group中,这种情况就是广播模式,消息会被发送到所有订阅该topic的group中,那么所有的consumer都会消费到该消息。kafka的设计原理决定,对于同一个topic,同一个group中consumer的数量不能多于partition的数量,否则就会有consumer无法获取到消息。Offset: Offset专指Partition以及User Group而言,记录某个user group在某个partiton中当前已经消费到达的位置。kafka使用场景目前主流使用场景基本如下: 消息队列(MQ)在系统架构设计中,经常会使用消息队列(Message Queue)——MQ。MQ是一种跨进程的通信机制,用于上下游的消息传递,使用MQ可以使上下游解耦,消息发送上游只需要依赖MQ,逻辑上和物理上都不需要依赖其他下游服务。MQ的常见使用场景如流量削峰、数据驱动的任务依赖等等。在MQ领域,除了Kafka外还有传统的消息队列如ActiveMQ和RabbitMQ等。 追踪网站活动Kafka最出就是被设计用来进行网站活动(比如PV、UV、搜索记录等)的追踪。可以将不同的活动放入不同的主题,供后续的实时计算、实时监控等程序使用,也可以将数据导入到数据仓库中进行后续的离线处理和生成报表等。 MetricsKafka经常被用来传输监控数据。主要用来聚合分布式应用程序的统计数据,将数据集中后进行统一的分析和展示等。 日志聚合很多人使用Kafka作为日志聚合的解决方案。日志聚合通常指将不同服务器上的日志收集起来并放入一个日志中心,比如一台文件服务器或者HDFS中的一个目录,供后续进行分析处理。相比于Flume和Scribe等日志聚合工具,Kafka具有更出色的性能。 kafka 集群搭建安装kefka集群由于kafka依赖zookeeper环境所以先安装zookeeper,zk安装 安装环境 linux: CentSO-7.5_x64java: jdk1.8.0_191zookeeper: zookeeper3.4.10kafka: kafka_2.11-2.0.1# 下载$ wget http://mirrors.hust.edu.cn/apache/kafka/2.1.0/kafka_2.11-2.1.0.tgz# 解压$ tar -zxvf kafka_2.11-2.1.0.tgz# 编辑配置文件修改一下几个配置$ vim $KAFKA_HOME/config/server.properties# 每台服务器的broker.id都不能相同只能是数字broker.id=1# 修改为你的服务器的ip或主机名advertised.listeners=PLAINTEXT://node-1:9092# 设置zookeeper的连接端口,将下面的ip修改为你的IP称或主机名zookeeper.connect=node-1:2181,node-2:2181,node-3:2181启动Kafka集群并测试$ cd $KAFKA_HOME# 分别在每个节点启动kafka服务(-daemon表示在后台运行)$ bin/kafka-server-start.sh -daemon config/server.properties# 创建一个名词为 test-topic 的 Topic,partitions 表示分区数量为3 --replication-factor 表示副本数量为2$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 2 --partitions 3 --topic test-topic# 查看topic$ bin/kafka-topics.sh --list --zookeeper localhost:2181# 查看topic状态$ bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test-topic# 查看topic详细信息$ bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test-topic# 修改topic信息$ bin/kafka-topics.sh --alter --topic test-topic --zookeeper localhost:2181 --partitions 5# 删除topic(简单的删除,只是标记删除)$ bin/kafka-topics.sh --delete --topic test-topic --zookeeper localhost:2181# 在一台服务器上创建一个 producer (生产者)$ bin/kafka-console-producer.sh --broker-list node-1:9092,node-2:9092,node-3:9092 --topic test-topic# 在一台服务器上创建一个 consumer (消费者)$ bin/kafka-console-consumer.sh --bootstrap-server node-2:9092,node-3:9092,node-4:9092 --topic test-topic --from-beginning# 现在可以在生产者的控制台输入任意字符就可以看到消费者端有消费消息。java 客户端连接kafka普通java形式pom.xml<dependencies> <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka_2.11</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency></dependencies><build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins></build>JavaKafkaConsumer.java 消费者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.clients.producer.Producer;import org.apache.kafka.common.TopicPartition;import org.apache.kafka.common.serialization.StringDeserializer;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.Collection;import java.util.Collections;import java.util.Properties;/** * <p> * * @author leone * @since 2018-12-26 **/public class JavaKafkaConsumer { private static Logger logger = LoggerFactory.getLogger(JavaKafkaConsumer.class); private static Producer<String, String> producer; private final static String TOPIC = "kafka-test-topic"; private static final String ZOOKEEPER_HOST = "node-2:2181,node-3:2181,node-4:2181"; private static final String KAFKA_BROKER = "node-2:9092,node-3:9092,node-4:9092"; private static Properties properties; static { properties = new Properties(); properties.put("bootstrap.servers", KAFKA_BROKER); properties.put("group.id", "test"); properties.put("enable.auto.commit", "true"); properties.put("auto.commit.interval.ms", "1000"); properties.put("key.deserializer", StringDeserializer.class.getName()); properties.put("value.deserializer", StringDeserializer.class.getName()); } public static void main(String[] args) { final KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties); consumer.subscribe(Collections.singletonList(TOPIC), new ConsumerRebalanceListener() { public void onPartitionsRevoked(Collection<TopicPartition> collection) { } public void onPartitionsAssigned(Collection<TopicPartition> collection) { // 将偏移设置到最开始 consumer.seekToBeginning(collection); } }); while (true) { ConsumerRecords<String, String> records = consumer.poll(100); for (ConsumerRecord<String, String> record : records) { logger.info("offset: {}, key: {}, value: {}", record.offset(), record.key(), record.value()); } } }}JavaKafkaProducer.java 生产者import org.apache.kafka.clients.producer.KafkaProducer;import org.apache.kafka.clients.producer.Producer;import org.apache.kafka.clients.producer.ProducerRecord;import org.apache.kafka.common.serialization.StringSerializer;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.Properties;import java.util.UUID;/** * <p> * * @author leone * @since 2018-12-26 **/public class JavaKafkaProducer { private static Logger logger = LoggerFactory.getLogger(JavaKafkaProducer.class); private static Producer<String, String> producer; private final static String TOPIC = "kafka-test-topic"; private static final String ZOOKEEPER_HOST = "node-2:2181,node-3:2181,node-4:2181"; private static final String KAFKA_BROKER = "node-2:9092,node-3:9092,node-4:9092"; private static Properties properties; static { properties = new Properties(); properties.put("bootstrap.servers", KAFKA_BROKER); properties.put("acks", "all"); properties.put("retries", 0); properties.put("batch.size", 16384); properties.put("linger.ms", 1); properties.put("buffer.memory", 33554432); properties.put("key.serializer", StringSerializer.class.getName()); properties.put("value.serializer", StringSerializer.class.getName()); } public static void main(String[] args) { Producer<String, String> producer = new KafkaProducer<>(properties); for (int i = 0; i < 200; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String uuid = UUID.randomUUID().toString(); producer.send(new ProducerRecord<>(TOPIC, Integer.toString(i), uuid)); logger.info("send message success key: {}, value: {}", i, uuid); } producer.close(); }}KafkaClient.javaimport kafka.admin.AdminUtils;import kafka.admin.RackAwareMode;import kafka.server.ConfigType;import kafka.utils.ZkUtils;import org.apache.kafka.clients.admin.AdminClient;import org.apache.kafka.clients.admin.CreateTopicsResult;import org.apache.kafka.clients.admin.NewTopic;import org.apache.kafka.common.security.JaasUtils;import org.junit.Test;import java.util.*;/** * <p> * * @author leone * @since 2018-12-26 **/public class KafkaClient { private final static String TOPIC = "kafka-test-topic"; private static final String ZOOKEEPER_HOST = "node-2:2181,node-3:2181,node-4:2181"; private static final String KAFKA_BROKER = "node-2:9092,node-3:9092,node-4:9092"; private static Properties properties = new Properties(); static { properties.put("bootstrap.servers", KAFKA_BROKER); } /** * 创建topic */ @Test public void createTopic() { AdminClient adminClient = AdminClient.create(properties); List<NewTopic> newTopics = Arrays.asList(new NewTopic(TOPIC, 1, (short) 1)); CreateTopicsResult result = adminClient.createTopics(newTopics); try { result.all().get(); } catch (Exception e) { e.printStackTrace(); } } /** * 创建topic */ @Test public void create() { ZkUtils zkUtils = ZkUtils.apply(ZOOKEEPER_HOST, 30000, 30000, JaasUtils.isZkSecurityEnabled()); // 创建一个3个分区2个副本名为t1的topic AdminUtils.createTopic(zkUtils, "t1", 3, 2, new Properties(), RackAwareMode.Enforced$.MODULE$); zkUtils.close(); } /** * 查询topic */ @Test public void listTopic() { ZkUtils zkUtils = ZkUtils.apply(ZOOKEEPER_HOST, 30000, 30000, JaasUtils.isZkSecurityEnabled()); // 获取 topic 所有属性 Properties props = AdminUtils.fetchEntityConfig(zkUtils, ConfigType.Topic(), "streaming-topic"); Iterator it = props.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); System.err.println(entry.getKey() + " = " + entry.getValue()); } zkUtils.close(); } /** * 修改topic */ @Test public void updateTopic() { ZkUtils zkUtils = ZkUtils.apply(ZOOKEEPER_HOST, 30000, 30000, JaasUtils.isZkSecurityEnabled()); Properties props = AdminUtils.fetchEntityConfig(zkUtils, ConfigType.Topic(), "log-test"); // 增加topic级别属性 props.put("min.cleanable.dirty.ratio", "0.4"); // 删除topic级别属性 props.remove("max.message.bytes"); // 修改topic 'test'的属性 AdminUtils.changeTopicConfig(zkUtils, "log-test", props); zkUtils.close(); } /** * 删除topic 't1' */ @Test public void deleteTopic() { ZkUtils zkUtils = ZkUtils.apply(ZOOKEEPER_HOST, 30000, 30000, JaasUtils.isZkSecurityEnabled()); AdminUtils.deleteTopic(zkUtils, "t1"); zkUtils.close(); }}log4j.properties 日志配置log4j.rootLogger=info, stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n基于spirngboot整合kafkapom.xml<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <artifactId>spring-boot-kafka</artifactId> <groupId>com.andy</groupId> <version>1.0.7.RELEASE</version> <packaging>jar</packaging> <modelVersion>4.0.0</modelVersion> <dependencyManagement> <dependencies> <dependency> <groupId>io.spring.platform</groupId> <artifactId>platform-bom</artifactId> <version>Cairo-SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.0.3.RELEASE</version> <configuration> <!--<mainClass>${start-class}</mainClass>--> <layout>ZIP</layout> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build></project>application.ymlspring: application: name: spring-jms kafka: bootstrap-servers: node-2:9092,node-3:9092,node-4:9092 producer: retries: batch-size: 16384 buffer-memory: 33554432 compressionType: snappy acks: all consumer: group-id: 0 auto-offset-reset: earliest enable-auto-commit: trueMessage.java 消息/** * <p> * * @author leone * @since 2018-12-26 **/@ToStringpublic class Message<T> { private Long id; private T message; private Date time; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public T getMessage() { return message; } public void setMessage(T message) { this.message = message; } public Date getTime() { return time; } public void setTime(Date time) { this.time = time; }}KafkaController.java 控制器import com.andy.jms.kafka.service.KafkaSender;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;/** * <p> * * @author leone * @since 2018-12-26 **/@Slf4j@RestControllerpublic class KafkaController { @Autowired private KafkaSender kafkaSender; @GetMapping("/kafka/{topic}") public String send(@PathVariable("topic") String topic, @RequestParam String message) { kafkaSender.send(topic, message); return "success"; }}KafkaReceiver.javaimport lombok.extern.slf4j.Slf4j;import org.apache.kafka.clients.consumer.ConsumerRecord;import org.springframework.kafka.annotation.KafkaListener;import org.springframework.stereotype.Component;import java.util.Optional;/** * <p> * * @author leone * @since 2018-12-26 **/@Slf4j@Componentpublic class KafkaReceiver { @KafkaListener(topics = {"order"}) public void listen(ConsumerRecord<?, ?> record) { Optional<?> kafkaMessage = Optional.ofNullable(record.value()); if (kafkaMessage.isPresent()) { Object message = kafkaMessage.get(); log.info("record:{}", record); log.info("message:{}", message); } }}KafkaSender.javaimport com.andy.jms.kafka.commen.Message;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.kafka.core.KafkaTemplate;import org.springframework.stereotype.Component;import java.util.Date;/** * <p> * * @author leone * @since 2018-12-26 **/@Slf4j@Componentpublic class KafkaSender { @Autowired private KafkaTemplate<String, String> kafkaTemplate; @Autowired private ObjectMapper objectMapper; /** * * @param topic * @param body */ public void send(String topic, Object body) { Message<String> message = new Message<>(); message.setId(System.currentTimeMillis()); message.setMessage(body.toString()); message.setTime(new Date()); String content = null; try { content = objectMapper.writeValueAsString(message); } catch (JsonProcessingException e) { e.printStackTrace(); } kafkaTemplate.send(topic, content); log.info("send {} to {} success!", message, topic); }}启动类import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * @author Leone * @since 2018-04-10 **/@SpringBootApplicationpublic class JmsApplication { public static void main(String[] args) { SpringApplication.run(JmsApplication.class, args); }}github地址 ...

May 15, 2019 · 5 min · jiezi

Kafka保证消息传输

Kafka保证消息传输生产2000W条数据在本地机器生产2000W条数据,单条数据中消息部分在40~50个字节左右. 大概耗时在2分钟多发送完场.代码如下 private static Properties initConfig() { final Properties properties = new Properties(); properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092"); return properties;}public static void main(String[] args) { long start = System.currentTimeMillis(); final KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(initConfig()); for (int i = 0; i < 20000000; i++) { // 用utf8编码生成45个字节. 一个字符占一个字节.c final ProducerRecord<String, String> record = new ProducerRecord<>("TwentyMillion", String.valueOf(i), UUID.randomUUID() + " " + i); kafkaProducer.send(record, new Callback() { @Override public void onCompletion(RecordMetadata metadata, Exception exception) { if (exception == null) { System.out.println(metadata.toString()); } else { exception.printStackTrace(); } } }); } System.out.println(System.currentTimeMillis() - start + " ms");}在上面的例子中,一共发送了2000W条数据,但是最后只收到了19,458,873条数据,很明显缺少了几万条数据,这是由于没有加 ACKS_CONFIG, 只需要加上一行props.put(ProducerConfig.ACKS_CONFIG, "1");,就能保证partition leader肯定会接收到数据. 但是不能100%保证消息不会丢失. 如果写入的消息还没同步到partition replica,此时partition leader挂了,重新选举partition leader之后,则会造成消息丢失. ...

May 14, 2019 · 1 min · jiezi

新的Kfaka学习小册

还有另外的两个,非常优质的小册: https://segmentfault.com/a/11...

May 14, 2019 · 1 min · jiezi

Kafka-日志存储

在进行详解之前,我想先声明一下,本次我们进行讲解说明的是 Kafka 消息存储的信息文件内容,不是所谓的 Kafka 服务器运行产生的日志文件,这一点希望大家清楚。 Kafka 消息是以主题为单位进行归类,各个主题之间是彼此独立的,互不影响。每个主题又可以分为一个或多个分区。每个分区各自存在一个记录消息数据的日志文件。也就是该文要着重关注的内容。我们根据如下的图进行进一步说明: 图中,创建了一个 demo-topic 主题,其存在 7 个 Parition,对应的每个 Parition 下存在一个 [Topic-Parition] 命名的消息日志文件。在理想情况下,数据流量分摊到各个 Parition 中,实现了负载均衡的效果。在分区日志文件中,你会发现很多类型的文件,比如:.index、.timestamp、.log、.snapshot 等,其中,文件名一致的文件集合就称为 LogSement。我们先留有这样的一个整体的日志结构概念,接下来我们一一的进行详细的说明其中的设计。 LogSegment我们已经知道分区日志文件中包含很多的 LogSegment ,Kafka 日志追加是顺序写入的,LogSegment 可以减小日志文件的大小,进行日志删除的时候和数据查找的时候可以快速定位。同时,ActiveLogSegment 也就是活跃的日志分段拥有文件拥有写入权限,其余的 LogSegment 只有只读的权限。 日志文件存在多种后缀文件,重点需要关注 .index、.timestamp、.log 三种类型。其他的日志类型功能作用,请查询下面图表: 类别作用.index偏移量索引文件.timestamp时间戳索引文件.log日志文件.snaphot快照文件.deleted .cleaned日志清理时临时文件.swapLog Compaction 之后的临时文件Leader-epoch-checkpoint 每个 LogSegment 都有一个基准偏移量,用来表示当前 LogSegment 中第一条消息的 offset。偏移量是一个 64 位的长整形数,固定是20位数字,长度未达到,用 0 进行填补,索引文件和日志文件都由该作为文件名命名规则(00000000000000000000.index、00000000000000000000.timestamp、00000000000000000000.log)。特别说明一下,如果日志文件名为 00000000000000000121.log ,则当前日志文件的一条数据偏移量就是 121,偏移量是从 0 开始的。 如果想要查看相应文件内容可以通过 kafka-run-class.sh 脚本查看 .log : /data/kafka/bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files ./00000000000000000000.log 2.0 中可以使用 kafka-dump-log.sh 查 看.index 文件 ...

May 11, 2019 · 2 min · jiezi

Fabric-Kafka入门

Hyperledger Fabric推荐Kafka用于生产环境。Kafka是一个分布式、具有水平伸缩能力、崩溃容错能力的日志系统。在Hyperledger Fabric区块链中可以有多个Kafka节点,使用zookeeper进行同步管理。本文将介绍Kfaka的基本工作原理,以及在HyperledgerFabric中使用Kafka和zookeeper实现共识的原理,并通过一个实例剖析Hyperledger Farbic中Kafka共识的达成过程。 如果希望快速掌握Fabric区块链的链码及应用开发,建议访问汇智网的在线互动课程: Fabric区块链Java开发详解Fabric区块链NodeJs开发详解一、Kafka工作原理Kafka本质上是一个消息处理系统,它使用的是经典的发布-订阅模型。消息的消费者订阅特定的主题,以便收到新消息的通知,生产者则负责消息的发布。 当主题的数据规模变得越来越大时,可以拆分为多个分区,Kafka保障在一个分区内的消息是按顺序排列的。 Kafka并不跟踪消费者读取了哪些消息,也不会自动删除已经读取的消息。Kafka会保存消息一段时间,例如一天,或者直到数据规模超过一定的阈值。消费者需要轮询新的消息,这是的他们可以根据自己的需求来定位消息,因此可以重放或重新处理事件。消费者处于不同的消费者分组,对应一个或多个消费者进程。每个分区被分贝给单一的消费者进程,因此同样的消息不会被多次读取。 崩溃容错机制是通过在多个Kafka代理之间复制分区来实现的。因此如果一个代理由于软件或硬件故障挂掉,数据也不会丢失。当然接下来还需要一个领导-跟随机制,领导者持有分区,跟随者则进行分区的复制。当领导者挂掉后,会有某个跟随者转变为新的领导者。 如果一个消费者订阅了某个主体,那么它怎么知道从哪个分区领导者来读取订阅的消息? 答案在于zookeeper服务。 zookeeper是一个分布式key-value存储库,通常用于存储元数据及集群机制的实现。zookeeper允许服务(Kafka代理)的客户端订阅变化并获得实时通知。这就是代理如何确定应当使用哪个分区领导者的原因。zookeeper有超强的故障容错能力,因此Kafka的运行严重依赖于它。 在zookeeper中存储的元数据包括: 消费者分组在每个分区的读取偏移量访问控制清单,用于访问授权与限制生产者及消费者配额,每秒最多消息数量分区领导者及健康信息二、Hyperledger Fabric中的Kafka要理解在超级账本Hyperledger Fabric中的Kafka是如何工作的,首先需要理解几个重要的术语: Chain - 指的是一组客户端(通道/channel)可以访问的日志Channel - 一个通道类似于一个主题,授权的对等节点(peer)可以订阅并且成为通道的成员。 只有通道的成员可以在通道上交易,一个通道中的交易在其他通道中看不到。OSN - 即排序服务节点(Ordering Service Node),在Fabric中被称为排序节点。排序节点负责: 进行客户鉴权允许客户端通过一个简单的接口写入或读取通道执行配置交易的过滤与验证,实现通道的重新配置或创建新的通道RPC - 即远程过程调用(Remote Procedure Call),是一种用于调用其他机器上的服务而无需了解 通信与实现细节的通信协议,目的是像调用本地函数一样调用网络中其他机器上的函数广播PRC - 交易提交调用,由排序节点执行分发RPC - 交易分发请求,当交易由kafka代理处理后,分发给请求节点注意,虽然在Hyperledger Fabric中Kafka被称为共识(Consensus),但是其核心是交易排序服务以及额外的崩溃容错能力。 在Hyperledger Fabric中的Kafka实际运行逻辑如下: 对于每一条链,都有一个对应的分区每个链对应一个单一的分区主题排序节点负责将来自特定链的交易(通过广播RPC接收)中继到对应的分区排序节点可以读取分区并获得在所有排序节点间达成一致的排序交易列表一个链中的交易是定时分批处理的,也就是说当一个新的批次的第一个交易进来时,开始计时当交易达到最大数量时或超时后进行批次切分,生成新的区块定时交易是另一个交易,由上面描述的定时器生成每个排序节点为每个链维护一个本地日志,生成的区块保存在本地账本中交易区块通过分发RPC返回客户端当发生崩溃时,可以利用不同的排序节点分发区块,因为所有的排序节点都维护有本地日志 三、Hyperledger Fabric Kafka实例解析考虑下图,假设排序节点OSN0和OSN2时连接到广播客户端,OSN1连接到分发客户端。 OSN0已经有了交易foo,中继到kafka集群此时OSN2将交易baz广播到集群中最后,交易bar由OSN0发送到集群中集群现在有三个交易,可以在图中看到三个交易的在日志中的位置偏移量客户端发送分发请求,在OSN1的本地日志中,上述三个交易在4#区块里。因此OSN1将4#区块返回客户端,处理结束Kakfa的高性能对于Hyperledger Fabric有很大的帮助,多个排序节点通过Kafka实现同步,而Kafka本身并不是排序节点,它只是将排序节点通过流连接起来。虽然Kafka支持崩溃容错,它并不能提供对网络中恶意攻击的保护。需要一种拜占庭容错方案(BFT)才可以对抗恶意的攻击,但是目前在Farbic框架中还有待实现这一机制。 总而言之,在Hyperledger Farbic中,Kafka共识模块是可以用于生产环境的,它可以支持崩溃容错,但无法对抗恶意攻击。 原文:The ABCs of Kafka in Hyperledger Fabric

April 28, 2019 · 1 min · jiezi

EFK接入kafka消息队列

1 前言在笔者最开始维护的日志服务中,日质量较小,没有接入kafka。随着业务规模扩增,日质量不断增长,接入到日志服务的产品线不断增多,遇到流量高峰,写入到es的性能就会降低,cpu打满,随时都有集群宕机的风险。因此,接入消息队列,进行削峰填谷就迫在眉睫。本文主要介绍在EFK的基础上如何接入kafka,并做到向前兼容。 2 主要内容如何搭建kafka集群原有EFK升级3 搭建kafka集群3.1 搭建zookeeper集群主要参考文章:【zookeeper安装指南】由于是要线上搭建集群,为避免单点故障,就需要部署至少3个节点(取决于多数选举机制)。 3.1.1 下载进入要下载的版本的目录,选择.tar.gz文件下载 3.1.2 安装使用tar解压要安装的目录即可,以3.4.5版本为例这里以解压到/home/work/common,实际安装根据自己的想安装的目录修改(注意如果修改,那后边的命令和配置文件中的路径都要相应修改) tar -zxf zookeeper-3.4.5.tar.gz -C /home/work/common3.1.3 配置在主目录下创建data和logs两个目录用于存储数据和日志: cd /home/work/zookeeper-3.4.5mkdir data mkdir logs在conf目录下新建zoo.cfg文件,写入如下配置: tickTime=2000 dataDir=/home/work/common/zookeeper1/data dataLogDir=/home/work/common/zookeeper1/logs clientPort=2181 initLimit=5 syncLimit=2 server.1=192.168.220.128:2888:3888 server.2=192.168.222.128:2888:3888 server.3=192.168.223.128:2888:3888在zookeeper1的data/myid配置如下: echo '1' > data/myidzookeeper2的data/myid配置如下: echo '2' > data/myidzookeeper2的data/myid配置如下: echo '3' > data/myid3.1.4 启停进入bin目录,启动、停止、重启分和查看当前节点状态(包括集群中是何角色)别执行: ./zkServer.sh start ./zkServer.sh stop ./zkServer.sh restart./zkServer.sh statuszookeeper集群搭建完成之后,根据实际情况开始部署kafka。以部署2个broker为例。 3.2 搭建kafka broker集群3.2.1 安装下载并解压包: curl -L -O http://mirrors.cnnic.cn/apache/kafka/0.9.0.0/kafka_2.10-0.9.0.0.tgz tar zxvf kafka_2.10-0.9.0.0.tgz 3.2.2 配置进入kafka安装工程根目录编辑config/server.properties #不同的broker对应的id不能重复broker.id=1delete.topic.enable=trueinter.broker.protocol.version=0.10.0.1log.message.format.version=0.10.0.1listeners=PLAINTEXT://:9092,SSL://:9093auto.create.topics.enable=falsessl.key.password=testssl.keystore.location=/home/work/certificate/server-keystore.jksssl.keystore.password=testssl.truststore.location=/home/work/certificate/server-truststore.jksssl.truststore.password=testnum.network.threads=3num.io.threads=8socket.send.buffer.bytes=102400socket.receive.buffer.bytes=102400socket.request.max.bytes=104857600log.dirs=/home/work/data/kafka/lognum.partitions=1num.recovery.threads.per.data.dir=1offsets.topic.replication.factor=1transaction.state.log.replication.factor=1transaction.state.log.min.isr=1log.retention.hours=72log.segment.bytes=1073741824log.retention.check.interval.ms=300000zookeeper.connect=192.168.220.128:2181,192.168.222.128:2181,192.168.223.128:2181zookeeper.connection.timeout.ms=6000group.initial.rebalance.delay.ms=03.2.3 启动kafka进入kafka的主目录 nohup sh bin/kafka-server-start.sh config/server.properties > /dev/null 2>&1 &3.2.4 连通性测试首先创建一个topic:topic_1 ...

April 24, 2019 · 2 min · jiezi

Burrow 使用记录

一、Burrow介绍Burrow是linkedin开源的一个监控Apache Kafka的工具,burrow可以将消费者滞后检查作为一项服务来对外提供。 它监视所有消费者的承诺偏移量,并根据需要计算消费者的状态,提供HTTP endpoint接口来获取消费者状态,能够监控Consumer消费消息的延迟,从而监控应用的健康状况,并且可以同时监控多个Kafka集群。 通知器可以通过配置电子邮件或HTTP通告进行告警,而无需指定阈值。Burrow的模块:Burrow采用模块化的设计,将所需要的工作分成多个子系统模块。1、Clusters: clusters运行一个kafka client,client 定期的更新topic 列表以及topic每个partition的 head offset。2、Consumers: consumers 从存储库里获取consumer group 的相关信息(根据kafka版本的不同,获取的位置不同,可能是zk 、kafka的__consumer_offsets topic 等)。3、Storage:storage 负责存储Burrow系统的所有数据内容。4、Evaluator:evaluator接收storage里的kafka 消费组的数据并计算出kafka consumer group的状态。5、Notifier:Notifier 通过配置的间隔时间,定期的获取consumer group的状态,并发送相关满足配置的消息提醒。6、HTTP Server:此模块提供了获取consumer gropu 信息的接口。二、安装和配置:Burrow使用GO语言编写,可以下载源码编译打包。也可以直接下载编译完成的安装包,地址为:https://github.com/linkedin/B… 下载完成后,解压 tar –xzvf .tar.gz 。1、配置解压完成进入config 目录,修改burrow.toml 配置文件,我们的配置文件内容为:[general]pidfile=“burrow.pid"stdout-logfile=“burrow.out"access-control-allow-origin=“mysite.example.com”[logging]filename=“logs/burrow.log"level=“info"maxsize=100maxbackups=30maxage=10use-localtime=falseuse-compression=true[zookeeper]servers=[ “10.102..:2181”,“10.102..:2181”,“10.102..: 2181” ]timeout=6root-path="/burrow”[client-profile.financial]client-id=“burrow"kafka-version=“1.1.0”[cluster.financial]class-name=“kafka"servers=[ “10.102..:9092”,“10.102..:9092” ]topic-refresh=120offset-refresh=30[consumer.financial]class-name=“kafka"cluster=“financial"servers=[ “10.102..:9092”,“10.102..:9092” ]client-profile=“financial"group-blacklist="^(console-consumer-|python-kafka-consumer-|quick-).$“group-whitelist=”"[consumer.financiali_zk]class-name=“kafka_zk"cluster=“financial"servers=[ “10.102..:2181”,“10.102..:2181” ][httpserver.default]address=":8004”[storage.default]class-name=“inmemory"workers=20intervals=15expire-group=604800min-distance=1[evaluator.mystorage]class-name=“caching"expire-cache=10[notifier.default]class-name=“http"url-open=“http://10.103..:8006/test/api"url-close=“http://10.103..:8006/test/api"interval=60timeout=5keepalive=30extras={ api_key=“REDACTED”, app=“burrow”, tier=“STG”, fabric=“mydc” }template-open=“config/default-http-post.tmpl"template-close=“config/default-http-delete.tmpl"method-close=“DELETE"send-close=truethreshold=2请输入代码2、启动 配置完成后,输入命令启动:./burrow –config-dir ./config & 3、接收通知notifier.default 的配置中,class-name 为http,提供一个接受notice的接口:接口格式大概为:@RequestMapping("/api”)@ResponseBodypublic String event(@RequestBody String json){ …….. return “success”;}三、附录burrow http 模块提供的接口requestmethodurl formatHealthcheckGET/burrow/adminList ClustersGET/v3/kafkaKafka Cluster DetailGET/v3/kafka/(cluster)List ConsumersGET/v3/kafka/(cluster)/consumerList Cluster TopicsGET/v3/kafka/(cluster)/topicGet Consumer DetailGET/v3/kafka/(cluster)/consumer/(group)Consumer Group StatusGET/v3/kafka/(cluster)/consumer/(group)/status /v3/kafka/(cluster)/consumer/(group)/lagRemove Consumer GroupDELETE/v3/kafka/(cluster)/consumer/(group)Get Topic DetailGET/v3/kafka/(cluster)/topic/(topic)Get General ConfigGET/v3/configList Cluster ModulesGET/v3/config/clusterGet Cluster Module ConfigGET/v3/config/cluster/(name)List Consumer ModulesGET/v3/config/consumerGet Consumer Module ConfigGET/v3/config/consumer/(name)List Notifier ModulesGET/v3/config/notifierGet Notifier Module ConfigGET/v3/config/notifier/(name)List Evaluator ModulesGET/v3/config/evaluatorGet Evaluator Module ConfigGET/v3/config/evaluator/(name)List Storage ModulesGET/v3/config/storageGet Storage Module ConfigGET/v3/config/storage/(name)Get Log LevelGET/v3/admin/loglevelSet Log LevelPOST/v3/admin/loglevel ...

April 11, 2019 · 1 min · jiezi

Kafka - PHP 使用 Rdkafka 生产/消费数据

Kafka集群部署安装rdkafkardkafka 依赖 libkafkayum install rdkafka rdkafka-develpecl install rdkafkaphp –ri rdkafkahttp://pecl.php.net/package/r… 可以参阅支持的kafka客户端版本生产者连接集群,创建 topic,生产数据。<?php$rk = new Rdkafka\Producer();$rk->setLogLevel(LOG_DEBUG);// 链接kafka集群$rk->addBrokers(“192.168.20.6:9092,192.168.20.6:9093”);// 创建topic$topic = $rk->newTopic(“topic_1”);while (true) { $message = “hello kafka " . date(“Y-m-d H:i:s”); echo “hello kafka " . date(“Y-m-d H:i:s”) . PHP_EOL; try { $topic->produce(RD_KAFKA_PARTITION_UA, 0, $message); sleep(2); } catch (\Exception $e) { echo $e->getMessage() . PHP_EOL; }}消费者-HighLevel自动分配partition,rebalance,comsumer group。<?php$conf = new RdKafka\Conf();// Set a rebalance callback to log partition assignments (optional)$conf->setRebalanceCb(function (RdKafka\KafkaConsumer $kafka, $err, array $partitions = null) { switch ($err) { case RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS: echo “Assign: “; var_dump($partitions); $kafka->assign($partitions); break; case RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS: echo “Revoke: “; var_dump($partitions); $kafka->assign(null); break; default: throw new \Exception($err); }});// Configure the group.id. All consumer with the same group.id will consume// different partitions.$conf->set(‘group.id’, ‘group_1’);// Initial list of Kafka brokers$conf->set(‘metadata.broker.list’, ‘192.168.20.6:9092,192.168.20.6:9093’);$topicConf = new RdKafka\TopicConf();// Set where to start consuming messages when there is no initial offset in// offset store or the desired offset is out of range.// ‘smallest’: start from the beginning$topicConf->set(‘auto.offset.reset’, ‘smallest’);// Set the configuration to use for subscribed/assigned topics$conf->setDefaultTopicConf($topicConf);$consumer = new RdKafka\KafkaConsumer($conf);// Subscribe to topic ’topic_1’$consumer->subscribe([’topic_1’]);echo “Waiting for partition assignment… (make take some time when\n”;echo “quickly re-joining the group after leaving it.)\n”;while (true) { $message = $consumer->consume(3e3); switch ($message->err) { case RD_KAFKA_RESP_ERR_NO_ERROR: var_dump($message); break; case RD_KAFKA_RESP_ERR__PARTITION_EOF: sleep(2); case RD_KAFKA_RESP_ERR__TIMED_OUT: echo $message->errstr() . PHP_EOL; break; default: throw new \Exception($message->errstr(), $message->err); break; }}消费者-LowLevel指定partition消费。php consumer_lowlevel.php [partitonNuo]LowLevel 没有消费组的概念,也可以认为每个消费者都属于一个独立消费组。<?phpif (!isset($argv[1])) { fwrite(STDOUT, “请指定消费分区:”); $partition = (int) fread(STDIN, 1024);} else { $partition = (int) $argv[1];}$topic = “topic_1”;$conf = new RdKafka\Conf();// Set the group id. This is required when storing offsets on the broker$conf->set(‘group.id’, ‘group_2’);$rk = new RdKafka\Consumer($conf);$rk->addBrokers(‘192.168.20.6:9092,192.168.20.6:9093’);$topicConf = new RdKafka\TopicConf();$topicConf->set(‘auto.commit.interval.ms’, 2000);// Set the offset store method to ‘file’// $topicConf->set(‘offset.store.method’, ‘file’);// $topicConf->set(‘offset.store.path’, sys_get_temp_dir());// Alternatively, set the offset store method to ‘broker’$topicConf->set(‘offset.store.method’, ‘broker’);// Set where to start consuming messages when there is no initial offset in// offset store or the desired offset is out of range.// ‘smallest’: start from the beginning$topicConf->set(‘auto.offset.reset’, ‘smallest’);$topic = $rk->newTopic($topic, $topicConf);// Start consuming partition 0$topic->consumeStart($partition, RD_KAFKA_OFFSET_STORED);while (true) { $message = $topic->consume($partition, 3 * 1000); switch ($message->err) { case RD_KAFKA_RESP_ERR_NO_ERROR: var_dump($message); break; case RD_KAFKA_RESP_ERR__PARTITION_EOF: case RD_KAFKA_RESP_ERR__TIMED_OUT: echo $message->errstr() . PHP_EOL; break; default: throw new \Exception($message->errstr(), $message->err); break; }} ...

March 18, 2019 · 2 min · jiezi

Kafka - Zookeeper/Kafka集群的部署

下载kafka,自带 zookeeper。搭建Zookeeper集群zookeeper 集群使用 Raft 选举模式,故至少要三个节点(生产中应部署在三个不同的服务器实例上,这里用于演示就不那么做了)。# 复制三分节点配置cp config/zookeeper.properties config/zookeeper.2181.propertiescp config/zookeeper.properties config/zookeeper.2182.propertiescp config/zookeeper.properties config/zookeeper.2183.properties修改配置config/zookeeper.2181.properties# the directory where the snapshot is stored.dataDir=/tmp/zookeeper/2181# the port at which the clients will connectclientPort=2181# disable the per-ip limit on the number of connections since this is a non-production configmaxClientCnxns=0tickTime=2000initLimit=10syncLimit=5server.1=localhost:12888:13888server.2=localhost:22888:23888server.3=localhost:32888:33888config/zookeeper.2182.properties 修改clientPort=2182 dataDir=/tmp/zookeeper/2182 其他一致config/zookeeper.2183.properties 修改clientPort=2183 dataDir=/tmp/zookeeper/2183 其他一致主要是修改服务端口clientPort和数据目录dataDir,其他参数表征如下:tickTime=2000为zk的基本时间单元,毫秒initLimit=10Leader-Follower初始通信时限(tickTime10)syncLimit=5Leader-Follower同步通信时限(tickTime5)server.实例集群标识=实例地址:数据通信端口:选举通信端口为实例添加集群标识echo 1 >> /tmp/zookeeper/2181/myidecho 2 >> /tmp/zookeeper/2182/myidecho 3 >> /tmp/zookeeper/2183/myid启动集群服务bin/zookeeper-server-start.sh config/zookeeper.2181.propertiesbin/zookeeper-server-start.sh config/zookeeper.2182.propertiesbin/zookeeper-server-start.sh config/zookeeper.2183.properties搭建Kafka集群Kafka集群节点>=2时便可对外提供高可用服务cp config/server.properties config/server.9092.propertiescp config/server.properties config/server.9093.properties修改节点标识、服务端口、数据目录和zk集群节点列表vi config/server.9092.propertiesbroker.id=1…listeners=PLAINTEXT://:9092…log.dirs=/tmp/kafka-logs/1…zookeeper.connect=localhost:2181,localhost:2182,localhost:2183vi config/server.9093.propertiesbroker.id=2…listeners=PLAINTEXT://:9093…log.dirs=/tmp/kafka-logs/2…zookeeper.connect=localhost:2181,localhost:2182,localhost:2183启动集群bin/kafka-server-start.sh config/server.9092.propertiesbin/kafka-server-start.sh config/server.9093.propertiesTopic管理创建topicbin/kafka-topics.sh –create --zookeeper localhost:2181,localhost:2182,localhost:2183 --replication-factor 2 --partition 4 --topic topic_1–replication-factor 2:副本集数量,不能大于 broker 节点数量,多了也没用,1个节点放>=2个副本挂了都完蛋。–partition 4:分区数查看topic列表bin/kafka-topics.sh --zookeeper localhost:2181,localhost:2182,localhost:2183 –listtopic_1topic_2查看Topic详情可以描述Topic分区数/副本数/副本Leader/副本ISR等信息:bin/kafka-topics.sh --zookeeper localhost:2181,localhost:2182,localhost:2183 --describe –topic topic_1Topic:topic_1 PartitionCount:4 ReplicationFactor:2 Configs: Topic: topic_1 Partition: 0 Leader: 2 Replicas: 2,1 Isr: 2,1 Topic: topic_1 Partition: 1 Leader: 1 Replicas: 1,2 Isr: 1,2 Topic: topic_1 Partition: 2 Leader: 2 Replicas: 2,1 Isr: 2,1 Topic: topic_1 Partition: 3 Leader: 1 Replicas: 1,2 Isr: 1,2删除Topic注意,只是删除Topic在zk的元数据,日志数据仍需手动删除。bin/kafka-topics.sh --zookeeper localhost:2181,localhost:2182,localhost:2183 --delete –topic topic_2#Topic topic_2 is marked for deletion.#Note: This will have no impact if delete.topic.enable is not set to true.#再查看topic列表bin/kafka-topics.sh --zookeeper localhost:2181,localhost:2182,localhost:2183 –list#topic_1#topic_2 - marked for deletion生产者bin/kafka-console-producer.sh --broker-list localhost:9092,localhost:9093 --topic topic_1# 进入 cli 输入消息回车发送# hello kafka [enter]# send message [enter]消费者新模式,offset存储在borker–new-consumer Use new consumer. This is the default.–bootstrap-server <server to connectto> REQUIRED (unless old consumer is used): The server to connect to.老消费模式,offset存储在zk–zookeeper <urls> REQUIRED (only when using old consumer): The connection string for the zookeeper connection in the form host:port. Multiple URLS can be given to allow fail-over.创建消费者bin/kafka-console-consumer.sh --new-consumer --bootstrap-server localhost:9092,localhost:9093 --from-beginning --topic topic_1可以尝试创建多个不同消费组的消费者(这里的sh脚本创建的都是不同消费组的),订阅同一个topic来实现发布订阅模式。查看消费组/消费者bin/kafka-consumer-groups.sh --new-consumer --bootstrap-server localhost:9092,localhost:9093 --list#这里有两个消费组的消费者console-consumer-47566console-consumer-50875查看消费详情可以查看到消费的订阅的 topic,负责的 partition,消费进度 offset, 积压的消息LAG。bin/kafka-consumer-groups.sh --new-consumer --bootstrap-server localhost:9092,localhost:9093 --group console-consumer-47566 --describeGROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG OWNERconsole-consumer-47566 topic_1 0 2 2 0 consumer-1_/127.0.0.1console-consumer-47566 topic_1 1 3 3 0 consumer-1_/127.0.0.1console-consumer-47566 topic_1 2 2 3 1 consumer-1_/127.0.0.1console-consumer-47566 topic_1 3 0 3 3 consumer-1_/127.0.0.1 ...

March 18, 2019 · 2 min · jiezi

kafka介绍

在一上篇文章介绍了消息队列的使用场景,现在介绍下kafkaKafka主要特点:同时为发布和订阅提供高吞吐量。据了解,Kafka每秒可以生产约25万消息(50 MB),每秒处理55万消息(110 MB)。可进行持久化操作。将消息持久化到磁盘,因此可用于批量消费,例如ETL,以及实时应用程序。通过将数据持久化到硬盘以及replication防止数据丢失。分布式系统,易于向外扩展。所有的producer、broker和consumer都会有多个,均为分布式的。无需停机即可扩展机器。消息被处理的状态是在consumer端维护,而不是由server端维护。当失败时能自动平衡。支持online和offline的场景。kafka 架构Kafka的整体架构非常简单,是显式分布式架构,producer、broker(kafka)和consumer都可以有多个。Producer,consumer实现Kafka注册的接口,数据从producer发送到broker,broker承担一个中间缓存和分发的作用。broker分发注册到系统中的consumer。broker的作用类似于缓存,即活跃的数据和离线处理系统之间的缓存。客户端和服务器端的通信,是基于简单,高性能,且与编程语言无关的TCP协议。几个基本概念:Topic:特指Kafka处理的消息源(feeds of messages)的不同分类。Partition:Topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)。Message:消息,是通信的基本单位,每个producer可以向一个topic(主题)发布一些消息。Producers:消息和数据生产者,向Kafka的一个topic发布消息的过程叫做producers。Consumers:消息和数据消费者,订阅topics并处理其发布的消息的过程叫做consumers。Broker:缓存代理,Kafka集群中的一台或多台服务器统称为broker。消息发送流程1.Producer根据指定的partition方法(round-robin、hash等),将消息发布到指定topic的partition里面2.kafka集群接收到Producer发过来的消息后,将其持久化到硬盘,并保留消息指定时长(可配置),而不关注消息是否被消费。3.Consumer从kafka集群pull数据,并控制获取消息的offset消息存储策略谈到kafka的存储,就不得不提到分区,即partitions,创建一个topic时,同时可以指定分区数目,分区数越多,其吞吐量也越大,但是需要的资源也越多,同时也会导致更高的不可用性,kafka在接收到生产者发送的消息之后,会根据均衡策略将消息存储到不同的分区中。在每个分区中,消息以顺序存储,最晚接收的的消息会最后被消费。与生产者的交互生产者在向kafka集群发送消息的时候,可以通过指定分区来发送到指定的分区中也可以通过指定均衡策略来将消息发送到不同的分区中如果不指定,就会采用默认的随机均衡策略,将消息随机的存储到不同的分区中与消费者的交互在消费者消费消息时,kafka使用offset来记录当前消费的位置在kafka的设计中,可以有多个不同的group来同时消费同一个topic下的消息,如图,我们有两个不同的group同时消费,他们的的消费的记录位置offset不互相干扰。对于一个group而言,消费者的数量不应该多余分区的数量,因为在一个group中,每个分区至多只能绑定到一个消费者上,即一个消费者可以消费多个分区,一个分区只能给一个消费者消费若一个group中的消费者数量大于分区数量的话,多余的消费者将不会收到任何消息。

March 13, 2019 · 1 min · jiezi

Some Knowledge about Apache Kafka(1)

Apache Kafka is an open source, distributed publish-subscribe messaging system,and its main characters include:-Persistent Messaging-High throughput-Distributed: support messages partitioning over Kafka Servers and distributing consumption over a cluster of consumer machines while maintaining per-partition ordering machines Producer(frontend, services, proxies, adapters, other producers) | Kafka ———> Zookeeper | Consumer(realtime, NoSQL, Hadoop, Warehouses)Install KafkaDownload Kafka[root@localhost opt] https://www.apache.org/dyn/closer.cgi/incubator/kafka/kafka-0.7.2-incubating/kafka-0.7.2-incubating-src.tgzExtract the Kafka downloaded package[root@localhost opt]# tar xzf kafka-0.8.0-beta1-src.tgzSetup Kafka Cluster ZooKeeper —————————— | | | | Producer –> Kafka Broker –> ConsumerZookeeperKafka provides the default and simple ZooKeeper configuration file used for lauching a single local ZooKeeper instance. Zookeeper allows distributed processes coordinating with each other through a shared hierarchical name space of data registersStart Zookeeper Server [root@localhost kafka-0.8]# bin/zookeeper-server-start.sh config/zookeeper.propertiesData directory where the zookeeper snapshot is stored ## dataDir=/tmp/zookeeperThe port listening for client request ## clientPort=2181Consumer group id ## groupid=test-consumer-groupzookeeper connection string ## zookeeper.connect=localhost:2181Start the Kafka broker[root@localhost kafka-0.8]# bin/kafka-server-start.sh config/server.propertiesCreate a Kafka Topic[root@localhost kafka-0.8]# bin/kafka-create-topic.sh –zookeeper localhost:2181 –replica 1 –partition 1 –topic kafkatopicStart a producer for sending messages[root@localhost kafka-0.8]# bin/kafka-console-producer.sh –broker-list localhost:9092 –topic kafkatopicStart a consumer for consuming messages[root@localhost kafka-0.8]# bin/kafka-console-consumer.sh –zookeeper localhost:2181 –topic kafkatopic –from-beginningCreate a Kafka topic[root@localhost kafka-0.8]# bin/kafka-create-topic.sh –zookeeper localhost:2181 –replica 2 –partition 2 –topic othertopicStart a producer for sending messages[root@localhost kafka-0.8]# bin/kafka-console-producer.sh –broker-list localhost:9092,localhost:9093 –topic othertopicStart a consumer for consuming messages[root@localhost kafka-0.8]# bin/kafka-console-consumer.sh –zookeeper localhost:2181 –topic othertopic –from-beginning ...

March 9, 2019 · 2 min · jiezi

Kafka原理你真的知道吗?

1.概述Apache Kafka最早是由LinkedIn开源出来的分布式消息系统,现在是Apache旗下的一个子项目,并且已经成为开源领域应用最广泛的消息系统之一。Kafka社区非常活跃,从0.9版本开始,Kafka的标语已经从“一个高吞吐量,分布式的消息系统”改为"一个分布式流平台"。Kafka和传统的消息系统不同在于:kafka是一个分布式系统,易于向外扩展。它同时为发布和订阅提供高吞吐量它支持多订阅者,当失败时能自动平衡消费者消息的持久化kafka和其他消息队列的对比:2.入门实例2.1生产者producerimport java.util.Properties;import org.apache.kafka.clients.producer.KafkaProducer;import org.apache.kafka.clients.producer.ProducerRecord;public class UserKafkaProducer extends Thread{ private final KafkaProducer<Integer, String> producer; private final String topic; private final Properties props = new Properties(); public UserKafkaProducer(String topic) { props.put(“metadata.broker.list”, “localhost:9092”); props.put(“bootstrap.servers”, “master2:6667”); props.put(“retries”, 0); props.put(“batch.size”, 16384); props.put(“linger.ms”, 1); props.put(“buffer.memory”, 33554432); props.put(“key.serializer”, “org.apache.kafka.common.serialization.StringSerializer”); props.put(“value.serializer”, “org.apache.kafka.common.serialization.StringSerializer”); producer = new KafkaProducer<Integer, String>(props); this.topic = topic; } @Override public void run() { int messageNo = 1; while (true) { String messageStr = new String(“Message_” + messageNo); System.out.println(“Send:” + messageStr); //返回的是Future<RecordMetadata>,异步发送 producer.send(new ProducerRecord<Integer, String>(topic, messageStr)); messageNo++; try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }}2.2 消费者Properties props = new Properties();/* 定义kakfa 服务的地址,不需要将所有broker指定上 /props.put(“bootstrap.servers”, “localhost:9092”);/ 制定consumer group /props.put(“group.id”, “test”);/ 是否自动确认offset /props.put(“enable.auto.commit”, “true”);/ 自动确认offset的时间间隔 /props.put(“auto.commit.interval.ms”, “1000”);props.put(“session.timeout.ms”, “30000”);/ key的序列化类 /props.put(“key.deserializer”, “org.apache.kafka.common.serialization.StringDeserializer”);/ value的序列化类 /props.put(“value.deserializer”, “org.apache.kafka.common.serialization.StringDeserializer”); / 定义consumer /KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);/ 消费者订阅的topic, 可同时订阅多个 /consumer.subscribe(Arrays.asList(“foo”, “bar”)); / 读取数据,读取超时时间为100ms */while (true) { ConsumerRecords<String, String> records = consumer.poll(100); for (ConsumerRecord<String, String> record : records) System.out.printf(“offset = %d, key = %s, value = %s”, record.offset(), record.key(), record.value());}3.Kafka架构原理对于kafka的架构原理我们先提出几个问题?1.Kafka的topic和分区内部是如何存储的,有什么特点?2.与传统的消息系统相比,Kafka的消费模型有什么优点?3.Kafka如何实现分布式的数据存储与数据读取?3.1Kafka架构图3.2kafka名词解释在一套kafka架构中有多个Producer,多个Broker,多个Consumer,每个Producer可以对应多个Topic,每个Consumer只能对应一个ConsumerGroup。整个Kafka架构对应一个ZK集群,通过ZK管理集群配置,选举Leader,以及在consumer group发生变化时进行rebalance。3.3Topic和Partition在Kafka中的每一条消息都有一个topic。一般来说在我们应用中产生不同类型的数据,都可以设置不同的主题。一个主题一般会有多个消息的订阅者,当生产者发布消息到某个主题时,订阅了这个主题的消费者都可以接收到生产者写入的新消息。kafka为每个主题维护了分布式的分区(partition)日志文件,每个partition在kafka存储层面是append log。任何发布到此partition的消息都会被追加到log文件的尾部,在分区中的每条消息都会按照时间顺序分配到一个单调递增的顺序编号,也就是我们的offset,offset是一个long型的数字,我们通过这个offset可以确定一条在该partition下的唯一消息。在partition下面是保证了有序性,但是在topic下面没有保证有序性。在上图中在我们的生产者会决定发送到哪个Partition。1.如果没有Key值则进行轮询发送。2.如果有Key值,对Key值进行Hash,然后对分区数量取余,保证了同一个Key值的会被路由到同一个分区,如果想队列的强顺序一致性,可以让所有的消息都设置为同一个Key。3.4消费模型消息由生产者发送到kafka集群后,会被消费者消费。一般来说我们的消费模型有两种:推送模型(psuh)和拉取模型(pull)基于推送模型的消息系统,由消息代理记录消费状态。消息代理将消息推送到消费者后,标记这条消息为已经被消费,但是这种方式无法很好地保证消费的处理语义。比如当我们把已经把消息发送给消费者之后,由于消费进程挂掉或者由于网络原因没有收到这条消息,如果我们在消费代理将其标记为已消费,这个消息就永久丢失了。如果我们利用生产者收到消息后回复这种方法,消息代理需要记录消费状态,这种不可取。如果采用push,消息消费的速率就完全由消费代理控制,一旦消费者发生阻塞,就会出现问题。Kafka采取拉取模型(poll),由自己控制消费速度,以及消费的进度,消费者可以按照任意的偏移量进行消费。比如消费者可以消费已经消费过的消息进行重新处理,或者消费最近的消息等等。3.5网络模型3.5.1 KafkaClient –单线程Selector单线程模式适用于并发链接数小,逻辑简单,数据量小。在kafka中,consumer和producer都是使用的上面的单线程模式。这种模式不适合kafka的服务端,在服务端中请求处理过程比较复杂,会造成线程阻塞,一旦出现后续请求就会无法处理,会造成大量请求超时,引起雪崩。而在服务器中应该充分利用多线程来处理执行逻辑。3.5.2 Kafka–server – 多线程Selector在kafka服务端采用的是多线程的Selector模型,Acceptor运行在一个单独的线程中,对于读取操作的线程池中的线程都会在selector注册read事件,负责服务端读取请求的逻辑。成功读取后,将请求放入message queue共享队列中。然后在写线程池中,取出这个请求,对其进行逻辑处理,即使某个请求线程阻塞了,还有后续的县城从消息队列中获取请求并进行处理,在写线程中处理完逻辑处理,由于注册了OP_WIRTE事件,所以还需要对其发送响应。3.6高可靠分布式存储模型在Kafka中保证高可靠模型的依靠的是副本机制,有了副本机制之后,就算机器宕机也不会发生数据丢失。3.6.1高性能的日志存储kafka一个topic下面的所有消息都是以partition的方式分布式的存储在多个节点上。同时在kafka的机器上,每个Partition其实都会对应一个日志目录,在目录下面会对应多个日志分段(LogSegment)。LogSegment文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为segment索引文件和数据文件。这两个文件的命令规则为:partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值,数值大小为64位,20位数字字符长度,没有数字用0填充,如下,假设有1000条消息,每个LogSegment大小为100,下面展现了900-1000的索引和Log:由于kafka消息数据太大,如果全部建立索引,即占了空间又增加了耗时,所以kafka选择了稀疏索引的方式,这样的话索引可以直接进入内存,加快偏查询速度。简单介绍一下如何读取数据,如果我们要读取第911条数据首先第一步,找到他是属于哪一段的,根据二分法查找到他属于的文件,找到0000900.index和00000900.log之后,然后去index中去查找 (911-900) =11这个索引或者小于11最近的索引,在这里通过二分法我们找到了索引是[10,1367]然后我们通过这条索引的物理位置1367,开始往后找,直到找到911条数据。上面讲的是如果要找某个offset的流程,但是我们大多数时候并不需要查找某个offset,只需要按照顺序读即可,而在顺序读中,操作系统会对内存和磁盘之间添加page cahe,也就是我们平常见到的预读操作,所以我们的顺序读操作时速度很快。但是kafka有个问题,如果分区过多,那么日志分段也会很多,写的时候由于是批量写,其实就会变成随机写了,随机I/O这个时候对性能影响很大。所以一般来说Kafka不能有太多的partition。针对这一点,RocketMQ把所有的日志都写在一个文件里面,就能变成顺序写,通过一定优化,读也能接近于顺序读。可以思考一下:1.为什么需要分区,也就是说主题只有一个分区,难道不行吗?2.日志为什么需要分段1.分区是为了水平扩展2.日志如果在同一个文件太大会影响性能。如果日志无限增长,查询速度会减慢3.6.2副本机制Kafka的副本机制是多个服务端节点对其他节点的主题分区的日志进行复制。当集群中的某个节点出现故障,访问故障节点的请求会被转移到其他正常节点(这一过程通常叫Reblance),kafka每个主题的每个分区都有一个主副本以及0个或者多个副本,副本保持和主副本的数据同步,当主副本出故障时就会被替代。在Kafka中并不是所有的副本都能被拿来替代主副本,所以在kafka的leader节点中维护着一个ISR(In sync Replicas)集合,翻译过来也叫正在同步中集合,在这个集合中的需要满足两个条件:节点必须和ZK保持连接在同步的过程中这个副本不能落后主副本太多另外还有个AR(Assigned Replicas)用来标识副本的全集,OSR用来表示由于落后被剔除的副本集合,所以公式如下:ISR = leader + 没有落后太多的副本; AR = OSR+ ISR;这里先要说下两个名词:HW(高水位)是consumer能够看到的此partition的位置,LEO是每个partition的log最后一条Message的位置。HW能保证leader所在的broker失效,该消息仍然可以从新选举的leader中获取,不会造成消息丢失。当producer向leader发送数据时,可以通过request.required.acks参数来设置数据可靠性的级别:1(默认):这意味着producer在ISR中的leader已成功收到的数据并得到确认后发送下一条message。如果leader宕机了,则会丢失数据。0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。-1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。但是这样也不能保证数据不丢失,比如当ISR中只有leader时(其他节点都和zk断开连接,或者都没追上),这样就变成了acks=1的情况。4.高可用模型及幂等 在分布式系统中一般有三种处理语义:at-least-once:至少一次,有可能会有多次。如果producer收到来自ack的确认,则表示该消息已经写入到Kafka了,此时刚好是一次,也就是我们后面的exactly-once。但是如果producer超时或收到错误,并且request.required.acks配置的不是-1,则会重试发送消息,客户端会认为该消息未写入Kafka。如果broker在发送Ack之前失败,但在消息成功写入Kafka之后,这一次重试将会导致我们的消息会被写入两次,所以消息就不止一次地传递给最终consumer,如果consumer处理逻辑没有保证幂等的话就会得到不正确的结果。 在这种语义中会出现乱序,也就是当第一次ack失败准备重试的时候,但是第二消息已经发送过去了,这个时候会出现单分区中乱序的现象,我们需要设置Prouducer的参数max.in.flight.requests.per.connection,flight.requests是Producer端用来保存发送请求且没有响应的队列,保证Producer端未响应的请求个数为1。at-most-once:如果在ack超时或返回错误时producer不重试,也就是我们讲request.required.acks=-1,则该消息可能最终没有写入kafka,所以consumer不会接收消息。exactly-once:刚好一次,即使producer重试发送消息,消息也会保证最多一次地传递给consumer。该语义是最理想的,也是最难实现的。在0.10之前并不能保证exactly-once,需要使用consumer自带的幂等性保证。0.11.0使用事务保证了4.1 如何实现exactly-once要实现exactly-once在Kafka 0.11.0中有两个官方策略:4.1.1单Producer单Topic每个producer在初始化的时候都会被分配一个唯一的PID,对于每个唯一的PID,Producer向指定的Topic中某个特定的Partition发送的消息都会携带一个从0单调递增的sequence number。在我们的Broker端也会维护一个维度为<PID,Topic,Partition>,每次提交一次消息的时候都会对齐进行校验:如果消息序号比Broker维护的序号大一以上,说明中间有数据尚未写入,也即乱序,此时Broker拒绝该消息,Producer抛出InvalidSequenceNumber如果消息序号小于等于Broker维护的序号,说明该消息已被保存,即为重复消息,Broker直接丢弃该消息,Producer抛出DuplicateSequenceNumber如果消息序号刚好大一,就证明是合法的上面所说的解决了两个问题:1.当Prouducer发送了一条消息之后失败,broker并没有保存,但是第二条消息却发送成功,造成了数据的乱序。2.当Producer发送了一条消息之后,broker保存成功,ack回传失败,producer再次投递重复的消息。上面所说的都是在同一个PID下面,意味着必须保证在单个Producer中的同一个seesion内,如果Producer挂了,被分配了新的PID,这样就无法保证了,所以Kafka中又有事务机制去保证。4.1.2事务在kafka中事务的作用是实现exactly-once语义保证操作的原子性,要么全部成功,要么全部失败。有状态的操作的恢复事务可以保证就算跨多个<Topic, Partition>,在本次事务中的对消费队列的操作都当成原子性,要么全部成功,要么全部失败。并且,有状态的应用也可以保证重启后从断点处继续处理,也即事务恢复。在kafka的事务中,应用程序必须提供一个唯一的事务ID,即Transaction ID,并且宕机重启之后,也不会发生改变,Transactin ID与PID可能一一对应。区别在于Transaction ID由用户提供,而PID是内部的实现对用户透明。为了Producer重启之后,旧的Producer具有相同的Transaction ID失效,每次Producer通过Transaction ID拿到PID的同时,还会获取一个单调递增的epoch。由于旧的Producer的epoch比新Producer的epoch小,Kafka可以很容易识别出该Producer是老的Producer并拒绝其请求。为了实现这一点,Kafka 0.11.0.0引入了一个服务器端的模块,名为Transaction Coordinator,用于管理Producer发送的消息的事务性。该Transaction Coordinator维护Transaction Log,该log存于一个内部的Topic内。由于Topic数据具有持久性,因此事务的状态也具有持久性。Producer并不直接读写Transaction Log,它与Transaction Coordinator通信,然后由Transaction Coordinator将该事务的状态插入相应的Transaction Log。Transaction Log的设计与Offset Log用于保存Consumer的Offset类似。最后关于消息队列或者Kafka的一些常见的面试题,通过上面的文章可以提炼出以下几个比较经典的问题:为什么使用消息队列?消息队列的作用是什么?Kafka的topic和分区内部是如何存储的,有什么特点?与传统的消息系统相比,Kafka的消费模型有什么优点?Kafka如何实现分布式的数据存储与数据读取?kafka为什么比rocketmq支持的单机partion要少?为什么需要分区,也就是说主题只有一个分区,难道不行吗?日志为什么需要分段?kafka是依靠什么机制保持高可靠,高可用?消息队列如何保证消息幂等?让你自己设计个消息队列,你会怎么设计,会考虑哪些方面?大部分问题都可以从上面总结后找到答案,如果还不会的话就关注我的公众号,让我为你解答吧。最后这篇文章被我收录于JGrowing-中间件篇,一个全面,优秀,由社区一起共建的Java学习路线,如果您想参与开源项目的维护,可以一起共建,github地址为:https://github.com/javagrowin… 麻烦给个小星星哟。打个广告,如果你觉得这篇文章对你有文章,可以关注我的技术公众号,你的关注和转发是对我最大的支持,O(∩_∩)O ...

March 4, 2019 · 1 min · jiezi

《从0到1学习Flink》—— Flink 读取 Kafka 数据批量写入到 MySQL

<!– more –>前言之前其实在 《从0到1学习Flink》—— 如何自定义 Data Sink ? 文章中其实已经写了点将数据写入到 MySQL,但是一些配置化的东西当时是写死的,不能够通用,最近知识星球里有朋友叫我: 写个从 kafka 中读取数据,经过 Flink 做个预聚合,然后创建数据库连接池将数据批量写入到 mysql 的例子。于是才有了这篇文章,更多提问和想要我写的文章可以在知识星球里像我提问,我会根据提问及时回答和尽可能作出文章的修改。准备你需要将这两个依赖添加到 pom.xml 中<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version></dependency>读取 kafka 数据这里我依旧用的以前的 student 类,自己本地起了 kafka 然后造一些测试数据,这里我们测试发送一条数据则 sleep 10s,意味着往 kafka 中一分钟发 6 条数据。package com.zhisheng.connectors.mysql.utils;import com.zhisheng.common.utils.GsonUtil;import com.zhisheng.connectors.mysql.model.Student;import org.apache.kafka.clients.producer.KafkaProducer;import org.apache.kafka.clients.producer.ProducerRecord;import java.util.Properties;/** * Desc: 往kafka中写数据,可以使用这个main函数进行测试 * Created by zhisheng on 2019-02-17 * Blog: http://www.54tianzhisheng.cn/tags/Flink/ /public class KafkaUtil { public static final String broker_list = “localhost:9092”; public static final String topic = “student”; //kafka topic 需要和 flink 程序用同一个 topic public static void writeToKafka() throws InterruptedException { Properties props = new Properties(); props.put(“bootstrap.servers”, broker_list); props.put(“key.serializer”, “org.apache.kafka.common.serialization.StringSerializer”); props.put(“value.serializer”, “org.apache.kafka.common.serialization.StringSerializer”); KafkaProducer producer = new KafkaProducer<String, String>(props); for (int i = 1; i <= 100; i++) { Student student = new Student(i, “zhisheng” + i, “password” + i, 18 + i); ProducerRecord record = new ProducerRecord<String, String>(topic, null, null, GsonUtil.toJson(student)); producer.send(record); System.out.println(“发送数据: " + GsonUtil.toJson(student)); Thread.sleep(10 * 1000); //发送一条数据 sleep 10s,相当于 1 分钟 6 条 } producer.flush(); } public static void main(String[] args) throws InterruptedException { writeToKafka(); }}从 kafka 中读取数据,然后序列化成 student 对象。final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();Properties props = new Properties();props.put(“bootstrap.servers”, “localhost:9092”);props.put(“zookeeper.connect”, “localhost:2181”);props.put(“group.id”, “metric-group”);props.put(“key.deserializer”, “org.apache.kafka.common.serialization.StringDeserializer”);props.put(“value.deserializer”, “org.apache.kafka.common.serialization.StringDeserializer”);props.put(“auto.offset.reset”, “latest”);SingleOutputStreamOperator<Student> student = env.addSource(new FlinkKafkaConsumer011<>( “student”, //这个 kafka topic 需要和上面的工具类的 topic 一致 new SimpleStringSchema(), props)).setParallelism(1) .map(string -> GsonUtil.fromJson(string, Student.class)); //,解析字符串成 student 对象因为 RichSinkFunction 中如果 sink 一条数据到 mysql 中就会调用 invoke 方法一次,所以如果要实现批量写的话,我们最好在 sink 之前就把数据聚合一下。那这里我们开个一分钟的窗口去聚合 Student 数据。student.timeWindowAll(Time.minutes(1)).apply(new AllWindowFunction<Student, List<Student>, TimeWindow>() { @Override public void apply(TimeWindow window, Iterable<Student> values, Collector<List<Student>> out) throws Exception { ArrayList<Student> students = Lists.newArrayList(values); if (students.size() > 0) { System.out.println(“1 分钟内收集到 student 的数据条数是:” + students.size()); out.collect(students); } }});写入数据库这里使用 DBCP 连接池连接数据库 mysql,pom.xml 中添加依赖:<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.1.1</version></dependency>如果你想使用其他的数据库连接池请加入对应的依赖。这里将数据写入到 MySQL 中,依旧是和之前文章一样继承 RichSinkFunction 类,重写里面的方法:package com.zhisheng.connectors.mysql.sinks;import com.zhisheng.connectors.mysql.model.Student;import org.apache.commons.dbcp2.BasicDataSource;import org.apache.flink.configuration.Configuration;import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;import javax.sql.DataSource;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.util.List;/* * Desc: 数据批量 sink 数据到 mysql * Created by zhisheng_tian on 2019-02-17 * Blog: http://www.54tianzhisheng.cn/tags/Flink/ /public class SinkToMySQL extends RichSinkFunction<List<Student>> { PreparedStatement ps; BasicDataSource dataSource; private Connection connection; /* * open() 方法中建立连接,这样不用每次 invoke 的时候都要建立连接和释放连接 * * @param parameters * @throws Exception / @Override public void open(Configuration parameters) throws Exception { super.open(parameters); dataSource = new BasicDataSource(); connection = getConnection(dataSource); String sql = “insert into Student(id, name, password, age) values(?, ?, ?, ?);”; ps = this.connection.prepareStatement(sql); } @Override public void close() throws Exception { super.close(); //关闭连接和释放资源 if (connection != null) { connection.close(); } if (ps != null) { ps.close(); } } /* * 每条数据的插入都要调用一次 invoke() 方法 * * @param value * @param context * @throws Exception */ @Override public void invoke(List<Student> value, Context context) throws Exception { //遍历数据集合 for (Student student : value) { ps.setInt(1, student.getId()); ps.setString(2, student.getName()); ps.setString(3, student.getPassword()); ps.setInt(4, student.getAge()); ps.addBatch(); } int[] count = ps.executeBatch();//批量后执行 System.out.println(“成功了插入了” + count.length + “行数据”); } private static Connection getConnection(BasicDataSource dataSource) { dataSource.setDriverClassName(“com.mysql.jdbc.Driver”); //注意,替换成自己本地的 mysql 数据库地址和用户名、密码 dataSource.setUrl(“jdbc:mysql://localhost:3306/test”); dataSource.setUsername(“root”); dataSource.setPassword(“root123456”); //设置连接池的一些参数 dataSource.setInitialSize(10); dataSource.setMaxTotal(50); dataSource.setMinIdle(2); Connection con = null; try { con = dataSource.getConnection(); System.out.println(“创建连接池:” + con); } catch (Exception e) { System.out.println(”———–mysql get connection has exception , msg = " + e.getMessage()); } return con; }}核心类 Main核心程序如下:public class Main { public static void main(String[] args) throws Exception{ final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); Properties props = new Properties(); props.put(“bootstrap.servers”, “localhost:9092”); props.put(“zookeeper.connect”, “localhost:2181”); props.put(“group.id”, “metric-group”); props.put(“key.deserializer”, “org.apache.kafka.common.serialization.StringDeserializer”); props.put(“value.deserializer”, “org.apache.kafka.common.serialization.StringDeserializer”); props.put(“auto.offset.reset”, “latest”); SingleOutputStreamOperator<Student> student = env.addSource(new FlinkKafkaConsumer011<>( “student”, //这个 kafka topic 需要和上面的工具类的 topic 一致 new SimpleStringSchema(), props)).setParallelism(1) .map(string -> GsonUtil.fromJson(string, Student.class)); // student.timeWindowAll(Time.minutes(1)).apply(new AllWindowFunction<Student, List<Student>, TimeWindow>() { @Override public void apply(TimeWindow window, Iterable<Student> values, Collector<List<Student>> out) throws Exception { ArrayList<Student> students = Lists.newArrayList(values); if (students.size() > 0) { System.out.println(“1 分钟内收集到 student 的数据条数是:” + students.size()); out.collect(students); } } }).addSink(new SinkToMySQL()); env.execute(“flink learning connectors kafka”); }}运行项目运行 Main 类后再运行 KafkaUtils.java 类!下图是往 Kafka 中发送的数据:下图是运行 Main 类的日志,会创建 4 个连接池是因为默认的 4 个并行度,你如果在 addSink 这个算子设置并行度为 1 的话就会创建一个连接池:下图是批量插入数据库的结果:总结本文从知识星球一位朋友的疑问来写的,应该都满足了他的条件(批量/数据库连接池/写入mysql),的确网上很多的例子都是简单的 demo 形式,都是单条数据就创建数据库连接插入 MySQL,如果要写的数据量很大的话,会对 MySQL 的写有很大的压力。这也是我之前在 《从0到1学习Flink》—— Flink 写入数据到 ElasticSearch 中,数据写 ES 强调过的,如果要提高性能必定要批量的写。就拿我们现在这篇文章来说,如果数据量大的话,聚合一分钟数据达万条,那么这样批量写会比来一条写一条性能提高不知道有多少。本文原创地址是: http://www.54tianzhisheng.cn/2019/01/15/Flink-MySQL-sink/ , 未经允许禁止转载。关注我微信公众号:zhisheng另外我自己整理了些 Flink 的学习资料,目前已经全部放到微信公众号了。你可以加我的微信:zhisheng_tian,然后回复关键字:Flink 即可无条件获取到。更多私密资料请加入知识星球!Github 代码仓库https://github.com/zhisheng17/flink-learning/以后这个项目的所有代码都将放在这个仓库里,包含了自己学习 flink 的一些 demo 和博客。本文的项目代码在 https://github.com/zhisheng17/flink-learning/tree/master/flink-learning-connectors/flink-learning-connectors-mysql相关文章1、《从0到1学习Flink》—— Apache Flink 介绍2、《从0到1学习Flink》—— Mac 上搭建 Flink 1.6.0 环境并构建运行简单程序入门3、《从0到1学习Flink》—— Flink 配置文件详解4、《从0到1学习Flink》—— Data Source 介绍5、《从0到1学习Flink》—— 如何自定义 Data Source ?6、《从0到1学习Flink》—— Data Sink 介绍7、《从0到1学习Flink》—— 如何自定义 Data Sink ?8、《从0到1学习Flink》—— Flink Data transformation(转换)9、《从0到1学习Flink》—— 介绍Flink中的Stream Windows10、《从0到1学习Flink》—— Flink 中的几种 Time 详解11、《从0到1学习Flink》—— Flink 写入数据到 ElasticSearch12、《从0到1学习Flink》—— Flink 项目如何运行?13、《从0到1学习Flink》—— Flink 写入数据到 Kafka14、《从0到1学习Flink》—— Flink JobManager 高可用性配置15、《从0到1学习Flink》—— Flink parallelism 和 Slot 介绍16、《从0到1学习Flink》—— Flink 读取 Kafka 数据批量写入到 MySQL ...

February 24, 2019 · 4 min · jiezi

从零搭建精准运营系统

2018刚过去,趁着春节放假对过去一年主导开发的项目做个梳理和总结项目背景平台运营到一定阶段,一定会累积大批量的用户数据,这些用户数据是运营人员的黄金财产。而如何利用用户的数据来做运营(消息推送、触达消息、优惠券发送、广告位等),正是精准运营系统需要解决的问题。本文是基于信贷业务实践后写出来的,其它行业如保险、电商、航旅、游戏等也可以参考。业务场景先看几个具有代表性的需求用户可用额度在20000~50000元,而且有借款记录,未还本金为0,性别为“男”用户发生了A行为且未还本金大于5000用户在1天内发生A行为次数大于等于3次用户在A行为前24小时内未发生B行为用户在A行为后一个月内未发生B行为业务上有两种消息类型日常消息:由业务人员通过条件筛选锁定用户群,定时或即时给批量用户发送消息或者优惠券触达消息:主要由用户自身的行为触发,比如登陆、进件申请、还款等,满足一定筛选条件实时给用户发送消息或优惠券对于用户筛选条件,也主要有两种类型用户状态:包括用户自身属性如性别、年龄、学历、收入等,还有用户相关联实体如进件订单、账户信息、还款计划、优惠券等的属性,以及用户画像数据如行为偏好、进件概率等用户行为:即用户的动作,包括登陆、进件申请、还款,甚至前端点击某个按钮、在某个文本框输入都算早期方案早期方案存在以下痛点至少两次跨部门沟通配合成本,周期被拉长非实时消息推送,无法实现基于用户行为的实时推送场景非实时效果验证,无法及时调整运营策略系统搭建的目标需要定义规则,提供可视化界面给业务人员动态配置,无需重启系统即使生效,减少沟通成本和避免重复开发,总之就是要更加 自动化 和 易配置采集实时数据,根据实时事件做实时推送,总之就是要 实时技术选型数据采集、转换、存储采集:状态类的数据主要放在各个业务系统的关系型数据库中,由于历史原因有postgres和mysql,需要实时采集表的数据变更,这里使用kafka connector读取mysql的binlog或postgres的xlog,另外还有标签系统计算出来的标签,在kafka中;而事件类数据主要来源于前端上报事件(有专门的服务接收再丢到kafka),关系型数据库里面也可以提取一些事件。转换:采集出来的数据需要做一些格式统一等操作,用kafka connector。存储:采用Elasticsearch存储用户数据,ES查询不像mysql或mongoDB用B-tree 或B+tree实现索引,而是使用bitset和skip list来处理联合索引,特别适合多字段的复杂查询条件。下面重点看下kafka connector和Elasticsearch如何使用kafka connectorkafka connector有Source和Sink两种组件,Source的作用是读取数据到kafka,这里用开源实现debezium来采集mysql的binlog和postgres的xlog。Sink的作用是从kafka读数据写到目标系统,这里自己研发一套组件,根据配置的规则将数据格式化再同步到ES。kafka connector有以下优点:提供大量开箱即用的插件,比如我们直接用debezium就能解决读取mysql和pg数据变更的问题伸缩性强,对于不同的connector可以配置不同数量的task,分配给不同的worker,,我们可以根据不同topic的流量大小来调节配置。容错性强,worker失败会把task迁移到其它worker上面使用rest接口进行配置,我们可以对其进行包装很方便地实现一套管理界面Elasticsearch对于状态数据,由于状态的写操作相对较少,我们采取嵌套文档的方式,将同个用户的相关实体数据都同步写入到同个文档,具体实现用painless脚本做局部更新操作。效果类似这样:{ “id”:123, “age”:30, “credit_line”:20000, “education”:“bachelor”, … “last_loan_applications”:{ “loan_id”:1234, “status”:“reject”, … } …}事件数据写入比较频繁,数据量比较多,我们使用父子文档的方式做关联,效果类似这样:{ “e_uid”:123, “e_name”:“loan_application”, “e_timestamp”:“2019-01-01 10:10:00” …}(e_前缀是为了防止同个index下同名字段冲突)ES这样存储一方面是方便做统计报表,另一方面跟用户筛选和触达有关。规则引擎在设计规则引擎前,我们对业界已有的规则引擎,主要包括Esper, Drools, Flink CEP,进行了初步调研。EsperEsper设计目标为CEP的轻量级解决方案,可以方便的嵌入服务中,提供CEP功能。优势:轻量级可嵌入开发,常用的CEP功能简单好用。EPL语法与SQL类似,学习成本较低。劣势:单机全内存方案,需要整合其他分布式和存储。以内存实现时间窗功能,无法支持较长跨度的时间窗。无法有效支持定时触达(如用户在浏览发生一段时间后触达条件判断)。DroolsDrools开始于规则引擎,后引入Drools Fusion模块提供CEP的功能。优势:功能较为完善,具有如系统监控、操作平台等功能。规则支持动态更新劣势:以内存实现时间窗功能,无法支持较长跨度的时间窗。无法有效支持定时触达(如用户在浏览发生一段时间后触达条件判断)。FlinkFlink 是一个流式系统,具有高吞吐低延迟的特点,Flink CEP是一套极具通用性、易于使用的实时流式事件处理方案。优势:继承了Flink高吞吐的特点事件支持存储到外部,可以支持较长跨度的时间窗。可以支持定时触达(用followedBy+PartternTimeoutFunction实现)劣势:无法动态更新规则(痛点)自定义规则综上对比了几大开源规则引擎,发现都无法满足业务需求:业务方要求支持长时间窗口(n天甚至n个月,比如放款一个月后如果没产生还款事件就要发消息)动态更新规则,而且要可视化(无论用哪个规则引擎都需要包装,需要考虑二次开发成本)最终我们选择自己根据业务需要,开发基于json的自定义规则,规则类似下面例子:{ “batchId”: “xxxxxxxx”, //流水号,创建每条运营规则时生成 “type”: “trigger”, //usual “triggerEvent”: “login”, “after”: “2h”, //分钟m,小时h,天d,月M “pushRules”: [//支持同时推送多条不同类型的消息 { “pushType”: “sms”, //wx,app,coupon “channel”: “cl”, “content”: “hello #{userInfo.name}” }, { “pushType”: “coupon”, “couponId”: 1234 } ], “statusConditions”: [ { “name”: “and”, //逻辑条件,支持与(and)或(or)非(not) “conditions”: [ { “name”: “range”, “field”: “credit_line”, “left”: 2000, “right”: 10000, “includeLeft”: true, “includeRight”: false }, { “name”:“in”, “filed”:“education”, “values”:[“bachelor”,“master”] } ] } ], “eventConditions”: [ { “name”: “or”,//逻辑条件,支持与(and)或(or)非(not) “conditions”: [ { “name”: “event”, “function”: “count”, //聚合函数,目前只支持count “eventName”: “xxx_button_click”, “range”: { //聚合结果做判断 “left”: 1, “includeLeft”: true }, “timeWindow”: { “type”: “fixed”, //fixed为固定窗口,sliding为滑动窗口 “start”: “2019-01-01 01:01:01”, “end”: “2019-02-01 01:01:01” }, “conditions”: [ //event查询条件继承and逻辑条件,所以事件也可以过滤字段 { “name”: “equals”, “field”: “f1”, “value”: “v1” } ] } ] } ]}使用面向对象思维对过滤条件做抽象后,过滤条件继承关系如下:然后代码里加一层parser把Condition都转成ES查询语句,实现轻量级的业务规则配置功能。整体技术方案系统组成模块及功能如下:mysql binlog:mysql的数据变更,由kafka connector插件读取到kafka,数据源之一postgres xlog:pg的数据变更,由kafka connector插件读取到kafka,数据源之一report server:事件上报服务,数据源之一tags:用户画像系统计算出来的标签,数据源之一触发场景路由:分实时触发和延迟触发,实时触发直接到下一步,延迟触发基于 rabbitmq的延迟队列实现用户筛选模块:将筛选规则翻译为ES查询语句到ES查询用户数据,可以是批量的和单个用户的变量渲染模块:对推送内容做处理推送适配器:兼容不同的推送方式定时任务调度器:基于elastic-job,处理定时推送任务规则配置控制台:提供可视化配置界面(运营规则配置、数据采集规则配置、字段元数据配置等)报表服务:提供报表查询功能运营位服务:提供外部接口,根据条件匹配运营位(如启动图、首页banner图片等)总结与展望系统基本满足了目前的业务需求,对转化率等运营指标提升显著可以扩展其它业务,如推荐、风控、业务监控等规则定时拉取,实时性差,可以用zk做发布订阅实现即时更新目前事件的聚合函数只支持count,能满足业务需求但是未来可能还需要支持其它函数系统只经过千万级用户的生产验证,再高数量级的话可能还有很多性能优化的工作,如ES并行查询(目前用scroll api批量拉取用户数据是串行的)事件类数据越来越多,目前采取定时删除半年前数据的方式,防止持续增长过快不可控,所以事件类条件不可超过半年的时间窗口虽然系统对业务无入侵,但是反过来看本系统依赖于上游数据,上游数据发生变化时如何做到影响最小?未来会继续从技术及业务两方面入手,将系统建设的更加易用、高效。 ...

February 17, 2019 · 1 min · jiezi

Kafka 集群 Golang 应用实例

项目见:???? kafka cluster example这个实例做了些什么?搭建了拥有 3 节点 kafka、 3 节点 zookeeper 的 docker 集群服务;分别创建了 1 个消息发布者和 2 个相同消费组的消息订阅者的 docker 应用;使用 ab 进行并发测试,验证该实例消息的订阅 / 发布功能;通过这个实例,能够了解些什么?使用 Docker Compose 构建 Kafka 集群使用 Golang 创建 Kafka Pub/Sub 实例使用 ApacheBench 进行并发测试使用 Makefile 简化构建操作命令如果这个实例,对你了解 kakfa 有所帮助,请为项目添加 star ,非常感谢!

February 15, 2019 · 1 min · jiezi

学习kafka教程(二)

欢迎关注公众号:n平方如有问题或建议,请后台留言,我会尽力解决你的问题。本文主要介绍【KafkaStreams】简介Kafka Streams编写关键任务实时应用程序和微服务的最简单方法,是一个用于构建应用程序和微服务的客户端库,其中输入和输出数据存储在Kafka集群中。它结合了在客户端编写和部署标准Java和Scala应用程序的简单性和Kafka服务器端集群技术的优点。Kafka Streams是一个用于构建关键任务实时应用程序和微服务的客户端库,其中输入和/或输出数据存储在Kafka集群中。Kafka Streams结合了在客户端编写和部署标准Java和Scala应用程序的简单性和Kafka服务器端集群技术的优点,使这些应用程序具有高度可伸缩性、灵活性、容错性、分布式等等。目标了解kafka Streams会使用kafka Streams过程1.首先WordCountDemo示例代码(Java8以上)// Serializers/deserializers (serde) for String and Long typesfinal Serde<String> stringSerde = Serdes.String();final Serde<Long> longSerde = Serdes.Long(); // Construct a KStream from the input topic “streams-plaintext-input”, where message values// represent lines of text (for the sake of this example, we ignore whatever may be stored// in the message keys).KStream<String, String> textLines = builder.stream(“streams-plaintext-input”, Consumed.with(stringSerde, stringSerde); KTable<String, Long> wordCounts = textLines // Split each text line, by whitespace, into words. .flatMapValues(value -> Arrays.asList(value.toLowerCase().split("\W+"))) // Group the text words as message keys .groupBy((key, value) -> value) // Count the occurrences of each word (message key). .count() // Store the running counts as a changelog stream to the output topic.wordCounts.toStream().to(“streams-wordcount-output”, Produced.with(Serdes.String(), Serdes.Long()));它实现了WordCount算法,该算法从输入文本计算单词出现的直方图。然而,与您以前可能看到的对有界数据进行操作的其他WordCount示例不同,WordCount演示应用程序的行为略有不同,因为它被设计为对无限、无界的数据流进行操作。与有界变量类似,它是一种有状态算法,用于跟踪和更新单词的计数。然而,由于它必须假定输入数据可能是无界的,因此它将周期性地输出当前状态和结果,同时继续处理更多的数据,因为它不知道何时处理了“所有”输入数据。2.安装并启动zookeeper和kafkabin/zookeeper-server-start.sh config/zookeeper.propertiesbin/kafka-server-start.sh config/server.properties3.创建主题接下来,我们创建名为streams-plain -input的输入主题和名为streams-wordcount-output的输出主题:bin/kafka-topics.sh –create \ –zookeeper localhost:2181 \ –replication-factor 1 \ –partitions 1 \ –topic streams-plaintext-inputCreated topic “streams-plaintext-input"我们创建启用压缩的输出主题,因为输出流是一个变更日志流.bin/kafka-topics.sh –create \ –zookeeper localhost:2181 \ –replication-factor 1 \ –partitions 1 \ –topic streams-wordcount-output \ –config cleanup.policy=compactCreated topic “streams-wordcount-output"创建的主题也可以使用相同的kafka主题进行描述bin/kafka-topics.sh –zookeeper localhost:2181 –describe4.启动Wordcount应用程序bin/kafka-run-class.sh org.apache.kafka.streams.examples.wordcount.WordCountDemoa)演示应用程序将从输入主题流(明文输入)中读取,对每个读取的消息执行WordCount算法的计算,并不断将其当前结果写入输出主题流(WordCount -output)。因此,除了日志条目之外,不会有任何STDOUT输出,因为结果是用Kafka写回去的。b)现在我们可以在一个单独的终端上启动控制台生成器,向这个主题写入一些输入数据和检查输出的WordCount演示应用程序从其输出主题与控制台消费者在一个单独的终端.bin/kafka-console-consumer.sh –bootstrap-server localhost:9092 \ –topic streams-wordcount-output \ –from-beginning \ –formatter kafka.tools.DefaultMessageFormatter \ –property print.key=true \ –property print.value=true \ –property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer \ –property value.deserializer=org.apache.kafka.common.serialization.LongDeserializerc)输入端:现在让我们使用控制台生成器将一些消息写入输入主题流——纯文本输入,方法是输入一行文本,然后单击。这将发送新消息输入主题,消息键为空和消息值是刚才输入的字符串编码的文本行。bin/kafka-console-producer.sh –broker-list localhost:9092 –topic streams-plaintext-input此时你可以在控制台输入如下字符:all streams lead to kafkad))输出端:此消息将由Wordcount应用程序处理,以下输出数据将写入streams-wordcount-output主题并由控制台使用者打印:bin/kafka-console-consumer.sh –bootstrap-server localhost:9092 \ –topic streams-wordcount-output \ –from-beginning \ –formatter kafka.tools.DefaultMessageFormatter \ –property print.key=true \ –property print.value=true \ –property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer \ –property value.deserializer=org.apache.kafka.common.serialization.LongDeserializer这个时候会接收到刚刚在控制台输入的单词统计结果:all 1streams 1lead 1to 1kafka 1如此类推:你可以在输入端输入单词,对应的在输出端就会有统计结果。小结:可以看到,Wordcount应用程序的输出实际上是连续的更新流,其中每个输出记录(即上面原始输出中的每一行)是单个单词的更新计数,也就是记录键,如“kafka”。对于具有相同键的多个记录,后面的每个记录都是前一个记录的更新。下面的两个图说明了幕后的本质。第一列显示KTable的当前状态的演变,该状态为count计算单词出现的次数。第二列显示KTable的状态更新所产生的更改记录,这些记录被发送到输出Kafka主题流-wordcount-output。最后本人水平有限,欢迎各位建议以及指正。顺便关注一下公众号呗,会经常更新文章的哦。 ...

January 28, 2019 · 1 min · jiezi

你弄懂Kafka使用什么性能策略吗

本文是我研究Kafka的一点心得,欢迎指出纰漏更多访问我的博客明确Kafka的性能阵地首先,明确研究问题的方向。Kafka 是一个分布式的流式数据平台它的重要功能有:消息系统,提供事件流的发布与订阅持久化存储,避免消费者节点故障的容错功能流处理,如流的聚合、连接,具象来说是处理乱序/延迟的数据、窗口、状态等操作在大数据需求背景下,Kafka 必然要对以上功能进行性能优化,性能的优化要点/瓶颈在于:数据流的传输效率生产者批量发送消息,消费者拉取消息的过程,消息的实时性、可靠性、吞吐量平台级别的持久化存储方案,高容错,多节点备份数据流的传输效率利用操作系统的IO优化技术,脱离JVM的内存局限。为什么从操作系统说起呢?人们每天都在使用操作系统,反而普遍忽略的操作系统的作用,让我们回想起来,操作系统的一大作用是消除硬件差异,为用户程序提供统一标准的API,由此,大部分人使用IO停留在调用系统的 read/write,后端工程师则会更多了解 NIO 的 epoll/kqueue。仅此而已了吗? 让我们看看下面优化策略:磁盘IO的优化策略 mmap实际上,现代的操作系统已经对磁盘IO做了复杂的优化,Linux 下有一个常见的缩写名词 vfs,即虚拟文件系统(virtual file system),它对内存与外存(磁盘)进行映射,使读写速度得到提升,比如以下且不限于:预读(read-ahead),提前将较大的磁盘块载入内存,用户程序读取该磁盘上数据的效率,就等同将内核的内存拷贝到用户程序分配的内存上的速度后写(write-behind),一定次数的小的逻辑写操作会映射到磁盘缓存(page cache),合并为一个大的物理写操作。写入的时机一般是操作系统周期性 sync 而定,用户亦可主动调用 sync,(PS:在Linux用户都该知道拔U盘前执行一次sync)以上的内存/磁盘映射的优化,这依赖于操作系统的预测策略,一般而言,往往是对磁盘的顺序访问,效率明显更高。操作系统除了自动完成以上的过程,还提供API mmap 给用户主动映射文件到page cache,系统会将这一片page cache共享给用户程序的内存,用户程序不必提前 alloc memory,直接读取页面缓存即可访问数据。于是,在频繁访问一个大文件时,比起单纯的write,mmap是一个更好的选择,它减少了普通write过程中的用户态与内核态的上下文切换次数(反复复制缓存)。socket IO 的优化策略 Zero-Copy上面我们认识了磁盘缓存的优化策略,那么对于另一个被频繁使用的IO对象—— socket ,如何优化呢先认识 Linux 2.1起引入的 sendfile 系统调用,通过 sendfile,我们可以把 page cache 的数据直接拷贝到 socket cache 中,仅仅将两者的文件描述符、数据的 offset 与 size 传参给 sendfile, DMA引擎 (Direct Memory Access,直接内存存取)会将内核缓冲区的数据拷贝到协议引擎中去,不需要经过用户态上下文,不需要用户程序准备缓存,于是用户缓存为零,这就是 Zero-Copy 技术。#include<sys/sendfile.h>ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);Zero-Copy 技术对 Java 程序来说无异于神兵,让缓存的大小与速度脱离了 JVM 的局限。结合Kafka的使用场景——多个订阅者拉取消息,消息被多个不同的消费者拉取,使用 Zero-Copy 技术,先调用 mmap 读取磁盘数据到一份 page cache,再多次调用 sendfile 将一份 page cache 复制到不同的 socket cache,整个复制过程在系统内核态中完成,极致的利用了操作系统的性能,瓶颈近乎限于硬件条件(磁盘读写、网络速度)。消息的实时性、可靠性、吞吐量制衡之道从人类实践活动,我们有一个经验是:在一项事情中需要实现多个目标时,往往得到在多个指标相互制衡的结果。以时间换空间——实时性与吞吐量Kafka为了解决网络请求过多的问题,生产者会合并多条消息再提交,降低网络IO的频繁,以牺牲一点延迟换取更高的吞吐量。在实践中,我们可以为生产者客户端配置这个过程的参数,例如:设置消息大小上限为64字节,延迟时间为100毫秒,它的效果是:100毫秒内消息大小达到64字节就要立即发送,如果在100毫秒没有达到64字节,但是100毫秒时间到了,也要发送缓存中的消息。分而治之大数据场景下,分布式的架构设计是必然之前分析了单机环境的策略(操作系统、通信IO),然后,在水平拓展上,Kafka有什么性能优化策略呢?做一个分布式的消息系统,需要考虑什么呢?如何利用分布式、多节点的优势,增强消息系统的吞吐量、容灾能力、灵活扩容的能力…让我们将思路抽象化,消息是流动的水,单机下是一条水管,多节点下是一片自来水网络,为了使消息的流动更加稳健,我们得保证在消息流动的每一个环节都有所保障先罗列消息流动的每一个环节消息的状态,在大多数消息队列应用中,通常有三个等级:生产者发布消息后不管是否被消费者收到生产者发布消息后必须有一个以上的消费者收到生产者发布消息后确保有且只有一个消费者收到消息的缓存流量削峰,在单位时间内,消费者处理流量低于生产者发布流量时,消息系统需要冗余消息消费者处理消息的事务出错,或者消费者收到消息后还未处理时出故障,那么,消息需要再次处理消息的顺序具体业务场景对消息的顺序有要求,需要严格按照先后顺序处理消息,要保证,消费者收到时序性的消息Kafka 如何应对这些环节呢?Kafka 规则一,消息存储在两个维度上,虚拟上的分区(partition) 与 物理上的节点(broker),两者是多对一关系Kafka 规则二,一个 Topic 的一个分区同一时间最多只有一个消费者线程拉取消息Kafka 规则三,维护一个分区上消息缓存的消费者拉取进度在水平拓展上,Kafka将一个 Topic 下从生产者收集到的消息存放到多个分区(partition)上,分区数大于等于Kafka节点数(brokers),每一个分区最多分配一个消费者对于消息的缓存容量、流量分化的问题,Kafka在zookeeper的协同下,可以灵活拓展节点(brokers)对于消息的状态问题,Kafka维护一个分区上的 offset 值,保存消息的消费进度,而这个进度需要由消费者提交若消费者拉取某一个分区的消息,一直不提交最新的 offset 值,那么分区上的 offset 一直落后于消息实际拉取的进度,当分区与消费者的关联重新分配时,分区从上一次开始的地方重新把消息再发送一遍若消费者拉取一条消息后,正常处理后,提交offset到分区,一直如此,到某一条消息的处理过程中,消费者意外发生故障,Kafka可以识别到消费者的通信连接中断,会触发分区与消费者关联的 rebalance对于消息的时序问题,每一个分区最多分配一个消费者,通过维护消费进度,就可以保证同一个分区下的消息的时序性,由此:在生产者发布对某一个对象具有时序性的消息时,可以为该消息标记上该对象的id,Kafka会通过该对象id的哈希值,分配消息到某一个符合哈希值的分区,由此,某一个对象的时序性消息即存放在一个分区内,由一个消费者拉取该分区的消息,可以确保消息按时序被消费者收到上面场景要求在分区没有重新分配(rebalance)下才生效,若是broker节点拓展或故障,会触发分区的重新分配,另一方面,消费者节点拓展或故障,都会触发分区与消费者关联的重新分配直观来看,分区(partition)、消费者(consumer)会发生一下几种情况的 rebalance一开始有四个分区,一个消费者,四个分区的消息都需要被拉取,只好关联同一个消费者消费者有两个了,可以均衡分配消费者四个了,更好了消费者五个,为了保存消息的时序性,维护一个offset值,一个分区最多只能关联一个消费者,所以这里多出一个消费者空闲了由于业务要求,消费者有两个分组了,消息的时序性是只对一个分区、一个消费者分组生效的,这里一个分区可以关联多个相互不同分组的消费者,维护多个 offset未雨绸缪除了水平拓展的分区,还要对总分区进行多个备份(Replicas),对一个分区设置一个 leader 多个 follower,由一个 leader 处理该分区的事务,follower 需要处于 ISR 状态(In Sync Replicas),一旦 leader 故障,通过在备份最新的 follower 中产生新的 leader总结IO密集、大文件的操作,可以在操作系统层次上优化,善用 mmap, sendfile重复操作密集时,可以“批量”处理,以时间换空间,获得更大的吞吐量分布式有一个广泛使用的设计,从两个维度进行水平拓展,虚拟分区与物理节点Reference浅析Linux中的零拷贝技术Kafka Consumers: Reading Data from Kafka ...

January 28, 2019 · 1 min · jiezi

学习kafka教程(一)

简介kafka是用于构建实时数据管道和流应用程序。具有横向扩展,容错,wicked fast(变态快)等优点,并已在成千上万家公司运行。目标了解kafka的基本原理掌握kafka的基本操作kafka的深度探究在另一篇文章。相关概念producer:生产者,就是它来生产“叉烧包”的饭堂阿姨。consumer:消费者,生产出来的“叉烧包”它来消费。topic:你把它理解为标签,生产者每生产出来一个叉烧包就贴上一个标签(topic),消费者可不是谁生产的“叉烧包”都吃的,这样不同的生产者生产出来的“叉烧包”,消费者就可以选择性的“吃”了。broker:就是蒸笼了。所以整个过程可以如下形象的说明:饭堂阿姨制作一个叉烧包,消费者就消费一个叉烧包。1.假设消费者消费叉烧包的时候噎住了(系统宕机了),生产者还在生产叉烧包,那新生产的叉烧包就丢失了。2.再比如生产者很强劲(大交易量的情况),生产者1秒钟生产100个叉烧包,消费者1秒钟只能吃50个叉烧包,那要不了一会,消费者就吃不消了(消息堵塞,最终导致系统超时),消费者拒绝再吃了,”叉烧包“又丢失了。3.这个时候我们放个篮子在它们中间,生产出来的叉烧包都放到篮子里,消费者去篮子里拿叉烧包,这样叉烧包就不会丢失了,都在篮子里,而这个篮子就是”kafka“。4.叉烧包其实就是“数据流”,系统之间的交互都是通过“数据流”来传输的(就是tcp、http什么的),也称为报文,也叫“消息”。5.消息队列满了,其实就是篮子满了,”叉烧包“ 放不下了,那赶紧多放几个篮子,其实就是kafka的扩容。所以说 kafka == 篮子。安装1.由于kafka需要zookeeper的。所以您可以参考【谈谈zookeeper】2.kafka安装2.1下载地址:http://mirror.bit.edu.cn/apac…2.2 配置:(注:KAFKA_HOME为你配置的环境变量。hadoop01为你配置hosts)编辑$KAFKA_HOME/config/下的server.properties文件server.propertiesbroker.id=0#listeners=PLAINTEXT://:9092log.dirs=/root/app/tmp/kafkalognum.partitions=1zookeeper.connect=hadoop01:21812.3 多broker的kafka安装配置config/server-1.properties: broker.id=1 listeners=PLAINTEXT://:9093 log.dir=/tmp/kafka-logs-1config/server-2.properties: broker.id=2 listeners=PLAINTEXT://:9094 log.dir=/tmp/kafka-logs-2常用操作命令启动kafkakafka-server-start.sh -daemon $KAFKA_HOME/config/server.properties创建topicbin/kafka-topics.sh –create –zookeeper hadoop01:2181 –replication-factor 1 –partitions 1 –topic hello_topic查看topic./kafka-topics.sh –list –zookeeper hadoop01:2181查看指定topic的详细信息kafka-topics.sh –describe –zookeeper hadoop01:2181生产消息./kafka-console-producer.sh –broker-list hadoop01:9092 –topic hello_topic消费消息./kafka-console-consumer.sh –bootstrap-server hadoop01:9092 –topic hello_topic –from-beginning0.9.0版本的用下面的命令./kafka-console-consumer.sh –zookeeper hadoop01:2181 –topic hello_topic –from-beginning解析:–from-beginning:是从producer开始的位置开始拿数据的。Springboot操作kafka特别注意(巨坑):kafka有很多版本的。各版本对应使用的springboot或者jar是不一样。请参考spring官网的说明:http://spring.io/projects/spr…本文使用的是springboot1.5系列+0.10.0.x的pom.xml<dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>1.0.5.RELEASE</version></dependency>生产者代码主要是向kafka服务发送消息(生产消息)。/** * 测试kafka生产者 /@RestController@RequestMapping(“kafka”)public class TestKafkaProducerController { @Autowired private KafkaTemplate<String, String> kafkaTemplate; @RequestMapping(“send”) public String send(String msg){ kafkaTemplate.send(“hello_topic”, msg); return “success”; }}消费者代码从主题(topic)中获取消息进行消费。/* * kafka消费者测试 */@Componentpublic class TestConsumer { @KafkaListener(topics = “hello_topic”) public void listen (ConsumerRecord<?, ?> record) throws Exception { System.out.printf(“topic = %s, offset = %d, value = %s \n”, record.topic(), record.offset(), record.value()); }}yml配置文件主要是配置kafka的服务地址。spring: kafka: bootstrap-servers: 120.79.xxx.x:9092 producer: key-serializer: org.apache.kafka.common.serialization.StringSerializer value-serializer: org.apache.kafka.common.serialization.StringSerializer consumer: group-id: test enable-auto-commit: true auto-commit-interval: 1000 key-deserializer: org.apache.kafka.common.serialization.StringDeserializer value-deserializer: org.apache.kafka.common.serialization.StringDeserializer最后本人水平有限,欢迎各位建议以及指正。顺便关注一下公众号呗,会经常更新文章的哦。 ...

January 27, 2019 · 1 min · jiezi

高级开发人员必备技术:MQ

也许在你们公司从没有使用过MQ,也不知道这东西是用来干什么的,但是一旦你进入大公司你就会发现,这东西处处可见。今天就来说说MQ方面的东西,我公众号有activemq的 demo,大家可以自己去看看。什么是MQMessage Queue简称MQ,中文消息队列。“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。你可以把它理解为一个中间件,帮你完成多个系统或应用间的消息传递。为什么要使用MQ首先它有3个核心,解耦,异步,削峰,因此我们可以想到以下使用场景:你的系统要和多个系统发生关系,别的系统要从你这获取一些数据,今天A系统和你要这样的数据,明天B系统说你的数据有问题,后天C系统让你加个别的数据。你一个人要维护解决很多问题,忙得不可开交。而有了MQ,你就可以通过Pub/Sub 发布订阅消息这么一个模型,系统就跟其它系统彻底解耦了。只要把消息放到队列里,其它系统就自己去处理自己需要的数据,自己不再考虑该给谁发送数据。比如:下完订单,不再去通知库存做同步处理。把该物品的信息放在队列中,库存自己去选择什么时候去处理计算自己的库存。还是上面的例子,之前的流程,user浏览器端发起购物请求3ms,订单系统处理数据库300ms,之后库存系统处理数据库300ms,这样同步情况下加起来就要603ms。即使前端有加载提示框,等待时间超过300ms,人眼是能感受到这种延迟的,体验很不好,速度越快才能留住user。现在使用MQ采用异步消息处理,假如消息放进队列需要3ms,那么最终的响应时间是3+3=6ms,对于创建订单和库存计算user并不关心,这样极大的提高了响应时间。一个大的网站或是应用,总会迎来访问量的高峰,可能是营销活动突然带来的大流量,或是节假日。比如双十一,购物人数突然猛增,并发数提高,数据库的压力突然增大,超出了每秒钟的处理能力,就会导致网站瘫痪。使用mq后,数据库可以不必立马处理这么多的请求,可以自己选择能承受的消息慢慢去处理。所有的消息积压在队列中,而不是同时积压到数据库。加入队列中积压了1亿条数据,而我的数据库只能每秒处理100万条数据,那我就每秒从队列中取出100万条来处理,始终不会超出阈值,这样数据库就不会被挤垮。把峰值慢慢消耗。现在想想你为什么没有使用到mq吧?或是考略使用mq使用后带来的威胁任何事物都有它的两面性,既然有优点那也有缺点:系统可用性降低万一mq挂了,队列里面的数据没有了,其它系统数据还没处理完,这可咋整?系统的复杂度提高了你用个mq是爽了,其它系统也要对应的修改自己的系统,来消费队列中的消息。硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。一致性问题你订单系统操作成功了,但是库存系统却失败了,这样导致了数据的不一致。所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了 10 倍。但是关键时刻,用,还是得用的。主流的MQ产品KafkaActiveMQRabbitMQ特性ActiveMQRabbitMQRocketMQKafka单机吞吐量万级,比 RocketMQ、Kafka 低一个数量级同 ActiveMQ10 万级,支撑高吞吐10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景topic 数量对吞吐量的影响 topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topictopic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源时效性ms 级微秒级,这是 RabbitMQ 的一大特点,延迟最低ms 级延迟在 ms 级以内可用性高,基于主从架构实现高可用同 ActiveMQ非常高,分布式架构非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用消息可靠性有较低的概率丢失数据基本不丢经过参数优化配置,可以做到 0 丢失同 RocketMQ功能支持MQ 领域的功能极其完备基于 erlang 开发,并发能力很强,性能极好,延时很低MQ 功能较为完善,还是分布式的,扩展性好功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用上面表格来自:https://github.com/doocs/adva…推荐使用早期大家都在使用ActiveMQ ,适合小型项目,如果你尝试使用MQ,你可以选择。RabbitMQ社区活跃度比较高,开源,有问题可以在社区寻求帮助。但是底层使用了erlang 语言,不是小公司又能力掌控的 。RocketMQ 阿里出品,是用的中国公司比较多,经历过使用场景的考验,且自家产品也在用,不用担心。但是社区活跃度不高。推荐使用。如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。

January 25, 2019 · 1 min · jiezi

《从0到1学习Flink》—— Flink 写入数据到 Kafka

前言之前文章 《从0到1学习Flink》—— Flink 写入数据到 ElasticSearch 写了如何将 Kafka 中的数据存储到 ElasticSearch 中,里面其实就已经用到了 Flink 自带的 Kafka source connector(FlinkKafkaConsumer)。存入到 ES 只是其中一种情况,那么如果我们有多个地方需要这份通过 Flink 转换后的数据,是不是又要我们继续写个 sink 的插件呢?确实,所以 Flink 里面就默认支持了不少 sink,比如也支持 Kafka sink connector(FlinkKafkaProducer),那么这篇文章我们就讲讲如何将数据写入到 Kafka。准备添加依赖Flink 里面支持 Kafka 0.8、0.9、0.10、0.11 ,以后有时间可以分析下源码的实现。这里我们需要安装下 Kafka,请对应添加对应的 Flink Kafka connector 依赖的版本,这里我们使用的是 0.11 版本:<dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-kafka-0.11_2.11</artifactId> <version>${flink.version}</version></dependency>Kafka 安装这里就不写这块内容了,可以参考我以前的文章 Kafka 安装及快速入门。这里我们演示把其他 Kafka 集群中 topic 数据原样写入到自己本地起的 Kafka 中去。配置文件kafka.brokers=xxx:9092,xxx:9092,xxx:9092kafka.group.id=metrics-group-testkafka.zookeeper.connect=xxx:2181metrics.topic=xxxstream.parallelism=5kafka.sink.brokers=localhost:9092kafka.sink.topic=metric-teststream.checkpoint.interval=1000stream.checkpoint.enable=falsestream.sink.parallelism=5目前我们先看下本地 Kafka 是否有这个 metric-test topic 呢?需要执行下这个命令:bin/kafka-topics.sh –list –zookeeper localhost:2181可以看到本地的 Kafka 是没有任何 topic 的,如果等下我们的程序运行起来后,再次执行这个命令出现 metric-test topic,那么证明我的程序确实起作用了,已经将其他集群的 Kafka 数据写入到本地 Kafka 了。程序代码Main.javapublic class Main { public static void main(String[] args) throws Exception{ final ParameterTool parameterTool = ExecutionEnvUtil.createParameterTool(args); StreamExecutionEnvironment env = ExecutionEnvUtil.prepare(parameterTool); DataStreamSource<Metrics> data = KafkaConfigUtil.buildSource(env); data.addSink(new FlinkKafkaProducer011<Metrics>( parameterTool.get(“kafka.sink.brokers”), parameterTool.get(“kafka.sink.topic”), new MetricSchema() )).name(“flink-connectors-kafka”) .setParallelism(parameterTool.getInt(“stream.sink.parallelism”)); env.execute(“flink learning connectors kafka”); }}运行结果启动程序,查看运行结果,不段执行上面命令,查看是否有新的 topic 出来:执行命令可以查看该 topic 的信息:bin/kafka-topics.sh –describe –zookeeper localhost:2181 –topic metric-test分析上面代码我们使用 Flink Kafka Producer 只传了三个参数:brokerList、topicId、serializationSchema(序列化)其实也可以传入多个参数进去,现在有的参数用的是默认参数,因为这个内容比较多,后面可以抽出一篇文章单独来讲。总结本篇文章写了 Flink 读取其他 Kafka 集群的数据,然后写入到本地的 Kafka 上。我在 Flink 这层没做什么数据转换,只是原样的将数据转发了下,如果你们有什么其他的需求,是可以在 Flink 这层将数据进行各种转换操作,比如这篇文章中的一些转换:《从0到1学习Flink》—— Flink Data transformation(转换),然后将转换后的数据发到 Kafka 上去。本文原创地址是: http://www.54tianzhisheng.cn/2019/01/06/Flink-Kafka-sink/ , 未经允许禁止转载。关注我微信公众号:zhisheng另外我自己整理了些 Flink 的学习资料,目前已经全部放到微信公众号了。你可以加我的微信:zhisheng_tian,然后回复关键字:Flink 即可无条件获取到。Github 代码仓库https://github.com/zhisheng17/flink-learning/以后这个项目的所有代码都将放在这个仓库里,包含了自己学习 flink 的一些 demo 和博客相关文章1、《从0到1学习Flink》—— Apache Flink 介绍2、《从0到1学习Flink》—— Mac 上搭建 Flink 1.6.0 环境并构建运行简单程序入门3、《从0到1学习Flink》—— Flink 配置文件详解4、《从0到1学习Flink》—— Data Source 介绍5、《从0到1学习Flink》—— 如何自定义 Data Source ?6、《从0到1学习Flink》—— Data Sink 介绍7、《从0到1学习Flink》—— 如何自定义 Data Sink ?8、《从0到1学习Flink》—— Flink Data transformation(转换)9、《从0到1学习Flink》—— 介绍Flink中的Stream Windows10、《从0到1学习Flink》—— Flink 中的几种 Time 详解11、《从0到1学习Flink》—— Flink 写入数据到 ElasticSearch12、《从0到1学习Flink》—— Flink 项目如何运行?13、《从0到1学习Flink》—— Flink 写入数据到 Kafka ...

January 17, 2019 · 1 min · jiezi

【kafka KSQL】游戏日志统计分析(3)

接上篇文章 【kafka KSQL】游戏日志统计分析(2),本文主要通过实例展示KSQL的连接查询功能。创建另一个topicbin/kafka-topics –create –zookeeper localhost:2181 –replication-factor 1 –partitions 4 –topic prop-normalized往新topic中写入数据bin/kafka-console-producer –broker-list localhost:9092 –topic prop-normalized>{“user__name”:“lzb”, “prop__id”:“id1”}从prop-normalized主题创建StreamCREATE STREAM PROP_USE_EVENT \ (user__name VARCHAR, \ prop__id VARCHAR ) \ WITH (KAFKA_TOPIC=‘prop-normalized’, \ VALUE_FORMAT=‘json’);重新设置ROWKEY为user__nameCREATE STREAM PROP_USE_EVENT_REKEY AS \ SELECT * FROM PROP_USE_EVENT \ PARTITION BY user__name;查询完成3局对局且没有使用过道具的所有玩家查询出所有玩家的对局情况,并创建表USER_SCORE_TABLE(前面已经创建过了):CREATE TABLE USER_SCORE_TABLE AS \ SELECT username, COUNT() AS game_count, SUM(delta) AS delta_sum, SUM(tax) AS tax_sum \ FROM USER_SCORE_EVENT \ WHERE reason = ‘game’ \ GROUP BY username;查询出所有玩家的道具使用情况,并创建表USER_PROP_TABLE:CREATE TABLE USER_PROP_TABLE AS \ SELECT username, COUNT() \ FROM PROP_USE_EVENT_REKEY \ GROUP BY username;使用LEFT JOIN进行左关联查询:SELECT s.username AS username \FROM USER_SCORE_TABLE s \LEFT JOIN USER_PROP_TABLE p \ON s.username = p.username; ...

January 9, 2019 · 1 min · jiezi

如何使用kafka增加topic的备份数量,让业务更上一层楼

本文由云+社区发表一、困难点建立topic的时候,可以通过指定参数 –replication-factor 设置备份数量。但是,一旦完成建立topic,则无法通过kafka-topic.sh 或者 命令修改replica数量。二、解决办法 实际上,我们可以考虑一种 “另类” 的办法:可以利用 kafka-reassign-partitions.sh 命令对所有分区进行重新分布,在做分区重新分布的时候,通过增加每个分区的replica备份数量来达到目的。 本文将介绍如何利用 kafka-reassign-partitions.sh 命令增加topic的备份数量。注意:以下命令使用到的topic名称、zookeeper的ip和port,需要读者替换成为实际集群的参数。(假设kafka集群有4个broker,id分别为:1001,1002,1003,1004)2.1、获取当前topic的所有分区分布在broker的情况[root@tbds bin]# ./kafka-topics.sh –zookeeper 172.16.32.13:2181 –topic ranger_audits –describeTopic:ranger_audits PartitionCount:10 ReplicationFactor:1 Configs: Topic: ranger_audits Partition: 0 Leader: 1001 Replicas: 1001 Isr: 1001 Topic: ranger_audits Partition: 1 Leader: 1002 Replicas: 1002 Isr: 1002 Topic: ranger_audits Partition: 2 Leader: 1001 Replicas: 1001 Isr: 1001 Topic: ranger_audits Partition: 3 Leader: 1002 Replicas: 1002 Isr: 1002 Topic: ranger_audits Partition: 4 Leader: 1001 Replicas: 1001 Isr: 1001 Topic: ranger_audits Partition: 5 Leader: 1002 Replicas: 1002 Isr: 1002 Topic: ranger_audits Partition: 6 Leader: 1001 Replicas: 1001 Isr: 1001 Topic: ranger_audits Partition: 7 Leader: 1002 Replicas: 1002 Isr: 1002 Topic: ranger_audits Partition: 8 Leader: 1001 Replicas: 1001 Isr: 1001 Topic: ranger_audits Partition: 9 Leader: 1002 Replicas: 1002 Isr: 1002可以看出,ranger_audits 这个topic有10个分区,每个分区只有一个feplica备份,分布在1001和1002两台broker上面。下面我们需要将ranger_audits 的每个分区数据都增加到2个replica备份,且分布到4个broker上面。2.2、创建增加replica备份数量的配置文件(注意:尽量保持topic的原有每个分区的主备份不变化。因此,配置文件的每个分区的第一个broker保持不变。)[root@tbds bin]# vim ../config/increase-replication-factor.json{“version”:1,“partitions”:[{“topic”:“ranger_audits”,“partition”:0,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:1,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:2,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:3,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:4,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:5,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:6,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:7,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:8,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:9,“replicas”:[1002,1004]}]}上面的配置文件说明,我们将topic的每个分区都增加了一个replica,且保持每个分区原有的主备份所在broker不变化,将每个分区新增的replica备份数据放到到1003和1004两个broker上面。2.3、开始执行增加分区[root@tbds bin]# ./kafka-reassign-partitions.sh -zookeeper 172.16.32.13:2181 –reassignment-json-file ../config/increase-replication-factor.json –executeCurrent partition replica assignment{“version”:1,“partitions”:[{“topic”:“ranger_audits”,“partition”:3,“replicas”:[1002]},{“topic”:“ranger_audits”,“partition”:9,“replicas”:[1002]},{“topic”:“ranger_audits”,“partition”:8,“replicas”:[1001]},{“topic”:“ranger_audits”,“partition”:1,“replicas”:[1002]},{“topic”:“ranger_audits”,“partition”:4,“replicas”:[1001]},{“topic”:“ranger_audits”,“partition”:2,“replicas”:[1001]},{“topic”:“ranger_audits”,“partition”:5,“replicas”:[1002]},{“topic”:“ranger_audits”,“partition”:0,“replicas”:[1001]},{“topic”:“ranger_audits”,“partition”:6,“replicas”:[1001]},{“topic”:“ranger_audits”,“partition”:7,“replicas”:[1002]}]}Save this to use as the –reassignment-json-file option during rollbackSuccessfully started reassignment of partitions {“version”:1,“partitions”:[{“topic”:“ranger_audits”,“partition”:0,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:8,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:5,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:2,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:9,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:1,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:3,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:4,“replicas”:[1001,1003]},{“topic”:“ranger_audits”,“partition”:7,“replicas”:[1002,1004]},{“topic”:“ranger_audits”,“partition”:6,“replicas”:[1001,1003]}]}2.4、查看执行进度[root@tbds bin]# ./kafka-reassign-partitions.sh -zookeeper 172.16.32.13:2181 –reassignment-json-file ../config/increase-replication-factor.json –verifyStatus of partition reassignment:Reassignment of partition [ranger_audits,0] completed successfullyReassignment of partition [ranger_audits,8] completed successfullyReassignment of partition [ranger_audits,5] completed successfullyReassignment of partition [ranger_audits,2] completed successfullyReassignment of partition [ranger_audits,9] completed successfullyReassignment of partition [ranger_audits,1] completed successfullyReassignment of partition [ranger_audits,3] completed successfullyReassignment of partition [ranger_audits,4] completed successfullyReassignment of partition [ranger_audits,7] completed successfullyReassignment of partition [ranger_audits,6] completed successfully上面显示增加分区操作成功2.5、再次查看topic的情况[root@tbds bin]# ./kafka-topics.sh –zookeeper 172.16.32.13:2181 –topic ranger_audits –describeTopic:ranger_audits PartitionCount:10 ReplicationFactor:2 Configs: Topic: ranger_audits Partition: 0 Leader: 1001 Replicas: 1001,1003 Isr: 1001,1003 Topic: ranger_audits Partition: 1 Leader: 1002 Replicas: 1002,1004 Isr: 1002,1004 Topic: ranger_audits Partition: 2 Leader: 1001 Replicas: 1001,1003 Isr: 1001,1003 Topic: ranger_audits Partition: 3 Leader: 1002 Replicas: 1002,1004 Isr: 1002,1004 Topic: ranger_audits Partition: 4 Leader: 1001 Replicas: 1001,1003 Isr: 1001,1003 Topic: ranger_audits Partition: 5 Leader: 1002 Replicas: 1002,1004 Isr: 1002,1004 Topic: ranger_audits Partition: 6 Leader: 1001 Replicas: 1001,1003 Isr: 1001,1003 Topic: ranger_audits Partition: 7 Leader: 1002 Replicas: 1002,1004 Isr: 1002,1004 Topic: ranger_audits Partition: 8 Leader: 1001 Replicas: 1001,1003 Isr: 1001,1003 Topic: ranger_audits Partition: 9 Leader: 1002 Replicas: 1002,1004 Isr: 1002,1004从上面可以看出,备份数量增加成功三、进一步思考 利用上述介绍的办法,除了可以用来增加topic的备份数量之外,还能够处理以下几个场景:1、对topic的所有分区数据进行整体迁移。怎么理解呢?假如集群有N个broker,后来新扩容M个broker。由于新扩容的broker磁盘都是空的,原有的broker磁盘占用都很满。那么我们可以利用上述方法,将存储在原有N个broker的某些topic整体搬迁到新扩容的M个broker,进而实现kafka集群的整体数据均衡。 具体使用方法就是:通过编写2.2章节的配置文件,将topic的所有分区都配置到新的M个broker上面去,再执行excute,即可完成topic的所有分区数据整体迁移到新扩容的M个broker节点。*2、broker坏掉的情况。*导致某些topic的某些分区的replica数量减少,可以利用kafka-reassign-partitions.sh增加replica;*3、kafka 某些broker磁盘占用很满,某些磁盘占用又很少。*可以利用kafka-reassign-partitions.sh迁移某些topic的分区数据到磁盘占用少的broker,实现数据均衡;*4、kafka集群扩容。*需要把原来broker的topic数据整体迁移到新的broker,合理利用新扩容的broker,实现负载均衡。此文已由作者授权腾讯云+社区在各渠道发布获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号 ...

January 8, 2019 · 2 min · jiezi

【kafka KSQL】游戏日志统计分析(2)

接上一篇文章【kafka KSQL】游戏日志统计分析(1),展示一下KSQL WINDOW 功能的使用。测试用日志数据:{“cost”:7, “epoch”:1512342568296,“gameId”:“2017-12-04_07:09:28_高手2区_500_015_185175”,“gameType”:“situan”,“gamers”: [{“balance”:4405682,“delta”:-60,“username”:“lza”}, {“balance”:69532,“delta”:-60,“username”:“lzb”}, {“balance”:972120,“delta”:-60,“username”:“lzc”}, {“balance”:23129,“delta”:180,“username”:“lze”}],“reason”:“game”}KSQL三种Window统计每2分钟内完成对局大于等于3局的玩家根据时间窗口(Tumbling window)建立table:CREATE TABLE users_per_minute AS \ SELECT username, COUNT() AS game_count, SUM(delta) AS delta_sum, SUM(tax) AS tax_sum , WINDOWSTART() AS win_start, WINDOWEND() AS win_end \ FROM USER_SCORE_EVENT \ WINDOW TUMBLING (SIZE 2 MINUTE) \ WHERE reason = ‘game’ \ GROUP BY username;过滤出game_count大于3局的玩家:SELECT username, game_count, win_start, win_end FROM users_per_minute WHERE game_count >= 3;输出:lze | 6 | 1546744320000 | 1546744440000lzc | 6 | 1546744320000 | 1546744440000lza | 6 | 1546744320000 | 1546744440000lzb | 6 | 1546744320000 | 1546744440000lzb | 3 | 1546744440000 | 1546744560000lzc | 3 | 1546744440000 | 1546744560000lza | 3 | 1546744440000 | 1546744560000lze | 3 | 1546744440000 | 1546744560000统计曾在10分钟之内完成过3局牌局的玩家不限定某个特定的10分钟,只要在某个10分钟之内完成了即可。创建HOPPING WINDOW时间窗口Table:CREATE TABLE users_hopping_10_minute AS \ SELECT username, COUNT() AS game_count, SUM(delta) AS delta_sum, SUM(tax) AS tax_sum , TIMESTAMPTOSTRING(WindowStart(), ‘yyyy-MM-dd HH:mm:ss’) AS win_start, TIMESTAMPTOSTRING(WindowEnd(), ‘yyyy-MM-dd HH:mm:ss’) AS win_end \ FROM USER_SCORE_EVENT \ WINDOW HOPPING (SIZE 10 MINUTE, ADVANCE BY 30 SECONDS) \ WHERE reason = ‘game’ \ GROUP BY username;过滤出game_count大于等于3的玩家SELECT username \FROM users_hopping_10_minute \WHERE game_count >= 3 \GROUP BY username; ...

January 6, 2019 · 1 min · jiezi

【kafka KSQL】游戏日志统计分析(1)

【kafka KSQL】游戏日志统计分析(1)以游戏结算日志为例,展示利用KSQL对日志进行统计分析的过程。启动confluentcd ~/Documents/install/confluent-5.0.1/bin/confluent start查看kafka主题列表bin/kafka-topics –list –zookeeper localhost:2181创建接受游戏结算日志的topicbin/kafka-topics –create –zookeeper localhost:2181 –replication-factor 1 –partitions 4 –topic score-normalized使用生产者命令行工具往topic中写日志bin/kafka-console-producer –broker-list localhost:9092 –topic score-normalized> {“cost”:7, “epoch”:1512342568296,“gameId”:“2017-12-04_07:09:28_高手1区_200_015_185175”,“gameType”:“situan”,“gamers”: [{“balance”:4405682,“delta”:-60,“username”:“0791754000”}, {“balance”:69532,“delta”:-60,“username”:“70837999”}, {“balance”:972120,“delta”:-60,“username”:“abc6378303”}, {“balance”:23129,“delta”:180,“username”:“a137671268”}],“reason”:“xiayu”}使用消费者命令行工具查看日志是否正常写入bin/kafka-console-consumer –bootstrap-server localhost:9092 –topic score-normalized –from-beginning;; 可以看到{“cost”:7, “epoch”:1512342568296,“gameId”:“2017-12-04_07:09:28_高手1区_200_015_185175”,“gameType”:“situan”,“gamers”: [{“balance”:4405682,“delta”:-60,“username”:“0791754000”}, {“balance”:69532,“delta”:-60,“username”:“70837999”}, {“balance”:972120,“delta”:-60,“username”:“abc6378303”}, {“balance”:23129,“delta”:180,“username”:“a137671268”}],“reason”:“xiayu”}启动KSQL客户端bin/ksql http://localhost:8088可以看到ksql启动后的图标,和操作终端。ksql终端查看kafka topic列表ksql> show topics;打印topic中的消息PRINT ‘score-normalized’;可以看到:Format:STRING19-1-5 下午11时59分31秒 , NULL , {“cost”:7, “epoch”:1512342568296,“gameId”:“2017-12-04_07:09:28_\xE9\xAB\x98\xE6\x89\x8B1\xE5\x8C\xBA_200_015_185175”,“gameType”:“situan”,“gamers”: [{“balance”:4405682,“delta”:-60,“username”:“0791754000”}, {“balance”:69532,“delta”:-60,“username”:“70837999”}, {“balance”:972120,“delta”:-60,“username”:“abc6378303”}, {“balance”:23129,“delta”:180,“username”:“a137671268”}],“reason”:“xiayu”}其中:第一个逗号19-1-5 下午11时59分31秒表示消息时间。第二个逗号NULL为消息的Key,因为是从kafka-console-producer推送的,默认为NULL。后面的就是推送过来的消息内容。从topic score-normalized创建一个StreamCREATE STREAM SCORE_EVENT \ (epoch BIGINT, \ gameType VARCHAR, \ cost INTEGER, \ gamers ARRAY< \ STRUCT< \ username VARCHAR, \ balance BIGINT, \ delta BIGINT \ > \ >, \ gameId VARCHAR, \ tax BIGINT, \ reason VARCHAR) \ WITH ( KAFKA_TOPIC=‘score-normalized’, \ VALUE_FORMAT=‘JSON’);删除一个STREAMDROP STREAM stream_name ;如果有查询语句在查询该流,则会出现错误:Cannot drop USER_SCORE_EVENT. The following queries read from this source: []. The following queries write into this source: [CSAS_USER_SCORE_EVENT_2, InsertQuery_4, InsertQuery_5, InsertQuery_3]. You need to terminate them before dropping USER_SCORE_EVENT.需要用TERMINATE命令停止这些查询语句,然后再删除流:TERMINATE CSAS_USER_SCORE_EVENT_2;TERMINATE InsertQuery_4;从最早记录开始查询ksql> SET ‘auto.offset.reset’ = ’earliest’;从Stream中查询所有数据ksql> SELECT * FROM SCORE_EVENT;可以看到:1546702389664 | null | 1512342568296 | situan | 7 | [{USERNAME=0791754000, BALANCE=4405682, DELTA=-60}, {USERNAME=70837999, BALANCE=69532, DELTA=-60}, {USERNAME=abc6378303, BALANCE=972120, DELTA=-60}, {USERNAME=a137671268, BALANCE=23129, DELTA=180}] | 2017-12-04_07:09:28_高手1区_200_015_185175 | null | xiayu其中:第1列为记录的时间戳。第2列为记录的key。第3列以后就是消息中的各个字段的值,对应创建流时的顺序。倒数第2列的null,是因为消息中tax字段不存在。统计2017-12-04日的对局总数;; 增加一个game_date字段,用于统计CREATE STREAM SCORE_EVENT_WITH_DATE AS \ SELECT SUBSTRING(gameId, 0, 10) AS game_date, * \ FROM SCORE_EVENT; SELECT game_date, COUNT() \ FROM SCORE_EVENT_WITH_DATE \ WHERE game_date = ‘2017-12-04’ AND reason = ‘game’ \ GROUP BY game_date;目前KSQL还不支持类似下面的查询:SELECT COUNT() \ FROM SCORE_EVENT \ WHERE gameId LIKE ‘2017-12-04_%’;统计参与对局的总玩家数(去重)因为一条日志中包含多个玩家的对局信息,所以想法把每个玩家拆分成单独的事件整合各个玩家的事件到一个统一的流USER_SCORE_EVENT:CREATE STREAM USER_SCORE_EVENT AS \ SELECT epoch, gameType, cost, gameId, tax, reason, gamers[0]->username AS username, gamers[0]->balance AS balance, gamers[0]->delta AS delta \ FROM SCORE_EVENT; INSERT INTO USER_SCORE_EVENT \ SELECT epoch, gameType, cost, gameId, tax, reason, gamers[1]->username AS username, gamers[1]->balance AS balance, gamers[1]->delta AS delta \ FROM SCORE_EVENT; INSERT INTO USER_SCORE_EVENT \ SELECT epoch, gameType, cost, gameId, tax, reason, gamers[2]->username AS username, gamers[2]->balance AS balance, gamers[2]->delta AS delta \ FROM SCORE_EVENT; INSERT INTO USER_SCORE_EVENT \ SELECT epoch, gameType, cost, gameId, tax, reason, gamers[3]->username AS username, gamers[3]->balance AS balance, gamers[3]->delta AS delta \ FROM SCORE_EVENT;统计各个玩家总的对局数、输赢总数、贡献的总税收,并以此创建一个表USER_SCORE_TABLE:CREATE TABLE USER_SCORE_TABLE AS \ SELECT username, COUNT(*) AS game_count, SUM(delta) AS delta_sum, SUM(tax) AS tax_sum \ FROM USER_SCORE_EVENT \ WHERE reason = ‘game’ \ GROUP BY username;查看USER_SCORE_TABLE所有数据:ksql> SELECT * FROM USER_SCORE_TABLE;1546709338711 | 70837999 | 70837999 | 4 | -240 | 01546709352758 | 0791754000 | 0791754000 | 4 | -240 | 01546709338711 | a137671268 | a137671268 | 4 | 720 | 01546709352758 | abc6378303 | abc6378303 | 4 | -240 | 0查询某个玩家的对局数、输赢总数、贡献的总税收:ksql> SELECT * FROM USER_SCORE_TABLE WHERE username = ‘70837999’;输出:1546709338711 | 70837999 | 70837999 | 4 | -240 | 0统计玩家总数(去重)添加一个傀儡列用于统计:CREATE TABLE USER_SCORE_WITH_TAG AS \ SELECT 1 AS tag, * FROM USER_SCORE_TABLE;统计去重后的玩家总数SELECT tag, COUNT(username) \FROM USER_SCORE_WITH_TAG \GROUP BY tag;续KSQL WINDOW 功能。 ...

January 6, 2019 · 2 min · jiezi

2018年第52周-Kafka知识点

预祝2019年元旦节快乐!2018年最后一周,分享些Kafka的知识点。Kafka数据分区和消费者的关系分区(partition)topic是逻辑概念,分区(partition)是物理概念,对于用户来说是透明的。producer只需要关心消息网哪个topic发送,而consumer之关系自己订阅哪个topic,不需要关心每条消息存于整个集群的哪个Broker。 为了性能考虑,如果topic的消息都放在一个Broker,这个Broker必然称为瓶颈,而且无法做到水平扩展。所以topic内的数据分布到整个集群就是个自然而然的设计了。分区的引入就是解决水平扩展问题的一个解决方案。 Kafka尽量将所有的分区均匀分配到整个集群上。基本算法如下:将所有存货的N个Broker和待分配的分区排序。将第i个分区分配到第(i mon n)个Broker上,这个分区的第一个副本(Replica)存在于这个分配的Broker上,并且会作为分区的优先副本。将第i个分区的第j个副本分配到第(i+j)mod n 个Broker上。但实际情况Kafka的算法是上述基础上再加些,看Kafka的函数assignReplicasToBrokers。变了两点:限制了副本因子(replication factor)不得大于Broker的个数。因为当j大于n时,就会存在一个Broker有两个副本,这没意义且浪费。起始位置不是第0个Broker,是第rand.nextInt(Brokers.size)个。那是因为(i mon n)造成的问题就是,永远都会有0,未必有n。所以必须加上随机数,也就是第i个分区分配到第(i mon n)+rand.nextInt(Brokers.size)个Broker上。如果考虑多机架问题,那么Broker顺序就未必是0,1,2,3,4,5。而是如果0,1,2是机架A,3,4,5是机架B。则Broker的顺序为0,3,1,4,2,5。错开,每个机架依次选一次。所以当副本因子为3时,保证每一个分区在各个机架都至少有一个副本。分区中副本一般都是 Leader,其余的都是 Follow 副本。生产者消费者都固定在 Leader进行生产和消费。分区与生产者 负载均衡(Load balancing) 生产者直接发送数据到Broker,不需要任何的中间路由层,而接受的Broker是该分区的Leader。 为了帮助生产者实现这一点,所有Broker都可以回答关于哪些是可用服务器的元数据的请求,以及在任何给定的时间内,某个主题的分区的Leader是否允许生产者适当地发送它的请求。 客户端可以控制往哪个分区生产消息。这可以随机地进行,实现一种随机的负载平衡,或者可以通过一些语义分区函数来实现负载平衡。 Kafka提供了语义分区的接口,允许用户指定一个分区的key,并使用这个key来做hash到一个分区(如果需要的话,也是可以复写这分区功能的)。例如,我们选择user的id作为可用,则所以该用户的信息都会发送到同样的分区。异步发送(Asynchronus send)批处理是效率的主要驱动因素之一,为了能够批处理,Kafka的生产者会尝试在内存中积累数据,然后在一起在一个请求中以大批量的形式发送出去。批处理这个可以设置按固定的消息数量或按特定的延迟(64k或10ms)。这允许累积更多字节的发送出去,这样只是在服务器上做少量的大IO操作。这种缓冲是可配置的,这样提供了一种机制来以额外的延迟来提高吞吐量。具体的配置)和生产者的api可以在这文档中找到。分区与消费者消费者的工作方式是,向分区的Leader发送“fetch”请求。在每个请求中消费者指定日志的偏移量(position),然后接受回一大块从偏移量开始的日志。因此,消费者对偏移量有重要的控制权,如果需要,可以重置偏移量来重新消费数据。Push和pull我们首先考虑的一个问题是,消费者应该是从Broker拉取消息,还是应该是Broker把消息推送给消费者。在这方面,Kafka遵循了一种更传统的设计,大多数消息队列系统也会用的,那就是数据是从生产者push到Broker,消费者是从Broker拉取数据。一些日志集中系统,如Scribe和Apache Flume,遵循一个非常不同的,基于推送的路径,将数据被推到下游。这两种方法都由利弊,在基于推送的系统,由于是Broker得控制数据传输的速率,不同消费者可能要不同的速率。然而消费者一般的目的都是让消费者自己能够以最大的速度进行消费,但在基于push的系统,当消费速率低于生产效率时,消费者就不知道该怎么办好了(本质上就是一种拒绝服务攻击(DOS))。一个基于pull的系统就拥有很好的熟悉,消费者可以简单的调控速率。基于pull的系统的另一个优点是,它可以对发送给消费者的数据进行聚合的批处理。基于推送的系统必须选择立即发送请求或积累更多数据,然后在不知道下游用户是否能够立即处理它的情况下发送它。基于pull的系统的缺点是,如果Broker没数据,则消费者可能会不停的轮训。为了避免这一点,我们在pull请求上提供了参数,允许消费者在“长轮训”中阻塞,直到数据达到(并且可以选择等待,直到一定数量的自己可以,确保传输的大小)。消费者的Position(Consumer Position)令人惊讶的是,跟踪消息是否使用了,是消息队列系统的关键性能点之一。 很多消息队列系统在Broker中保存了关于什么消息是被消费了的元数据。也就是说,当消息队列给消费者时,Broker要么立即记录信息到本地,要么就是等待消费者的确认。这是一个相当直观的选择,而且对于一台机器服务器来说,很清楚地知道这些消息的状态。由于许多消息队列系统中用于存储的数据结构都很糟糕,因此这(记录消息状态)也是一个实用的选择——因为Broker知道什么是已经被消费的,所以可以立即删除它,保持数据的大小。 让Broker和消费者就已经消费的东西达成一致,这可不是小问题。如果一条消息发送到网络上,Broker就把它置为已消费,但消费者可能处理这条消息失败了(或许是消费者挂了,也或许是请求超时等),这条消息就会丢失了。为了解决这个问题,很多消息队列系统增加了确认机制。当消息被发送时,是被标志为已发送,而不是已消费;这是Broker等待消费者发来特定的确认信息,则将消息置为已消费。这个策略虽然解决了消息丢失的问题,但却带来了新的问题。第一,如果消费者在发送确认信息之前,在处理完消息之后,消费者挂了,则会导致此消息会被处理两次。第二个问题是关于性能,Broker必须保存每个消息的不同状态(首先先锁住消息以致于不会让它发送第二次,其次标志位已消费从而可以删除它)。还有些棘手的问题要处理。如消息被发送出去,但其确认信息一直没返回。 Kafka处理则不一样。我们的主题被分为一个有序分区的集合,且每个分区在任何给定的时间内只会被订阅它的消费者组中的一个消费者给使用。这意味着每个分区中的消费者的position仅仅是一个整数,这是下一次消费时,消息的偏移量。这使状态(记录是否被消费)非常小,每个分区只有一个数字。这个状态可以被定期检查。这样确认一条消息是否被消费的成本就很低。 这样还附加了一个好处。消费者可以重置其最先的position从而重新消费数据。这虽然违反了队列的公共契约,但它却变成关键功能给许多消费者。例如,如果消费者代码有一个bug,并且在一些消息被消费后才被发现,那么当bug被修复后,消费者就可以重新使用这些消息。消费组每群消费者都会被标志有消费组名。有消费组这个概念,Kafka就可以实现类似与工作队列(Worke Queues)模式和发布/订阅(Publish/Subscribe)。 如果消费者都在同一个消费组,则消息则会负载均衡的分配每个消费者,一条消息不会分配个两个及以上的消费者。 如果消费者不在同一个组,则消息会被广播到每一个消费组中。Kafka的数据offset读取流程每个消息在分区中都是被分配一个有序的ID数字,而这数字,我们称之为偏移量(offset)。在一个分区上,offset唯一标识一个消息。 由每个消费者维护offset。在Kafka文件存储中,同一个topic下有多个不同分区,每个分区为一个目录,分区命名规则为topic名称+有序序号,第一个分区序号从0开始,序号最大值为分区数量减1。partition物理上由多个大小相等的segment组成。segment由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀".index"和“.log”分别表示为segment索引文件、数据文件.segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。00000000000000000.index00000000000000000.log00000000000368769.index00000000000368769.log00000000000737337.index00000000000737337.log00000000001105814.index00000000001105814.logindex file的结构:1,03,4976,14078,1686….N,positionindex file结构是两个数字两个数字一组,N,position。N用于查找相对于当前文件名的offset值的N个消息。如00000000000368769.index的3,497,则为368769+3=第368772个消息。而position 497是指data file的偏移量497。data file由许多message组成,message物理结构如下:8 byte offset4 byte message size4 byte CRC321 byte “magic"1 byte “attributes"4 byte key lengthK byte key4 byte payload lengthvalue bytes payload这样的结构,配合index file,很快就可以知道某条消息的大小。 在partition中如何通过offset查找message例如读取offset=368776的message,需要通过下面2个步骤查找。第一步查找segment file上述为例,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.同样,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset 二分查找文件列表,就可以快速定位到具体文件。当offset=368776时定位到00000000000000368769.index|log第二步通过segment file查找message通过第一步定位到segment file,当offset=368776时,依次定位到00000000000000368769.index的元数据物理位置和00000000000000368769.log的物理偏移地址,然后再通过00000000000000368769.log顺序查找直到offset=368776为止。从上述图3可知这样做的优点,segment index file采取稀疏索引存储方式,它减少索引文件大小,通过mmap可以直接内存操作,稀疏索引为数据文件的每个对应message设置一个元数据指针,它比稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。Kafka高效文件存储设计特点Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。通过索引信息可以快速定位message和确定response的最大大小。通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。注: 稀疏索引类似于带一级索引的跳表,但是一级索引是数组可以使用二分法查找。注:mmap()函数是Linux的文件空间映射函数,用来将文件或设备空间映射到内存中,可以通过映射后的内存空间存取来获得与存取文件一致的控制方式,不必再使用read(),write()函数。mmap和常规文件操作的区别回顾一下常规文件系统操作(调用read/fread等类函数)中,函数的调用过程:进程发起读文件请求。内核通过查找进程文件符表,定位到内核已打开文件集上的文件信息,从而找到此文件的inode。inode在address_space上查找要请求的文件页是否已经缓存在页缓存中。如果存在,则直接返回这片文件页的内容。如果不存在,则通过inode定位到文件磁盘地址,将数据从磁盘复制到页缓存。之后再次发起读页面过程,进而将页缓存中的数据发给用户进程。总结来说,常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝。而使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此mmap效率更高。函数原型void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);也就是可以将大数据的文件,局部映射到内存中,在内存中进行此部分文件的操作。对此内存操作,都不涉及到内核空间到用户空间之间交互。直接操作内存,内存直接写入(读取)文件。就只有一次IO。 如果是普通文件操作,则需要文件复制到内核,再由内核复制到用户空间,用户空间才能操作。从而达到零拷贝。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。Kafka内部如何保证顺序,结合外部组建如何保证消费者的顺序Kafka中每个分区都是有序,由于Kafka的消息是不可变的,所以都是追加的形式有序的往上加消息。这个结构体叫 结构化提交日志(a structured commit log)。 首先就要考虑是否真的需要所有消息在队列都得有序。一般情况,不止一般,而是很大一部分,是可以无序的。就跟分布式一样。有很多业务,看起来是同步的,静下来慢慢思考,就会发现很多东西是可以异步执行的。如果实在有这样保证顺序的需要,保证生产者需将有序地提交给一个分区,首先是生产者不能提交错顺序。其次,消费者组就不能拥有两个或以上消费者实例了。连两个或以上的消费者组也不能有。持久化Kafka会根据保留时间这参数,持久化所有已经收到的消息。虽然可以设置保留时间这参数,但是Kafka优秀的性能,添加删除都是常量级的性能,所以理论上,数据保存很长时间也不成问题。 参考: http://kafka.apache.org/https://www.zhihu.com/questio...https://blog.csdn.net/yangyut...http://www.cnblogs.com/huxiao...https://www.cnblogs.com/ITtan…稀疏索引:https://blog.csdn.net/qq_2223…跳表:https://www.jianshu.com/p/dc2… ...

December 31, 2018 · 1 min · jiezi

kafka之旅总览

初识kafka 初识kafka集群 初识kafka中的生产者与消费者 初识kafka对消息处理与可靠性做出的保证

December 31, 2018 · 1 min · jiezi

kafka核心概念讲解

一、borkerbroker可以理解成一个kafka服务node,是一个运行的kafka服务。broker与broker之间是平等的关系,任意broker都可以down机而不影响其他broker正常工作。kafaka在启动的时候,会将自己的信息同步到zk上。数据存储使用了zk的零时节点,broker需要通过心跳机制维护与zk的注册关系,一旦broker宕机,zk上面对应的零时节点也会被删除。kafka Controller我们可以把controller当成集群的管理者,集群中borker启动时如果没有controller回主动的去zk上注册znode节点来抢夺controller的位置,注册成功的broker会被当选为kafka controller。若当前的controller宕机其他borker会从重新进入controller争抢流程,从而选出新的controller。controller主要的功能如下:UpdateMetadataRequest:更新元数据请求。topic分区状态经常会发生变更(比如leader重新选举了或副本集合变化了等)。由于当前clients只能与分区的leader broker进行交互,那么一旦发生变更,controller会将最新的元数据广播给所有存活的broker。具体方式就是给所有broker发送UpdateMetadataRequest请求CreateTopics: 创建topic请求。当前不管是通过API方式、脚本方式抑或是CreateTopics请求方式来创建topic,做法几乎都是在Zookeeper的/brokers/topics下创建znode来触发创建逻辑,而controller会监听该path下的变更来执行真正的“创建topic”逻辑。DeleteTopics:删除topic请求。和CreateTopics类似,也是通过创建Zookeeper下的/admin/delete_topics/<topic>节点来触发删除topic,controller执行真正的逻辑分区重分配:即kafka-reassign-partitions脚本做的事情。同样是与Zookeeper结合使用,脚本写入/admin/reassign_partitions节点来触发,controller负责按照方案分配分区Preferred leader分配:preferred leader选举当前有两种触发方式:1. 自动触发(auto.leader.rebalance.enable = true);2. kafka-preferred-replica-election脚本触发。两者“玩法”相同,向Zookeeper的/admin/preferred_replica_election写数据,controller提取数据执行preferred leader分配分区扩展:即增加topic分区数。标准做法也是通过kafka-reassign-partitions脚本完成,不过用户可直接往Zookeeper中写数据来实现,比如直接把新增分区的副本集合写入到/brokers/topics/<topic>下,然后controller会为你自动地选出leader并增加分区集群扩展:新增broker时Zookeeper中/brokers/ids下会新增znode,controller自动完成服务发现的工作broker崩溃处理:同样地,controller通过Zookeeper可实时侦测broker状态。一旦有broker挂掉了,controller可立即感知并为受影响分区选举新的leaderControlledShutdown:broker除了崩溃,还能“优雅”地退出。broker一旦自行终止,controller会接收到一个ControlledShudownRequest请求,然后controller会妥善处理该请求并执行各种收尾工作Controller leader选举:controller必然要提供自己的leader选举以防这个全局唯一的组件崩溃宕机导致服务中断。这个功能也是通过Zookeeper的帮助实现的。二、topic & partitonTopic相当于传统消息系统MQ中的一个队列queue,可以把topic当成是消息的分类。partiton可以看成是topic的一个分区,目的是突破IO瓶颈。kafka在存储topic日志的时候,将topic分开存储,这样就能将同一个消息的写压力分配到不同的分区,这样可以提升kafka的整体吞吐能力。为了保证数据的高可用,kafka使用partiton-Replica进行数据备份,若partition leader挂了,kafka controller会自动从partiton-Replica选举新的leader。提到备份不得不提到ISR,这是一个同步备份列表,每当用户添加新的消息时,分区leader成功写入日志后,后必须保证ISR列表里面的备份也成功写入日志后,才能给客户端相应成功。因此ISR列表的备份的日志总是和leader保持一致,在leader宕机的时候,可以使用ISR列表的备份取代leader的位置。三、log & segmentkafka最终的数据承载是通过log的方式进行,kafka会按照请求的顺序将消息存储到log中。我们知道一个topic可能会被分配到到个分区partiton来减轻单点负载。每个partiton实际上在写log的时候也会存在,单个文件大小物理极限的问题。因此kafka引入了segment解决方案,即将日志分段存储。不同的segment log组合起来的数据就是分区的存储消息数据。为了方便通过offset定位消息,segment log使用first-offset格式进行文件命名,first-offset是该文件存储的第一条消息的offset。这样就能通过消费者提供的offset很快定位到文件,然后通过offset偏移量可以快速定位消息的存储位置。四、producer消息/数据生产者生产者负责消息的发送,生产者需要指定消息的topic来区分不同消息。kafka收到消息后通过loadbalance策略,使用hash(message) % topic分片数 决定将数据存储到哪一个分片。 然后将message发送到制定分片的leader,leader收到消息后,将消息保存下来,接着等待ISR(a set of in-sync replicas,该列表的备份数据时刻保持和leader数据一致)中的replica消费消息并发送ack,若ISR列表中的备份分区都已经确认收到消息并保存成功后,leader将成功的消息返回给producer以表明,消息被妥善保存。五、consumer [group]消息/数据消费者&offset与其他消息系统不同的是:kafka不会复制去保存客服端之前消费了那条消息,以及下一条应当消费那条消息,kafka将这些工作交给了消费客服端来做,因此kafka在消息消费可以做到无状态。offset就是用来保存某个消费组(consumer group)消费的在当前分区日志下的偏移量的。通常情况下,多个客服端在同时消费同一个消息分区消息的时候会存在并发问题,对于offset的控制就会出现问题,这样就会出现消费重复的情况,kafka使用无锁机制解决这个问题。kafka规定,同一个分区(partition)下的数据只能被通一个consumer group中的一个线程消费,这样就避免了不同线程之间争夺通一个资源,通过这种设计kafka做到了无锁,这样可以避免锁竞争造成效率下降。因此建议consumer group里面的线程数应当和分区数保持一致,这样做可以有效的利用线程资源,线程多了会被浪费掉,少了一个线程可能会处理多个分区的数据。如果你需要多个业务消费同一个消息,由于不同的consumer group对同意主题分区的offset是分开存储的,我们可以创建多个consumer group实现多个线程来消费同一个消息的目的。kafaka如何常量时间复杂度?写数据:通过上面消息的存储过程可以发现,除了数据存储和备份操作,并没有其他耗时操作。路由分区->leader写数据->数据复制,这些操作都和现有数据规模没有任何关系。每次写数据只会在原来的基础上做追加存储。由于kafka使用了顺序存储而不是非随机存储(据说磁盘的顺序存储效率远高于磁盘的随机存储、有时候甚至比内存的随机写效率还高),同时kafka还使用了批量存储的方式减少了对io的操作,提升了io效率。 读数据:consumer在消费某个topic的时候,消费者会将所有的分区数据消费完,kafka要求,同一时刻对同一分区的数据只会被一个线程消费,这样避免了锁操作。同时通过consumer group提供的offset数据,通过kafka的文件存储机制可以快速的定位到一个segment文件,并且通过计算offset偏移量可以快速定位到数据。从整个消费流程来看,数据规模对每个过程效率是不敏感的。kafaka如何做到高可用的&动态扩展高可用的解决方案通常是采用数据冗余以及快速恢复来解决的。kafka通过分区数据备份(partition replica)&分区数据分散到不同的机器以及kafka controller可以快速检测到宕机节点,通过读取节点的分区数据,可以快速重新选取分区leader,以恢复故障。同时在故障的处理过程中,就算该分区不可用,不往分区写入数据即可,对kafka的数据读取也是没有影响的。kafka使用hash 取余的目的在于均衡负载,并不在于为了通过message可以快速的查找到这个message所在位置,这个不是kafka关注的业务。kafka通过数据复制和快速恢复做到了高可用,同时基于message不关注通过某个具体message的具体存存储位置,因此在扩展kafka的时候,或者在扩展消息分区的时候,不需要进行额为的数据复制操作,降低了扩展时候的成本。引用Kafka HA Kafka一致性重要机制之ISR(kafka replica)Kafka史上最详细原理总结Kafka controller重设计kafka document disn更多文章可以访问jframe.cn

December 25, 2018 · 1 min · jiezi