ActiveMQ与REST API实践

摘要

1. 如何搭建Active REST服务的环境

2. REST API以及相关配置参数介绍

3. ActiveMQ中optimizeAck机制介绍

 

    ActiveMQ实现了基于RESTFUL的API,允许开发者可以直接通过HTTP POST/GET方式发布消息和消费消息,这是一种基于文本(/文件)传输的协议.因为在某些特定的环境中,通过HTTP方式操作消息更加便捷,比如基于HTML5的移动终端上,通过javascript或者窗体事件来获取消息.

 

    通常,在开放互联网环境中,非核心消息/类日志系统中,我们会使用ActiveMQ REST来push(保存)/delete(消费)消息。我们可以将REST服务伪装成一个日志存储与分发系统,我们不需要额外的框架支撑,就能实现基于大数据消息的存储与分发。同时也可以在web brower中使用REST,通过轮询或者keepAlive来实现消息的实时推送。

 

    REST协议的最大特点就是更加面向互联网应用(而非企业级应用),其交互便捷, 数据可读性强.当然其局限性也很明显: 在安全上/消息ACK机制上/对事务的支持上/还有消息的异步操作上都有所欠缺(相对于openwire).无论消费者还是生产者,都需要通过URL来描述操作,这就局限了在REST协议下client端调优的情况,同时它还受限于HTTP协议(:keep-alive)以及servlet容器对异步的支持能力。

 

    每个ActiveMQ broker都集成了servlet环境(默认为jetty环境),在启动activeMQ broker的时候,默认挂载启动.org.apache.activemq.web.MessageServlet是官方已经实现的REST代理类,用来代理REST请求.实现原理也非常简单,servlet接收到请求后,内部将使用普通的JMS API + openwire协议与broker通讯.在REST协议中,AcitveMQ约定了URL格式以及参数列表,以及发布消息必须基于HTTP POST,消费消息可以基于GET/DELETE方式.

>>>http://localhost:8161/api/message/test-queue?type=queue&clientId=test1&json=true  
Hello,Restful! 

 

1. 环境准备

   AcitveMQ broker在启动的时候,会挂载启动多个web系统,其中一个web系统就是"api",你可以参看{activemq}/webapps/api中查看相应的配置.这个api系统,就是为REST协议而服务的.{activemq}/conf/activemq.xml文件,是activemq所有配置文件的入口,文件中通过配置引入jetty环境即可使用api系统.

<import resource="jetty.xml"/>

 

<transportConnector name="rest" uri="tcp://0.0.0.0:51616?maximumConnections=1000&wireformat.maxFrameSize=1048576&keepAlive =true&soTimeout =30000"/>

    此外,因为REST底层仍然基于openwire(tcp通道)支持,且REST的特性需要贴近HTTP的“短期”/“小数据”等特性,所以我们在transportConnector中,将uri中增加soTimeout=3000,以及控制最大数据尺寸为1M。

 

    从jetty.xml中,我们可以看到所有web系统的配置信息:

<bean id="securityConstraint" class="org.eclipse.jetty.util.security.Constraint">
	<property name="name" value="BASIC" />
	<property name="roles" value="admin" />
	<!-- 是否使用安全认证 -->
	<property name="authenticate" value="true" />
</bean>
<bean id="securityConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
	<property name="constraint" ref="securityConstraint" />
	<property name="pathSpec" value="/*" />
</bean>

 

    5.8之后,jetty环境中所有的web系统默认都是需要授权的,这就意味着,使用REST API时需要提供"name" + "password"校验信息,否则将REST请求将会被拒绝.当然,开发者可以通过注释如下参数式REST不再需要认证.

<!-- 是否使用授权认证,"true"/"false" -->
<!--
<property name="authenticate" value="true" />
-->

 

    此外,让ActiveMQ broker来提供REST服务似乎不是一种良好的架构设计,这将很大程度上限制了开发者对REST服务的扩展性,在很多情况下,我们需要把REST服务迁移到专门的web应用中,那么我们就不需要让jetty环境挂载启动api系统.

