1. 自定义消费者使用

  • 我们之前呢都是在代码中编写while循环,进行 consumer.nextDelivery 方法进行获取下一条消息,然后进行消费处理!
  • 其实我们还可以使用自定义的Consumer,它更加的方便,解耦性更加的强,也是在实际工作中最常用的使用方式!
  • 自定义消费端实现只需要继承 DefaultConsumer 类,重写 handleDelivery 方法即可

 

自定义消费端演示

  1. public class Producer {
  2. public static void main(String[] args) throws Exception {
  3. //1 创建ConnectionFactory
  4. ConnectionFactory connectionFactory = new ConnectionFactory();
  5. connectionFactory.setHost("192.168.244.11");
  6. connectionFactory.setPort(5672);
  7. connectionFactory.setVirtualHost("/");
  8. connectionFactory.setHandshakeTimeout(20000);
  9. //2 获取Connection
  10. Connection connection = connectionFactory.newConnection();
  11. //3 通过Connection创建一个新的Channel
  12. Channel channel = connection.createChannel();
  13. String exchange = "test_consumer_exchange";
  14. String routingKey = "consumer.save";
  15. String msg = "Hello RabbitMQ Consumer Message";
  16. //4 发送消息
  17. for(int i =0; i<5; i ++){
  18. channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
  19. }
  20. }
  21. }

 

  1. public class MyConsumer extends DefaultConsumer {
  2. public MyConsumer(Channel channel) {
  3. super(channel);
  4. }
  5. @Override
  6. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  7. //consumerTag: 内部生成的消费标签 properties: 消息属性 body: 消息内容
  8. System.err.println("-----------consume message----------");
  9. System.err.println("consumerTag: " + consumerTag);
  10. //envelope包含属性:deliveryTag(标签), redeliver, exchange, routingKey
  11. //redeliver是一个标记,如果设为true,表示消息之前可能已经投递过了,现在是重新投递消息到监听队列的消费者
  12. System.err.println("envelope: " + envelope);
  13. System.err.println("properties: " + properties);
  14. System.err.println("body: " + new String(body));
  15. }
  16. }

 

  1. public class Consumer {
  2. public static void main(String[] args) throws Exception {
  3. //1 创建ConnectionFactory
  4. ConnectionFactory connectionFactory = new ConnectionFactory();
  5. connectionFactory.setHost("192.168.244.11");
  6. connectionFactory.setPort(5672);
  7. connectionFactory.setVirtualHost("/");
  8. connectionFactory.setHandshakeTimeout(20000);
  9. //2 获取Connection
  10. Connection connection = connectionFactory.newConnection();
  11. //3 通过Connection创建一个新的Channel
  12. Channel channel = connection.createChannel();
  13. String exchangeName = "test_consumer_exchange";
  14. String routingKey = "consumer.#";
  15. String queueName = "test_consumer_queue";
  16. //4 声明交换机和队列,然后进行绑定设置路由Key
  17. channel.exchangeDeclare(exchangeName, "topic", true, false, null);
  18. channel.queueDeclare(queueName, true, false, false, null);
  19. channel.queueBind(queueName, exchangeName, routingKey);
  20. //5 设置channel,使用自定义消费者
  21. channel.basicConsume(queueName, true, new MyConsumer(channel));
  22. }
  23. }

 

运行说明

先启动消费端,访问管控台:http://ip:15672,检查Exchange和Queue是否设置OK,然后启动生产端。消费端打印内容如下

 

2.消费端的限流策略

2.1 限流的场景与机制

  • 假设一个场景,我们Rabbitmq服务器有上万条未处理的消息,我们随便打开一个消费者客户端,会出现这种情况:巨量的消息瞬间全部推送过来,但是我们单个客户端无法同时处理这么多数据!此时很有可能导致服务器崩溃,严重的可能导致线上的故障。
  • 除了这种场景,还有一些其他的场景,比如说单个生产者一分钟生产出了几百条数据,但是单个消费者一分钟可能只能处理60条数据,这个时候生产端和消费端肯定是不平衡的。通常生产端是没办法做限制的。所以消费端肯定需要做一些限流措施,否则如果超出最大负载,可能导致消费端性能下降,服务器卡顿甚至崩溃等一系列严重后果。

 

