Rabbitmq+Spring boot

Rabbitmq加Spring boot

依赖:

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

配置,写的是jvm中rabbit的地址和自己设置rabbit账户

spring:
  rabbitmq:
    host: 192.168.120.150
    username: admin
    password: admin
    # virtual-host: 自己的空间

Spring提供的Queue类,是队列的封装对象,它封装了队列的参数信息.

RabbitMQ的自动配置类,会发现这些Queue实例,并在RabbitMQ服务器中定义这些队列.

创建一个容器管理的队列,斜面都系要,只是名字不同

	/*创建 spring 的 Queue 对象,用来封装队列的参数,
	RabbitAutoConfiguration 自动配置类,会自动发现 Queue 实例,
	使用这里的参数,连接服务器在服务器上创建队列
	 */
	@Bean
	public Queue task_queue() {
		/*
		 * 可用以下形式: (默认)
		 * new Queue("helloworld") - 持久,非排他,非自动删除
		 * new Queue("helloworld",ture,false,false,null)
		 */
		return new Queue("helloworld");
	}

第一、简单模式

qizgogn AmqpTemplate是rabbitmq客户端API的一个封装工具,由自动配置类自动创建

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Product {
	// spring提供的用来发送消息的工具对象
	//在 RabbitAutoConfiguration中创建

	@Autowired
	AmqpTemplate t;
	
	public void send() {
		// 这里向 helloworld 队列发送消息
		//如果说是对象的话,需要将对象序列化(byte[])
		t.convertAndSend("helloworld", "Hello world!! "+System.currentTimeMillis());
		System.out.println("消息已发送");
	}
}

2.通过@RabbitListener从指定的队列接收消息,使用@RebbitHandler处理消息

方式一

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = "helloworld")
public class Consumer {
	@RabbitHandler
	public void receive(String msg) {
		System.out.println("收到: "+msg);
	}
}

方式二、同时也可以只使用@RebbitHandler直接接收和处理消息

@Component
public class Consumer {
	@RabbitListener(queues = "helloworld")
	public void receive(String msg) {
		System.out.println("收到: "+msg);
	}
}

@RabbitListener也能直接定义队列

	@RabbitListener(queuesToDeclare = @Queue(name = "helloworld",durable = "false"))

测试,工作模式和ASK可用
试一试代码效果

import java.util.Scanner;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ProductTests {
	@Autowired
	Product p ;

	@Test
	void test1() throws Exception {
		p.send();
		System.out.println("[按回车结束]");
		new Scanner(System.in).nextLine();
	}
}

或者在启动类测试哦

 // 添加测试代码,调用生产者发送消息
    @Autowired
    private Producer p;
    /*
      spring 的执行流程:
      包扫描创建所有实例 --> 完成所有的依赖注入 --> @PostConstruct --> 后续流程自动配置类,消费者.....
     */
    @PostConstruct
    public void test() {
        // new Thread(new Runnable() {
        //     @Override
        //     public void run() {
        //         p.send();
        //     }
        // }).start();

        // lambda表达式,是匿名内部类的简化语法
        new Thread(() -> p.send()).start();
    }

第二、工作模式

现在需要创建持久队列
因为上面使用过名为helloworld的队列,如果还需要使用该队列名,只能把注册的队列删除之后再创建
springboot中queue默认是,持久,非独占,非自动删除

@Component
public class Product {
	@Autowired
	AmqpTemplate t;
	
	public void send() {
		while (true) {
			System.out.print("输入:");
			String s = new Scanner(System.in).nextLine();
			
			//spring 默认将消息的 DeliveryMode 设置为 PERSISTENT 持久化,
			t.convertAndSend("task_queue", s);
			// t.convertAndSend( "task_queue",s,消息预处理器,可以重新设置消息的属性);
		}
	}
}

