纪念一下刚入行时,植入后端程序的ActiveMq消息队列(文末附配置文件详情和我封装的工具类)

目录

1. 应用场景

2. 通信模式

3. 手把手从头安装(相关安装包官网都能下载)

4. 配置文件

4.1 订阅/发布

4.2 消息队列

 5. JMS编程模型


1.应用场景

    在不使用ActiveMQ,不运用消息队列的情况下,用户的请求会直接打到数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也会使得系统响应加剧延迟,在介入MQ之后,使用了队列,用户的请求发送到队列后立即返回,再由消息队列的消费者进程从消息队列中提取消息,异步写入数据库,消息队列的服务处理速度远快于数据库,因此用户的响应延迟得到了大大的改善。

常用的应用场景主要有四种:

1.1 异步处理

    举个例子比如用户注册后,需要发注册邮件和注册短信,传统的方式有并行和串行两种,但是如果在用户输入注册信息和发消息发短信之间嵌入Activemq,将不是必须得业务逻辑进行处理,响应速度比串行快三倍,比并行快两倍,大大的提高的处理速度和相应速度。

1.2 应用解耦

    举个例子,有两个系统,一个订单一个库存,如果他们是直接关联,如果库存系统崩了,无法访问了,订单就会无法生成,两个系统之间是耦合的,在两个系统之间嵌入MQ,让订单写入MQ,库存系统去读MQ,这样,在用户下单之后,订单系统完成持久化处理,将消息写入队列,返回订单下发成功等待物流配送。库存系统采用推拉的方式获取下单的信息,酷讯系统根据下单信息进行配送,这种情况下,库存系统崩了,一样可以进行下单,因为订单系统写入消息队列之后就什么都不用管了,实现了应用之间的解耦。

1.3 流量消峰

    顾名思义,降低系统的压力,最常见的,双11,双12,618,还有拼多多上的什么一块钱抢东西等一系列秒杀活动,用户的请求首先打到MQ上,设置消息队列最大的长度,加入就100,那前一百条消息成功打进来了,说明是抢购成功的,之后101条消息开始,不进行处理,直接抛弃用户的请求或者直接页面提示,“秒杀已结束,下次继续努力”。

1.4 消息通讯

    简单来说,ActiveMQ可以实现点对点的通讯,或者聊天室的通讯,点对点通讯就是两边使用同一队列进行消息通讯,聊天室通讯就是多个客户端同时订阅一个主题,进行消息的发布和接收,实现类似聊天室的效果。

根据消息通讯,其实就引出了另外一个概念:通信模式。

2. 通信模式

2.1 点对点模式(就是queue)P2P模式

    这种点对点的通信其实就是说一个消息只能被一个服务接收,消息一旦被消费就会消失,如果没有被消费,就会一直等待,直到被消费,如果多个服务同时监听一个消费空间,先到先得,谁快谁先取。

2.2 发布/订阅模式(也就是Tpoic)Pub/Sub模式

    这种模式就是一个消息可以被多个服务接收,订阅一个主题的消费者,就只能消费它订阅的主题的消息,消费端如果在生产端发送消息之后启动,是接收不到消息的,除非生产端对消息进行了持久化(例如广播,只有当时听到的人能听到消息)。

3. 手把手从头安装(相关安装包官网都能下载)

1. 在服务器的usr/local(路径随意,这个是我用来存储的路径)路径下创建activemq文件夹,用于存放activemq。上传apache-activemq-5.15.9-bin.tar.gz到该文件夹下,备用。

2. 接着在usr/local路径下创建java文件夹,用于存放JDK, 上传jdk-8u211-linux-x64.tar.gz, activemq要搭载jdk才可以启动运(注意activemq15必须配用jdk8,搭配jdk7的话会报冲突,10~14版本用jdk7,5~9版本用jdk6,再之前的用jdk5,下载不同版本的activemq,要注意对应的jdk,否则容易引起冲突导致mq无法运行)

3. 上传完压缩包之后,就可以登录Xshell进行控制,压缩这两个压缩包,首先解压activemq,路径定位到压缩文件,cd usr/local/activemq  使用命令tar zxvf apache-activemq-5.15.9-bin.tar.gz进行解压

4. 再解压jdk压缩包,cd usr/local/java 使用命令 tar zxvf jdk-8u211-linux-x64.tar.gz 进行解压

5. 配置jdk的环境变量,使用命令vim /etc/profile  进入编辑环境变量  (如果系统中没有安装vim,命令行会提示, 按照提示执行命令sudo apt-get install vim就可以,遇到Y/n,输出Y,进行下载并安装), 安装完成之后再次执行vim /etc/profile 命令,  进入到编辑页面,移动下方向键到最后,输入i 进行编辑模式, 添加如下代码:

export JAVA_HOME=/usr/local/java/jdk1.8.0_211  (路径要跟自己的jdk存放的路径一致)

export JRE_HOME=${JAVA_HOME}/jre

export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib

export PATH=${JAVA_HOME}/bin:$PATH

