activeMQ 入门指南

1.简介

1.1. ActiveMQ 由Apache出品的开源消息总线。是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。


1.2 它的优点是:
支持多种语言编写客户端;
对spring的支持,很容易和spring整合 ;
支持多种传输协议:TCP,SSL,NIO,UDP等 ;
支持AJAX ;
非常成熟,功能强大,在早些年业内大量的公司以及项目中都有应用 ;
单机吞吐量:万级
时效性:ms级
可用性:高,基于主从架构实现高可用性
消息可靠性:有较低的概率丢失数据
功能支持:MQ领域的功能极其完备


1.3 它的缺点是
偶尔会有较低概率丢失消息;
现在社区以及国内应用都越来越少,官方社区现在对ActiveMQ 5.x维护越来越少,几个月才发布一个版本;


1.4 使用场景
主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用

2.安装

2.1 下载安装包

到官网下载:https://activemq.apache.org/components/classic/

2.2 启动activeMQ

解压缩,进入bin目录,启动/停止/查看状态:./activemq [start|stop|status]

​ 启动信息如下


INFO | Apache ActiveMQ 5.15.9 (localhost, ID:LAPTOP-BRJ9A33I-60490-1563860323722-0:1) is starting

INFO | Listening for connections at: tcp://LAPTOP-BRJ9A33I:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600

INFO | Connector openwire started

INFO | Listening for connections at: amqp://LAPTOP-BRJ9A33I:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600

INFO | Connector amqp started

INFO | Listening for connections at: stomp://LAPTOP-BRJ9A33I:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600

INFO | Connector stomp started

INFO | Listening for connections at: mqtt://LAPTOP-BRJ9A33I:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600

INFO | Connector mqtt started

INFO | Starting Jetty server

INFO | Creating Jetty connector

WARN | ServletContext@o.e.j.s.ServletContextHandler@f107c50{/,null,STARTING} has uncovered http methods for path: /

INFO | Listening for connections at ws://LAPTOP-BRJ9A33I:61614?maximumConnections=1000&wireFormat.maxFrameSize=104857600

INFO | Connector ws started

INFO | Apache ActiveMQ 5.15.9 (localhost, ID:LAPTOP-BRJ9A33I-60490-1563860323722-0:1) started

INFO | For help or more information please see: http://activemq.apache.org

INFO | No Spring WebApplicationInitializer types detected on classpath

INFO | ActiveMQ WebConsole available at http://0.0.0.0:8161/

INFO | ActiveMQ Jolokia REST API available at http://0.0.0.0:8161/api/jolokia/

INFO | Initializing Spring FrameworkServlet 'dispatcher'

2.3 查看控制台

进入管理后台查看消息:http://ip:8161/admin 用户名:admin 密码:admin

3.编码实现

3.1 发送端编码

<dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.15.3</version>
        </dependency>


