消息中间件的背景分析
看过zk的源码、学过并发编程的同学应该知道,我们可以使用阻塞队列+线程池来实现生产者消费者模式。比如说在一个应用中,A方法调用B方法去执行一些任务处理。我们可以同步调用。但是如果这个时候请求比较多的情况下,同步调用比较耗时会导致请求阻塞。我们会使用阻塞队列加线程池来实现异步任务的处理。
那么,问题来了,如果是在分布式系统中,两个服务之间需要通过这种异步队列的方式来处理任务,那单进程级别的队列就无法解决这个问题了。
因此,引入了消息中间件,也就是把消息处理交给第三方的服务,这个服务能够实现数据的存储以及传输,使得在分布式架构下实现跨进程的远程消息通信。
所以,简单来说: 消息中间件是指利用高效可靠的消息传输机制进行平台无关的数据交流,并且基于数据通信来进行分布式系统的集成。
思考一下消息中间件的设计
可以先从基本的需求开始思考
- 最基本的是要能支持消息的发送和接收,需要涉及到网络通信就一定会涉及到
NIO
- 消息中心的消息存储(持久化/非持久化)
- 消息的序列化和反序列化
- 是否跨语言
- 消息的确认机制,如何避免消息重发
高级功能
- 消息的有序性
- 是否支持事务消息
- 消息收发的性能,对高并发大数据量的支持
- 是否支持集群
- 消息的可靠性存储
- 是否支持多协议
这个思考的过程其实就是做需求的整理,然后在使用已有的技术体系进行技术的实现。而我们目前阶段所去了解的,无非就是别人根据实际需求进行实现之后,我们如何使用他们提供的api进行应用而已。但是有了这样一个全局的思考,那么对于后续学习这个技术本身而言,也显得很容易了。
发展过程
实际上消息中间件的发展也是挺有意思的,我们知道任何一个技术的出现都是为了解决实际问题,这个问题是通过一种通用的软件“总线”也就是一种通信系统,解决应用程序之间繁重的信息通信工作。最早的小白鼠就是金融交易领域,因为在当时这个领域中,交易员需要通过不同的终端完成交易,每台终端显示不同的信息。如果接入消息总线,那么交易员只需要在一台终端上操作,然后订阅其他终端感兴趣的消息。于是就诞生了发布订阅模型(pubsub
),同时诞生了世界上第一个现代消息队列软件(TIB
)The information Bus
, TIB
允许开发者建立一系列规则去描述消息内容,只要消息按照这些规则发布出去,任何消费者应用都能订阅感兴趣的消息。随着TIB
带来的甜头被广泛应用在各大领域,IBM
也开始研究开发自己的消息中间件,3年后IBM
的消息队列IBM MQ
产品系列发布,之后的一段时间MQ系列进化成了WebSphere MQ
统治商业消息队列平台市场。
包括后期微软也研发了自己的消息队列(MSMQ
)
各大厂商纷纷研究自己的MQ,但是他们是以商业化模式运营自己的MQ软件,商业MQ想要解决的是应用互通的问题,而不是创建标准接口来允许不同MQ产品互通。所以有些大型的金融公司可能会使用来自多个供应商的MQ产品,来服务企业内部不同的应用。那么问题来了,如果应用已经订阅了TIB MQ
的消息然后突然需要消费IBM MQ
的消息,那么整个实现过程会很麻烦。为了解决这个问题,在2001年诞生了 Java Message Service(JMS)
,JMS
通过提供公共的Java API
方式,隐藏单独MQ产品供应商的实现接口,从而跨越了不同MQ消费和解决互通问题。从技术层面来说,Java应用程序只需要针对JMS API
编程,选择合适的MQ驱动即可。JMS会处理其他部分。这种方案实际上是通过单独标准化接口来整合很多不同的接口,效果还是不错的,但是碰到了互用性的问题。两套使用两种不同编程语言的程序如何通过它们的异步消息传递机制相互通信呢。这个时候就需要定义一个异步消息传递的通用标准。
所以AMQP
(Advanced Message Queuing Protocol
)高级消息队列协议产生了,它使用了一套标准的底层协议,加入了许多其他特征来支持互用性,为现代应用丰富了消息传递需求,针对标准编码的任何人都可以和任意AMQP
供应商提供的MQ服务器进行交互。
除了JMS
和AMQP
规范以外,还有一种MQTT
(Message Queueing Telemetry Transport
),它是专门为小设备设计的。因为计算性能不高的设备不能适应AMQP
上的复杂操作,它们需要一种简单而且可互用的方式进行通信。这是MQTT
的基本要求,而如今,MQTT
是物联网(IOT
)生态系统中主要成分之一。
kafka
的介绍
Kafka
是一款分布式消息发布和订阅系统,它的特点是高性能、高吞吐量。
最早设计的目的是作为 LinkedIn
的活动流和运营数据的处理管道。这些数据主要是用来对用户做用户画像分析以及服务器性能数据的一些监控。
所以kafka
一开始设计的目标就是作为一个分布式、高吞吐量的消息系统,所以适合运用在大数据传输场景。
Kafka
的应用场景
由于kafka
具有更好的吞吐量、内置分区、冗余及容错性的优点(kafka
每秒可以处理几十万消息),让kafka
成为了一个很好的大规模消息处理应用的解决方案。所以在企业级应用长,主要会应用于如下几个方面。
行为跟踪:kafka
可以用于跟踪用户浏览页面、搜索及其他行为。通过发布-订阅模式实时记录到对应的topic
中,通过后端大数据平台接入处理分析,并做更进一步的实时处理和监控
日志收集:日志收集方面,有很多比较优秀的产品,比如Apache Flume
,很多公司使用kafka
代理日志聚合。日志聚合表示从服务器上收集日志文件,然后放到一个集中的平台(文件服务器)进行处理。在实际应用开发中,我们应用程序的log
都会输出到本地的磁盘上,排查问题的话通过linux
命令来搞定,如果应用程序组成了负载均衡集群,并且集群的机器有几十台以上,那么想通过日志快速定位到问题,就是很麻烦的事情了。所以一般都会做一个日志统一收集平台管理log
日志用来快速查询重要应用的问题。所以很多公司的套路都是把应用日志集中到kafka
上,然后分别导入到es
和hdfs
上,用来做实时检索分析和离线统计数据备份等。而另一方面,kafka
本身又提供了很好的api
来集成日志并且做日志收集。
Kafka
本身的架构
一个典型的kafka
集群包含若干Producer
(可以是应用节点产生的消息,也可以是通过Flume
收集日志产生的事件),若干个Broker
(kafka
支持水平扩展)、若干个Consumer Group
,以及一个zookeeper
集群。kafka
通过zookeeper
管理集群配置及服务协同。Producer
使用push
模式将消息发布到broker
,consumer
通过监听使用pull
模式从broker
订阅并消费消息。
多个broker
协同工作,producer
和consumer
部署在各个业务逻辑中。三者通过zookeeper
管理协调请求和转发。这样就组成了一个高性能的分布式消息发布和订阅系统。
图上有一个细节是和其他mq
中间件不同的点,producer
发送消息到broker
的过程是push
,而consumer
从broker
消费消息的过程是pull
,主动去拉数据。而不是broker
把数据主动发送给consumer
。
名词解释
Broker
Kafka
集群包含一个或多个服务器,这种服务器被称为broker
。broker
端不维护数据的消费状态,提升了性能。直接使用磁盘进行存储,线性读写,速度快:避免了数据在JVM
内存和系统内存之间的复制,减少耗性能的创建对象和垃圾回收。Producer
负责发布消息到Kafka broker
Consumer
消息消费者,向Kafka broker
读取消息的客户端,consumer
从broker
拉取(pull
)数据并进行处理。Topic
每条发布到Kafka
集群的消息都有一个类别,这个类别被称为Topic
。(物理上不同Topic
的消息分开存储,逻辑上一个Topic
的消息虽然保存于一个或多个broker
上但用户只需指定消息的Topic
即可生产或消费数据而不必关心数据存于何处)Partition
Parition
是物理上的概念,每个Topic
包含一个或多个Partition
.Consumer Group
每个Consumer
属于一个特定的Consumer Group
(可为每个Consumer
指定group name
,若不指定group name
则属于默认的group
)Topic & Partition
Topic
在逻辑上可以被认为是一个queue
,每条消费都必须指定它的Topic
,可以简单理解为必须指明把这条消息放进哪个queue
里。为了使得Kafka
的吞吐率可以线性提高,物理上把Topic
分成一个或多个Partition
,每个Partition
在物理上对应一个文件夹,该文件夹下存储这个Partition
的所有消息和索引文件。若创建topic1
和topic2
两个topic
,且分别有13个和19个分区,则整个集群上会相应会生成共32个文件夹(本文所用集群共8个节点,此处topic1
和topic2 replication-factor
均为1)。
Kafka
的安装部署
下载kafka
archive.apache.org/dist/kafka/…
安装过程
安装过程非常简单,只需要解压就行,因为这个是编译好之后的可执行程序
tar -zxvf kafka_2.11-2.0.0.tgz 解压
配置zookeeper
因为kafka
依赖于zookeeper
来做master
选举以及其他数据的维护,所以需要先启动zookeeper
节点
kafka
内置了zookeeper
的服务,所以在bin
目录下提供了这些脚本
zookeeper-server-start.sh
zookeeper-server-stop.sh
在config
目录下,存在一些配置文件
zookeeper.properties
server.properties
所以我们可以通过下面的脚本来启动zk服务,当然,也可以自己搭建zk的集群来实现
sh zookeeper-server-start.sh -daemon ../config/zookeeper.properties
启动和停止kafka
- 修改
server.properties
, 增加zookeeper
的配置
zookeeper.connect=localhost:2181
- 启动
kafka
sh kafka-server-start.sh -damoen config/server.properties
- 停止
kafka
sh kafka-server-stop.sh -daemon config/server.properties
kafka
的基本操作
创建topic
sh kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 -- partitions 1 --topic test
Replication-factor
:表示该topic
需要在不同的broker
中保存几份,这里设置成1,表示在两个broker
中保存两份
Partitions
:分区数
查看topic
sh kafka-topics.sh --list --zookeeper localhost:2181
查看topic属性
sh kafka-topics.sh --describe --zookeeper localhost:2181 --topic first_topic
消费消息
sh kafka-console-consumer.sh --bootstrap-server 192.168.13.106:9092 --topic test --from-beginning
发送消息
sh kafka-console-producer.sh --broker-list 192.168.244.128:9092 --topic first_topic
集群环境安装
环境准备
- 准备三台虚拟机
- 分别把
kafka
的安装包部署在三台机器上
修改配置
以下配置修改均为server.properties
-
分别修改三台机器的
server.properties
配置,同一个集群中的每个机器的id必须唯一broker.id=0 broker.id=1 broker.id=2
-
修改
zookeeper
的连接配置
zookeeper.connect=192.168.13.106:2181
-
修改
listeners
配置
如果配置了listeners
,那么消息生产者和消费者会使用listeners
的配置来进行消息的收发,否则,会使用localhost
PLAINTEXT
表示协议,默认是明文,可以选择其他加密协议listeners=PLAINTEXT://192.168.13.102:9092
-
分别启动三台服务器
sh kafka-server-start.sh -daemon ../config/server.properties