RabbitMQ初识

概述   

        RabbitMQ入门到精通,作为RabbitMQ入门是一个很好的范例.

        RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现,是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.目前类似于RabbitMQ的消息中间件有很多, RabbitMq、ActiveMq、ZeroMq、kafka都是可选择,RabbitMQ解决的问题

  • 信息的发送者和接收者如何维持这个连接,如果一方的连接中断,这期间的数据如何方式丢失
  • 如何降低发送者和接收者的耦合度
  • 如何让Priority高的接收者先接到数据
  • 如何做到load balance?有效均衡接收者的负载
  • 如何有效的将数据发送到相关的接收者?也就是说将接收者subscribe 不同的数据,如何做有效的filter
  • 如何做到可扩展,甚至将这个通信模块发到cluster上
  • 如何保证接收者接收到了完整,正确的数据

RabbitMQ基本概念

架构

RabbitMQ初识_第1张图片
        名词解释:
  • RabbitMQ Server: RabbitMQ 服务核心,是一种投递服务,在应用程序(生成者)与服务器(消费者)之间扮演着路由器的角色,接收生成者消息,同时将消费者路由至消费者
  • ClientA&B:生成者,生成消息,消息的构成:有效载荷payload(数据的载体),标签label(描述payload,包含消费者相关信息)
  • Client1&2&3:消费者,消费消息(注:消息到达消费者时,其label已经清空,也就是消费者不能识别消息来自何处)
        RabbitMQ server 构成
  • Connection:TCP连接.生成者与消费者通过TCP与RabbitMQ连接
  • Channel:信道,虚拟连接,一个Connection下可建立无数Channel,消息在Channel中进行传输(Channel节约通信开销,提高性能,Connection比喻成电缆,Channel就是独立光纤)
  • Exchange&Binding&Queue:交换,绑定,队列.AMQP的三大构成要素.生产者把消息发布到交换器上,消息最终到达队列,并被消费者接收,绑定决定了消息如何从路由器路由到特定的队列.交换器分
  • vhost:一个vhost对应一个独立的RabbitMQ Server,实现逻辑上的分离,互相干扰
         概念补充:
Exchange
       一个消息的传递
  • 生成者发布Messgae至Exchange,指定RootingKey(或者为空串),同时绑定Queue(也绑定RootingKey 与Message 中的RootingKey匹配)--queue需提前申明创建
  • 消费者读取Queue中Message进行消费(1.进行消息确认,消息从Queue中删除2.转发,消息转移至下一个消费者3.拒绝,消息删除或转发)
       Exchange 有三种类型:direct, fanout,topic,还有一种header
  • Direct : 直连,通过RoutingKey匹配Queue
  • Fanout:广播,忽略RoutingKey匹配,发送至所有Queue
  • Topic:规则匹配.按照一定的规则匹配RoutingKey
  • header:用键值对取代RootingKey进行匹配
       安装好的RabbitMQ有几个默认Exchange(好像是8个)
RabbitMQ初识_第2张图片

RabbitMQ安装

        window下安装RabbitMQ
  • 下载Erlang,安装(exe直接安装,zip安装需要配系统变量ERLANG_HOME)
  • 下载RabbitMQ.下一步安装...
  • 激活RabbitMQ管理插件,cmd 执行"C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.6\sbin\rabbitmq-plugins.bat" enable rabbitmq_management
  • 重启RabbitMQ cmd执行net stop RabbitMQ && net start RabbitMQ
  • 访问http://localhost:15672 使用guest/guest(guest账户在localhost下具有所有权限)登录进入ui界面

RabbitMQ基本使用

       为方便测试使用ui创建一个全权限账户zl/123(也可以使用 RabbitMQ 命令)

Direct

package producerAndconsume;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

/**
 * Hello world!
 *
 */
public class DirectMQ 
{  
    //交换器名称  
    public  final static String EXCHANGE_NAME = "test.amq.direct";  
    //队列名称  
    public  final static String QUEUE_NAME = "test.queue";  
    
    //RoutingKey  
    public  final static String ROUTINGKEY = "test.routingkey";  
    
    public static ConnectionFactory factory;
    
    public static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    static {
        /** 
         * 创建连接连接到MabbitMQ 
         */  
        factory = new ConnectionFactory();  
        //设置MabbitMQ所在主机ip或者主机名  
        factory.setHost("localhost"); 
        //指定用户 密码  
        factory.setUsername("zl");  
        factory.setPassword("123");  
        //指定端口  
        factory.setPort(AMQP.PROTOCOL.PORT); 
    }
    