输入完成之后按esc退出编辑模式, 再按shift+: 输入wq(保存并退出),如果觉得自己配置环境变量写的有问题,可以shift+; 之后输入q! (意思是不保存并退出),之后重新进入编辑模式进行编辑即可。

然后系统重新加载该文件 输入命令 source /etc/profile 让修改的环境变量生效。

验证 输入java -version

 到此为止jdk安装成功。

6. JDK安装成功之后,就可以启动activemq了,定位到activemq的bin文件夹下

cd usr/local/activemq/apache-activemq-5.15.9/bin

再输入activemq的启动命令 ./activemq start

 即表示成功启动。

还有一些常用的指令比如:

./activemq status 显示当前的ActiveMQ是否运行正常,并且能显示pid

                                                                                                                                                           

  ./activemq list    列出当前Broker名字

                                                                                                                                                           

 ./activemq bstat  显示当前Broker的统计信息

                                                                                                                                                           

./activemq query  根据筛选条件显示Broker的统计信息。如:activemq query -QQueue=string_queue,是只显示string_queue这个队列的统计信息。更多使用方法可以使用activemq query --help显示帮助。

                                                                                                                                                           

./activemq browse  可以查询当前Broker为被消费的消息,会显示消息的详细信息,如消息头,消息内容,优先级等。类似于数据库的查询功能。有自己的一套查询语法但是不是很复杂,同样可以使用activemq browse --help显示帮助。需要注意的是,只有Queue才可以查询,Topic是不可以的,所以这个功能虽然强大,但是有点鸡肋。

                                                                                                                                                           

./activemq dstat,比较有用的一个功能,可以用来查询队列的关键数值,如队列大小,生产者消费者数量,消息出队入队统计等。还可以支持通过类别查询,如只查询队列或者只查询主题。

4. 配置文件

1. conf文件夹下的activemq.xml是Activemq的主要配置文件,一些调优和内存分配等操作设置等都在里面进行。这里主要注意,在winscp中打开activemq.xml文件,如果想从外面复制内容到winscp里面的xml文件,注意不要用记事本复制,记事本默认的编码有问题,要用note+等编辑器转码之后再复制粘贴就不会出现问题,出问题的话activemq就无法正常启动。

2.配置文件详解:

连接到ActiveMQ的用户认证:

<!-- Allows us to use system properties as variables in this configuration file -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <value>file:${activemq.conf}/credentials.properties</value>
        </property>
    </bean>

   <!-- Allows accessing the server log -->
    <bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"
          lazy-init="false" scope="singleton"
          init-method="start" destroy-method="stop">
    </bean>

broker节点:

brokerName:

    参数类型是String类型,默认值是localhost,机器名网络内是唯一的。

persistent:

    参数类型是boolean类型,默认值是true,是否持久化。 true表示持久化,需要与元素persistenceAdapter结合使用。 false表示非持久化,重启后消息会丢失,且十分消耗内存(persistent这个参数,通常我们不会在xml配置文件中设置,在项目中进行设置就可以)

dataDirectory:

    参数类型是String类型,默认持久化数据的目录,通常我们都设置${activemq.data}

deleteAllMessagesOnStartup:

    参数类型是boolean,默认值是false,启动时是否清空持久化的消息。(通常不用在xml配置文件中配置)。

enableStatistics:

    参数类型是boolean,默认值是true,是否启动数据收集(通常不用在xml配置文件中设置)。

<!--
        The <broker> element is used to configure the ActiveMQ broker.
    -->
    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">

4.1 订阅/发布

目的地策略policyEntry节点:

topic:匹配的主题,自定义

    producerFlowControl: 是否对producer进行控制,如果对自己ActiveMQ服务端的底层性能和消费者端的性能足够自信的话,可以设置为false,否则设为true,同时设置memoryLimit来限制队列使用内存的大小(这里推荐设置成true,因为消费者的性能在搭建初期是没法掌控的,需要慢慢调优到最佳的性能状态,稳定之后可以设置成false)。

memoryLimit :

    队列可使用的内存上线

<policyEntry topic=">"  producerFlowControl="true" optimizedDispatch="true"  memoryLimit="16mb" >

消息限制策略:

    面向Slow Consumer的, 此策略只对Topic有效,只对nondurable订阅者有效,当通道中有大量的消息积压时,broker可以保留的消息量,为了防止Topic中有慢速消费者,导致整个通道消息积压。(对于Topic而言,一条消息只有所有的订阅者都消费才会被删除)

ConstantPendingMessageLimitStrategy:

    保留固定条数的消息,如果消息量超过limit,将使用“MessageEvictionStrategy”移除消息。

PrefetchRatePendingMessageLimitStrategy:

     保留prefetchSize倍数条消息。如果prefetchSize为100,则保留10 * 100条消息。

<pendingMessageLimitStrategy>
      <prefetchRatePendingMessageLimitStrategy multiplier="10"/>
</pendingMessageLimitStrategy>

