activemq知识点总结

消息中间件(MQ)的定义

其实并没有标准定义。一般认为,消息中间件属于分布式系统中一个子系统,关注于数据的发送和接收,利用高效可靠的异步消息传递机制对分布式系统中的其余各个子系统进行集成。

为什么要用消息中间件

假设一个电商交易的场景,用户下单之后调用库存系统减库存,然后需要调用物流系统进行发货,如果交易、库存、物流是属于一个系统的,那么就是接口调用。但是随着系统的发展,各个模块越来越庞大、业务逻辑越来越复杂,必然是要做服务化和业务拆分的。这个时候就需要考虑这些系统之间如何交互,第一反应就是RPC(Remote Procedure Call)。系统继续发展,可能一笔交易后续需要调用几十个接口来执行业务,比如还有风控系统、短信服务等等。这个时候就需要消息中间件登场来解决问题了。
所以消息中间件主要解决分布式系统之间消息的传递,同时为分布式系统中其他子系统提供了伸缩性和扩展性。

为系统带来了:
	1,低耦合,不管是程序还是模块之间,使用消息中间件进行间接通信。
	2,异步通信能力,使得子系统之间得以充分执行自己的逻辑而无需等待。
	3,缓冲能力,消息中间件像是一个巨大的蓄水池,将高峰期大量的请求存储下来慢慢交给后台进行处理,对于秒杀业务来说尤为重要。

和RPC有何区别

RPC和消息中间件的场景的差异很大程度上在于就是“依赖性”和“同步性”。
比如短信通知服务并不是事交易环节必须的,并不影响下单流程,不是强依赖,所以交易系统不应该依赖短信服务。比如一些数据分析程序可能需要在拿到一天的总销售量,这个就只需要销售中心提供接口在需要时调用即可。
消息中间件出现以后对于交易场景可能是调用库存中心等强依赖系统执行业务,之后发布一条消息(这条消息存储于消息中间件中)。像是短信通知服务、数据统计服务等等都是依赖于消息中间件去消费这条消息来完成自己的业务逻辑。
RPC方式是典型的同步方式,让远程调用像本地调用。消息中间件方式属于异步方式。消息队列是系统级、模块级的通信。RPC是对象级、函数级通信。
相同点:都是分布式下面的通信方式。

消息中间件有些什么使用场景

1,异步处理:提高相应速度,提高并发量
2,应用解耦:服务端和消费端均不用关心对方,类似于阻塞队列
3,流量削峰:类似于秒杀系统,在流量突然达到峰值的时候,可以保证大量的请求在队列中排队,不会对服务器造成压力
4,日志处理:日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题
5,消息通讯:消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等

常见的消息中间件比较

支持并发量:ActiveMQ(6000+) < RabbitMQ(万级) < RocketMQ(阿里的,十万级) < KafKa(百万级)
如果一般的业务系统要引入MQ,怎么选型:
	用户访问量在ActiveMQ的可承受范围内,而且确实主要是基于解耦和异步来用的,可以考虑ActiveMQ,也比较贴近Java工程师的使用习惯。 
	RabbitMQ,但是确实erlang语言阻止了我们去深入研究和掌控,对公司而言,几乎处于不可控的状态,但是确实是开源的,有比较稳定的支持,活跃度也高。
	对自己公司技术实力有绝对自信的,可以用RocketMQ 。
	所以中小型公司,技术实力较为一般,技术挑战不是特别高,用ActiveMQ、RabbitMQ是不错的选择;大型公司,基础架构研发实力较强,用RocketMQ是很好的选择
	如果是大数据领域的实时计算、日志采集等场景,用Kafka是业内标准的,绝对没问题,社区活跃度很高,几乎是全世界这个领域的事实性规范。

JMS

1,JMS和ActiveMQ
	JMS(Java Messaging Service)是Java平台上有关面向消息中间件的技术规范,实际上是一套api,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发,ActiveMQ而是这个规范的一个具体实现。