    public static void init() throws Exception{
    	//创建一个连接  
        Connection connection = factory.newConnection(); 
        //创建一信道  
        Channel channel = connection.createChannel();  
        //指定一个队列  
        boolean durable = false; //设置消息持久化  RabbitMQ不允许使用不同的参数重新定义一个队列,所以已经存在的队列,我们无法修改其属性。
        
        //申明一个exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", durable);
        
		/* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map arguments)     
		 * queue:队列名称
		 * durable:持久化(重启不丢失消息)
		 * exclusive:排他性,指针对此connection可见
		 * autoDelete:不使用时自动删除
		 * arguments:其他参数
		 */
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);  
        channel.close();
        connection.close();
    }
    
    public static void main(String[] argv) throws Exception {
    	init();
    	//生产者要比消费者先启动
    	new Thread(new  Runnable() {
			public void run() {
				try {
					comsume();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
    	
    	TimeUnit.SECONDS.sleep(1);
    	
        produce();
        
/*    
 * 	   result print
 *     2017-09-04 20:45:09 send message: hello world!
       2017-09-04 20:45:09 Received Message:'hello world!'
        
        */
    }  
    
    public static void comsume() throws Exception {
        Connection connection = factory.newConnection(); 
        Channel channel = connection.createChannel();  
        
        //绑定queue
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME, ROUTINGKEY);
          
        //创建队列消费者  
        QueueingConsumer consumer = new QueueingConsumer(channel);  
        //指定消费队列  
        boolean ack = false ; //打开应答机制   
        // 指定消费队列    
        channel.basicConsume(QUEUE_NAME, ack, consumer);    
          
        //设置最大服务转发消息数量    只有在消费者空闲的时候会发送下一条信息。 (即该消费者处理的最大吞吐量) 
        int prefetchCount = 10;  
        channel.basicQos(prefetchCount);  
          
        while (true)    
        {    
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();    
            String message = new String(delivery.getBody());    
            System.out.println(sf.format(new Date())+" Received Message:'" + message + "'");    
            //发送应答    
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),true);    
        }    
    
    }
    
    public static void produce()  throws Exception{
        Connection connection = factory.newConnection(); 
        Channel channel = connection.createChannel();  	  

       //发送的消息  
        String message = "hello world!";  
        channel.basicPublish(EXCHANGE_NAME, ROUTINGKEY, null, message.getBytes());    
        
        System.out.println(sf.format(new Date())+" send message: "+ message);
        //关闭连接与信道
        channel.close();  
        connection.close();  
    }
}

Topic

package producerAndconsume;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

/**
 * Hello world!
 *
 */
public class TopicMQ 
{  
    //交换器名称  
    public  final static String EXCHANGE_NAME = "test.amq.topic";  
    //队列名称 1
    public  final static String QUEUE_NAME = "test.queue";  
    
    //RoutingKey one  
    public  final static String TOPIC_ROUTINGKEY_ONE = "test.routingkey.one";  
    //RoutingKey one  
    public  final static String TOPIC_ROUTINGKEY_TWO = "test.routingkey.two";  
    
    public static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    public static ConnectionFactory factory;
    
    static {
        /** 
         * 创建连接连接到MabbitMQ 
         */  
        factory = new ConnectionFactory();  
        //设置MabbitMQ所在主机ip或者主机名  
        factory.setHost("localhost"); 
        //指定用户 密码  
        factory.setUsername("zl");  
        factory.setPassword("123");  
        //指定端口  
        factory.setPort(AMQP.PROTOCOL.PORT); 
    }
    
    public static void init() throws Exception{
    	//创建一个连接  
        Connection connection = factory.newConnection(); 
        //创建一信道  
        Channel channel = connection.createChannel();  
        //指定一个队列  
        boolean durable = false; //设置消息持久化  RabbitMQ不允许使用不同的参数重新定义一个队列,所以已经存在的队列,我们无法修改其属性。
        
        //申明一个exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "topic", durable);
        
		/* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map arguments)     
		 * queue:队列名称
		 * durable:持久化(重启不丢失消息)
		 * exclusive:排他性,指针对此connection可见
		 * autoDelete:不使用时自动删除
		 * arguments:其他参数
		 */
        channel.queueDeclare(QUEUE_NAME, durable, false, false, null);  
        channel.close();
        connection.close();
    }
    