消息剔除策略:

    面向Slow Consumer的, 配合PendingMessageLimitStrategy,只对Topic有效,只对nondurable订阅者有效。当PendingMessage的数量超过限制时,broker该如何剔除多余的消息。当Topic接收到信息消息后,会将消息“Copy”给每个订阅者,在保存这个消息时(保存策略"PendingSubscriberMessageStoragePolicy"),将会检测pendingMessages的数量是否超过限制(由"PendingMessageLimitStrategy"来检测),如果超过限制,将会在pendingMessages中使用,MessageEvicationStrategy移除多余的消息,此后将新消息保存在PendingMessages中。

OldestMessageEvictionStrategy:

    移除旧消息,默认策略

OldestMessageWithLowestPriorityEvictionStrategy:

    旧数据中权重较低的消息,将会被移除。

UniquePropertyMessageEvictionStrategy:

    移除具有指定property的旧消息。开发者可以指定property的名称,从此属性值相同的消息列表中移除较旧的(根据消息的创建时间)。

    根据项目需要进行配置,通常不建议配置此选项,如果mq自己判断你之前的消息是多余的消息给剔除掉了,如果真是有用的消息只是堵塞了,消息会丢失。

慢速消费者策略:

    Broker将如何处理慢消费者。Broker将会启动一个后台线程用来检测所有的慢速消费者,并定期关闭它们,

AbortSlowConsumerStrategy:

    中断慢速消费者,慢速消费将会被关闭。

abortConnection:

    是否关闭连接,

AbortSlowConsumerStrategy:

    如果慢速消费者最后一个ACK距离现在的时间间隔超过阀maxTimeSinceLastAck,则中断慢速消费者。

转发策略:

将消息转发给消费者的方式。

RoundRobinDispatchPolicy:

     “轮询”,消息将依次发送给每个“订阅者”。“订阅者”列表默认按照订阅的先后顺序排列,在转发消息时,对于匹配消息的第一个订阅者,将会被移动到“订阅者”列表的尾部,这也意味着“下一条”消息,将会较晚的转发给它。

StrictOrderDispatchPolicy:

    严格有序,消息依次发送给每个订阅者,按照“订阅者”订阅的时间先后。它和RoundRobin最大的区别是,没有移动“订阅者”顺序的操作。

PriorityDispatchPolicy:

    基于“property”权重对“订阅者”排序。它要求开发者首先需要对每个订阅者指定priority,默认每个consumer的权重都一样。

SimpleDispatchPolicy:

     默认值,按照当前“订阅者”列表的顺序,其中PriorityDispatchPolicy是其子类。

<dispatchPolicy>
      <strictOrderDispatchPolicy/>
</dispatchPolicy>

恢复策略:

 ActiveMQ重启后恢复数据。

FixedSizedSubscriptionRecoveryPolicy:

    保存一定size的消息,broker将为此Topic开辟定额的RAM用来保存最新的消息。使用maximumSize属性指定保存的size数量。

FixedCountSubscriptionRecoveryPolicy:

    保存一定条数的消息,使用maximumSize属性指定保存的size数量。

LastImageSubscriptionRecoveryPolicy:

    只保留最新的一条数据。

QueryBasedSubscriptionRecoveryPolicy:

    符合置顶selector的消息都将被保存,具体能够“恢复”多少消息,由底层存储机制决定;比如对于非持久化消息,只要内存中还存在,则都可以恢复。

TimedSubscriptionRecoveryPolicy:

    保留最近一段时间的消息。使用recoverDuration属性指定保存时间 单位毫秒。

NoSubscriptionRecoveryPolicy:

    关闭“恢复机制”,默认值。

恢复最近30分钟内的信息:

<subscriptionRecoveryPolicy>
    <timedSubscriptionRecoveryPolicy recoverDuration="1800000"/>
</subscriptionRecoveryPolicy>

 根据自己项目的需求进行设置此参数,不需要的话不用配置,默认关闭就好。

"死信"策略:

处理过去消息, 缺省死信队列(Dead Letter Queue)叫做ActiveMQ.DLQ;所有的未送达消息都会被发送到这个队列,以致会非常难于管理。 默认情况下,无论是Topic还是Queue,broker将使用Queue来保存DeadLeader,即死信通道通常为Queue;不过开发者也可以指定为Topic。

IndividualDeadLetterStrategy:

    把DeadLetter放入各自的死信通道中,queuePrefix自定义死信前缀,useQueueForQueueMessages使用队列保存死信,还有一个属性为“useQueueForTopicMessages”,此值表示是否将Topic的DeadLetter保存在Queue中,默认为true。

   <individualDeadLetterStrategy  queuePrefix="DLQ." useQueueForQueueMessages="true"/>

SharedDeadLetterStrategy:

    将所有的DeadLetter保存在一个共享的队列中,这是ActiveMQ broker端默认的策略。共享队列默认为“ActiveMQ.DLQ”,可以通过“deadLetterQueue”属性来设定。还有2个很重要的可选参数,“processExpired”表示是否将过期消息放入死信队列,默认为true,“processNonPersistent”表示是否将“非持久化”消息放入死信队列,默认为false。

  <sharedDeadLetterStrategy deadLetterQueue="DLQ-QUEUE"/>