消费端限流机制

RabbitMQ提供了一种qos (服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息 (通过基于consume或者channel设置Qos的值) 未被确认前,不进行消费新的消息。

需要注意:

1.不能设置自动签收功能(autoAck = false)

2.如果消息没被确认,就不会到达消费端,目的就是给消费端减压

 

2.2 限流相关API

限流设置 - BasicQos()

void BasicQos(uint prefetchSize, ushort prefetchCount, bool global);

prefetchSize: 单条消息的大小限制,消费端通常设置为0,表示不做限制

prefetchCount: 一次最多能处理多少条消息,通常设置为1

global: 是否将上面设置应用于channel,false代表consumer级别

注意事项

prefetchSizeglobal这两项,rabbitmq没有实现,暂且不研究

prefetchCountautoAck=false 的情况下生效,即在自动应答的情况下这个值是不生效的

 

手工ACK - basicAck()

void basicAck(Integer deliveryTag,boolean multiple)

手工ACK,调用这个方法就会主动回送给Broker一个应答,表示这条消息我处理完了,你可以给我下一条了。参数multiple表示是否批量签收,由于我们是一次处理一条消息,所以设置为false

 

2.3 限流演示

生产端

生产端就是正常的逻辑

  1. public class Producer {
  2. public static void main(String[] args) throws Exception {
  3. ConnectionFactory connectionFactory = new ConnectionFactory();
  4. connectionFactory.setHost("192.168.244.11");
  5. connectionFactory.setPort(5672);
  6. connectionFactory.setVirtualHost("/");
  7. connectionFactory.setHandshakeTimeout(20000);
  8. Connection connection = connectionFactory.newConnection();
  9. Channel channel = connection.createChannel();
  10. String exchange = "test_qos_exchange";
  11. String routingKey = "qos.save";
  12. String msg = "Hello RabbitMQ QOS Message";
  13. // 发送消息
  14. for (int i = 0; i < 5; i++) {
  15. channel.basicPublish(exchange, routingKey, true, null,
  16. msg.getBytes());
  17. }
  18. }
  19. }

 

自定义消费者

为了看到限流效果,这里不进行ACK

  1. public class MyConsumer extends DefaultConsumer {
  2. //接收channel
  3. private Channel channel ;
  4. public MyConsumer(Channel channel) {
  5. super(channel);
  6. this.channel = channel;
  7. }
  8. @Override
  9. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  10. System.err.println("-----------consume message----------");
  11. System.err.println("consumerTag: " + consumerTag);
  12. System.err.println("envelope: " + envelope);
  13. //System.err.println("properties: " + properties);
  14. System.err.println("body: " + new String(body));
  15. //手工ACK,参数multiple表示不批量签收
  16. //channel.basicAck(envelope.getDeliveryTag(), false);
  17. }
  18. }

 

消费端

关闭autoACK,进行限流设置

  1. public class Consumer {
  2. public static void main(String[] args) throws Exception {
  3. //1 创建ConnectionFactory
  4. ConnectionFactory connectionFactory = new ConnectionFactory();
  5. connectionFactory.setHost("192.168.244.11");
  6. connectionFactory.setPort(5672);
  7. connectionFactory.setVirtualHost("/");
  8. connectionFactory.setHandshakeTimeout(20000);
  9. //2 获取Connection
  10. Connection connection = connectionFactory.newConnection();
  11. //3 通过Connection创建一个新的Channel
  12. Channel channel = connection.createChannel();
  13. String exchangeName = "test_qos_exchange";
  14. String queueName = "test_qos_queue";
  15. String routingKey = "qos.#";
  16. //4 声明交换机和队列,然后进行绑定设置路由Key
  17. channel.exchangeDeclare(exchangeName, "topic", true, false, null);
  18. channel.queueDeclare(queueName, true, false, false, null);
  19. channel.queueBind(queueName, exchangeName, routingKey);
  20. //进行参数设置:单条消息的大小限制,一次最多能处理多少条消息,是否将上面设置应用于channel
  21. channel.basicQos(0, 1, false);
  22. //限流: autoAck设置为 false
  23. channel.basicConsume(queueName, false, new MyConsumer(channel));
  24. }
  25. }

 

运行说明

