package net.jueb.util4j.test.testRabbitMq;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.DeliverCallback;
import com.rabbitmq.client.Envelope;

/**
 * 通配符模式。
 * 一个消费者将消息首先发送到交换器,交换器绑定到多个队列,然后被监听该队列的消费者所接收并消费。
 * 交换器主要有四种类型:direct(路由)、fanout(广播)、topic、headers
 * 设置模糊的绑定方式,“*”操作符将“.”视为分隔符,匹配单个字符;“#”操作符没有分块的概念,它将任意“.”均视为关键字的匹配部分,能够匹配多个字符。
 * 如果交换机下面有相同通配符的队列,则数据会复制到这些队列中
 * 如果一个队列下面有2个消费者,那么这个队列的消息会被消费者分摊消费
 */
public class ProducerExchangeConsumer_Topic {
	
	private final static String EXCHANGE_NAME = "topic_exchange";
	private final static String QUEUE_NAME1 = "topic_queue1";
	private final static String QUEUE_NAME2 = "topic_queue2";
	private final static String QUEUE_NAME3 = "topic_queue3";
    
    public static ExecutorService es=Executors.newCachedThreadPool();
    
    public static void main(String[] args) throws Exception{
    	ProducerExchangeConsumer_Topic pr=new ProducerExchangeConsumer_Topic();
    	es.submit(()->{try {
			pr.producer();
		} catch (Exception e) {
			e.printStackTrace();
		}});
    	es.submit(()->{try {
			pr.consumer1A();
		} catch (Exception e) {
			e.printStackTrace();
		}});
    	es.submit(()->{try {
			pr.consumer1B();
		} catch (Exception e) {
			e.printStackTrace();
		}});
    	es.submit(()->{try {
			pr.consumer2();
		} catch (Exception e) {
			e.printStackTrace();
		}});
    	es.submit(()->{try {
    		pr.consumer3();
    	} catch (Exception e) {
    		e.printStackTrace();
    	}});
    }
    
    public void producer() throws Exception{
    	 //1、获取连接
        Connection connection = RabbitMqConnectionFactoy.getConnection();
        //2、声明信道
        Channel channel = connection.createChannel();
        //3、声明(创建)交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        Thread.sleep(3000);
        //4、定义消息内容(发布多条消息)
        for(int i = 0 ; i < 10 ; i++){
            String message = "hello rabbitmq "+i;
            //定义消息头
            Map<String,Object> header=new HashMap<>();
            header.put("i",i);
            BasicProperties bp=new BasicProperties.Builder().headers(header).build();
            //5、发布消息
            if(i%2!=0)
            {//单数进key1的队列
            	 channel.basicPublish(EXCHANGE_NAME,"test.a",bp,message.getBytes());
            }else
            {//偶数进key2的队列
            	 channel.basicPublish(EXCHANGE_NAME,"test.b",bp,message.getBytes());
            }
            System.out.println("[x] Sent'"+message+"'");
            //模拟发送消息延时,便于演示多个消费者竞争接受消息
            Thread.sleep(i*10);
        }
        //6、关闭通道
        channel.close();
        //7、关闭连接
        connection.close();
    }
	
    public boolean autoAck=false;
    
    public void consumer1A() throws Exception {
		//1、获取连接
        Connection connection =RabbitMqConnectionFactoy.getConnection();
        //2、声明通道
        Channel channel = connection.createChannel();
        //3、声明队列
        channel.queueDeclare(QUEUE_NAME1, false, false, false, null);
        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME1, EXCHANGE_NAME,"test.a");//只收到基数
        //同一时刻服务器只会发送一条消息给消费者(如果设置为N,则当客户端堆积N条消息后服务端不会推送给客户端了)
        //channel.basicQos(1);//每次处理1个
        //4、定义队列的消费者
        //定义消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,BasicProperties properties, byte[] body)
                    throws IOException {
                //获取并转成String
                String message = new String(body, "UTF-8");
                System.out.println("-->消费者1A号,收到消息,msg :"+message+",header:"+properties.getHeaders().toString());
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(QUEUE_NAME1, autoAck,consumer);
	}
    
    public void consumer1B() throws Exception {
		//1、获取连接
        Connection connection =RabbitMqConnectionFactoy.getConnection();
        //2、声明通道
        Channel channel = connection.createChannel();
        //3、声明队列
        channel.queueDeclare(QUEUE_NAME1, false, false, false, null);
        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME1, EXCHANGE_NAME,"test.a");//只收到基数
        //同一时刻服务器只会发送一条消息给消费者(如果设置为N,则当客户端堆积N条消息后服务端不会推送给客户端了)
        //channel.basicQos(1);//每次处理1个
        //4、定义队列的消费者
        //定义消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,BasicProperties properties, byte[] body)
                    throws IOException {
                //获取并转成String
                String message = new String(body, "UTF-8");
                System.out.println("-->消费者1B号,收到消息,msg :"+message+",header:"+properties.getHeaders().toString());
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(QUEUE_NAME1, autoAck,consumer);
	}
    
    public void consumer2() throws Exception {
		//1、获取连接
        Connection connection =RabbitMqConnectionFactoy.getConnection();
        //2、声明通道
        Channel channel = connection.createChannel();
        //3、声明队列
        channel.queueDeclare(QUEUE_NAME2, false, false, false, null);
        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME2, EXCHANGE_NAME,"test.#");//基数偶数都接收
        //同一时刻服务器只会发送一条消息给消费者(如果设置为N,则当客户端堆积N条消息后服务端不会推送给客户端了)
        //channel.basicQos(1);//每次只从服务器取1个处理
        //4、定义队列的消费者
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("-->消费者2号,收到消息,msg :"+message+",header:"+delivery.getProperties().getHeaders().toString());
            channel.basicAck( delivery.getEnvelope().getDeliveryTag(), false);
        };
        channel.basicConsume(QUEUE_NAME2, autoAck, deliverCallback, consumerTag -> { });
	}	
    
    public void consumer3() throws Exception {
		//1、获取连接
        Connection connection =RabbitMqConnectionFactoy.getConnection();
        //2、声明通道
        Channel channel = connection.createChannel();
        //3、声明队列
        channel.queueDeclare(QUEUE_NAME3, false, false, false, null);
        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME3, EXCHANGE_NAME,"test.#");//基数偶数都接收
        //同一时刻服务器只会发送一条消息给消费者(如果设置为N,则当客户端堆积N条消息后服务端不会推送给客户端了)
        //channel.basicQos(1);//每次只从服务器取1个处理
        //4、定义队列的消费者
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("-->消费者3号,收到消息,msg :"+message+",header:"+delivery.getProperties().getHeaders().toString());
            channel.basicAck( delivery.getEnvelope().getDeliveryTag(), false);
        };
        channel.basicConsume(QUEUE_NAME3, autoAck, deliverCallback, consumerTag -> { });
	}
}