DiscardingDeadLetterStrategy:

    broker将直接抛弃DeadLeatter。如果开发者不需要关心DeadLetter,可以使用此策略。AcitveMQ提供了一个便捷的插件:DiscardingDLQBrokerPlugin,来抛弃DeadLetter。下面这个必须配置plugins节点中才对。

丢弃所有死信:

     <discardingDLQBrokerPlugin dropAll="true" dropTemporaryTopics="true" dropTemporaryQueues="true" />

丢弃指定死信:

      <discardingDLQBrokerPlugin dropOnly="MY.EXAMPLE.TOPIC.29 MY.EXAMPLE.QUEUE.87" reportInterval="1000" />

使用丢弃正则匹配到死信:

      <discardingDLQBrokerPlugin dropOnly="MY.EXAMPLE.TOPIC.[0-9]{3} MY.EXAMPLE.QUEUE.[0-9]{3}" reportInterval="3000" />

<deadLetterStrategy>
     <individualDeadLetterStrategy  queuePrefix="DLQ.TOPIC." useQueueForQueueMessages="true"/>
</deadLetterStrategy>

非耐久待处理消息处理策略:

类似于配置消息队列里的pendingQueuePolicy

支持三种策略:storeCursor, vmCursor和fileCursor

<pendingSubscriberPolicy>
       <fileCursor/>
</pendingSubscriberPolicy>

耐久待处理消息处理策略:

类似于配置消息队列里pendingQueuePolicy

支持三种策略:storeDurableSubscriberCursor, vmDurableCursor和 fileDurableSubscriberCursor

<pendingDurableSubscriberPolicy>
       <storeDurableSubscriberCursor/>   
</pendingDurableSubscriberPolicy>

4.2 消息队列

前面一直到死信策略的配置方式都是一样的

待消费消息策略:

pendingQueuePolicy:

    通道中有大量Slow Consumer时,Broker该如何优化消息的转发,以及在此情况下,“非持久化”消息达到内存限制时该如何处理。

    当Broker接受到消息后,通常将最新的消息写入内存以提高消息转发的效率,提高消息ACK的效率,减少对对底层Store的操作;如果Consumer非常快速,那么消息将会立即转发给Consumer,不需要额外的操作;但当遇到Slow Consumer时,情况似乎并没有那么美好。

    持久化消息,通常为:写入Store->线程轮询,从Store中pageIn数据到PendingStorage->转发给Consumer->从PendingStorage中移除->消息ACK后从Store中移除。

    对于非持久化数据,通常为:写入内存->如果内存足够,则PendingStorage直接以内存中的消息转发->如果内存不足,则将内存中的消息swap到临时文件中->从临时文件中pageIn到内存,转发给Consumer。

AcitveMQ提供了几个的Cursor机制,它就是用来保存Pending Messages。

1) vmQueueCursor: 将待转发消息保存在额外的内存(JVM linkeList)的存储结构中。是“非持久化消息”的默认设置,如果Broker不支持Persistent,它是任何类型消息的默认设置。有OOM风险。

2) fileQueueCursor: 将消息保存到临时文件中。文件存储方式有broker的tempDataStore属性决定。是“持久

3) storeCursor: “综合”设置,对于非持久化消息,将采用vmQueueCursor存储,对于持久化消息采用fileQueueCursor。这是强烈推荐的策略,也是效率最好的策略

ActiveMQ的特性之一是很好的支持JMX。通过JMX MBeans可以很方便的监听和控制ActiveMQ的broker。官方网站提供的JMX特性说明对于远程访问的配置流程坑爹,如果想使用jconsole对ActiveMQ进行监控,

    无密码访问>

    需要在borker节点设置useJmx属性为true,且managementContext节点的createConnector属性为true。通过jconsole访问地址service:jmx:rmi:///jndi/rmi://ip:1099/jmxrmi进行连接,默认端口为1099,可以通过connectorPort属性修改连接端口,远程访问需要设置connectorHost属性为本机ip以供远程访问

    有密码访问>

    需要在borker节点设置useJmx属性为true,且managementContext节点的createConnector属性为false。然后在${actviemq.base}/conf目录下的jmx.access和jmx.password中添加用户权限和密码,最后修改${activemq.base}/bin/activemq文件,找到下面的内容然后去掉注释,保存退出,重启activemq即可

# ACTIVEMQ_SUNJMX_START="-Dcom.sun.management.jmxremote.port=11099"

# ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START-Dcom.sun.management.jmxremote.password.file=${ACTIVEMQ_CONF}/jmx.password"

# ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START-Dcom.sun.management.jmxremote.access.file=${ACTIVEMQ_CONF}/jmx.access"

#ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START-Dcom.sun.management.jmxremote.ssl=false"

持久化存储:

有四种持久化方式:

1.AMQ方式:

  性能高于JDBC,写入消息时,会将消息写入日志文件,由于是顺序追加写,性能很高。为了提升性能,创建消息主键索引,并且提供缓存机制,进一步提升性能。每个日志文件的大小都是有限制的(默认32m,可自行配置)

  当超过这个大小,系统会重新建立一个文件。当所有的消息都消费完成,系统会删除这个文件或者归档(取决于配置)。