package com.qhs.mq.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class MsgProducer {

    public static void main(String[] args) {

        testQueue();

        testTopic();

    }

    private static void testQueue() {

        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.137.1:61616");

        Connection connection = null;

        Session session = null;

        MessageProducer producer = null;

        try {

            connection = factory.createConnection();

            connection.start();

            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            Queue queue = session.createQueue("qhs-queue");

            producer = session.createProducer(queue);

            TextMessage textMessage = null;

            for (int i = 0; i < 50; i++) {

                textMessage = session.createTextMessage("字符串queue消息:" + i);

                producer.send(textMessage);

            }

        } catch (JMSException e) {

            e.printStackTrace();

        } finally {

            try {

                producer.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                session.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                connection.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

        }

    }

    private static void testTopic() {

        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.137.1:61616");

        Connection connection = null;

        Session session = null;

        MessageProducer producer = null;

        try {

            connection = factory.createConnection();

            connection.start();

            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            Topic topic = session.createTopic("qhs-topic");

            producer = session.createProducer(topic);

            TextMessage textMessage = null;

            for (int i = 0; i < 50; i++) {

                textMessage = session.createTextMessage("字符串topic消息:" + i);

                producer.send(textMessage);

            }

        } catch (JMSException e) {

            e.printStackTrace();

        } finally {

            try {

                producer.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                session.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                connection.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

        }

    }

}

3.2 接收端编码


package com.qhs.mq.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class MsgConsumer {

    public static void main(String[] args) {

        testQueue();

        testTopic();

    }

    public static void testQueue() {

        ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.137.1:61616");

        Connection connection = null;

        Session session = null;

        MessageConsumer queueConsumer = null;

        MessageConsumer topicConsumer = null;

        try {

            connection = factory.createConnection();

            connection.start();

            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            Queue queue = session.createQueue("qhs-queue");

            queueConsumer = session.createConsumer(queue);

            queueConsumer.setMessageListener(new MessageListener() {

                @Override

                public void onMessage(Message message) {

                    try {

                        System.out.println(((TextMessage)message).getText());

                    } catch (JMSException e) {

                        e.printStackTrace();

                    }

                }

            });

            Topic topic = session.createTopic("qhs-topic");

            topicConsumer = session.createConsumer(topic);

            topicConsumer.setMessageListener(new MessageListener() {

                @Override

                public void onMessage(Message message) {

                    try {

                        System.out.println(((TextMessage)message).getText());

                    } catch (JMSException e) {

                        e.printStackTrace();

                    }

                }

            });

            System.in.read();

        } catch (Exception e) {

            e.printStackTrace();

        }finally {

            try {

                queueConsumer.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                topicConsumer.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                session.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                connection.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

        }

    }

    public static void testTopic() {

        ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.137.1:61616");

        Connection connection = null;

        Session session = null;

        MessageConsumer consumer = null;

        try {

            connection = factory.createConnection();

            connection.start();

            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            System.in.read();

        } catch (Exception e) {

            e.printStackTrace();

        }finally {

            try {

                consumer.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                session.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

            try {

                connection.close();

            } catch (JMSException e) {

                e.printStackTrace();

            }

        }

    }

}

运行程序,先运行接收端,再运行发送端即可(topic消息默认不持久化)。
通过web控制台可以查看到对应的消息。

4.一些特征

  • 点对点模式不会造成重复消费,一个生产者的消息只能给一个消费者消费;
  • 发布订阅模型和点对点模型不一样,若发布消息之前未启动订阅者,则订阅者启动时,订阅者也无法接收到消息(未持久化状态)。另外,发布订阅模型,消费者是可以重新消费的;
  • 消息保序方式是broker维持了一个consumer列表,对于同一session的消息取列表中的第一个来发送消息;

5.实现机制

5.1 可靠性保证

  1. 自动签收,生产者向队列发送消息后,只要消费者监听了消息队列,消费者将立刻获得消息,不管消费者是否成功取得消息,过程是否抛出异常导致消费者无法获得消息,都不会触发重试机制。--没有事务机制,没有补偿机制
  2. 事务签收,对于生产者而言,生产者要想向消息队列发送消息,必须提交事务。对于消费者而言,如果消费没有提交事务,则默认表示没有消费,会触发重试机制。-- 双方事务提交
  3. 手动签收,需要消费者手动签收,如果消费者没有进行签收,则默认消息没有被消费。--单方事务提交

代码范例:


//场景1 

//生产者不开启事务,客户端必须有手动签收模式

Session session = createConnection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);

//消费者不开启事务,客户端必须有手动签收模式

TextMessage textMessage = (TextMessage) createConsumer.receive();

//接受消息

textMessage.acknowledge();

//场景2

//生产者不开启事务,客户端自动签收模式

Session session = createConnection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

//消费者不开启事务,自动签收消息

Session session = createConnection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);

//场景3

//在支持事务的session中,producer发送message时在message中带有transactionID。broker收到message后判断是否有transactionID,如果有就把message保存在transaction store中,等待commit或者rollback消息

//事务消息 生产者以事务形式,必须要将消息提交事务,才可以提交到队列中。

Session session = createConnection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);

session.commit();

//消费者 消费完后必须提交,不提交生产者不知道消费者消费了

Session session = createConnection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);

session.commit();

