rabbitmq整合springboot 回调 确认一体化

1.首先我们简单了解一下消息中间件的应用场景

异步处理
场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种1.串行的方式;2.并行的方式 
(1)串行方式:将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有一个问题是,邮件,短信并不是必须的,它只是一个通知,而这种做法让客户端等待没有必要等待的东西. 

(2)并行方式:将注册信息写入数据库后,发送邮件的同时,发送短信,以上三个任务完成后,返回给客户端,并行的方式能提高处理的时间。 

 

 

假设三个业务节点分别使用50ms,串行方式使用时间150ms,并行使用时间100ms。虽然并性已经提高的处理时间,但是,前面说过,邮件和短信对我正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,英爱是写入数据库后就返回. 
(3)消息队列 
引入消息队列后,把发送邮件,短信不是必须的业务逻辑异步处理 

由此可以看出,引入消息队列后,用户的响应时间就等于写入数据库的时间+写入消息队列的时间(可以忽略不计),引入消息队列后处理后,响应时间是串行的3倍,是并行的2倍。

 应用解耦

场景:双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口. 

 

这种做法有一个缺点:

    • 当库存系统出现故障时,订单就会失败。
    • 订单系统和库存系统高耦合. 
      引入消息队列 

订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。

库存系统:订阅下单的消息,获取下单消息,进行库操作。 
就算库存系统出现故障,消息队列也能保证消息的可靠投递,不会导致消息丢失。
流量削峰
流量削峰一般在秒杀活动中应用广泛 
场景:秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。 
作用: 
1.可以控制活动人数,超过此一定阀值的订单直接丢弃(我为什么秒杀一次都没有成功过呢^^) 
2.可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单) 

1.用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面. 

2.秒杀业务根据消息队列中的请求信息,再做后续处理.

以上内容的来源是:https://blog.csdn.net/qq_38455201/article/details/80308771,在此感谢

2.各种消息中间件性能的比较:

TPS比较 一ZeroMq 最好,RabbitMq 次之, ActiveMq 最差。

持久化消息比较—zeroMq不支持,activeMq和rabbitMq都支持。持久化消息主要是指:MQ down或者MQ所在的服务器down了,消息不会丢失的机制。

可靠性、灵活的路由、集群、事务、高可用的队列、消息排序、问题追踪、可视化管理工具、插件系统、社区—RabbitMq最好,ActiveMq次之,ZeroMq最差。

高并发—从实现语言来看,RabbitMQ最高,原因是它的实现语言是天生具备高并发高可用的erlang语言。

综上所述:RabbitMQ的性能相对来说更好更全面,是消息中间件的首选。

3.接下来我们在springboot当中整合使用RabbitMQ
第一步:导入maven依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

第二步:在application.properties文件当中引入RabbitMQ基本的配置信息

spring.application.name=spirng-boot-rabbitmq-sender
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=test
spring.rabbitmq.password=1qazxsw@
server.port=80
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns = true
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual

第三步:编写RabbitConfig类,类里面设置很多个EXCHANGE,QUEUE,ROUTINGKEY,是为了接下来的不同使用场景。

package com.rabbit.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class SenderConfig {
    
    public static final String EXCHANGE   = "exchange";
    public static final String ROUTINGKEY = "routingkey";
    public static final String QUEUE = "message";
    public static final String QUEUE1      = "message1";
        @Bean
        public Queue queueMessage() {
            return new Queue(QUEUE);
        }

        @Bean
        public Queue queueMessage1() {
            return new Queue(QUEUE1);
        }

        @Bean
        public TopicExchange exchange() {
            return new TopicExchange(EXCHANGE);
        }

        @Bean
        Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
            return BindingBuilder.bind(queueMessage).to(exchange).with(QUEUE);
        }

        @Bean
        Binding bindingExchangeMessages(Queue queueMessage1, TopicExchange exchange) {
            return BindingBuilder.bind(queueMessage1).to(exchange).with(QUEUE1);
        }
}

第四步:编写消息的生产者

package com.rabbit.service;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.rabbit.config.SenderConfig;
import com.rabbit.model.User;

@Component
public class HelloSender implements RabbitTemplate.ReturnCallback{
    /*@Autowired
    private AmqpTemplate template;*/
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void send() {
        User user = new User();
        user.setUsername("测试");
        user.setPassword("12345");
        this.rabbitTemplate.setReturnCallback(this);
        this.rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (!ack) {
                System.out.println("HelloSender消息发送失败" + cause + correlationData.toString());
            } else {
                System.out.println("HelloSender 消息发送成功 ");
            }
        });
        this.rabbitTemplate.convertAndSend(SenderConfig.EXCHANGE, SenderConfig.QUEUE1, user.getUsername());
    //template.convertAndSend("exchange", "topic.message", user.getUsername());
    }
   /* public void send1() {
        User user = new User();
        user.setUsername("测试");
        user.setPassword("12345");
    template.convertAndSend("exchange", "topic.message1", user.getPassword());
    }*/

    @Override
    public void returnedMessage(Message message, int replycode, String replytext, String exchange, String routingKey) {
        System.out.println("消息主题 message:"+message);
        System.out.println("消息主题 message:"+replycode);
        System.out.println("描述:"+replytext);
        System.out.println("消息使用的交换器 exchange:"+exchange);
        System.out.println("消息使用的路由器 routing:"+routingKey);
    }
    /*@Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("消息唯一标识" + correlationData.getId());
        System.out.println("确认结果" + ack);
        System.out.println("失败原因" + cause);
    }*/
}

第六步:编写消息的消费者,这一步也是最复杂的,因为可以编写出很多不同的需求出来,写法也有很多的不同。

package com.rabbit.config;

import java.io.IOException;
import java.util.Date;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
public class HelloReceive {

    @RabbitListener(queues=ReceiveConf.QUEUE)    //监听器监听指定的Queue
    public void process1(String str,Message message, Channel channel) throws IOException {  
        System.out.println("HelloReceiver收到  : " + str +"收到时间"+new Date());
        try {
            //告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
            System.out.println("receiver success");
        } catch (IOException e) {
            e.printStackTrace();
            //ack返回false,并重新回到队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,true);
            System.out.println("receiver fail");
        }
    }
    @RabbitListener(queues=ReceiveConf.QUEUE1)    //监听器监听指定的Queue
    public void process2(String str) {
        System.out.println("message1:"+str);
    }

}

 

 

posted @ 2019-03-22 09:24  散落人间  阅读(640)  评论(0编辑  收藏  举报
interface food{} class A implements food{} class B implements food{} class C implements food{} public class StaticFactory { private StaticFactory(){} public static food getA(){ return new A(); } public static food getB(){ return new B(); } public static food getC(){ return new C(); } } class Client{ //客户端代码只需要将相应的参数传入即可得到对象 //用户不需要了解工厂类内部的逻辑。 public void get(String name){ food x = null ; if ( name.equals("A")) { x = StaticFactory.getA(); }else if ( name.equals("B")){ x = StaticFactory.getB(); }else { x = StaticFactory.getC(); } } }