要的缺点是AMQ Message会为每一个Destination创建一个索引,如果使用了大量的Queue,索引文件的大小会占用很多磁盘空间。而且由于索引巨大,一旦Broker崩溃,重建索引的速度会非常慢。

一半多用于远古版本的Activemq。

2.LevelDB方式:

    从ActiveMQ 5.8版本之后,推出的持久化引擎。目前默认的持久化方式是KahaDB,不过LevelDB持久化性能高于KahaDB,可能是以后的趋势。
  比kahaDB有更快的持久性,不同的是,不是使用传统的B-Tree实现对日志文件的提前写,多用于和zookeeper结合,用于数据复制。用于Master-slave方式的首选数据复制方案

3.JDBC持久化方式:

    数据库会创建3个表:activemq_msgs,activemq_acks和activemq_lock。
activemq_msgs用于存储消息,Queue和Topic都存储在这个表中。

    首先定义一个mysql-ds的MySQL数据源,然后在persistenceAdapter节点中配置jdbcPersistenceAdapter并且引用刚才定义的数据源。

dataSource指定持久化数据库的bean,createTablesOnStartup是否在启动的时候创建数据表,默认值是true,这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为true,之后改成false。
使用MySQL配置JDBC持久化:

数据库中表的信息:

activemq_msgs用于存储消息,Queue和Topic都存储在这个表中:
ID:自增的数据库主键
CONTAINER:消息的Destination
MSGID_PROD:消息发送者客户端的主键
MSG_SEQ:是发送消息的顺序,MSGID_PROD+MSG_SEQ可以组成JMS的MessageID
EXPIRATION:消息的过期时间,存储的是从1970-01-01到现在的毫秒数
MSG:消息本体的Java序列化对象的二进制数据
PRIORITY:优先级,从0-9,数值越大优先级越高

activemq_acks用于存储订阅关系。如果是持久化Topic,订阅者和服务器的订阅关系在这个表保存:
主要的数据库字段如下:
CONTAINER:消息的Destination
SUB_DEST:如果是使用Static集群,这个字段会有集群其他系统的信息
CLIENT_ID:每个订阅者都必须有一个唯一的客户端ID用以区分
SUB_NAME:订阅者名称
SELECTOR:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作
LAST_ACKED_ID:记录消费过的消息的ID。

    表activemq_lock在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,其他的只能作为备份等待Master Broker不可用,才可能成为下一个Master Broker。
  这个表用于记录哪个Broker是当前的Master Broker。

4.KahaDB方式

    KahaDB是从ActiveMQ 5.4开始默认的持久化插件,也是我们项目现在使用的持久化方式。KahaDb恢复时间远远小于其前身AMQ并且使用更少的数据文件,所以可以完全代替AMQ。
kahaDB的持久化机制同样是基于日志文件,索引和缓存。

可设置日志的最大长度journalMaxFileLength : 指定保存消息的日志文件大小,具体根据你的实际应用配置 

  以日志形式存储消息,新消息以APPEND的方式进行追加到文件末尾,APPEND减少了

Broker向producter返回Acknowledge的时间。

  data/kahadb这个目录下,会生成四个文件,来完成消息持久化

1.db.data 它是消息的索引文件,本质上是B-Tree(B树),使用B-Tree作为索引指向db-*.log里面存储的消息 

2.db.redo 用来进行消息恢复

3. db-*.log 存储消息内容。新的数据以APPEND的方式追加到日志文件末尾。属于顺序写入,因此消息存储是比较 快的。默认是32M,达到阀值会自动递增 

4.lock文件 锁,写入当前获得kahadb读写权限的broker ,用于在集群环境下的竞争处理

KahaDB的索引使用一个transaction log,所有的destination只用一个index,生产

环境中支持一万个active connection,每个connection有一个独立的queue,足已应付大部分的需求。

systemUsage标记

 

systemUsage:该标记用于设置整个ActiveMQ节点在进程级别的各种“容量”的设置情况。其中可设置的属性包括:sendFailIfNoSpaceAfterTimeout,当ActiveMQ收到一条消息时,如果ActiveMQ这时已经没有多余“容量”了,那么就会等待一段时间(这里设置的毫秒数),如果超过这个等待时间ActiveMQ仍然没有可用的容量,那么就拒绝接收这条消息并在消息的发送端抛出javax.jms.ResourceAllocationException异常;sendFailIfNoSpace,当ActiveMQ收到一条消息时,如果ActiveMQ这时已经没有多余“容量”了,就直接拒绝这条消息(不用等待一段时间),并在消息的发送端抛出javax.jms.ResourceAllocationException异常。

memoryUsage:该子标记设置整个ActiveMQ节点的“可用内存限制”。这个值不能超过上文中您设置的JVM maxmemory的值。其中的percentOfJvmHeap属性表示使用“百分数值”进行设置,除了这个属性以外,您还可以使用limit属性进行固定容量授权,例如:limit=1000 mb。这些内存容量将供所有队列使用