2,JMS对象模型
	1)连接工厂。连接工厂负责创建一个JMS连接。
	2)JMS连接。JMS连接(Connection)表示JMS客户端和服务器端之间的一个活动的连接,是由客户端通过调用连接工厂的方法建立的。
	3)JMS会话。JMS会话(Session)表示JMS客户与JMS服务器之间的会话状态。JMS会话建立在JMS连接上,表示客户与服务器之间的一个会话线程。
	4)JMS目的/ Broker。客户用来指定它生产的消息的目标和它消费的消息的来源的对象,一个消息中间件的实例。
	5)JMS生产者和消费者。生产者(Message Producer)和消费者(Message Consumer)对象由Session对象创建,用于发送和接收消息。
	消息的消费可以采用以下两种方法之一:
	• 同步消费。通过调用 消费者的receive 方法从目的地中显式提取消息。receive 方法可以一直阻塞到消息到达。
	• 异步消费。客户可以为消费者注册一个消息监听器,以定义在消息到达时所采取的动作。

3,JMS规范中的消息
	JMS 消息由以下三部分组成:
		• 消息头。每个消息头字段都有相应的getter 和setter 方法。
		• 消息属性。如果需要除消息头字段以外的值,那么可以使用消息属性。
		• 消息体。JMS 定义的消息类型有TextMessage、MapMessage、BytesMessage、
		StreamMessage 和 ObjectMessage。ActiveMQ也有对应的实现。