5.2 事务机制

  • 消息事务是在生产者producer到broker或broker到consumer过程中同一个session中发生的,保证几条消息在发送过程中的原子性。(Broker:消息队列核心,相当于一个控制中心,负责路由消息、保存订阅和连接、消息确认和控制事务)
  • 消息生产者-异步发送
    消息生产者使用持久(persistent)传递模式发送消息的时候,Producer.send() 方法会被阻塞,直到 broker 发送一个确认消息给生产者(ProducerAck),这个确认消息暗示broker已经成功接收到消息并把消息保存到二级存储中。这个过程通常称为同步发送。
    如果应用程序能够容忍一些消息的丢失,那么可以使用异步发送。异步发送不会在受到 broker 的确认之前一直阻塞 Producer.send 方法。但有一个例外,当发送方法在一个事务上下文中时,被阻塞的是 commit 方法而不是 send 方法。commit 方法成功返回意味着所有的持久消息都以被写到二级存储中。
    想要使用异步,在brokerURL中增加 jms.alwaysSyncSend=false&jms.useAsyncSend=true
    如果设置了alwaysSyncSend=true系统将会忽略useAsyncSend设置的值都采用同步
  1. 当alwaysSyncSend=false时,“NON_PERSISTENT”(非持久化)、事务中的消息将使用“异步发送”;
  2. 当alwaysSyncSend=false时,如果指定了useAsyncSend=true,“PERSISTENT”类型的消息使用异步发送。如果useAsyncSend=false,“PERSISTENT”类型的消息使用同步发送。

总结:

  • 默认情况(alwaysSyncSend=false,useAsyncSend=false),非持久化消息、事务内的消息均采用异步发送;对于持久化消息采用同步发送。
    jms.sendTimeout:发送超时时间,默认等于0,如果jms.sendTimeout>0将会忽略(alwaysSyncSend、useAsyncSend、消息是否持久化)所有的消息都是用同步发送!
  • 即使使用异步发送,也可以通过producerWindowSize来控制发送端无节制的向broker发送消息:
    producerWindowSize:窗口尺寸,用来约束在异步发送时producer端允许积压的(尚未ACK)的消息的尺寸,且只对异步发送有意义。每次发送消息之后,都将会导致memoryUsage尺寸增加(+message.size),当broker返回producerAck时,如果达到了producerWindowSize上限,即使是异步调用也会被阻塞,防止不停向broker发送消息。
    通过jms.producerWindowSize=。。。来设置

5.3 持久化机制

ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB和LevelDB,无论使用哪种持久化方式,消息的存储逻辑都是一致的。在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等,然后试图将消息发送给接收者,发送成功则将消息从存储中删除,失败则继续尝试。消息中心启动以后首先要检查指定的存储位置,如果有未发送成功的消息,则需要把消息发送出去。

5.4 消息幂等性

消费者保证消息幂等性,不被重复消费
网络延迟传输中,会造成进行MQ重试中,在重试过程中,可能会造成重复消费
使用全局MessageID判断消费方使用同一个,解决幂等性
activeMQ提供了textMessageId,使用业务ID(订单号)也可以

String jmsMessageId = textMessage.getJMSMessageID();

网络延迟环境下,第二次请求过来,应该使用全局ID判断该消息是否被使用过

if(jmsMessageId == redis内的id){

    //把消息签收掉,否则将继续重试,有些人觉得难理解,我解释一下:

    textMessage.acknowledge();  //避免第三次重试

   // 消息可以重发,但是消息不能做重复操作,如重复向数据库中做插入操作,造成幂等性问题,

    //所以当第二次发送过来的时候,就可能造成重复提交问题,我们使用手动提交(业务中一般也是使用手动提交多)

    //手动提交可以把重复发送的消息从队列中移除,那么接下来就不会触发重试了。

}

    //将拿到的消息做业务处理,如插入修改操作等...

   // 消费成功,把jmsMessageId放入redis

5.5 队列消息过滤收取

不同消息可以放到不同的队列,也可以放到一个队列中,通过增加消息的属性,在发送message的时候,可以设置它的消息属性值,然后在消费端建立消费队列时,传入过滤条件即可。

发送端:


MapMessage message1 = session.createMapMessage();

          message1.setString("name","张1");//设置了name值

          message1.setIntProperty("age",23);//设置了age属性

接受端:

MessageConsumer consumer = session.createConsumer(queue,"age > 30");

这样,这个consumer就只消费这个queue中age>30的消息。

5.6 消息保序性

利用Activemq的高级特性:consumer之独有消费者(exclusive consumer)

  queue = new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true");

这样会确保consumer列表中,始终取一个主消费者来消费。但是缺点在于将会始终发往这个消费者,导致无法负载均衡。比如,订单消息,希望一个订单内的消息是保序的,但各个订单之间可以并发,这就需要用到下文消息组 messageGroup 的概念。