storeUsage:该标记设置整个ActiveMQ节点,用于存储“持久化消息”的“可用磁盘空间”。该子标记的limit属性必须要进行设置。在使用后续介绍的KahaDB方案或者LevelDB方案进行PERSISTENT Message持久化存储时,这个storeUsage属性都会起作用;但是如果使用数据库存储方案,这个属性就不会起作用了。

tempUsage:在ActiveMQ 5.X+ 版本中,一旦ActiveMQ服务节点存储的消息达到了memoryUsage的限制,NON_PERSISTENT Message就会被转储到 temp store区域。虽然我们说过NON_PERSISTENT Message不进行持久化存储,但是ActiveMQ为了防止“数据洪峰”出现时NON_PERSISTENT Message大量堆积致使内存耗尽的情况出现,还是会将NON_PERSISTENT Message写入到磁盘的临时区域——temp store。这个子标记就是为了设置这个temp store区域的“可用磁盘空间限制”。最后提醒各位读者storeUsage和tempUsage并不是“最大可用空间”,而是一个阀值。

 5. JMS编程模型

1. ConnectionFactory

   创建Connection对象的工厂,针对两种不同的JMS消息模型,分别有QueueConnectionFactory和TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象

2. Destination

    Destination的意思是消息生产者的消息发送目标或者说消息消费者的消息来源。对于消息生产者来说,它的Destination是某个队列(Queue)或某个主题(Topic);对于消息消费者来说,它的Destination也是某个队列或主题(即消息来源)。

3. Connection

    Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装)。Connection可以产生一个或多个Session。跟ConnectionFactory一样,Connection也有两种类型:QueueConnection和TopicConnection。

4. Session

    是我们操作消息的接口,可通过session创建生产者,消费者,消息等等,session提供了事务功能,当需要使用session发送或接收多个消息时,可将这些动作放到一个事务中,同样分为QueueSession和TopicSession。

5. 消息的生产者producer

    消息生产者是由session创建,用于将消息发送到Destination,同样,消息生产者分为QueueSender和TopicSender,可以调用send或publish方法将消息发送到MQ上。

6. 消息消费者consumer

    消息消费者同样是由session创建,用于接收被发送到Destination的消息,可分别通过session的createReceiver(Queue)或createSubscriber(Topic)来创建,也可以通过session的creatDurableSubscriber方法来创建持久化的订阅者

7. MessageListener

    消息监听器,如果注册了消息监听器,一旦消息到达,将自动调用消息监听器的onMessage方法,EJB中的MDBMessage-Driven Bean)就是一种MessageListener

当时我运用的配置文件详情:

<!--
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    this work for additional information regarding copyright ownership.
    The ASF licenses this file to You under the Apache License, Version 2.0
    (the "License"); you may not use this file except in compliance with
    the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<!-- START SNIPPET: example -->