我们先注释掉手工ACK方法,然后启动消费端和生产端,此时消费端只打印了一条消息

这是因为我们设置了手工签收,并且设置了一次只处理一条消息,当我们没有回送ack应答时,Broker端就认为消费端还没有处理完这条消息,基于这种限流机制就不会给消费端发送新的消息了,所以消费端只打印了一条消息。

通过管控台也可以看到队列总共收到了5条消息,有一条消息没有ack。

将手工签收代码取消注释,再次运行消费端,此时就会打印5条消息的内容。

 

3. 消费端ACK与重回队列机制

3.1 ACK与NACK

当我们设置 autoACK=false 时,就可以使用手工ACK方式了,那么其实手工方式包括了手工ACK与NACK。

当我们手工 ACK 时,会发送给Broker一个应答,代表消息成功处理了,Broker就可以回送响应给生产端了。NACK 则表示消息处理失败了,如果设置重回队列,Broker端就会将没有成功处理的消息重新发送。

 

使用方式

  1. 消费端进行消费的时候,如果由于业务异常我们可以手工 NACK 并进行日志的记录,然后进行补偿!

    方法:void basicNack(long deliveryTag, boolean multiple, boolean requeue)
  2. 如果由于服务器宕机等严重问题,那我们就需要手工进行 ACK 保障消费端消费成功!

    方法:void basicAck(long deliveryTag, boolean multiple)

 

3.2 重回队列演示

  • 消费端重回队列是为了对没有处理成功的消息,把消息重新会递给Broker!
  • 重回队列,会把消费失败的消息重新添加到队列的尾端,供消费者继续消费。
  • 一般我们在实际应用中,都会关闭重回队列,也就是设置为false

 

生产端

对消息设置自定义属性以便进行区分

  1. public class Producer {
  2. public static void main(String[] args) throws Exception {
  3. //1 创建ConnectionFactorys
  4. ConnectionFactory connectionFactory = new ConnectionFactory();
  5. connectionFactory.setHost("192.168.244.11");
  6. connectionFactory.setPort(5672);
  7. connectionFactory.setVirtualHost("/");
  8. connectionFactory.setHandshakeTimeout(20000);
  9. //2 获取Connection
  10. Connection connection = connectionFactory.newConnection();
  11. //3 通过Connection创建一个新的Channel
  12. Channel channel = connection.createChannel();
  13. String exchange = "test_ack_exchange";
  14. String routingKey = "ack.save";
  15. for(int i =0; i<5; i ++){
  16. //设置消息属性
  17. Map<String, Object> headers = new HashMap<String, Object>();
  18. headers.put("num", i);
  19. AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
  20. .deliveryMode(2)
  21. .contentEncoding("UTF-8")
  22. .headers(headers)
  23. .build();
  24. //发送消息
  25. String msg = "Hello RabbitMQ ACK Message " + i;
  26. channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
  27. }
  28. }
  29. }

 

自定义消费

对第一条消息进行NACK,并设置重回队列

  1. public class MyConsumer extends DefaultConsumer {
  2. private Channel channel ;
  3. public MyConsumer(Channel channel) {
  4. super(channel);
  5. this.channel = channel;
  6. }
  7. @Override
  8. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  9. System.err.println("-----------consume message----------");
  10. System.err.println("body: " + new String(body));
  11. try {
  12. Thread.sleep(2000);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. if((Integer)properties.getHeaders().get("num") == 0) {
  17. //NACK,参数三requeue:是否重回队列
  18. channel.basicNack(envelope.getDeliveryTag(), false, true);
  19. } else {
  20. channel.basicAck(envelope.getDeliveryTag(), false);
  21. }
  22. }
  23. }

 

消费端