Message Groups特性是一种负载均衡的机制。在一个消息被分发到consumer之前,broker首先检查消息JMSXGroupID属性。如果存在,那么broker会检查是否有某个consumer拥有这个message group。如果没有,那么broker会选择一个consumer,并将它关联到这个message group。此后,这个consumer会接收这个message group的所有消息,直到:

  • Consumer被关闭
  • Message group被关闭,通过发送一个消息,并设置这个消息的JMSXGroupSeq为-1

例如:

message.setStringProperty("JMSXGroupID", "constact-20100000002");
.....
message.setIntProperty("JMSXGroupSeq", -1);

5.6 死信队列

在使用Message Queue的过程中,总会由于种种原因而导致消息失败。一个经典的场景是一个生成者向Queue中发消息,里面包含了一组邮件地址和邮件内容。而消费者从Queue中将消息一条条读出来,向指定邮件地址发送邮件。消费者在发送消息的过程中由于种种原因会导致失败,比如网络超时、当前邮件服务器不可用等。这样我们就希望建立一种机制,对于未发送成功的邮件再重新发送,也就是重新处理。重新处理超过一定次数还不成功,就放弃对该消息的处理,记录下来,继续对剩余消息进行处理。

ActiveMQ为我们实现了这一功能,叫做ReDelivery(重新投递)。当消费者在处理消息时有异常发生,会将消息重新放回Queue里,进行下一次处理。当超过重试次数时,会给broker发送一个"Poison ack",这个消息被认为是a poison pill(毒丸),这时broker会将这个消息发送到DLQ。

在以下四种情况中,ActiveMQ消息会被重发给客户端/消费者:

  • 在一个事务session中,并且调用了session.rollback()方法。
  • 在一个事务session中,session.commit()之前调用了commit.close()。
  • 在session中使用CLIENT_ACKNOWLEDGE签收模式,并且调用了session.recover()方法。
  • 在session中使用AUTO_ACKNOWLEDGE签收模式,在异步(messageListener)消费消息情况下,如果onMessage方法异常且没有被catch,此消息会被redelivery。

缺省情况下:持久消息过期,会被送到DLQ,非持久消息不会送到DLQ(不会redelivery)。
可以在connectionFactory中注入自定义的redeliveryPolicy来改变缺省参数。
activeMQ默认是发送六次,每次间隔1秒。

6.常见问题

6.1 消息堆积导致服务不可用

  • ActiveMQ分为持久化和非持久化。非持久化消息是存储在内存中的,持久化消息是存储在文件中的,它们的最大限制在配置文件的<systemUsage>节点中配置。
  • 非持久化消息堆积到内存告急时,ActiveMQ会将内存中的非持久化消息写入临时文件中,以腾出内存。(重启后持久化消息会从文件中恢复,非持久化的临时文件会直接删除)
  • 当文件增大到达了配置中的最大限制的时:
  • 持久化情况,到达文件限额时,生产者阻塞,消费者可连接可消费,消费后生产者继续运行:
    设置2G左右的持久化文件限制,大量生产持久化消息直到文件达到最大限制,此时生产者阻塞,但消费者可正常连接并消费消息,等消息消费掉一部分,文件删除又腾出空间之后,生产者又可继续发送消息,服务自动恢复正常。
  • 非持久化情况,到达文件限额时,生产者阻塞,消费者可连接但不能消费,系统崩溃:
    设置2G左右的临时文件限制,大量生产非持久化消息并写入临时文件,在达到最大限制时,生产者阻塞,消费者可正常连接但不能消费消息,或者原本慢速消费的消费者,消费突然停止。整个系统可连接,但是无法提供服务,就这样挂了。
    解决方案:尽量不要用非持久化消息,非要用的话,将临时文件限制尽可能的调大

6.2 消息丢失

6.3 消息持久化慢

默认情况非持久化的消息是异步发送的,持久化的消息是同步发送的,遇到慢一点的硬盘,发送消息的速度很慢。

但是在开启事务的情况下,消息都是异步发送的,效率会有2个数量级的提升。所以在发送持久化消息时,请务必开启事务模式。发送非持久化消息时也建议开启事务,因为根本不会影响性能;

解决方案:消息发送启用事务;

6.4 消息不均匀消费(存在无法处理消息,prefech机制)