<beans
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

    <!-- Allows us to use system properties as variables in this configuration file -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <value>file:${activemq.conf}/credentials.properties</value>
        </property>
    </bean>

   <!-- Allows accessing the server log -->
    <bean id="logQuery" class="io.fabric8.insight.log.log4j.Log4jLogQuery"
          lazy-init="false" scope="singleton"
          init-method="start" destroy-method="stop">
    </bean>

    <!--
        The <broker> element is used to configure the ActiveMQ broker.
    -->
    <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">

        <destinationPolicy>
            <policyMap>
              <policyEntries>
                <policyEntry topic=">"  producerFlowControl="true" optimizedDispatch="true"  memoryLimit="16mb" >
                    <!-- The constantPendingMessageLimitStrategy is used to prevent
                         slow topic consumers to block producers and affect other consumers
                         by limiting the number of messages that are retained
                         For more information, see:

                         http://activemq.apache.org/slow-consumer-handling.html

                    -->
                  <pendingMessageLimitStrategy>
                    <prefetchRatePendingMessageLimitStrategy multiplier="10"/>
                  </pendingMessageLimitStrategy>
 
                  <slowConsumerStrategy> 
                   <abortSlowConsumerStrategy abortConnection="false"/><!-- 不关闭底层链接 -->    
                  </slowConsumerStrategy>

                  <dispatchPolicy>
                    <strictOrderDispatchPolicy/>
                  </dispatchPolicy>

                  <deadLetterStrategy>
                    <individualDeadLetterStrategy  queuePrefix="DLQ.TOPIC." useQueueForQueueMessages="true"/>
                  </deadLetterStrategy>

                  <pendingSubscriberPolicy>
                   <fileCursor/>
                  </pendingSubscriberPolicy>

                  <pendingDurableSubscriberPolicy>
                    <storeDurableSubscriberCursor/>   
                  </pendingDurableSubscriberPolicy>                  
                 
                </policyEntry>
        

                <!--消息队列-->
                <policyEntry queue=">" producerFlowControl="true" optimizedDispatch="true" memoryLimit="64mb">
                     <pendingMessageLimitStrategy>
                        <prefetchRatePendingMessageLimitStrategy multiplier="10"/>  
                     </pendingMessageLimitStrategy>
					 
					 <!--慢速消费者策略Broker将如何处理慢消费者-->
                     <slowConsumerStrategy> 
                        <abortSlowConsumerStrategy abortConnection="false"/>   
                     </slowConsumerStrategy>
					
					 <!--转发策略 将消息转发给消费者的方式-->					
                     <dispatchPolicy>
                        <strictOrderDispatchPolicy/>
                     </dispatchPolicy>
					
					 <!--"死信"策略 如何处理过去消息-->
                     <deadLetterStrategy>
                        <individualDeadLetterStrategy  queuePrefix="DLQ.QUEUE." useQueueForQueueMessages="true"/>
                     </deadLetterStrategy>
             
                </policyEntry>
              </policyEntries>
            </policyMap>
        </destinationPolicy>
        

                <!--
            The managementContext is used to configure how ActiveMQ is exposed in
            JMX. By default, ActiveMQ uses the MBean server that is started by
            the JVM. For more information, see:

            http://activemq.apache.org/jmx.html
        -->
        <managementContext>
            <managementContext  createConnector="false"/>
        </managementContext>
        <!--
            Configure message persistence for the broker. The default persistence
            mechanism is the KahaDB store (identified by the kahaDB tag).
            For more information, see:

            http://activemq.apache.org/persistence.html
        -->
        <persistenceAdapter>
            <kahaDB directory="${activemq.data}/kahadb"/>
        </persistenceAdapter>


          <!--
            The systemUsage controls the maximum amount of space the broker will
            use before disabling caching and/or slowing down producers. For more information, see:
            http://activemq.apache.org/producer-flow-control.html
          -->
          <systemUsage>
            <systemUsage sendFailIfNoSpaceAfterTimeout="3000" sendFailIfNoSpace="true" >
                <memoryUsage>
                    <memoryUsage percentOfJvmHeap="70" />
                </memoryUsage>
                <storeUsage>
                    <storeUsage limit="100 gb"/>
                </storeUsage>
                <tempUsage>
                    <tempUsage limit="50 gb"/>
                </tempUsage>
            </systemUsage>
        </systemUsage>

        <!--
            The transport connectors expose ActiveMQ over a given protocol to
            clients and other brokers. For more information, see:

            http://activemq.apache.org/configuring-transports.html
        -->
        <transportConnectors>
            <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->
            <transportConnector name="openwire" uri="tcp://0.0.0.0:34595?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
        </transportConnectors>

        <!-- destroy the spring context on shutdown to stop jetty -->
        <shutdownHooks>
            <bean xmlns="http://www.springframework.org/schema/beans" class="org.apache.activemq.hooks.SpringContextHook" />
        </shutdownHooks>

    </broker>

    <!--
        Enable web consoles, REST and Ajax APIs and demos
        The web consoles requires by default login, you can disable this in the jetty.xml file

        Take a look at ${ACTIVEMQ_HOME}/conf/jetty.xml for more details
    -->
    <import resource="jetty.xml"/>

</beans>
<!-- END SNIPPET: example -->

java封装的生产者工具类:

package com.qudou.app.framework.util;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/**
 * activemq工具类
 * 用于接口自动下发发消息
 * @author yaohe
 */
public class ActivemqUtilForInterface {
	/**
	 * 接口自动下发
	 * 消息生产者
	 */
    private static ActiveMQConnectionFactory connectionFactory;

	public static ActiveMQConnectionFactory getMyActiveMQConnectionFactory() {
		if (connectionFactory == null) {
			connectionFactory = new ActiveMQConnectionFactory(Constant.ACTIVEMQ_USERNAME, Constant.ACTIVEMQ_PASSWORD, Constant.ACTIVEMQ_PROURL);
		}
		return connectionFactory;
	}

	public void producterForInterface(String jsonStr) {

			 Connection connection = null;

			 //1.实例化连接工厂 "admin", "admin", "tcp://39.104.167.212:34595"
			// ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.ACTIVEMQ_USERNAME, Constant.ACTIVEMQ_PASSWORD, Constant.ACTIVEMQ_PROURL);
		     getMyActiveMQConnectionFactory();

			 //2.通过连接工厂获取连接
				try {
				   	connection = connectionFactory.createConnection();
			        //启动连接
			        connection.start();
			        //3.创建session
			        Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
			        //4.创建一个连接的消息队列   AUTOISSUSED_CHANNEL_BATCHPAY  PERSONAPPROVAL_CHANNEL_BATCHPAY  INTERFACE_CHANNEL_BATCHPAY
			        Destination destination = session.createQueue("INTERFACE_CHANNEL_BATCHPAY");//接口
			        //5.创建消息生产者
			        MessageProducer messageProducer = session.createProducer(destination);
			        //6.设置持久化/非持久化
			        messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
			        //7.发送消息
			        ObjectMessage message = session.createObjectMessage(jsonStr);
			        System.out.println("发送消息:Activemq 发送消息" +jsonStr);
			        //通过消息生产者发出消息
			        messageProducer.send(message);

			        session.commit();

				   } catch (Exception e) {
				       e.printStackTrace();
				   }finally{
				       if(connection != null){
				           try {
				           	//8.释放链接
				               connection.close();
				           } catch (JMSException e) {
				               e.printStackTrace();
				           }
				       }
				  }
		}

}