<!--
<bean class="org.eclipse.jetty.webapp.WebAppContext">
	<property name="contextPath" value="/api" />
	<property name="resourceBase" value="${activemq.home}/webapps/api" />
	<property name="logUrlOnStart" value="true" />
</bean>
-->

    接下来,我们将自己搭建ActiveMQ REST服务环境。

 

2.配置文件

    如果你基于activeMQ jetty环境,你可能需要查看一下{activemq}/webapps/api/WEB-INF/web.xml文件,如果你的REST server是自己搭建的,你可能需要把此web.xml文件中的核心信息copy到自己的项目中.无论如何,你都需要在servlet环境中,配置并启动"org.apache.activemq.web.MessageServlet". 其中"org.jolokia.http.AgentServlet"仅作为可选,对于自己搭建REST服务,jolokia就无须配置了。

 

    构建一个servlet系统,我们必须首先确定servlet版本,因为这将直接关系到REST的异步特性,目前比较稳定的版本组合为:servlet 2.4(2.5) + tomcat 6.x,servlet 3.0.1 + tomcat7.x,servlet 3.1 + tomcat8.x。在servlet2.x与servlet3.x中,web.xml配置有少许的差别,主要是因为它们对“异步”操作的控制方式不同,所谓异步,就是当REST请求中消费消息时,如果此时Queue/Topic中没有消息,那么此次请求是否挂起直到有消息可供消费。

 

    Tomcat配置(Tomcat 6.x+)

 

    <Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" 
               URIEncoding="utf-8" 
               useBodyEncodingForURI="false"
               maxThreads="120" maxKeepAliveRequests="60"/>
    protocol协议为“HTTP/1.1",如果你指定了"HTTP/1.0",那么可能无法良好的使用"keep-alive",需要一些额外的控制,在此就不再赘言。其中connectionTimeout可以根据实际情况调整,用来控制connector释放链接前阻塞的时间,默认情况下keepAliveTimeout和connectionTimeout值一样,用来描述链接保持活性的时间。

    pom.xml(servlet-2.4)

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.4</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-util</artifactId>
    <version>6.1.26</version>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-web</artifactId>
    <version>5.8.0</version>
</dependency>
	<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-all</artifactId>
    <version>5.8.0</version>
</dependency>

    因为servlet2.x不支持"异步"操作,因此需要使用FauxContinuation(伪延续)来提供"阻塞" + "事件通知"特性,因此引入了"jetty-util"依赖包。

 

    web.xml(servlet-2.4)

