闲扯kafka mq

本文主要讲解关于kafka mq的设计思想及个人理解。关于kafka的详细信息,大家可以参考官网的文献http://kafka.apache.org/documentation.html这是一篇相当不错的文章,值得仔细研读。

 

第一个问题:消息队列(Message Queue)是干嘛用的?

首先,要对消息队列有一个基本的理解。不少人虽然在用消息队列,却并没有搞清楚消息队列是干嘛的。

有人会回答,消息队列就是为了分发消息用的。这当然没错,废话总是真理嘛。那么,消息队列是用来提高性能,加速消息传输的吗?显然不是,消息队列虽然提供了数据上的冗余,但它不是一种缓存。如果你想加速,直接在把生产者与消费者合在一起写,中间自己加一个全内存的queue,没有了持久化,没有了网络传输,岂不更快。有人说,消息队列,就是一个数据源,作为下一级输入的数据源,存放中间结果用的。这当然也没错,但是如果纯作存放中间结果用,你为什么不直接用数据库,或者用redis,说不定性能还更佳。
在我看来,对消息队列最好的诠释,还是之前在看active mq文档时看到的那句:"fire and forget"。说中文,两个字:“解耦”。它实现了生产者与消费者的有效解耦,降低了系统复杂性。作为一个生产者,它主要关心的应该就是自己的生产工作,它不应该关心自己生产的东西,到底被谁消费,如何消费。它应该就是简单的把生产好的东西,往一个仓库一放(即fire),然后就可以不管了(forget),毫无心理负担。至于后面的事,消息如何交付给消费者,这种交付方式是不是会丢失消息之类的可靠性问题一概不管(这也就是为什么消息队列不仅是一个中间结果存放区的原因)。这个作为中间仓库,负责与消费者打交道,同时保证后续交付可靠性的角色,就是消息队列来担当的。
这里打一个不太和谐的比喻。就好比约炮,开完一炮之后,就转身就走,头都不回,很潇洒,fire and forget。至于后续的事,是不是怀孕了,要奶孩子了,抚养成人之类的问题,producer可以一概不管,由消息队列成功接盘。所以,这里的producer有点类似隔壁老王,而消息队列,则无私担当了冤大头这个伟大角色。

 

神奇的kafka

相对于传统的jms系统,kafka的设计是相当激进的。传统jms之于kafka,有点类似于mongodb之于mysql,走的是粗犷路线,从一开始的设计上就是追求分布式,高可用与并发性能去的。跟我们老大讨论时,他也提到,active mq是为实现jms去的,所以搞得会过于复杂,而kafka mq根本就不去支持jms,没有约束。
先贴一段,官网上的原话:

The Kafka cluster retains all published messages—whether or not they have been consumed—for a configurable period of time. For example if the log retention is set to two days, then for the two days after a message is published it is available for consumption, after which it will be discarded to free up space. Kafka's performance is effectively constant with respect to data size so retaining lots of data is not a problem.

kafka集群会保存所有发布的消息,无论该消息,是否已经确认被消费者所接收。所有这些消息,是作为log被保存的。 消息存起来,好几天后才删,这一点就很神奇,大部份消息队列在确认consumer已接收之后,很快就会把消息删除(即便是持久化保存的消息)。而更神奇的是,kafka卡的性能基本不会因持久化的信息量的增长而变差,基本为一个常量。
其实这跟kafka的log(即持久化的消息)的存储方式有很大关系,说白了,kafka的log是以数据文件配合索引文件来完成查询的(没错,对kafka的一条消息发送,其实就是一次consumer的一次查询操作),所以每次对通过指定的offset对消息的读取,基本都只需要恒定次数的磁头寻道次数就可以完成。

In fact the only metadata retained on a per-consumer basis is the position of the consumer in the log, called the "offset". This offset is controlled by the consumer: normally a consumer will advance its offset linearly as it reads messages, but in fact the position is controlled by the consumer and it can consume messages in any order it likes. For example a consumer can reset to an older offset to reprocess.