4,JMS消息模型
	Point-to-Point(P2P),点对点:
		消息通过称为队列的一个虚拟通道来进行交换。队列是生产者发送消息的目的地和接受者消费消息的消息源。每条消息通仅会传送给一个接受者。可能会有多个接受者在一个队列中侦听,但是每个队列中的消息只能被队列中的一个接受者消费。消息存在先后顺序。一个队列会按照消息服务器将消息放入队列中的顺序,把它们传送给消费者当消息已被消费时,就会从队列头部将它们删除。每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
		接收者在成功接收消息之后需向队列应答成功,如果希望发送的每个消息都应该被成功处理的话,使用这个P2P模式。
	
	Topic,主题(发布订阅(Pub/Sub):
		1、消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。
		2、如果你希望发送的消息可以不被做任何处理、或者被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用topic模型 

ActiveMQ

嵌入式ActiveMQ

在自己的应用程序中可以嵌入一个消息队列。

消息存储的持久化方式

	ActiveMQ的另一个问题就是只要是软件就有可能挂掉,挂掉不可怕,怕的是挂掉之后把信息给丢了,怎么办,可以进行消息的持久化,ActiveMQ提供了几种持久化方式:
		1.	AMQ消息存储-基于文件的存储方式,它具有写入速度快和容易恢复的特点。消息存储在一个个文件中,文件的默认大小为32M,如果一条消息的大小超过了32M,那么这个值必须设置大一点。当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3之前的版本。
		2.	KahaDB消息存储-提供了容量的提升和恢复能力,是现在的默认存储方式;KahaDB是基于文件的本地数据库储存形式,虽然没有AMQ的速度快,但是它具有强扩展性,恢复的时间比AMQ短,从5.4版本之后KahaDB做为默认的持久化方式。
			KahaDB:
				由于KahaDB是默认的持久化存储方案。所以即使你不配置任何的KahaDB参数信息,ActiveMQ也会启动KahaDB。这种情况下,KahaDB文件所在位置是你的ActiveMQ安装路径下的/data/KahaDB子目录。
		3.	JDBC消息存储-消息基于JDBC存储的;
			在配置文件中配置jdbc数据源,并且对应相应的库,在启动之后会自动生成三张表:
				1,activemq_acks:用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存
				2,activemq_lock:在集群环境中才有用,只有一个Broker可以获得消息,称为MasterBroker,其他的只能作为备份等待MasterBroker不可用,才可能成为下一个Master Broker。这个表用于记录哪个Broker是当前的Master Broker
				3,activemq_msgs:用于存储消息,Queue和Topic都存储在这个表中

		4.	Memory消息存储-基于内存的消息存储,由于内存不属于持久化范畴。所以内存存储不在讨论范围内。

消息的持久化订阅

	分别运行订阅模式和P2P模式,可以发现,P2P模式缺省把消息进行持久化,而topic模式是没有的。
	一般topic模式实验:
		1、	启动两个消费者,启动一个生产者,发送消息,两个消费者都可以收到。
		2、	关闭一个消费者,生产者发送消息,活跃的消费者可以收到消息,启动被关闭的消费者,无法收到消息。
		3、	关闭所有消费者,生产者发送消息,在ActiveMQ控制台可以看见消息已被接收,关闭再启动ActiveMQ,启动消费者收不到消息。
		如果topic模式下,需要消费者在离线又上线后,不管ActiveMQ是否重启过,都保证可以接受到消息,就需要进行持久化订阅。
	持久Topic消费者端:
		需要设置客户端id:connection.setClientID("Mark");
		消息的destination变为 Topic 
		消费者类型变为TopicSubscriber
		消费者创建时变为session.createDurableSubscriber(destination,"任意名字,代表订阅名 ");
		运行一次消费者,将消费者在ActiveMQ上进行一次注册。然后在ActiveMQ的管理控制台subscribers页面可以看见我们的消费者。
	效果:
		1、	运行生产者,发布消息,多个消费者可以正常收到。
		2、	关闭一个消费者,运行生产者,发布消息后再启动被关闭的消费者,可以收到离线后的消息;
		3、	关闭所有消费者,运行生产者,发布消息后,关闭ActiveMQ再启动,启动所有消费者,都可以收到消息。
	注意:生产者端无需另外单独配置,即使进行了持久订阅,但是消息本身如果是不持久化的,ActiveMQ关闭再启动,这些非持久化的消息会丢失,进行持久订阅的消费者也是收不到自身离线期间的消息的。

消息的可靠性

消息发送成功后,接收端接收到了消息。然后进行处理,但是可能由于某种原因,高并发也好,IO阻塞也好,反正这条消息在接收端处理失败了。而点对点的特性是一条消息,只会被一个接收端给接收,只要接收端A接收成功了,接收端B,就不可能接收到这条消息,如果是一些普通的消息还好,但是如果是一些很重要的消息,比如说用户的支付订单,用户的退款,这些与金钱相关的,是必须保证成功的,那么这个时候要怎么处理呢?必须要保证消息的可靠性,除了消息的持久化,还包括两个方面,一是生产者发送的消息可以被ActiveMQ收到,二是消费者收到了ActiveMQ发送的消息。

	生产者:
		send方法
			在生产者端,我们会使用send() 方法向ActiveMQ发送消息,默认情况下,持久化消息以同步方式发送,send()方法会被阻塞,直到broker发送一个确认消息给生产者,这个确认消息表示broker已经成功接收到消息,并且持久化消息已经把消息保存到二级存储中。

		事务消息
			事务中消息(无论是否持久化),会进行异步发送,send()方法不会被阻塞。但是commit方法会被阻塞,直到收到确认消息,表示broker已经成功接收到消息,并且持久化消息已经把消息保存到二级存储中。

		总结
			非持久化又不在事务中的消息,可能会有消息的丢失。为保证消息可以被ActiveMQ收到,我们应该采用事务消息或持久化消息。

	消费者:
		对消息的确认有4种机制
		1、	AUTO_ACKNOWLEDGE = 1    自动确认
			    1,“同步”(receive)方法返回message给消息时会立即确认。
			    2,在"异步"(messageListener)方式中,将会首先调用listener.onMessage(message),如果onMessage方法正常结束,消息将会正常确认。如果onMessage方法异常,将导致消费者要求ActiveMQ重发消息。此外需要注意,消息的重发次数是有限制的,每条消息中都会包含“redeliveryCounter”计数器,用来表示此消息已经被重发的次数,如果重发次数达到阀值,将导致broker端认为此消息无法消费,此消息将会被删除或者迁移到"dead letter"通道中。
 				因此当我们使用messageListener方式消费消息时,可以在onMessage方法中使用try-catch,这样可以在处理消息出错时记录一些信息,而不是让consumer不断去重发消息;如果你没有使用try-catch,就有可能会因为异常而导致消息重复接收的问题,需要注意onMessage方法中逻辑是否能够兼容对重复消息的判断。

		2、	CLIENT_ACKNOWLEDGE = 2    客户端手动确认   
				客户端手动确认,这就意味着AcitveMQ将不会“自作主张”的为你ACK任何消息,开发者需要自己择机确认。可以用方法: message.acknowledge(),或session.acknowledge();效果一样。
				如果忘记调用acknowledge方法,将会导致当consumer重启后,会接受到重复消息,因为对于broker而言,那些尚未真正ACK的消息被视为“未消费”。
				我们可以在当前消息处理成功之后,立即调用message.acknowledge()方法来"逐个"确认消息,这样可以尽可能的减少因网络故障而导致消息重发的个数;当然也可以处理多条消息之后,间歇性的调用acknowledge方法来一次确认多条消息,减少ack的次数来提升consumer的效率,不过需要自行权衡。

		3、	DUPS_OK_ACKNOWLEDGE = 3    自动批量确认
				类似于AUTO_ACK确认机制,为自动批量确认而生,而且具有“延迟”确认的特点,ActiveMQ会根据内部算法,在收到一定数量的消息自动进行确认。在此模式下,可能会出现重复消息,什么时候?当consumer故障重启后,那些尚未ACK的消息会重新发送过来。

		4、	SESSION_TRANSACTED = 0    事务提交并确认
				当session使用事务时,就是使用此模式。当决定事务中的消息可以确认时,必须调用session.commit()方法,commit方法将会导致当前session的事务中所有消息立即被确认。在事务开始之后的任何时机调用rollback(),意味着当前事务的结束,事务中所有的消息都将被重发。当然在commit之前抛出异常,也会导致事务的rollback。

		ACK_MODE描述了Consumer与broker确认消息的方式(时机),比如当消息被Consumer接收之后,Consumer将在何时确认消息。所以ack_mode描述的不是producer于broker之间的关系,而是customer于broker之间的关系。
		对于broker而言,只有接收到ACK指令,才会认为消息被正确的接收或者处理成功了,通过ACK,可以在consumer与Broker之间建立一种简单的“担保”机制.
		session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);第一个参数:是否支持事务,如果为true,则会忽略第二个参数,自动被jms服务器设置为SESSION_TRANSACTED