如果需要将发送非持久化消息,
则使用 MessagePostProcessor 前置处理器参数,在属性中把 DeliveryMode 设置为非持久化

	//如果需要设置消息为非持久化,可以取得消息的属性对象,修改它的deliveryMode属性
	t.convertAndSend("task_queue", (Object) s, new MessagePostProcessor() {
		@Override
		public Message postProcessMessage(Message message) throws AmqpException {
			MessageProperties props = message.getMessageProperties();
			props.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
			return message;
		}
	});

设置两个消费者接受消息

@Component
public class Consumer {
	@RabbitListener(queues="task_queue")
	public void receive1(String s) throws Exception {
		System.out.println("receiver1 - 收到: "+s);
		
	}
	
	@RabbitListener(queues="task_queue")
	public void receive2(String s) throws Exception {
		System.out.println("receiver2 - 收到: "+s);
		
		
	}

第三、合理分发ack模式,懂?哈哈,另外一篇简单测试有介绍

ACK\QOS

ack-- spring封装的rabbitmq,已经是手动ack模式,
spring会自动发送回执
qos=1 – spring默认设置的qos是250,
yml中可以改变

配置一下

spring:
  rabbitmq:
    listener:
      simple:
        # acknowledgeMode: NONE # rabbitmq的自动确认
        acknowledgeMode: AUTO # rabbitmq的手动确认, springboot会自动发送确认回执 (默认)
        # acknowledgeMode: MANUAL # rabbitmq的手动确认, springboot不发送回执, 必须自己编码发送回执

抓取数量的话,还要配置
qos 设置成 1, 每次只接收一条消息, 处理完成后才接收下一条消息

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1 # qos=1, 默认250

手动执行确认操作
MANUAL 模式,必须手动执行确认操作