关闭自动签收功能

  1. public class Consumer {
  2. public static void main(String[] args) throws Exception {
  3. ConnectionFactory connectionFactory = new ConnectionFactory();
  4. connectionFactory.setHost("192.168.244.11");
  5. connectionFactory.setPort(5672);
  6. connectionFactory.setVirtualHost("/");
  7. connectionFactory.setHandshakeTimeout(20000);
  8. Connection connection = connectionFactory.newConnection();
  9. Channel channel = connection.createChannel();
  10. String exchangeName = "test_ack_exchange";
  11. String queueName = "test_ack_queue";
  12. String routingKey = "ack.#";
  13. //声明交换机和队列,然后进行绑定设置路由Key
  14. channel.exchangeDeclare(exchangeName, "topic", true, false, null);
  15. channel.queueDeclare(queueName, true, false, false, null);
  16. channel.queueBind(queueName, exchangeName, routingKey);
  17. //手工签收 必须要设置 autoAck = false
  18. channel.basicConsume(queueName, false, new MyConsumer(channel));
  19. }
  20. }

 

运行说明

先启动消费端,然后启动生产端,消费端打印如下,显然第一条消息由于我们调用了NACK,并且设置了重回队列,所以会导致该条消息一直重复发送,消费端就会一直循环消费。

 

一般工作中不会设置重回队列这个属性,都是自己去做补偿或者投递到延迟队列里的,然后指定时间去处理即可。

 

4. TTL

TTL说明

  • TTL是Time To Live的缩写,也就是生存时间
  • RabbitMQ支持消息的过期时间,在消息发送时可以进行指定
  • RabbitMQ支持为每个队列设置消息的超时时间,从消息入队列开始计算,只要超过了队列的超时时间配置,那么消息会自动的清除

 

TTL演示

这次演示我们不写代码,只通过管控台进行操作,实际测试也会更为方便一些。

 

1. 创建Exchange

选择Exchange菜单,找到下面的Add a new exchange

 

2.创建Queue

选择Queue菜单,找到下面的Add a new queue

 

3.建立队列和交换机的绑定关系

点击Exchange表格中的test002_exchange,在下面添加绑定规则

 

4.发送消息

点击Exchange表格中的test002_exchange,在下面找到Publish message,设置消息进行发送

 

5.验证

点击Queue菜单,查看表格中test002已经有了一条消息,10秒后表格显示0条,说明过期时间到了消息被自动清除了。

 

6.设置单条消息过期时间

点击Exchange表格中的test002_exchange,在下面找到Publish message,设置消息的过期时间并进行发送,此时观察test002队列,发现消息5s后就过期被清除了,即使队列设置的过期时间是10s。

 

TTL代码设置过期时间

  1. AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
  2. .expiration("10000") //10s过期
  3. .build();
  4. //发送消息
  5. channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());

 

队列过期时间设置

  1. //设置队列的过期时间10s
  2. Map<String,Object> param = new HashMap<>();
  3. param.put("x-message-ttl", 10000);
  4. //声明队列
  5. channel.queueDeclare(queueName, true, false, false, null);

 

注意事项

  1. 两者的区别是设置队列的过期时间是对该队列的所有消息生效的。
  2. 为消息设置TTL有一个问题:RabbitMQ只对处于队头的消息判断是否过期(即不会扫描队列),所以,很可能队列中已存在死消息,但是队列并不知情。这会影响队列统计数据的正确性,妨碍队列及时释放资源。

 

5.死信队列

死信队列介绍

  • 死信队列:DLX,dead-letter-exchange
  • 利用DLX,当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX

 

消息变成死信有以下几种情况

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false
  • 消息TTL过期
  • 队列达到最大长度

 

死信处理过程

  • DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。
  • 当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。
  • 可以监听这个队列中的消息做相应的处理。

 

死信队列设置

  1. 首先需要设置死信队列的exchange和queue,然后进行绑定:

  1. 然后需要有一个监听,去监听这个队列进行处理
  2. 然后我们进行正常声明交换机、队列、绑定,只不过我们需要在队列加上一个参数即可:arguments.put(" x-dead-letter-exchange","dlx.exchange");,这样消息在过期、requeue、 队列在达到最大长度时,消息就可以直接路由到死信队列!

 

死信队列演示