以active mq为例的消息队列,其订阅发布模式,都可以认为是有状态的。消息队列这一头必须要记录consumer的接收情况,然后才能决定,发送哪一条消息。试想一下,就算我们就实现一个简单的数据结构 queue,我们肯定也要记录当前队列的top的引用是指向哪个节点的。众所周知,有状态的服务,难以做横向扩展(直接加机器)。那么,kafka是如何保证其消息发送(其实就是pull查询)是无状态的呢?
从上面的这段官方的英文讲解中可以看出答案,就是kafka这边干脆不记录consumer的具体读取到队列哪个位置的这种状态信息,这个位置信息(也就是offset),交由每个consumer中负责连接kafka的部分自行管理,例如kafka提供的consumer端的client实现就是将这个offset信息定时存到zookeeper上,而kafka本身所做的事,就快跟一个分布式存储系统差不多了。这样的做法也带来了额外的好处,上面文档中所提的最后一句,一个consumer可以根据一个较早的offset进行查找,重新获得某条消息。估计有人要惊了,这算哪门子的好处,我用来作消息队列,又不是数据库,一般看队列头的消息就够了,为什么老要去查找过去的消息?关于这个问题,下文来表。

分布式kafka

从分布这个角度来看,还是那句话,kafka之于active mq,相当于mongodb之于mysql。无论active mq还是mysql,起始都是从单机开始发展起来的,一开始就不是为了分布式而设计,而后再在原来的基本础上再做分布式的处理。所以这样的分布式,总觉得差那么一点味道,不纯正。例如active mq的Master-Slave模式无法做负载均衡,而Broker Cluster却又不是HA(高可靠)的。 回头看kafka,天生为分布式而生。它的分布式是行列式形式的,如下图。



 每个topic的log信息,被分成多个partition分布在不同的broker(kafka实例)上。一般我们可以按照某个key的hash值去分partition,实现路由,具体的路由方式可以自行指定或者实现。然后,每个partition包含多个复本,分散在不同的broker,每个复本同步存储相同的log信息,保证高靠性。每个partition的复本组中有一个选作leader,而其他作follower,典型的行列式分布式布署。唯一让人觉着不痛快的,就是写和读都是走leader的,这样就无法把一些读负载均衡到follower上去。

并行与有序的矛盾

对于消息队列来说,并行与有序是矛盾的。假设,消息队列中存放的消息,是对数据库某表的内容修改操作命令,那么对同一条记录的修改操作命令必须有序到达,不然后面的结果选到,可能造成混乱,结果无意义。还是以active mq为例,满足这样的需求,要怎么办?没有办法,唯一的办法,就是保证一个queue,只有一个consumer在取,如果有多个consumer同时取的话,虽然consumer内部的消息能够保持有序,但是多个consumer之间的消息就无法保证有序了。这样的话,反正你只有一个consumer能取,再怎么分布式也是白搭,无法并行消费。
Kafka做了一定的改进。我们都知道,kafka的log存储是分partition的。而大多数有序需求,并不同要求全局有序。就像上文提到的要求,可能只要保证对同一个id的记录的操作保证有序便可。我们可以按照key(这里就是id值),进行分组,将消息分到不同的partition中去,同一个id的相关纪录,肯定会归到同一个partition中去,而且在partition内部有序。这时就可以认为每一个partition就是一个单独的消息队列,可以为每个partition指定一个consumer。当然,如果为一个partition指定多个consumer又会丢失有序性。虽然不够完美,但相对传统jms,这种并行性的提高,已算是一个不小的进步。
那么如果你要求全局有序呢?抱歉,这种需求,kafka也只能通过指定一个单独的consumer来实现。幸好,一般的应用中很少出现这样的需求。按key分组,基本能满足大多数的需求。

 

终极一问:为什么kafka在consumer确认接收消息之后,还不删除消息,甚至提供consumer利用offset查找较早消息的功能?