<?xml version="1.0" encoding="utf-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>test-web</display-name>

    <!-- activeMQ global param -->
    <!-- WebClient -->
    <!-- Required-->
    <context-param>
        <param-name>org.apache.activemq.connectionFactory</param-name>
        <param-value>activeMQConnectionFactory</param-value>
    </context-param>
    <!-- Required-->
    <context-param>
        <param-name>org.apache.activemq.brokerURL</param-name>
        <!-- underlying openwire transport-->
        <param-value>tcp://127.0.0.1:51616</param-value>
    </context-param>
    <!-- optional, global prefetchSize,default "1" -->
    <context-param>
        <param-name>org.apache.activemq.connectionFactory.prefetch</param-name>
        <param-value>1</param-value>
    </context-param>
    <!-- optional,optimizeAcknowledge -->
    <context-param>
        <param-name>org.apache.activemq.connectionFactory.optimizeAck</param-name>
        <param-value>true</param-value>
    </context-param>
    <!-- optional,HTTP header -->
    <context-param>
        <param-name>org.apache.activemq.selectorName</param-name>
        <!-- parameter of selector,default is "selector" -->
        <param-value>selector</param-value>
    </context-param>
    <servlet>
        <servlet-name>OrdersQueueServlet</servlet-name>
        <servlet-class>org.apache.activemq.web.MessageServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <!-- Required,but can be specified in the url -->
        <init-param>
            <param-name>destination</param-name>
            <param-value>orderQueue?consumer.prefetchSize=10</param-value>
        </init-param>
        <!-- Required -->
        <init-param>
            <param-name>topic</param-name>
            <param-value>false</param-value>
        </init-param>
        <!-- optional,important -->
        <!--
        <init-param>
            <param-name>destinationOptions</param-name>
            <param-value></param-value>
        </init-param>
        -->
        <!-- optional,default is "20000" -->
        <init-param>
            <param-name>maximumReadTimeout</param-name>
            <param-value>10000</param-value>
        </init-param>
        <!-- optional,defaut is "1000" -->
        <init-param>
            <param-name>replyTimeout</param-name>
            <param-value>10000</param-value>
        </init-param>
        <!-- optional,important,default is "text/xml" -->
        <init-param>
            <param-name>defaultContentType</param-name>
            <param-value>text/plain; charset=utf-8</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>OrdersQueueServlet</servlet-name>
        <url-pattern>/rest/order/*</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>continuationFilter</filter-name>
        <filter-class>org.eclipse.jetty.continuation.ContinuationFilter</filter-class>
        <init-param>
            <param-name>faux</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>continuationFilter</filter-name>
        <url-pattern>/rest/*</url-pattern>
    </filter-mapping>


</web-app>

    在web.xml中,我们需要引入continuationFitler,这个Fitler主要用来构建"continuation"实例,此filter将与MessageServlet的内部实现互相协调,通过"轮询"(Filter中) +"阻塞" + "事件通知"(continuation中),来实现伪异步。具体相关的jetty continuation原理设计请参见其他文档。

    配置文件中标记为"Required"部分是必须要按照规则配置的,"optional"部分为可选的。

 

 

    pom.xml(servlet 3.x)

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-web</artifactId>
    <version>5.8.0</version>
</dependency>
	<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-all</artifactId>
    <version>5.8.0</version>
</dependency>

    servlet 3.x已经支持了异步,因此我们无需jetty-util的依赖包。

 

    web.xml(servlet 3.x)

?xml version="1.0" encoding="utf-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>test-web</display-name>
    <!-- 同 servlet 2.4 -->
    <servlet>
        <servlet-name>OrdersQueueServlet</servlet-name>
        <servlet-class>org.apache.activemq.web.MessageServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
        <!-- 同 servlet 2.4 -->

    </servlet>

    <servlet-mapping>
        <servlet-name>OrdersQueueServlet</servlet-name>
        <url-pattern>/queue/order/*</url-pattern>
    </servlet-mapping>

</web-app>

    此文件和servlet 2.4几乎一样,只是在<servlet>中增加了<async-supported> 表示使用"异步",此外在servlet 3.x中,也无需配置continuationFitler。

 

    此处需要特别声明一下,因为tomcat容器的Thread模型的限制,在servlet2.4中“异步”操作仅仅是一种“模拟的”(Faux),其内部机制仍然是基于当前线程阻塞(wait)而实现,因此servlet2.4中使用NIO + 长连接的方式,tomcat仍然无法支撑过多的request。在servlet3.x之后,tomcat 7.x之后,才能配合实现相对完善的NIO/AIO + 长连接方案,tomcat可以在支撑较大请求(包括连接数)的情况下,仍然能够保证性能(内存消耗,吞吐能力)上相对平稳

 

 

3. MessageServlet原理与使用注意事项

 

    [###]在上述web.xml中,其中<context-param>包含"brokerUrl"/"optimizeAck"/"prefetch"/"selectorName"主要四个参数。

    "brokerUrl"部分需要明确配置brokerUrl参数,这个值和我们常用的openwire协议中的 brokerUrl一样,也允许在brokerUrl中使用查询字符串等用于调优,不过这个brokerUrl需要是一个openwire协议中合法的transport通道(AcitveMQ broker中可以有任意多个openwire transportConnector),比如"tcp://""failover://"等。MessageServlet将根据"brokerUrl"的值构建ConnectionFactory。

 

    "optimizeAck"表明consumer是否使 用"optimizeAck"选项来优化消息ACK策略,它用来描述"消息确认"的时机,如果此值为true,将采取"延迟ACK",否则将会"立即ACK"。如果开启,client端将会使用"延迟 + 批量ACK"策略来确认消息,它可以有效的提高消息消费的效率,但有可能会接收到重复的消息,具体原理请参考其他文档。在MessageServlet中,消息的确认方式为“AUTO_ACKNOWLEDGE”,可以有效的在ActiveMQ "optimizeAcknowledge"机制中运行(简析:AUTO_ACK + optimizeAck + prefetchSize > 1 最终构成“批量延迟ACK”优化机制,这是一种高效的消息消费调优策略)。[optimizeACK机制]

 

    "prefetch"用来描 述消费者"预获取"消息的条数(批量消息),默认值为"1",此参数值即为prefetchSize。如果broker端有足够多的消息,那么Consumer客户端将会在每次"optimizeAck"确认之后,client端可以一次性获取(或者由broker推送)多条消 息并缓存,总缓存的消息量(未消费 + 已消费但未ACK)控制在prefetchSize之内,这是一个优化消费者的参数,通常和"optimizeAck"协同;如果我们的应用中,REST 客户端1) Queue/Topic通道中消息的数量少 2) REST调用频率都比较低 3) 同一个Queue中Consumer个数较少时,我们需要将optimizeAck=false,或者将 prefetchSize设置为1,场景例如:(获取"推荐商品"消息,"即时通讯"消息);这两个参数非常重要,同时REST API中没有提供ACK的接口(url),所以希望开发者慎重考虑这两个参数。"prefetch"参数值将会对所有Connection有效,如果开发者在destinationOptions中没有指定额外的"prefetchSize"选项,那么将会使用此"prefetch"参数作为默认值。

 

    [###]selectorName为可选参数,默认值为"selector",selector在 ActiveMQ中,称为"消息选择器";我们可以在REST Url中通过传递"selector=type%3D1"(备注:urlEncode之前为"type=1"),此后将会根据此selector值来构建 Consumer实例,那么此Consumer将会接收到符合selector条件的消息,具体请参考其他文档;不过此处需要注意,对于每个 clientId,只会创建一个Consumer实例,一旦创建,selector值将不会被修改,即使此后使用REST URL传递不同的selector值也不会生效;这个实现和ActiveMQ在openwire协议下一样,如果需要更新selector,需要重建consumer,在REST API中,重建consumer的API就是"unsubscribe"。

    上面的内容,就是ActiveMQ 消息者优化策略的核心原理,如果您还没有明白,我只能"跪"下了!!!

 

    在<servlet>配置中,还有几个相对简单的配置信息<init-param>,这些配置信息通常和url查询字符串有很大关系,它们决定了REST API的URL风格。

    destination表示"消息目的地",无论是发送消息还是接受消息,必须指定destination才能与broker交互。此外,我们可以在destination中添加"destinationOptions",比如在示例中添加了"customer.prefetchSize=1"表示通过此destination创建的所有的consumer都使用prefetchSize=1。你可以参考更多的文档,来使用"destinationOptions"调优等。不过此后仍然可以在REST URL中使用"&destination=orderQueue"来指定destination,不过我们强烈建议不要使用这种方式,通常情况下,我们会为每个destination单独配置成一个servlet。

 

    destinationOptions也可以通过<init-param>指定,不过此值只会在REST URL中指定了"&destination=orderQueue"查询字符串时才会有效。如果你在web.xml中配置了"destination"参数,那就需要把相应options追加其后即可,而无需再次配置"destinationOptions"参数。

 

    maxinumReadTimeout表示REST请求阻塞的最长时间,REST URL可以通过查询字符串"&readTimeout=1000"来指定当前请求阻塞时间,不过不能指定比"maxunumReadTimeout"更大的值.设置此值的时候必须要注意,它最好不要大于tomcat中connector链接的keepAlive时间。

 

    replyTimeout,在请求应答(request-reply)模式中,等待"reply"的最大时间。通过REST 发送消息时,可以指定消息是否为"sync"(同步)模式,如果URL中"sync=true",此时消息发送之后即等待消费者"reply",如果等待replyTimeout之后,仍然没有得到应答,那么此消息将意味着发送失败(REST客户端将会得到一个501错误),在sync=true下,内部使用了camel组件来实现了这种机制。如果没有指定sync,那么消息将会立即发送给broker,也不会涉及到replyTimeout的问题。

 

    [###] REST API列表

    消费消息

        URL:  /queue/order?clientId=client-001&readTimeout=1000&type=json   

        HTTP METHOD:  GET,DELETE

    发送消息

        URL:  /queue/order?clientId=client-00&JMSDeliveryMode=persistent1 或者
                  /queue/order?clientId=client-001&sync=true&replyTimeout=1000   

        HTTP METHOD: POST
                 Header
                 body:消息内容

    取消消费(关闭consumer)

        URL: /queue/order?clientId=client-001&action=unsubscribe   

        HTTP METHOD: POST

 

    提醒,所有的REST URL中都必须携带clientId参数,无论如何,调用者都需要采取一定的手段,不能让clientId总是变化。这和MessageServlet的内部实现有关,它为了提升性能以及避免对broker造成太多的资源浪费,将会根据clientId来缓存(存入Map)一个"webClient"实例,每个webClient实例中都会持有"JMS Session"/"Consumer"/"producer";因此过多的webClient实例会带来一些风险。当REST调用异常时,比如tomcat server返回501内部错误,我们应该"unsubscribe"相应的clientId,这会触发MessageServlet从缓存中移除相应的webClient实例(包括关闭consumer和producer等)。

    "发送消息"URL中,可以指定"JMSDeliveryMode=persistent"表示消息的传输模式(持久化与否),不过当sync=true时,"JMSDeliveryMode"参数将不会有效。

 

4. 使用REST API

    我们可以使用浏览器,或者任何http客户端,都可以访问这写REST API,在MessageServlet中,支持"POST"/"DELETE"/"GET"三种合法的请求方法(request method),其中"DELETE"/"GET"用于消费消息,"POST"用于发布消息和关闭Consumer链接。如下,我们通过apache httpclient来模拟REST API调用过程以及简单的异常处理。HttpClientUtil.java样例:

 

public class HttpClientUtil {

    private static final CloseableHttpClient httpClient;
    public static final String CHARSET = "UTF-8";

    static {
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(60000)
                .setSocketTimeout(-1)
                .build();
        httpClient = HttpClientBuilder.create()
                .setDefaultRequestConfig(config)
                .build();
        /**
        keepAliveClient = HttpClientBuilder.create()
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                .build();
         **/
    }

    public static String restGet(String url,Map<String,String> params) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        try {
            HttpGet httpget = new HttpGet(url);
            if (params != null && !params.isEmpty()) {
                List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    String value = entry.getValue();
                    if (value != null) {
                        pairs.add(new BasicNameValuePair(entry.getKey(), value));
                    }
                }
                url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs,CHARSET));
            }
            CloseableHttpResponse response = httpClient.execute(httpget); //block here
            int statusCode = response.getStatusLine().getStatusCode();
            // >= 400
            if(statusCode >= HttpStatus.SC_BAD_REQUEST || statusCode < HttpStatus.SC_OK) {
                httpget.abort();
                response.close();
                throw new RuntimeException("HttpClient,error status code :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, "utf-8");
            }
            //will close inputStream.
            EntityUtils.consume(entity);
            response.close();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String restPost(String url, Map<String,String> params) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        try {
            HttpPost httpPost = new HttpPost(url);
            if (params != null && !params.isEmpty()) {
                List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    String value = entry.getValue();
                    if (value != null) {
                        pairs.add(new BasicNameValuePair(entry.getKey(), value));
                    }
                }
                httpPost.setEntity(new UrlEncodedFormEntity(pairs, CHARSET));//Utf-8
            }

            CloseableHttpResponse response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                httpPost.abort();
                response.close();
                throw new RuntimeException("HttpClient,error status code :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            String result = null;
            if (entity != null) {
                result = EntityUtils.toString(entity, CHARSET);
            }
            EntityUtils.consume(entity);
            response.close();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

  

   REST测试客户端代码,RestClient.java

 

public class RestClient {
    private static String baseUrl = "http://localhost:8080/rest/order";
    //any client must have one clientId
    // but the best is that they should be different.
    private static String clientId = "restful";

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
        String selfId = clientId + "_" + RandomStringUtils.random(6,true,true);
        for(int i=0; i< 5; i++){
            postMessage(selfId,"Hello,rest activeMQ.." + i);
        }
        System.out.println("post message end..");
        consume(selfId);
        Thread.sleep(3000);
        consume(selfId);
        System.out.println("end!!");
    }

    public static void consume(String selfId){

        while(true){
            try{
                Map<String,String> params = new HashMap<String,String>();
                params.put("clientId",selfId);
                params.put("readTimeout","10000");//max blocking time as MS.
                String response = HttpClientUtil.restGet(baseUrl,params);
                System.out.println(response);
                if(StringUtils.isBlank(response)){
                    Thread.sleep(3000);
                }
            }catch (InterruptedException e){
                break;
            } catch (Exception e){
                unSubscribe(selfId);
            }
        }
    }

    protected  static void unSubscribe(String selfId){
        try{
            Map<String,String> params = new HashMap<String,String>();
            params.put("clientId",selfId);
            params.put("action", "unsubscribe");
            HttpClientUtil.restPost(baseUrl, params);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void postMessage(String selfId,String body){
        try{
            Map<String,String> params = new HashMap<String,String>();
            params.put("clientId",selfId);
            //sync or async
            // if "sync",after sending out a message,it should be blocking util the message is consumed.
            //params.put("sync","true");
            params.put("body",body);
            HttpClientUtil.restPost(baseUrl,params);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}

    如果按照,上述的配置,测试代码应该可以顺利通过!!

    Active web项目中,还提供了便于Ajax调用调用的REST方案(Servlet + javascript),如果读者能够清楚上述讲解的内容,我相信你自己已经可以搭建和配置响应的环境了。

实战指南:如何利用Docker快速搭建Hadoop未授权访问漏洞测试环境 本文提供了一份实战指南,详细讲解如何利用Docker在十分钟内快速搭建一个用于安全研究的Hadoop测试环境。文章重点剖析了Hadoop组件(尤其是YARN ResourceManager)因默认配置不当而存在的未授权访问漏洞风险,并演示了基础的漏洞验证方法。同时,横向对比了ActiveMQ、RabbitMQ等其他中间件的类似安全隐患,旨在帮助安全研究人员和渗透测试工程师高效构建可控、可复现的漏洞研究沙箱。 阅读详情

相关推荐

ActiveMQ使用RESTful API发送消息

ActiveMQ 使用 HTTP 请求发送消息到queue/topic中。

甘蓝的专栏 5330

使用 REST API 操作 RabbitMQ

如果安装了插件,即安装了 RabbitMQ 中的 Web 管理客户端,那么我们就可以通过 REST API 来进行 RabbitMQ 的管理。如果项目使用了如 Granglia 或者 Graphite 之类的图形工具,我们想抓取当前 RabbitMQ 上消息消费/累积的情况,就可以使用使用 REST API 去查询这些信息并将查询结果传输到新的图形工具上,同时,由于 REST API 就是 HTTP 请求,所以支持的客户端也是多样化,只要能发送 HTTP 请求,就能用,特别方便。

我的博客 408

使用ActiveMQ REST管理API进行编程

本文介绍了如何使用ActiveMQ REST API发送、接收和删除消息的基本操作,并提供了相应的源代码示例。然后,我们定义了要发送的消息的内容和类型,并将其作为JSON格式的请求正文发送到ActiveMQ的REST API。在上面的代码中,我们构建了一个GET请求,指定了ActiveMQ服务器的URL、端口号和要接收消息的队列名称。然后,我们发送这个请求并检查响应的状态码。在上面的代码中,我们构建了一个DELETE请求,指定了ActiveMQ服务器的URL、端口号、队列名称以及要删除的消息的ID。

2301_79326857的博客 247

activemq的几种基本通信方式总结

简介      在前面一篇文章里讨论过几种应用系统集成的方式,发现实际上面向消息队列的集成方案算是一个总体比较合理的选择。这里,我们先针对具体的一个消息队列Activemq的基本通信方式进行探讨。activemq是JMS消息通信规范的一个实现。总的来说,消息规范里面定义最常见的几种消息通信模式主要有发布-订阅、点对点这两种。另外,通过结合这些模式的具体应用,我们在处理某些应用场景的时候也衍生...

点缀星辰 1300

ActiveMQ REST Management API

ActiveMQ的管理接口是基于JMX的(参见 JMX操作ActiveMQ(1)、JMX操作ActiveMQ(2)),通过管理接口我们可以与Broker交互,查询各种Broker状态、统计数据,浏览连接、消费者、生产者,以及管理消息。 虽然ActiveMQ通过CMS(支持c++)、NMS(支持.net)以及多种protocol(STOMP、MQTT、AMQP等)支持了多种平台环境下的消息发送接收

KimmKing的技术博客 1万+

ActiveMQ REST 管理 API 编程

它提供了多种编程接口,其中包括 REST Management API,使得管理和监控 ActiveMQ 变得更加便捷和灵活。本文将介绍如何使用 ActiveMQ REST Management API 进行编程,包括创建队列、发送和接收消息等操作。综上所述,ActiveMQ REST Management API 提供了一种方便灵活的方式来管理和监控 ActiveMQ。以上示例代码演示了如何通过 ActiveMQ REST Management API 进行队列创建、发送消息和接收消息的操作。

DevProZ的博客 321

Activemq Jolokia

打开JMX <broker … useJmx="true"> …   <managementContext> <managementContext createConnector="true"/> </managementContext> </broker> 启动ActiveMQ $ ...

weixin_30706507的博客 877

Windows环境下DataEase一站式安装指南(含WSL2+Docker配置)

本文详细介绍了在Windows环境下通过WSL2和Docker快速部署DataEase的完整教程。从WSL2配置、Docker环境搭建到DataEase离线安装,逐步指导用户完成一站式部署,特别适合需要快速搭建数据分析平台的开发者。教程包含常见问题解决方案和性能优化技巧,帮助用户高效使用这款国产开源数据可视化工具。

weixin_42526166的博客 398

activemq控制发送频率_ActiveMQ与REST API实践

摘要1. 如何搭建Active REST服务的环境2. REST API以及相关配置参数介绍3. ActiveMQ中optimizeAck机制介绍ActiveMQ实现了基于RESTFUL的API,允许开发者可以直接通过HTTP POST/GET方式发布消息和消费消息,这是一种基于文本(/文件)传输的协议.因为在某些特定的环境中,通过HTTP方式操作消息更加便捷,比如基于HTML5的移动终端上,通过...

weixin_39942318的博客 348

Ubuntu终端一键安装英伟达驱动:无需手动下载的完整指南

本文提供了一份在Ubuntu系统上通过终端命令一键安装英伟达显卡驱动的完整指南。该方法利用官方PPA源,无需手动下载驱动文件,可自动检测硬件并安装最佳版本,极大简化了流程,适合深度学习环境搭建等场景,有效提升安装效率与稳定性。

weixin_29232121的博客 180

gtsam官方文档: The new IMU Factor学习笔记

gtsam官方文档:The new IMU Factor 本文为gtsam imu预积分文档学习笔记,为gtsam基于切向量实现的imu预积分的理论基础。 导航状态 定义导航状态为 Xbn={Rbn,Pbn,Vbn}X^n_b=\{R^n_b,P^n_b,V^n_b\}Xbn​={Rbn​,Pbn​,Vbn​} 上角标n:导航(navigation)坐标系 下角标b:载体(body)坐标系 下文均省略上下标 矢量场与微分方程 对于导航问题我们用如下微分方程描述 X˙(t)=F(t,X)\dot X(t)

南柯一梦的博客 1609

REST与RESTFul API最佳实践(转载来自于慕课网)

REST与RESTFul API最佳实践

Diors 968

【Fluent】太阳辐射模型—Solar Ray Tracing

一、引言Fluent提供了一个太阳能负载模型,可用于计算进入计算域的太阳光线的辐射效应。该模型可以分为:太阳光线追踪和太阳辐照。其中太阳辐照需要使用DO或MC模型,直接向辐射模型提供外光束方向和强度参数。太阳光线追踪不限制辐射模型,可以独立于辐射方程使用,甚至可以在辐射模型off的情况下使用。本文主要介绍太阳光线追踪模型。该模型非常适用于以下领域汽车气候控制 (ACC) 应用建筑物中的人体热舒适建模应用该模型使用着色算法进行太阳光线追踪。光线会透过半透明的面加载在照射面上。

a5567899的博客 7589

RESTful API 设计最佳实践

目前互联网上充斥着大量的关于RESTful API(为了方便,以后API和RESTful API 一个意思)如何设计的文章,然而却没有一个”万能“的设计标准:如何鉴权?API格式如何?你的API是否应该加入版本信息?

技术学习与分享 2554

【CASS精品教程】013:CASS9.1生成标准图幅案例教程

在CASS中,可以很方便的生成标准图幅,如50*50。本文演示cass中,根据测区范围标准图幅生成过程。

智慧空间实验室 1万+

关于红队方面的学习资料

关于红队方面的学习资料

Sumarua的博客 1085

java操作activemq queues_ActiveMQ REST Management API

ActiveMQ的管理接口是基于JMX的(参见JMX操作ActiveMQ(1)、JMX操作ActiveMQ(2)),通过管理接口我们可以与Broker交互,查询各种Broker状态、统计数据,浏览连接、消费者、生产者,以及管理消息。虽然ActiveMQ通过CMS(支持c++)、NMS(支持.net)以及多种protocol(STOMP、MQTT、AMQP等)支持了多种平台环境下的消息发送接收,但...

weixin_35012566的博客 245

机器人视觉仿真软件:PCL (Point Cloud Library)_(8).点云分割技术

点云分割技术在机器人视觉处理中起着至关重要的作用,通过不同的分割方法可以有效地将点云数据中的不同物体或区域分离出来。PCL提供了丰富的分割算法,包括基于平面的分割、基于聚类的分割、基于模型的分割和基于条件的分割等。每种方法都有其适用的场景和优缺点,选择合适的方法可以显著提高点云处理的效率和准确性。希望本节的内容能够帮助读者更好地理解和应用点云分割技术。

kkchenjj的博客 822

理解ActiveMQ中的REST API和AjaxAPI

  Sending Message as XML Payload,参考代码: import java.io.StringWriter; import java.util.Hashtable; import java.util.Map; import javax.jms.Connection; import javax.jms.ConnectionFactory; import j...

SpringsSpace 281

新手福利!大龙虾OpenClaw可视化管理工具来了!告别命令行,这个可视化工具让OpenClaw管理变得超简单

还在用命令行管理OpenClaw?🚀 这个跨平台可视化工具支持14+AI模型,一键切换,还能对接微信飞书钉钉等8大消息渠道,让AI助手管理变得简单直观 ✅ 暗色主题护眼舒适,Rust后端高性能低占用,新手也能轻松上手 🎯

XiaoqiangClub的博客 760

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

ActiveMQ监控

转自:http://blog.sina.com.cn/s/blog_6795e13e0102whos.html现在很多公司都遵循一个准则,没有监控的程序不能上线,可见监控的重要性。这里先介绍下ActiveMQ都需要监控的内容。监控硬件物理空间是否充足:ActiveMQ有3个重要的参数,存储空间百分比,内存空间百分比和临时空间百分比。这三个参数的意义很明显,如果值到了100,则表明硬件空间已满,Br...

fan_balalld的博客 6794

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

ActiveMQ_rest API 方式 reload 日志

http://www.tuicool.com/articles/vmm2Ib

1632

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

activemq java client_通过rest api管理activemq

importorg.apache.activemq.command.ActiveMQQueue;importorg.apache.activemq.command.ActiveMQTopic;importorg.apache.http.auth.AuthScope;importorg.apache.http.auth.UsernamePasswordCredentials;importorg.ap...

weixin_36239971的博客 187

使用jolokia api监控ActiveMQ

jolokiaapi提供了一种通过HTTP访问JMX获得AMQ后台数据的一种方式,即RestfulApi #!/usr/bin/env python # -*- coding:utf-8 -*- import requests import json import re import time import sys n = 1 while True: queue...

weixin_30784501的博客 1469
上一篇: JAVA Socket与Bufferd IO
下一篇: ActiveMQ消息传送机制以及ACK机制详解
iteye_19607
博客等级 码龄8年 34粉丝 198原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值