java封装的消费者工具类:

package com.qudou.app.framework.util;

import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

/**
 * activemq消费者工具类
 * @author yaohe
 */
public class ActivemqUtil {

    private static final Logger logger = LogManager.getLogger(ActivemqUtil.class);

    /**
     * 消息的消费者
     *
     * @author yaohe
     * @return
     */
    public static ActiveMQConnectionFactory connectionFactory;
    public static boolean ActiveMQStatus = false;

    static {
    	//"admin", "admin", "tcp://39.104.167.212:34595"
        connectionFactory = new ActiveMQConnectionFactory(Constant.ACTIVEMQ_USERNAME, Constant.ACTIVEMQ_PASSWORD, Constant.ACTIVEMQ_TESTCONSURL);
    }

    public  List<String> consumerForFile(long Activemq_size) {

    	List<String> list = new ArrayList<>();
        Connection connection = null;
        Session session = null;

        try {
            //通过连接工厂获取连接
            connection = createConnection(connectionFactory);
            //启动连接
            connection.start();
            //主动的接收----手动的调用MessageConsumer的receive方法
            for (int i = 0; i < Activemq_size; i++) {
 
                ObjectMessage message =null;
                
					try 
					{
						if(session!=null) {
							session.close();
						}
			            //创建session
			            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			            //创建一个连接的消息队列 
			            Queue destination = session.createQueue("AUTOISSUSED_CHANNEL_BATCHPAY");
			            //创建消息消费者
			            MessageConsumer messageConsumer = session.createConsumer(destination);
			            //message = (ObjectMessage) messageConsumer.receiveNoWait();
						message = (ObjectMessage) messageConsumer.receive(100);
 
						if(message!=null) {
							String dstStr = (String) message.getObject();
		                    list.add(dstStr);
 						}else {
		                    logger.info("AUTOISSUSED_CHANNEL_BATCHPAY通道消息为空==================================");
							if(messageConsumer!=null) {
								messageConsumer.close();
							}
		                    break;
						}
						
						if(messageConsumer!=null) {
		                   // logger.info("messageConsumer==================================关闭");
 							messageConsumer.close();
						}
						
					} catch (Exception e) {
	 		            logger.error("messageConsumer.receive 异常~~~~~~~~~" + e.getMessage());
					}
            }

        } catch (SocketTimeoutException e) {
            e.printStackTrace();
            logger.error("Activemq连接超时:" + e.getMessage());
        } catch (JMSException e) {
            e.printStackTrace();
            logger.error("创建session发生异常:" + e.getMessage());
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.error("未知异常:" + ex.getMessage());
        }finally { 
        
	        try {
	        	if(session!=null) {
	            	session.close();
	        	}
	        	if(connection!=null) {
	            	connection.close();
	        	}
	        } catch (JMSException e) {
	        	logger.error("关闭连接发生异常:" + e.getMessage());
	        }
        }
        return list;
    }
    
    public static Connection createConnection(ActiveMQConnectionFactory activeMQConnectionFactory) throws SocketTimeoutException {

        Connection coon = null;
        try {
            coon = activeMQConnectionFactory.createConnection();
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return coon;
    }
    
   /* public static void main(String[] args) throws Exception {
 
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://127.0.0.1:"+"11099"+"/jmxrmi" );
        JMXConnector connector = JMXConnectorFactory.connect(url, null);
        connector.connect();
        MBeanServerConnection connection = connector.getMBeanServerConnection();

         // 需要注意的是,这里的jms-broker必须和上面配置的名称相同
        ObjectName name = new ObjectName("myDomain"+":BrokerName=localhost,Type=Broker");
        BrokerViewMBean mBean =  (BrokerViewMBean)MBeanServerInvocationHandler.newProxyInstance(connection,  
        		name, BrokerViewMBean.class, true);
        // System.out.println(mBean.getBrokerName());
        
        for(ObjectName queueName : mBean.getQueues()) {
            QueueViewMBean queueMBean =  (QueueViewMBean)MBeanServerInvocationHandler
            			.newProxyInstance(connection, queueName, QueueViewMBean.class, true);
            System.out.println("\n------------------------------\n");

            // 消息队列名称
            System.out.println("States for queue --- " + queueMBean.getName());

            // 队列中剩余的消息数
            System.out.println("Size --- " + queueMBean.getQueueSize());

            // 消费者数
            System.out.println("Number of consumers --- " + queueMBean.getConsumerCount());

            // 出队数
            System.out.println("Number of dequeue ---" + queueMBean.getDequeueCount() );
        }
        
        }*/

}

    很久之前做的一个东西了,当时也是写了一个word用来记录了,现在存一下博客,方便以后自己的阅读哈哈哈

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值