目录
1.AMQP
1.1 消息转换
Artemis支持AMQP 1.0,可以使用任何兼容AMQP1.0规范的客户端。
当发送和接收AMQP消息时,代理不会对消息进行协议转换,不会将消息转换成其他协议。如想要用AMQP JMS Client接收消息,必须遵循JMS映射约定,使用基于JMS的客户端发送AMQP消息。如果发送的body类型此规范无法识别,则AMQP和其他协议一样消息会直接转换变成二进制消息。如果要打算跨协议或语言,确保遵循协议转换规范。
通过兼容性设置,允许将AMQP队列的命名和CORE规则命名一致,需要通过在代理中配置启用此功能:
amqp-use-core-subscription-naming
true:使用与CORE一致的队列命名规范
false(默认值):使用旧的命名规范
1.2 拦截和更改消息
不建议在服务端对消息拦截进行更改或转换消息,原因如下:
- AMQP消息是不可变的
- 该消息不是用户发送的原始消息
- AMQP可能对消息进行签名,签名将被破坏
- 出于性能原因,我们尽量不重新编解码消息
如果还是需要拦截和更改AMQP消息,可以查看拦截器例子。
1.3 AMQP广播地址(TOPIC)
尽管AMQP没有主题概念,仍然可以将AMQP消费者视为订阅,而不仅仅是队列中的消费者。默认情况下,任何连接到广播地址的AMQP消费者都被视为订阅,并会创建想要的订阅队列。如果持久性标志是UNSETTLED_STATE或CONFIGURATION,那么队列将变为持久化(类似于JMS的持久化订阅),并给出一个由容器id和连接名组成的名称,如:my-container-id:my-link。如果持久化标志NONE,将创建易失性的广播队列(连接断则队列被删)。
1.4 AMPQ事务消息
AMQP的连接目标可以是协调器。协调器用于处理事务,如果使用协调器底层会话将被事务处理,通过协调器进行回滚或者提交。
AMQP协议本身运行每个会话使用多个事务。但是在这个版本的Artemis中仅支持每个会话单个事务。
1.5 AMQP消息投递调度
AMQP消息可以通过调度信息来控制消息的投递时间。通过在发送的消息中添加消息标注来实现,有两种不同的消息标注:
x-opt-delivery-time指定一个long类型的整数,对应消息开始进行投递的时间(单位毫秒)
x-opt-delivery-delay指定一个long类型的整数,对应代理收到消息之后多少毫秒后开始进行投递。
如果两个标注一起出现在同一个消息中,代理倾向于使用x-opt-delivery-time。
2.MQTT
MQTT是一种轻量级的发布/订阅消息传递协议。MQTT专门设计用于减少客户端设备上的传输开销(网络流量)以及减少客户端代码封装。Artemis支持MQTT v3.1.1(以及旧的v3.1消息格式)。
2.1 留用(retain)消息
MQTT支持一个有趣的功能,可以为特定的地址保留消息。意味着一旦保留消息被发送到该地址,该地址的任何新订阅者都将在接收其他消息之前收到最新的保留消息,即使是在客户端连接或者订阅这个地址之前这个消息已经被发送到了这个地址。比如此功能可以在物联网环境中,设备需要在系统登入时快速获取系统当前状态。
2.2 will消息
当客户端最初连接到代理时,可以发送will消息。客户端能够将“will消息”设置为连接数据包的一部分。如果客户端异常断开连接,例如由于设备或网络故障,代理将开始吧“will消息”发送到指定的地址(也在连接数据包中定义)。其他订阅这个will主题的的订阅者将受到“will消息”,并可以做出相应的操作。此功能可以在物联网中进行检测可能大规模部署设备的错误。
2.3 debug日志
在Artemis中可以通过如下步骤开启详细的协议日志记录:
- 打开<ARTEMIS_INSTANCE>/etc/logging.properties
- 添加org.apache.activemq.artemis.core.protocol.mqtt到loggers列表中
- 在新的log中添加新记录器为TRACE(追踪)logger.org.apache.activemq.artemis.core.protocol.mqtt.level=TRACE
- 确保处理日志级别不会限制TRACE日志。例如修改CONSOLE处理器级别如下:handler.CONSOLE.level=TRACE
MQTT规范没有指定客户端发布消息格式的载荷大小。就代理而已有效载荷消息只是一个字节数组。但是为了便于日志记录,代理会把有效的载荷消息编码为UTF-8字符串,并且最多打印其256个字符。有效载荷消息日志记录是有限的,避免日志可能会有数百兆字节无用信息。
2.4 通配符(wild)订阅
MQTT地址和文件系统类似,使用特殊字符(/)来分离层次级别。订阅者可以订阅特定的主题也可以订阅整个层次结构的分支。
要订阅整个地址层次结构的分支,可以使用通配符,MQTT有两种类型的通配符:
Multi level (# by default):此通配符能匹配指定节点下的所有层次结构,如/uk/#可以匹配/uk/cities、/uk/cities/newcastle等。订阅地址#将导致订阅代理中的所有主题,这个很有用但是需要注意,其对性能影响很大。
Single level (+ by default):用于匹配地址层次中指定单层次。例如/uk/+/stores匹配/uk/newcastle/stores但是不匹配/uk/cities/newcastle/stores。
3.STOMP
STOMP一种简单的面向文本的消息传输协议,允许STOMP客户端和STOMP代理进行通信。Artemis执行STOMP1.0,1.1和1.2.
STOMP规范中将确认事务确认为可选功能。Artemis为实现其对事务确认的功能,ACK结果不是事务的一部分,如果设置了事务头信息,其会被忽略。
3.1 将STOMP目的地映射到地址和队列
当STOMP客户端发送消息时,协议管理器会使用发送帧中以下任何信息来确定路由类型:
1.destination-type头信息中的值,有效值为ANYCAST和MULTICAST(区分大小写)
2.destination头信息中的前缀。
如果没有指定路由问题默认使用anycast。
destination头信息映射到同名的地址,如果destination头信息使用了前缀,则剥离前缀。
同样当STOMP客户端订阅消息时,协议管理器使用一下任何信息来确定路由类型:
1.subscription-type头信息中的值,有效值为ANYCAST和MULTICAST(区分大小写)
2.destination头信息中的前缀。
如果没有指定路由问题默认使用anycast。
如果使用multicast,使用destination信息映射到同名的地址,如果使用anycast,则映射到同名的队列。如果destination头信息使用了前缀,则剥离前缀。
3.2 STOMP心跳和连接TTL
正常关闭连接的STOMP客户端将在关闭连接之前会发送DISCONNECT帧,在这种情况下,服务器将同步清除任何服务端资源。但是客户端在没有发送DISCONNECT帧情况下退出,服务器将无法知道客户端是否仍然存在。因此STOMP连接有默认为1分钟的connection-ttl值。可以在代理中配置connection-ttl-override属性来覆盖客户端的connection-ttl。或者如果您的stomp连接需要特定的connectionTtl而不影响代理范围的connection-ttl-override设置,则可以使用connectionTtl属性配置您的stomp接受器。如下是在broker中在acceptor设置ttl。例如:
<acceptor name="stomp-acceptor">tcp://localhost:61613?protocols=STOMP;connectionTtl=20000</acceptor>
对于没有设置心跳的STOMP客户端(STOMP1.0不支持心跳)所有连接到代理的客户端将被代理强加上connectionTtl。
对于有发送心跳的STOMP客户端,有心跳的客户端ConnectionTtl值为心跳间隔值乘以heartBeatConnectionTtlModifer,heartBeatConnectionTtlModifer默认为2.0。例如,客户端发送心跳头为1000则连接的TTL将设置为2000。
3.3 选择/过滤表达式
STOMP消费者通过在selector头中指定表达式用于选择或者过滤消费者接受那些消息。
3.4 持久化订阅
SUBSCRIBE和UNSUBSCRIBE帧可以使用特殊标头进行扩充,以分别创建和销毁持久订阅。
要创建持久订阅,必须在CONNECT帧上设置client-id标头,并且必须在SUBSCRIBE帧上设置durable-subscription-name。这两个标头的组合将形成持久订阅的标识。要删除持久订阅,必须在CONNECT帧上设置client-id标头,并且必须在UNSUBSCRIBE帧上设置durable-subscription-name。这些标头的值应与SUBSCRIBE框架上设置的值相匹配,以删除相应的持久订阅。
由于STOMP实现以确定的方式(即使用client-id.subscription-name的格式)创建用于持久化订阅的队列,因此可以预先配置持久订阅。例如,如果要在地址myAddress上配置持久订阅,其客户端ID为myclientid,订阅名称为mysubscription:
<addresses>
<address name="myAddress">
<multicast>
<queue name="myclientid.mysubscription"/>
</multicast>
</address>
</addresses>
3.5 使用STOMP处理大型消息
STOMP客户端可能会发送超出代理内部缓冲区大型的帧,从而导致意外错误。为了防止这种情况,代理提供了STOMP配置属性stompMinLargeMessageSize,可以在stomp接收器内配置此属性作为参数:
<acceptor name="stomp-acceptor">tcp://localhost:61613?protocols=STOMP;stompMinLargeMessageSize=10240</acceptor>
配置此属性后,代理将检测每个STOMP帧大小,大于或等于此配置的值,则消息会作为大消息保留,当大消息被传递给STOMP消费者时,代理将自动处理大消息到正常消息的转换,然后再将其发送给客户端。stompMinLargeMessageSize的默认值与min-large-message-size的默认值相同。
3.6 STOMP的websocket支持
<acceptor name="stomp-ws-acceptor">tcp://localhost:61614?protocols=STOMP</acceptor>
使用此配置Artemis通过端口61614接收webSocket连接。webSocket帧的有效负载长度可以在客户端实现之间变化。默认情况下,代理将接受有效负载长度为65,536的帧。如果客户端需要在单个帧中发送超过此长度的有效负载,则可以使用接受器上的stompMaxFramePayloadLength URL参数调整此长度。
4.CORE
Artemis core是消息中间件自有的消息系统API。
4.1 消息
- 消息是在客户端和服务端直接传递的数据单元
- 消息中有body作为缓存,包含读取和写入数据的便捷方法。
- 消息有一系列键值对属性。每个属性key都是string属性值可以为integer, long, short, byte, byte[], String, double, float or boolean。
- 每个消息都有要发送的地址,当消息到达服务器时,它将路由到绑定该地址的队列。路由语义(即任播或多播)由地址和队列的“路由类型”确定。
- 消息可以是持久化和非持久化。
- 消息定义了0到9之间的优先级,0表示最低优先级,9表示最高优先级。代理将参数在优先级较低的消息之前投递优先级更高的消息。
- 消息可以设置到期时间,到期的消息将不会被broker投递。
- 消息有一个可选的时间戳,表示消息发送的时间。
- Artemis还支持发送/消费大消息,比可用RAM大的多的消息。
4.2 地址
服务器维护地址和一组队列之间的映射。零个或多个队列可以绑定到单个地址。每个队列都可以与可选的消息过滤器绑定。当路由消息时,它将路由到绑定到消息地址的队列集。如果任何队列与过滤器表达式绑定,则该消息将仅路由到与该过滤器表达式匹配的绑定队列子集。
4.3 队列
队列可以是持久的,这意味着它们包含的消息在服务器崩溃或重新启动后仍然存在,只要它们中的消息是持久的。 即使它们包含的消息是持久的,非持久队列也不会在服务器重启或崩溃后继续存在。
队列也可以是临时的,这意味着它们会在客户端连接关闭时自动删除,如果在此之前未明确删除它们。
队列可以与可选的过滤器表达式绑定。 如果提供了过滤器表达式,则服务器将仅将与该过滤器表达式匹配的消息路由到绑定到该地址的任何队列。
4.4 路由类型
路由类型确定将消息路由到绑定到发送消息的地址的队列时使用的语义。 支持两种类型:
ANYCAST:消息仅路由到绑定到该地址的其中一个队列。 如果多个队列绑定到该地址,则消息将以循环方式路由到它们。
MULTICAST:消息是路由到绑定到该地址的所有队列。
5.JMS
Artemis core和JMS无关,它没有JMS的topic概念。JMS的topic在core中作为一个地址,其中name=(topic名称),路由类型为MULTICAST并且有零个或者多个绑定到它的队列。绑定到该地址的每个队列代表主题订阅。
同样的,JMS队列可以应用于name=(JMS队列名称)并且路由类型为ANYCAST的地址
注:此系列文章为Apache Artemis V2.6.2官方使用文档的简要翻译文档(非完全按照官方文档排版进行翻译,有删减),个人能力有限如有错误请谅解。源文档地址:http://activemq.apache.org/artemis/docs/latest/index.html