	@RabbitListener(queues="task_queue")
	public void receive1(String s, Channel c, @Header(name=AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {
		System.out.println("receiver1 - 收到: "+s);
		// 手动发送确认回执
		c.basicAck(tag, false);
	}

第四、发布和订阅模式(广播模式)

创建的是Fanout交换机

import org.springframework.amqp.core.FanoutExchange;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;


	@Bean
	public FanoutExchange fanoutExchange() {
		return new FanoutExchange("logs");
	}

发送消息

import java.util.Scanner;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Publisher {
	@Autowired
	AmqpTemplate t;
	public void send() {
		while (true) {
			System.out.print("输入:");
			String s = new Scanner(System.in).nextLine();
			// 指定向 logs 交换机发送, 不指定队列名或路由键
			//对于fanout交换机来说,""时无效的
			t.convertAndSend("logs","",s);
		}
	}
}

接收的消费者

package cn.tedu.m3;

import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class Subscriber {
	@RabbitListener(bindings = @QueueBinding( //这里进行绑定设置
	value = @Queue, //这里定义随机队列,默认属性: 随机命名,非持久,排他,自动删除
	exchange = @Exchange(name = "logs", declare = "false") //指定 logs 交换机,因为主程序中已经定义,这里不进行定义
))
	public void receive1(String s) throws Exception {
		System.out.println("receiver1 - 收到: "+s);
	}
	@RabbitListener(bindings = @QueueBinding(value = @Queue, exchange = @Exchange(name = "logs", declare = "false")))
	public void receive2(String s) throws Exception {
		System.out.println("receiver2 - 收到: "+s);
	}
}

测试吗

@Test
	void test1() throws Exception {
		publisher.send();
	}

路由模式

使用 direct 交换机

@Bean
	public DirectExchange fanoutExchange() {
		return new DirectExchange("direct_logs");
	}
@Component
public class RouteProduct {
	@Autowired
	AmqpTemplate t;
	
	public void send() {
		while (true) {
			System.out.print("输入消息:");
			String s = new Scanner(System.in).nextLine();
			System.out.print("输入路由键:");
			String key = new Scanner(System.in).nextLine();
			// 第二个参数指定路由键
			t.convertAndSend("direct_logs",key,s);
		}
	}
}

@Component
public class RouteReceiver {
	@RabbitListener(bindings = @QueueBinding( // 这里做绑定设置
	value = @Queue, // 定义队列, 随机命名,非持久,排他,自动删除
	exchange = @Exchange(name = "direct_logs", declare = "false"), // 指定绑定的交换机,主程序中已经定义过队列,这里不进行定义
	key = {"error","info","warning"} // 设置绑定键
))

	public void receive1(String s) throws Exception {
		System.out.println("receiver1 - 收到: "+s);
	}
	@RabbitListener(bindings = @QueueBinding(value = @Queue, exchange = @Exchange(name = "direct_logs", declare = "false"),key = {"error","info","warning"}))
	public void receive2(String s) throws Exception {
		System.out.println("receiver2 - 收到: "+s);
	}
}

主题模式

使用 topic 交换机

@Bean
	public TopicExchange fanoutExchange() {
		return new TopicExchange("topic_logs");
	}
@Component
public class TopicSender {
	@Autowired
	AmqpTemplate t;
	
	public void send() {
		while (true) {
			System.out.print("输入消息:");
			String s = new Scanner(System.in).nextLine();
			System.out.print("输入路由键:");
			String key = new Scanner(System.in).nextLine();
			
			t.convertAndSend("topic_logs",key,s);
		}
	}
}
@Component
public class TopicReceiver {
	@RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(name = "topic_logs", declare = "false"),key = {"*.orange.*"}))
	public void receive1(String s) throws Exception {
		System.out.println("receiver1 - 收到: "+s);
	}
	@RabbitListener(bindings = @QueueBinding(value = @Queue, exchange = @Exchange(name = "topic_logs", declare = "false"),key = {"*.*.rabbit","lazy.#"}))
	public void receive2(String s) throws Exception {
		System.out.println("receiver2 - 收到: "+s);
	}
}

最麻烦最难的 RPC异步调用

需要定义两个队列
生产者发送到调用队列,在发送到消息上需要携带两个数据(1.返回的队列名2关联id标识)
关联i的话,是一个生产者发出多条消息时,后端负载均衡处理,返回时没有标识将不知道是第几次发出的响应消息。也可以设置每条消息发出将有一个新的返回队列,但这样太消耗内存
在这里插入图片描述
可以负载均衡方式接受处理队列消息
在这里插入图片描述
创建两个队列


@Bean
	public Queue sendQueue() {
		return new Queue("rpc_queue",false);
	}
	@Bean
	public Queue rndQueue() {
		return new Queue(UUID.randomUUID().toString(), false);
	}
	

处理:
@Rabbitlistener注解对于具有返回值的方法:
会自动获取 replyTo 属性
自动获取 correlationId 属性
向 replyTo 属性指定的队列发送计算结果, 并携带 correlationId 属性

@Component
public class Server {
    /*
    如果处理消息的方法不是 void,有返回值,
    那么 spring 会把返回值,通过返回队列发回到客户端,并携带关联id
     */
    @RabbitListener(queues = "rpc-queue")
    public long receive(int n) {
        long r = f(n);
        return r;
    }

    private long f(int n) {
        long a = 1;
        long b = 1;
        for (int i = 3; i <= n; i++) {
            b = a+b;
            a = b-a;
        }
        return b;
    }
}
 /* 
    SPEL --- Spring Expression Language
    直接访问 spring 容器中的对象
    #{rndQueue.name}

    OGNL --- Object Graph Navigation Language
    Struts2 中的一种标记
    ${}
     */
@Component
public class Client {
    @Autowired
    private AmqpTemplate t;

    @Value("#{rndQueue.name}")
    private String replyTo;

    public void send(int n) {
        t.convertAndSend("rpc-queue", n, (message) -> {
            MessageProperties p = message.getMessageProperties();
            p.setReplyTo(replyTo);
            p.setCorrelationId(UUID.randomUUID().toString());
            return message;
        });
    }

    @RabbitListener(queues = "#{rndQueue.name}")
    public void receive(long r, @Header(name = AmqpHeaders.CORRELATION_ID) String cid) {
        System.out.println(cid + "--- 结果:"+r);
    }
}

更精确的RPC模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值