REST
ActiveMQ Classic 实现了一个面向 REST 的消息 API,它允许任何支持 Web 的设备使用常规的 HTTP POST 或 GET 来发布或消费消息。
如果您有兴趣直接从 Web 浏览器发送消息,您可能想查看我们的 Ajax 或 WebSockets 支持,或尝试 运行 REST 示例
REST 到 JMS 的映射
要发布消息,请使用 HTTP POST。要消费消息,请使用 HTTP DELETE 或 GET。
ActiveMQ Classic 具有一个 Servlet,它负责处理 HTTP 和 ActiveMQ Classic 分发器之间的集成。
注意:以下示例需要在 URL 上进行 Servlet 映射。有关没有 Servlet 映射的发布方法,请参见后面的示例。
您可以将 URI 映射到 Servlet,然后使用 URI 的相对部分作为主题或队列名称。例如,您可以使用 HTTP POST 到
http://www.acme.com/orders/input
这将把 HTTP POST 的内容发布到 JMS 上的 orders.input 队列中。
类似地,您可以对上述 URL 执行 HTTP DELETE GET 以从同一队列中读取。在这种情况下,我们将 ActiveMQ Classic 中的 MessageServlet 映射到 URI
http://www.acme.com/queue
并将其配置为接受 URI 作为队列目标。我们也可以做类似的事情来支持主题目标。
我们可以使用 HTTP 会话来表示唯一的发布者或消费者。
请注意,严格的 REST 要求 GET 是一种只读操作;因此严格来说,我们不应该使用 GET 来允许人们消费消息。虽然我们允许这样做,因为它在一定程度上简化了 HTTP/DHTML/Ajax 集成。
对于更清晰的简单传输协议到不同语言的映射,您可能希望查看 Stomp.
默认配置
在 5.8 版本之前,REST API 是 Web 示例 的一部分,并映射到 https://127.0.0.1:8161/demo/message URL。从 5.8 版本开始,该 API 默认情况下在 https://127.0.0.1:8161/api/message URL 上可用。此外,从 5.8 版本开始,Web 服务器默认情况下是安全的(有关更多信息,请参见 Web 控制台),因此在尝试使用它时请牢记这一点。以下示例将假定新的 API 位置和安全的 Web 服务器。
生产
您可以通过向服务器发送 POST 请求来进行生产,例如
curl -u admin:admin -d "body=message" https://127.0.0.1:8161/api/message/TEST?type=queue
注意:如果未指定类型参数,则默认情况下会创建一个主题。要将默认值更改为队列,请在 webapps/demo/WEB-INF/web.xml
中使用 init 参数初始化 Servlet,
如下所示
<servlet>
<servlet-name>MessageServlet</servlet-name>
<servlet-class>org.apache.activemq.web.MessageServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>topic</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
备用生产语法
支持使用 URL 编码参数的备用发布语法;以下是一些示例:
# Send to queue orders.input:
curl -XPOST -d "body=message" http://admin:admin@localhost:8161/api/message?destination=queue://orders.input
# Send to topic orders.input:
curl -XPOST -d "body=message" http://admin:admin@localhost:8161/api/message?destination=topic://orders.input
消息大小限制
默认情况下,消息的大小限制为 100,000
个字符,以防止可能出现内存不足的情况。可以通过将 init 参数 maxMessageSize
设置为您选择的任何值来修改此值。将该值设置为 -1
将禁用限制。
要更改该值,请修改 webapps/api/WEB-INF/web.xml
文件,如下所示
<servlet>
<servlet-name>MessageServlet</servlet-name>
<servlet-class>org.apache.activemq.web.MessageServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
<init-param>
<param-name>maxMessageSize</param-name>
<param-value>-1</param-value>
</init-param>
</servlet>
超时
从队列中读取时,我们可能没有消息。我们可以使用超时查询参数来指示我们愿意等待消息到达的时间。这使我们能够轮询或阻塞,直到消息到达。
将其与 HTTP 1.1 保持活动套接字和管道处理结合使用,我们可以高效地访问通过 HTTP 的 JMS。
显然,如果您的客户端是 Java,那么使用 ActiveMQ Classic 的 JMS API 是与消息代理交互最快、最有效的方式;但是,如果您没有使用 Java 或更喜欢 HTTP 的简单性,那么它应该相当有效,特别是如果您的 HTTP 客户端支持保持活动套接字和管道处理。
消费
使用 REST API 消费消息时,您必须在 GET 请求之间保持会话活动,否则您将为每个请求创建一个单独的消费者,并且由于预取限制,您后续的调用将挂起。
例如,您可以使用 wget
来消费消息,如下所示
wget --user admin --password admin --save-cookies cookies.txt --load-cookies cookies.txt --keep-session-cookies https://127.0.0.1:8161/api/message/TEST1?type=queue
此外,如果您计划让多个消费者使用 REST,建议将预取大小设置为 1,这样所有消费者都有平等的机会获得消息。您可以在 webapps/demo/WEB-INF/web.xml
中向 MessageServlet
传递一个特殊参数来做到这一点
<servlet>
<servlet-name>MessageServlet</servlet-name>
<servlet-class>org.apache.activemq.web.MessageServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>destinationOptions</param-name>
<param-value>consumer.prefetchSize=1</param-value>
</init-param>
</servlet>
在 webapps/demo/WEB-INF/web.xml
中
备用消费语法
与生产一样,还支持使用 URL 编码参数的备用消费消息语法;以下是一些示例:
# Send to queue orders.input:
curl -XGET http://admin:admin@localhost:8161/api/message?destination=queue://orders.input
# Send to topic orders.input:
curl -XGET http://admin:admin@localhost:8161/api/message?destination=topic://orders.input
无会话消费
从 5.2.0 版本开始,您可以使用 clientId
参数来避免将实际 JMS 消费者存储在请求会话中。使用这种方法时,您不需要在请求之间保持会话活动,您只需要在每次使用相同的 clientId
。
wget --user admin --password admin https://127.0.0.1:8161/api/message/test?type=queue&clientId=consumerA
每次这样的调用都将使用相同的 JMS 消费者,并交付由代理发送给它的消息。
在 5.4.1 版本中,也可以取消订阅客户端。通过向服务器发送包含 clientId
和 action=unsubscribe
参数的 POST 调用来完成此操作,例如
https://127.0.0.1:8161/demo/message/test?clientId=consumerA&action=unsubscribe
使用选择器消费
从 ActiveMQ Classic 5.4.0 版本开始,您可以在使用 REST 协议消费时使用选择器。为此,只需指定包含选择器的适当标头即可。要为消费者定义选择器,您必须在适当的 HTTP 标头中提供它。默认情况下,选择器标头名称为 selector
,因此以下示例
wget --user admin --password admin --save-cookies cookies.txt --load-cookies cookies.txt --keep-session-cookies --header="selector: test=2" https://127.0.0.1:8161/api/message/test?type=queue
应仅消费具有设置为 2
的 test
属性的消息。
您可以使用 WEB-INF/web.xml
中的 org.apache.activemq.selectorName
Servlet 上下文属性来更改选择器标头的名称,例如
<context-param>
<param-name>org.apache.activemq.selectorName</param-name>
<param-value>activemq-selector</param-value>
</context-param>
有关更多信息,请查看 RestTest
使用一次性消费者消费
一次性消费允许 REST 调用接收一条消息,然后立即关闭关联的消费者。
到目前为止,所有示例都会导致 Servlet 在多个 HTTP 请求中创建和保存目标的消费者。如果不注意,这些消费者可能会导致混淆,因为消息将被分派给它们,但随后会闲置,因为消费 HTTP 会话或 clientId 无法连接以继续请求消息。解决该问题的一种方法是使用一次性消费者。 只需添加 ?oneShot=true
选项,消费者在消费完成后就会被删除;如下所示
curl -XGET http://admin:admin@localhost:8161/api/message?destination=queue://orders.input&oneShot=true
请注意,在消费者正在等待消息时中断调用,消费者可能会保留,直到服务器超时 HTTP 请求,或直到最终收到消息。
内容类型
默认情况下,消息将使用 text/xml
内容类型发送给消费者。您的基于 REST 的应用程序可能希望 JSON 响应而不是 XML 响应。在这种情况下,您可以配置 Servlet 以发送响应,方法是添加类似于以下内容的内容
<servlet>
<servlet-name>MessageServlet</servlet-name>
<servlet-class>org.apache.activemq.web.MessageServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>defaultContentType</param-name>
<param-value>application/json</param-value>
</init-param>
</servlet>
到您的 WEB-INF/web.xml
中。
也可以使用请求标头来覆盖默认内容类型。指定 xml=true
或 json=true
URL 参数,您将获得具有所需内容类型的响应。
wget --user admin --password admin https://127.0.0.1:8161/api/message/TEST?type=queue\\&clientId=A\\&json=true
安全
从 5.7.0 版本开始,REST API 可以连接到安全的代理。该 API 使用基本身份验证标头格式来获取用户名和密码信息。
例如,使用 curl,您可以执行以下操作
curl -u system:manager -d "body=message" https://127.0.0.1:8161/demo/message/TEST?type=queue
此外,您可能希望为您的连接启用 ssl
。为此,只需取消注释 conf/jetty.xml
中的 SecureConnector
<bean id="SecureConnector" class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
<property name="port" value="8162" />
<property name="keystore" value="file:${activemq.conf}/broker.ks" />
<property name="password" value="password" />
</bean>
REST 管理
从 5.8 版本开始,我们为代理提供了 REST 管理 API。使用 Jolokia JMX-HTTP 桥,可以使用 REST API 访问所有代理指标(例如内存使用情况)并执行管理操作(例如清除队列)。默认情况下,管理 API 在 https://127.0.0.1:8161/api/jolokia/ URL 上公开。因此,例如,您可以使用以下命令获取基本代理数据
wget --user admin --password admin --header "Origin: https://127.0.0.1" --auth-no-challenge https://127.0.0.1:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost
或者更具体地,使用以下命令获取总的消费者数量
wget --user admin --password admin --header "Origin: https://127.0.0.1" --auth-no-challenge https://127.0.0.1:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost/TotalConsumerCount
默认情况下,ActiveMQ Classic 使用 以下 Jolokia 安全策略
<restrict>
<!-- Enforce that an Origin/Referer header is present to prevent CSRF -->
<cors>
<strict-checking/>
</cors>
<!-- deny calling operations or getting attributes from these mbeans -->
<deny>
<mbean>
<name>com.sun.management:type=DiagnosticCommand</name>
<attribute>*</attribute>
<operation>*</operation>
</mbean>
<mbean>
<name>com.sun.management:type=HotSpotDiagnostic</name>
<attribute>*</attribute>
<operation>*</operation>
</mbean>
</deny>
</restrict>
可以通过编辑“webapps/api/WEB-INF/web.xml”并指定“jolokia-agent”Servlet 下的“policyLocation”参数来配置自定义 Jolokia 安全策略。
有关 Jolokia 安全的更多信息,请参阅其参考手册的 安全部分。像这样的 API 使得对代理进行监控和管理操作的脚本编写变得容易,另请参见 如何监控 ActiveMQ Classic?
陷阱和其他琐事
- 缺少“body”参数
在上面的 curl POST 示例中,使用“body=…”至关重要。如果在消息正文内容前面没有指定它,Web Servlet 将尝试从请求(而不是从 -d 参数)中读取正文,这将导致以下异常
java.lang.IllegalStateException: STREAMED
at org.eclipse.jetty.server.Request.getReader(Request.java:898)
at org.apache.activemq.web.MessageServletSupport.getPostedMessageBody(MessageServletSupport.java:347)
at org.apache.activemq.web.MessageServlet.doPost(MessageServlet.java:126)
...
但是,在这种情况下,一种选择是显式指定内容类型
curl -u admin:admin -d "hello world $(date)" -H "Content-Type: text/plain" -XPOST https://127.0.0.1:8161/api/message?destination=queue://abc.def