    public static void main(String[] argv) throws Exception {
    	init();
    	//生产者要比消费者先启动
    	new Thread(new  Runnable() {
			public void run() {
				try {
					comsume();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
    	
    	
    	TimeUnit.SECONDS.sleep(1);
    	
        produce(TOPIC_ROUTINGKEY_ONE);
        produce(TOPIC_ROUTINGKEY_TWO);
        
	/*      
	        result print
		2017-09-04 20:46:52 send message: test.routingkey.one
		2017-09-04 20:46:52 Received Message:'test.routingkey.one'
		2017-09-04 20:46:52 send message: test.routingkey.two
		2017-09-04 20:46:52 Received Message:'test.routingkey.two'
	        
	        *
	        */
        
    }  
    
    public static void comsume() throws Exception {
        Connection connection = factory.newConnection(); 
        Channel channel = connection.createChannel();  
        
/*       routingkey匹配规则
        *可以匹配一个标识符。
        #可以匹配0个或多个标识符。*/
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME, "test.routingkey.*");
          
        //创建队列消费者  
        QueueingConsumer consumer = new QueueingConsumer(channel);  
        //指定消费队列  
        boolean ack = false ; //打开应答机制   
        // 指定消费队列    
        channel.basicConsume(QUEUE_NAME, ack, consumer);    
          
        //设置最大服务转发消息数量    只有在消费者空闲的时候会发送下一条信息。 (即该消费者处理的最大吞吐量) 
        int prefetchCount = 10;  
        channel.basicQos(prefetchCount);  
          
        while (true)    
        {    
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();    
            String message = new String(delivery.getBody());    
            System.out.println(sf.format(new Date()) +" Received Message:'" + message + "'");    
            //发送应答    
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),true);    
        }    
    
    }
    
    public static void produce(String routingkey)  throws Exception{
        Connection connection = factory.newConnection(); 
        Channel channel = connection.createChannel();  	  

       //发送的消息  
        String message = routingkey;  
        channel.basicPublish(EXCHANGE_NAME, routingkey, null, message.getBytes());    
        
        System.out.println(sf.format(new Date()) +" send message: "+ message);
        //关闭连接与信道
        channel.close();  
        connection.close();  
    }
}

fanout

package producerAndconsume;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

/**
 * Hello world!
 *
 */
public class FanoutMQ 
{  
    //交换器名称  
    public  final static String EXCHANGE_NAME = "test.amq.fanout";  
    
    public static ConnectionFactory factory;
    
    public static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    static {
        /** 
         * 创建连接连接到MabbitMQ 
         */  
        factory = new ConnectionFactory();  
        //设置MabbitMQ所在主机ip或者主机名  
        factory.setHost("localhost"); 
        //指定用户 密码  
        factory.setUsername("zl");  
        factory.setPassword("123");  
        //指定端口  
        factory.setPort(AMQP.PROTOCOL.PORT); 
    }
    
    public static void init() throws Exception{
    	//创建一个连接  
        Connection connection = factory.newConnection(); 
        //创建一信道  
        Channel channel = connection.createChannel();  
        //指定一个队列  
        boolean durable = false; //设置消息持久化  RabbitMQ不允许使用不同的参数重新定义一个队列,所以已经存在的队列,我们无法修改其属性。
        
        //申明一个exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout", durable);
        
        channel.close();
        connection.close();
    }
    