通配符式订阅

	Wildcards 用来支持联合的名字分层体系(federated name hierarchies)。它不是JMS 规范的一部分,而是ActiveMQ 的扩展。ActiveMQ 支持以下三种
	wildcards:
	• "." 用于作为路径上名字间的分隔符。
	• "*" 用于匹配路径上的任何名字。
	• ">" 用于递归地匹配任何以这个名字开始的destination。
	订阅者可以明确地指定destination 的名字来订阅消息,或者它也可以使用wildcards 来定义一个分层的模式来匹配它希望订阅的 destination。

死信队列

	用来保存处理失败或者过期的消息。 
	出现下面情况时,消息会被重发: 
		i.	事务会话被回滚。
		ii.	事务会话在提交之前关闭。
		iii.会话使用CLIENT_ACKNOWLEDGE模式,并且Session.recover()被调用。 
		iv.	自动应答失败
	当一个消息被重发超过最大重发次数(缺省为6次,消费者端可以修改)时,会给broker发送一个"有毒标记“,这个消息被认为是有问题,这时broker将这个消息发送到死信队列,以便后续处理。 
	注意:可以在配置文件(activemq.xml)来调整死信发送策略,自定义策略,可以单独使用死信消费者处理这些死信,死信队列里面的消息必须要定时处理,不然会堆积的很多影响性能。

Mirrored Queue 镜像队列(了解即可)

ActiveMQ每一个queue中消息只能被一个消费者消费,然而,有时候,你希望能够监视生产者和消费者之间的消息流。
MirroredQueue: Broker会把发送到某一个队列上的所有消息转发到一个名称类似的topic,因此监控程序只需要订阅这个topic