ActiveMQ的一个主要的设计目标是:提供一个高性能的消息中间件。它使用了SEDA(Staged Event Driven Architecture)架构及异步传输。为了提供更高的性能,很重要的一点是 尽快地将消息传送给消费者,这样消费者利用消息缓冲区等待处理,而不是等待消息。
   然后,这样也有很大风险:不断地向 消费者 传送消息可能使得其消息缓冲溢出,因为传送的速度比消费者真正“消费”消息更快,这是很可能。
  因此,ActiveMQ使用了 消息”预取限制“(prefetch limit):表示在某个时间段内,可能向消费者传输的最大消息量,如果达到该上限,那么停止发送,直到ActiveMQ收到消费者的acknowledgements(确认,表示已经处理了该消息)。prefetch limit可以针对每个不同的consumer来设置。
  为了获取更高的性能,prefetch limit当然是越大越好,只要consumer有足够大的消息缓冲区(messagevolume)。如果消息的总量非常少,而且每个消息的处理时间非常的长,那么,可以将prefetch设置为1,这样,每次向consumer发送一个消息,等其确认已经处理完毕后,再发送第二个。
  特别地,如果prefetch设置为0,表示consumer每次 主动向activeMQ要求传输最大的数据量,而不是被动地接收消息

原因在于ActiveMQ的prefetch机制:每个消费者获取消息时,是批量获取的,相当于预定。因为activeMQ提倡是尽量消费理念,所以会采取消费者批量获取信息的方式来提高消费效率。prefech的值默认是1000,即表示一个消费者在取消息时,会一次取1000条到自己的消息缓冲区中(不溢出),然后再一条一条的处理,每处理完一条,mq就会删除这一条,没处理的在mq中依然可见。

在发送一些消息之后,开启2个消费者去处理消息。会发现一个消费者处理了所有的消息,另一个消费者根本没收到消息。原因在于ActiveMQ的prefetch机制。当消费者去获取消息时,不会一条一条去获取,而是一次性获取一批,默认是1000条。这些预获取的消息,在还没确认消费之前,在管理控制台还是可以看见这些消息的,但是不会再分配给其他消费者,此时这些消息的状态应该算作“已分配未消费”,如果消息最后被消费,则会在服务器端被删除,如果消费者崩溃,则这些消息会被重新分配给新的消费者。但是如果消费者既不消费确认,又不崩溃,那这些消息就永远躺在消费者的缓存区里无法处理。更通常的情况是,消费这些消息非常耗时,你开了10个消费者去处理,结果发现只有一台机器吭哧吭哧处理,另外9台啥事不干。

解决方案:将prefetch设为1,每次处理1条消息,处理完再去取,这样也慢不了多少.

另外,当连接来自一个连接池中,消费消息可能出现一些由于“prefetch”而产生的问题:预取的消息(还未被处理)当连接关闭时会被释放(即,可以在activeMQ中再次读取到该消息)。而连接池中的连接只有在连接池关闭后才真正的销毁。这使得 预取的消息直到连接被重用时才会被处理(或者等连接池关闭,可再次从activeMQ中读取)。这样导致了消息可能丢失,或者当连接池中有多个连接时,消息乱序(out-of-sequence)!

  • 消费者则不放入连接池!当然,这样在消费者的性能上,会受到影响(当有多个线程快速的消费消息时,对象被不断的创建销毁)。
  • 将消费者的连接池中数量设为1。
  • 在使用连接池的情况下,将prefetch设为1或者0。当使用Spring JMS和MessageDrivenPojo时,只能将prefetch设为1,而不能为0;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 个人专题目录[https://www.jianshu.com/p/140e2a59db2c] 一、JMS简介 全称...
    Java及SpringBoot阅读 2,075评论 0 10
  • 什么是activeMQ activeMQ是一种开源的,实现了JMS1.1规范的,面向消息(MOM)的中间件,为应用...
    赵铁柱啊阅读 1,873评论 1 6
  • 此刻正独自坐在一家简易的快餐店。 由于还没到饭点,来往用餐的人并不多。 我只点了一杯饮料,坐等时间的流逝。 天还是...
    枝角阅读 607评论 4 5
  • 感情很奇妙,我竟然看着你的笑脸,说傻姑娘。
    飞利浦净水郑凯阅读 174评论 0 0
  • 《奇双会》又名《贩马记》:包括《哭监》、《写状》、《三拉》三折戏。 主要剧情是:新任陕西褒城县令赵...
    象浦阅读 2,002评论 3 21