    public static void main(String[] argv) throws Exception {
    	init();
    	//生产者要比消费者先启动
    	new Thread(new  Runnable() {
			public void run() {
				try {
					comsume();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
    	
    	TimeUnit.SECONDS.sleep(1);
    	
        produce();
        
/*    
 * 	   result print
		2017-09-04 21:03:16 send message: hello world!
		2017-09-04 21:03:16 amq.gen-y1Apmus-2O0LGRUi8Du_NA Received Message:'hello world!'
        */
    }  
    
    public static void comsume() throws Exception {
        Connection connection = factory.newConnection(); 
        Channel channel = connection.createChannel();  
        
        // 创建一个非持久的、唯一的且自动删除的队列  
        String queueName = channel.queueDeclare().getQueue();  
        
        channel.queueBind(queueName, EXCHANGE_NAME, "");
        //创建队列消费者  
        QueueingConsumer consumer = new QueueingConsumer(channel);  
        //指定消费队列  
        boolean ack = false ; //打开应答机制   
        
        // 指定消费队列
        channel.basicConsume(queueName, ack, consumer);    
          
        //设置最大服务转发消息数量    只有在消费者空闲的时候会发送下一条信息。 (即该消费者处理的最大吞吐量) 
        int prefetchCount = 10;  
        channel.basicQos(prefetchCount);  
          
        while (true)    
        {    
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();    
            String message = new String(delivery.getBody());    
            System.out.println(sf.format(new Date())+" "+ queueName+" Received Message:'" + message + "'");    
            //发送应答    
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),true);    
        }    
    
    }
    
    public static void produce()  throws Exception{
        Connection connection = factory.newConnection(); 
        Channel channel = connection.createChannel();  	  

       //发送的消息  
        String message = "hello world!";  
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());    
        
        System.out.println(sf.format(new Date())+" send message: "+ message);
        //关闭连接与信道
        channel.close();  
        connection.close();  
    }
}

RPC

package producerAndconsume;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class RpcMQ 

{  
	private static final String RPC_QUEUE_NAME = "rpc_queue";  
    public static ConnectionFactory factory;
    
    public static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    static {
        /** 
         * 创建连接连接到MabbitMQ 
         */  
        factory = new ConnectionFactory();  
        //设置MabbitMQ所在主机ip或者主机名  
        factory.setHost("localhost"); 
        //指定用户 密码  
        factory.setUsername("zl");  
        factory.setPassword("123");  
        //指定端口  
        factory.setPort(AMQP.PROTOCOL.PORT); 
    }
    
    public static void main(String[] argv) throws Exception {
    	server();
    	
    	for (int i = 0; i < 2; i++) {
    		RPCClient fibonacciRpc =new RpcMQ().new RPCClient();
    		System.out.println(" [x] Requesting check hello "+i);
    		String response = fibonacciRpc.call("hello "+i);
    		System.out.println(" [.] Got '" + response + "'");
    		fibonacciRpc.close();
		}
    	
/*    	 result print
		 [x] Awaiting RPC requests
		 [x] Requesting check hello 0
		 [.] Got 'check 'hello 0' success'
		 [x] Requesting check hello 1
		 [.] Got 'check 'hello 1' success'
    	 */
    	
    }  
    
    public static void server()  throws Exception{
        Connection connection = factory.newConnection();  
        final Channel channel = connection.createChannel();  
        channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null); 
        
        //可以运行多个服务器进程。通过channel.basicQos设置prefetchCount属性可将负载平均分配到多台服务器上。  
        channel.basicQos(10);  
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                AMQP.BasicProperties replyProps = new AMQP.BasicProperties
                        .Builder()
                        .correlationId(properties.getCorrelationId())
                        .build();
                String response = "";
                try {
                    String message = new String(body,"UTF-8");
                    response += "check '"+message+"' success";
                    System.out.println(sf.format(new Date())+" get message " + message+" and return "+response);
                }
                catch (RuntimeException e){
                    System.out.println("get exception: " + e.toString());
                }
                finally {
                    channel.basicPublish( "", properties.getReplyTo(), replyProps, response.getBytes("UTF-8"));
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };  
        //打开应答机制autoAck=false  
        channel.basicConsume(RPC_QUEUE_NAME, false, consumer);  
        System.out.println(" [x] Awaiting RPC requests");  
    }
    
    class RPCClient {
    	private Connection connection;
    	private Channel channel;
    	private String replyQueueName;

    	public RPCClient() throws Exception {
    		connection = factory.newConnection();
    		channel = connection.createChannel();
    		// 注册'回调'队列,这样就可以收到RPC响应
    		replyQueueName = channel.queueDeclare().getQueue();
    	}

    	// 发送RPC请求
    	public String call(String message) throws Exception {
    		final String corrId = UUID.randomUUID().toString();

    		AMQP.BasicProperties props = new AMQP.BasicProperties.Builder().correlationId(corrId).replyTo(replyQueueName).build();

    		channel.basicPublish("", RPC_QUEUE_NAME, props, message.getBytes("UTF-8"));

    		final BlockingQueue response = new ArrayBlockingQueue(1);

    		channel.basicConsume(replyQueueName, true, new DefaultConsumer(channel) {
    			@Override
    			public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    				if (properties.getCorrelationId().equals(corrId)) {
    					response.offer(new String(body, "UTF-8"));
    				}
    			}
    		});
    		return response.take();
    	}

    	public void close() throws Exception {
    		connection.close();
    	}
    }
}


 

你可能感兴趣的:(mq)