生产端

  1. public class Producer {
  2. public static void main(String[] args) throws Exception {
  3. //1 创建ConnectionFactory
  4. ConnectionFactory connectionFactory = new ConnectionFactory();
  5. connectionFactory.setHost("192.168.244.11");
  6. connectionFactory.setPort(5672);
  7. connectionFactory.setVirtualHost("/");
  8. connectionFactory.setHandshakeTimeout(20000);
  9. //2 获取Connection
  10. Connection connection = connectionFactory.newConnection();
  11. //3 通过Connection创建一个新的Channel
  12. Channel channel = connection.createChannel();
  13. String exchange = "test_dlx_exchange";
  14. String routingKey = "dlx.save";
  15. String msg = "Hello RabbitMQ DLX Message";
  16. AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
  17. .deliveryMode(2)
  18. .contentEncoding("UTF-8")
  19. .expiration("10000")
  20. .build();
  21. //发送消息
  22. channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
  23. }
  24. }

 

自定义消费者

  1. public class MyConsumer extends DefaultConsumer {
  2. public MyConsumer(Channel channel) {
  3. super(channel);
  4. }
  5. @Override
  6. public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
  7. System.err.println("-----------consume message----------");
  8. System.err.println("consumerTag: " + consumerTag);
  9. System.err.println("envelope: " + envelope);
  10. System.err.println("properties: " + properties);
  11. System.err.println("body: " + new String(body));
  12. }
  13. }

 

消费端

  • 声明正常处理消息的交换机、队列及绑定规则
  • 在正常交换机上指定死信发送的Exchange
  • 声明死信交换机、队列及绑定规则
  • 监听死信队列,进行后续处理,这里省略
  1. public class Consumer {
  2. public static void main(String[] args) throws Exception {
  3. ConnectionFactory connectionFactory = new ConnectionFactory();
  4. connectionFactory.setHost("192.168.244.11");
  5. connectionFactory.setPort(5672);
  6. connectionFactory.setVirtualHost("/");
  7. connectionFactory.setHandshakeTimeout(20000);
  8. Connection connection = connectionFactory.newConnection();
  9. Channel channel = connection.createChannel();
  10. // 声明一个普通的交换机 和 队列 以及路由
  11. String exchangeName = "test_dlx_exchange";
  12. String routingKey = "dlx.#";
  13. String queueName = "test_dlx_queue";
  14. String deadQueueName = "dlx.queue";
  15. channel.exchangeDeclare(exchangeName, "topic", true, false, null);
  16. // 指定死信发送的Exchange
  17. Map<String, Object> agruments = new HashMap<String, Object>();
  18. agruments.put("x-dead-letter-exchange", "dlx.exchange");
  19. // 这个agruments属性,要设置到声明队列上
  20. channel.queueDeclare(queueName, true, false, false, agruments);
  21. channel.queueBind(queueName, exchangeName, routingKey);
  22. // 要进行死信队列的声明
  23. channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
  24. channel.queueDeclare(deadQueueName, true, false, false, null);
  25. channel.queueBind(deadQueueName, "dlx.exchange", "#");
  26. channel.basicConsume(queueName, true, new MyConsumer(channel));
  27. //channel.basicConsume(deadQueueName, true, new MyConsumer(channel));
  28. }
  29. }

 

运行说明

启动消费端,此时查看管控台,新增了两个Exchange,两个Queue。在test_dlx_queue上我们设置了DLX,也就代表死信消息会发送到指定的Exchange上,最终其实会路由到dlx.queue上。

此时关闭消费端,然后启动生产端,查看管控台队列的消息情况,test_dlx_queue的值为1,而dlx_queue的值为0。

10s后的队列结果如图,由于生产端发送消息时指定了消息的过期时间为10s,而此时没有消费端进行消费,消息便被路由到死信队列中。

实际环境我们还需要对死信队列进行一个监听和处理,当然具体的处理逻辑和业务相关,这里只是简单演示死信队列是否生效。