消费者集群下需要考虑的问题

	我们现实中往往有这样的需求:
		1. 消息接收方和发送方都是集群。 
		2. 同一个消息的接收方可能有多个集群进行消息的处理。
		3. 不同集群对于同一条消息的处理不能相互干扰。
	希望可以达到如下的效果:
		1,级联,将Jms的Topic和Queue进行级联使用。缺点,实现方式繁重,需要独立的中转的消息订阅者来完成,多了一次消息的投递和一次消息消费过程,对效率有影响,增加了消息中间件负载压力。
		2,ActiveMQ提供了虚拟主题和组合Destinations都可以达到这个效果。
			虚拟Destination:
				对于消息发布者来说,就是一个正常的Topic,名称以VirtualTopic.开头。例如VirtualTopic.vtgroup
				对于消息接收端来说,是个队列,不同应用里使用不同的前缀作为队列的名称,即可表明自己的身份即可实现消费端应用分组。
				例如Consumer.A.VirtualTopic. vtgroup,说明它是名称为A群组的消费端,同理Consumer.B.VirtualTopic. vtgroup说明是一个名称为B群组的客户端。
				默认虚拟主题的前缀是 :VirtualTopic.
				消费虚拟地址默认格式:Consumer.*.VirtualTopic.

			组合Destination:
				组合队列允许用一个虚拟的destination代表多个destinations。这样就可以通过composite destinations在一个操作中同时向多个destination发送消息。
				多个destination之间采用“,”分割。例如:
				Queue queue = new ActiveMQQueue("FOO.A,FOO.B,FOO.C");
				或
				Destination destination = session.createQueue("my-queue,my-queue2");
				如果希望使用不同类型的destination,那么需要加上前缀如queue:// 或topic://,例如:
				Queue queue = new ActiveMQQueue("cd.queue,topic://cd.mark");

ActiveMQ高可用集群

	ActiveMQ的集群方式主要由两种:Master-Slave和Broker Cluster
		1,Master-Slave
			Master-Slave方式中,只能是Master提供服务,Slave是实时地备份Master的数据,以保证消息的可靠性。当Master失效时,Slave会自动升级为Master,客户端会自动连接到Slave上工作。Master-Slave模式分为四类:Pure Master Slave、Shared FileSystemMasterSlave和JDBCMasterSlave,以及ReplicatedLevelDBStore方式。Master-Slave方式都不支持负载均衡,但可以解决单点故障的问题,以保证消息服务的可靠性。
			Pure Master Slave方式:
				ActiveMQ5.8以前支持,自从Activemq5.8开始,Activemq的集群实现方式取消了传统的PureMasterSlave方式,并从Activemq5.9增加了基于zookeeper+leveldb的实现方式。使用两个ActiveMQ服务器,一个作为Master,Master不需要做特殊的配置;另一个作为Slave,配置activemq.xml文件,在<broker>节点中添加连接到Master的URI和设置Master失效后不关闭Slave。

			Shared File System Master Slave方式:
				就是利用共享文件系统做ActiveMQ集群,是基于ActiveMQ的默认数据库kahaDB完成的,kahaDB的底层是文件系统。这种方式的集群,Slave的个数没有限制,哪个ActiveMQ实例先获取共享文件的锁,那个实例就是Master,其它的ActiveMQ实例就是Slave,当当前的Master失效,其它的Slave就会去竞争共享文件锁,谁竞争到了谁就是Master。这种模式的好处就是当Master失效时不用手动去配置,只要有足够多的Slave。如果各个ActiveMQ实例需要运行在不同的机器,就需要用到分布式文件系统了。

			Shared  JDBC Master Slave:
			JDBC Master Slave模式和Shared File Sysytem MasterSlave模式的原理是一样的,只是把共享文件系统换成了共享数据库。我们只需在所有的ActiveMQ的主配置文件中activemq.xml添加数据源,所有的数据源都指向同一个数据库。然后修改持久化适配器。这种方式的集群相对SharedFileSystemMasterSlave更加简单,更加容易地进行分布式部署,但是如果数据库失效,那么所有的ActiveMQ实例都将失效。

			
		2,Broker Cluster
			Broker Cluster主要是通过network of Brokers在多个ActiveMQ实例之间进行消息的路由。BrokerCluster模式支持负载均衡,可以提高消息的消费能力,但不能保证消息的可靠性。所以为了支持负载均衡,同时又保证消息的可靠性,我们往往会采用Msater-Slave+Broker Cluster的模式。

猜你喜欢

转载自blog.csdn.net/qq_28822933/article/details/85238379