我拿这个问题去问过我的几个不太熟悉kafka的逗比朋友,居然让他们折磨了一晚上也没想出来。我觉得为了理解kafka,必须要闹明白这个问题。
第一点,前文已述,kafka的存储方式,是按照数据文件(会按段划分)结合索引文件形成log来完成的,consumer用offset来查找,这种使用方式,注定不允许你对文件中的某条记录做删除操作。试想一下,你删了其中某条消息,你用来查的offset还会是对的吗?你是不是又要完全重新组织文件,想想就好烦。
第二点,就是确实存在consumer去找较老的消息的可能性存在。具体是什么场景呢?还是先上图吧



 这是一个最简单的生产者消费者模型。我们现在看到的消费者是一个完整的个体。消息队列,将消息发送给消费者,消费者反馈说已收到,消息队列就可以删消息了。确实很和谐,而且传统的jms就是这样做的。

但有的时候,消费者的处理并没有那么简单,消费者的处理可能分布式的处理,包含多个处理环节,第一个环节处理了,发送至下一个环节,下一个处理环节位于的可能就是不同的系统,已经是不同的服务器上了的进程了。当你第一个处理环节的节点接确认收到消息后,通知消息队列,已接收。那如果后续环节出现差错呢,比方如后面的传输中在到达终点前发现数据丢失,抑或是某个环节的服务挂掉了,这部份消息传输的可靠信又如何保证?难道你在每个处理节点之间再加具有能持久化功能,能保证消息可靠性的消息队列?这样想想,又是好复杂,好麻烦的样子。
利用kafka,就可以一直向第一个处理环节的节点发送消息,先不用管后续结点,当后续发现消息丢失的情况的下,就可以通过之前的offset,重新去从kafka获取这一条消息,全头重新执行(但是这样,存在有序性的问题)。刚才所述的多个处理环节的场景就是典型的流式计算的场景。这也是为什么storm流式计算框架官方推荐kafka作为其消息来源一个重要原因。
这部份属上个人理解,有要纠错的,或有补允的。欢迎在评论区留言。

最后,明天就是年三十了,祝各位读者老爷们,新年快乐!

20150217 首发于3dobe.com:http://3dobe.com/archives/68/

本站链接接:http://quentinxxz.iteye.com/blog/2186718

 

采用PyQt5框架与Python编程语言构建图书信息管理平台 本项目基于Python编程环境,结合PyQt5图形界面开发库,设计实现了一套完整的图书信息管理解决方案。该系统主要面向图书馆、书店等机构的日常运营需求,通过模块化设计实现了图书信息的标准化管理流程。 系统架构采用典型的三层设计模式,包含数据存储层、业务逻辑层和用户界面层。数据持久化方案支持SQLite轻量级数据库与MySQL企业级数据库的双重配置选项,通过统一的数据库操作接口实现数据存取隔离。在数据建模方面,设计了包含图书基本信息、读者档案、借阅记录等核心数据实体,各实体间通过主外键约束建立关联关系。 核心功能模块包含六大子系统: 1. 图书编目管理:支持国际标准书号、中国图书馆分类法等专业元数据的规范化著录,提供批量导入与单条录入两种数据采集方式 2. 库存动态监控:实时追踪在架数量、借出状态、预约队列等流通指标,设置库存预警阈值自动提醒补货 3. 读者服务管理:建立完整的读者信用评价体系,记录借阅历史与违规行为,实施差异化借阅权限管理 4. 流通业务处理:涵盖借书登记、归还处理、续借申请、逾期计算等标准业务流程,支持射频识别技术设备集成 5. 统计报表生成:按日/月/年周期自动生成流通统计、热门图书排行、读者活跃度等多维度分析图表 6. 系统维护配置:提供用户权限分级管理、数据备份恢复、操作日志审计等管理功能 在技术实现层面,界面设计遵循Material Design设计规范,采用QSS样式表实现视觉定制化。通过信号槽机制实现前后端数据双向绑定,运用多线程处理技术保障界面响应流畅度。数据验证机制包含前端格式校验与后端业务规则双重保障,关键操作均设有二次确认流程。 该系统适用于中小型图书管理场景,通过可扩展的插件架构支持功能模块的灵活组合。开发过程中特别注重代码的可维护性,采用面向对象编程范式实现高内聚低耦合的组件设计,为后续功能迭代奠定技术基础。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值