原生rabbitMq实现

原生实现rabbitMQ

  1. 为什么要使用rabbitMQ消息队列

我们模拟这样一个场景,假如你在网上购物商城买了东西,你支付过程5s、发送短信通知5s、邮件通知等时间就更长了,你整个支的过程我们模拟这样一个场景,假如你在网上购物商城买了东西,你支付过程5s、发送短信通知5s、邮件通知等时间就更长了,你整个支付的过程就和很长了,因为代码是耦合在一起,但是如果你使用消息中间系统的话,我们支付过程就很短了,因为消息通知都是异步的,由消息系实现。

  1. 实现原生rabbitMQ流程

创建连接工厂–>给工厂设置连接属性(地址,端口、账号密码等)–>由工厂得到连接
–>通过连接得到信道–>由信道设置交换器->信道发布消息

生产者 :

//创建连接工厂,设置工厂属性
ConnectionFactory factory = new ConnectionFactory();   
factory.setHost("127.0.0.1");        
factory.setUsername("guest");
factory.setPassword("guest");
  //声明路由键和交换器
 private final static String EXCHANGE_NAME = "direct_cc_confirm_1";   
  //路由键
 private final static String ROUTE_KEY = "error";
 Connection connection = factory.newConnection();  
 Channel channel = connection.createChannel();    
 //通过信道声明一个交换器 第一个参数时交换器的名字 第二个参数时交换器的种类
 channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); 
 //信道发布消息
channel.basicPublish(EXCHANGE_NAME,ROUTE_KEY,null,message.getBytes());
//关闭连接
 channel.close();
connection.close();

消费者:

// 在信道声明交换器前步骤相同,在以后就是不同了
channel.queueBind(queueName,EXCHANGE_NAME,routingKey);
        System.out.println("Waiting message.......");

        // 创建队列消费者 设置一个监听器监听消费消息 
        final Consumer consumerB = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
             String message = new String(body, "UTF-8");
             System.out.println( "Received ["+ envelope.getRoutingKey() + "] "+message);
            }
        };
        //消费者自动确认:autoAck参数为true
        channel.basicConsume(queueName, true, consumerB);
    }

以上只有一个生产者和消费者只有一个路由键,但是如果生产者由很多,消费者只有一个那就会只接受路由键匹配的生产者发送的消息

生产者:

       //定义一组路由键
        String[]routingKeys = {"error","info","warning"};
        for(int i=0;i<3;i++){
        	//路由键
            String routingKey = routingKeys[i];            
            //要发送的消息
            String message = "Hello world_"+(i+1);
            /**
             * 发送消息到交换器上
             * 参数1:交换器的名字
             * 参数2:路由键
             * 参数3:BasicProperties
             * 参数4:要发送的消息
             */
            channel.basicPublish(EXCHANGE_NAME,routingKey,null,message.getBytes());
            System.out.println("Sent "+routingKey+":"+message);
        }

消费者:

//只匹配生产者由error路由键发送的消息
 String routingKey = "error";
        
        //7.队列通过路由键绑定到交换器上
        channel.queueBind(queueName,EXCHANGE_NAME,routingKey);
        
        System.out.println("Waiting message.......");

        //8.设置一个监听器监听消费消息
        Consumer consumerB = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body)
                    throws IOException {
                String message = new String(body,"UTF-8");
                System.out.println("Accept:"+envelope.getRoutingKey()+":"+message);
            }
        };        
        //9.自动确认:autoAck参数为flase
        channel.basicConsume(queueName,flase,consumerB);
    }
//消费者如果要接受所有信息的话
 //5.声明随机队列
        String queueName = channel.queueDeclare().getQueue();
        
        //6.定义一组路由键消费所有日志
        String[]routingKeys = {"error","info","warning"};
        
        //7.队列通过路由键绑定到交换器上
        for(String routingKey:routingKeys){
            //队列和交换器的绑定
            channel.queueBind(queueName,EXCHANGE_NAME,routingKey);
        }
        System.out.println("Waiting message.......");

        //8.设置一个监听器监听消费消息
        Consumer consumerA = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                AMQP.BasicProperties properties, byte[] body)throws IOException {
                String message = new String(body,"UTF-8");
                System.out.println("Accept:"+envelope.getRoutingKey()+":"+message);
            }
        };
        channel.basicConsume(queueName,flase,consumerA);
  1. rabbbitMQ自动确认机制

自动确认机制分为同步确认和异步确认,就是在发送方发了消息,消费者返回ack,才确认这个消息为发送成功,否则就是发送失败,unacked.

原生rabbitMq实现_第1张图片

同步实现:

  channel.confirmSelect();

        //发布消息的交换器上
        for(int i=0;i<2;i++){
            String msg = "Hello "+(i+1);
            channel.basicPublish(EXCHANGE_NAME,ROUTE_KEY,null,msg.getBytes());
            //等待RabbitMQ返回消息确认消息已送达RabbitMQ服务器
            if (channel.waitForConfirms()){
                System.out.println("发送方同步确认: "+ROUTE_KEY+":"+msg);
            }
        }

异步实现

 //将信道设置为发送方确认
        channel.confirmSelect();

        //信道被关闭监听器 用于RabbitMQ服务器断线重连
        //channel.addShutdownListener();

        /**
         * 生产者异步确认监听
         * 参数deliveryTag代表了当前channel唯一的投递
         * 参数multiple:false
         * 
         */
        channel.addConfirmListener(new ConfirmListener() {
        	//RabbitMQ服务器确认收到消息
            public void handleAck(long deliveryTag, boolean multiple)
                    throws IOException {
                System.out.println("RabbitMQ服务器确认收到消息Ack deliveryTag="+deliveryTag
                        +"multiple:"+multiple);
            }

            //RabbitMQ服务器由于自己内部出现故障没有收到消息
            public void handleNack(long deliveryTag, boolean multiple)
                    throws IOException {
                System.out.println("RabbitMQ服务没有收到消息Ack deliveryTag="+deliveryTag
                        +"multiple:"+multiple);
            }
        });

        //生产者异步返回监听 这里和发布消息时的mandatory参数有关
        //参数mandatory:mandatory=true,投递消息时无法找到一个合适的队列,把消息返回给生产者,mandatory=false 丢弃消息(缺省)
        channel.addReturnListener(new ReturnListener() {
            public void handleReturn(int replyCode, String replyText,
                                     String exchange, String routingKey,
                                     AMQP.BasicProperties properties, byte[] body)
                    throws IOException {
                String msg = new String(body);
                System.out.println("replyText:"+replyText);
                System.out.println("exchange:"+exchange);
                System.out.println("routingKey:"+routingKey);
                System.out.println("msg:"+msg);
            }
        });
  //后面是进行发布操作

//9.消费者自动确认:autoAck参数为true
channel.basicConsume(queueName, true, consumerB);

你可能感兴趣的:(java技术总结)