RabbitMQ从入门到精通(三)的更多相关文章

  1. iOS开发-UI 从入门到精通(三)

    iOS开发-UI 从入门到精通(三)是对 iOS开发-UI 从入门到精通(一)知识点的综合练习,搭建一个简单地登陆界面,增强实战经验,为以后做开发打下坚实的基础! ※在这里我们还要强调一下,开发环境和 ...

  2. RabbitMQ从入门到精通

    RabbitMQ从入门到精通 学习了:http://blog.csdn.net/column/details/rabbitmq.html RabbitMQ是AMQP(advanced message ...

  3. MyBatis从入门到精通(三):MyBatis XML方式的基本用法之多表查询

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 多表查询 上篇博客中,我们示例的 ...

  4. visual studio 2015 搭建python开发环境,python入门到精通[三]

    在上一篇博客Windows搭建python开发环境,python入门到精通[一]很多园友提到希望使用visual studio 2013/visual studio 2015 python做demo, ...

  5. RabbitMQ 从入门到精通(二)

    目录 1. 消息如何保障百分之百的投递成功? 1.1 方案一:消息落库,对消息状态进行打标 1.2 方案二:消息的延迟投递,做二次确认,回调检查 2. 幂等性 2.1 幂等性是什么? 2.2 消息端幂 ...

  6. RabbitMQ 从入门到精通 (一)

    目录 1. 初识RabbitMQ 2. AMQP 3.RabbitMQ的极速入门 4. Exchange(交换机)详解 4.1 Direct Exchange 4.2 Topic Exchange 4 ...

  7. python入门到精通[三]:基础学习(2)

    摘要:Python基础学习:列表.元组.字典.函数.序列化.正则.模块. 上一节学习了字符串.流程控制.文件及目录操作,这节介绍下列表.元组.字典.函数.序列化.正则.模块. 1.列表 python中 ...

  8. MyBatis 入门到精通(三) 高级结果映射

    MyBatis的创建基于这样一个思想:数据库并不是您想怎样就怎样的.虽然我们希望所有的数据库遵守第三范式或BCNF(修正的第三范式),但它们不是.如果有一个数据库能够完美映射到所有应用程序,也将是非常 ...

  9. Atom编辑器入门到精通(三) 文本编辑基础

    身为编辑器,文本编辑的功能自然是放在第一位的,此节将总结常用的文本编辑的方法和技巧,掌握这些技巧以后可以极大地提高文本编辑的效率 注意此节中用到的快捷键是Mac下的,如果你用的系统是Win或者Linu ...

随机推荐

  1. FireUI live Preview使用方法-Berlin

    这是可以让开发者事先预览 mobile 画面的作法 1.先确定 Berlin IDE  Tools\Option\Form Designer 中 FireUI live Preview   broad ...

  2. 三种扩展 Office 软件功能的开发模型对比 – Office Add-In Model, VBA 和 VSTO

    当 Office 用户需要针对文档自定义新功能时,可以求助于 VBA 或者 VSTO 两种方式.Office 2013 富客户端以后,微软为 Office 平台上的开发者提供了一种新模型 --- Of ...

  3. strlen源码剖析(可查看glibc和VC的CRT源代码)

    学习高效编程的有效途径之一就是阅读高手写的源代码,CRT(C/C++ Runtime Library)作为底层的函数库,实现必然高效.恰好手中就有glibc和VC的CRT源代码,于是挑了一个相对简单的 ...

  4. c# Lambda扩展

    扩展类 public static class LinqExtensions { /// <summary> /// 创建lambda表达式:p=>true /// </sum ...

  5. Google C++测试框架系列高级篇:第一章 更多关于断言的知识

    原始链接:More Assertions 词汇表 现在你应该已经读完了入门篇并且会使用GTest来写测试.是时候来学一些新把戏了.这篇文档将教会你更多知识:用断言构造复杂的失败信息,传递致命失败,重用 ...

  6. 【linux杂谈】跟随大牛进行一次服务器间通讯问题的排查

    发现应用记录日志内,出现网络访问延迟较大的情况. 此类问题较为常见,特别是之前参与辅助一个朋友项目运维的过程中,经常因为网络访问延迟较大,朋友认为是遭到了ddos攻击或者是cc攻击.网络访问延迟较大常 ...

  7. 求你了,再问你Java内存模型的时候别再给我讲堆栈方法区了…

    GitHub 4.1k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 4.1k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 4.1k Star 的 ...

  8. 原子操作CAS-最小的线程安全

    原文连接:(http://www.studyshare.cn/blog-front/blog/details/1166/0 )一.原子操作是什么? 如果有两个线程分别执行两个操作A和B,从第一个线程执 ...

  9. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  10. 【maven 】jar包冲突-记一次冲突解决

    方法一:根据mvn提示一个一个排除 1.请到pom.xml文件所在的目录(包含父子目录)下分别执行下面的命令排查是什么原因导致fastjson版本不正确: mvn dependency:tree -D ...