Publisher : 生产者
Queue: 存储消息的容器队列;
Consumer:消费者
Connection:消费者与消息服务的TCP连接
Channel:信道,是TCP里面的虚拟连接。例如:电缆相当于TCP,信道是一条独立光纤束,一条TCP连接上创建多少条信道是没有限制的。TCP一旦打开,就会出AMQP信道。无论是发布消息,接收消息,订阅队列,这些动作都是通过信道完成的。
Broker: 一台消息服务就是一个Broker;
Exchange:交换机、负责接收生产者的消息,转发到队列中、交换机和队列通过路由键绑定、可以理解为每个队列都有自己的名称;
public Queue(String name) {
// 队列名称, 是否持久化,是否独占, 是否自动删除
this(name, true, false, false);
}
@RabbitListener(bindings=@QueueBinding(
value= @Queue(value="${mq.config.queue.info}",autoDelete="true"),
exchange=@Exchange(value="${mq.config.exchange}",type=ExchangeTypes.DIRECT),
key="${mq.config.queue.info.routing.key}"
)
)
用来标记消费者;exchange表示交换器信息、类型;bindings表示监听器要绑定的队列、以及队列信息;
key:代表交换机和队列通过key绑定的;
先安装RabbitMQ,创建SpringBoot项目,修改配置
# 应用名称
spring.application.name=boolfilter
# 应用服务 WEB 访问端口
server.port=8080
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
public class Tut1Sender {
@Autowired
private RabbitTemplate template;
@Autowired
private Queue queue;
@Scheduled(fixedDelay = 1000, initialDelay = 500)
public void send() {
String message = "Hello World!";
this.template.convertAndSend(queue.getName(), message);
System.out.println(" [x] Sent '" + message + "'");
}
}
消费者:
@RabbitListener(queues = "hello")
public class Tut1Receiver {
@RabbitHandler
public void receive(String in) {
System.out.println(" [x] Received '" + in + "'");
}
}
将生产者、消费者注入容器;
@Configuration
@EnableScheduling
public class Tut1Config {
@Bean
public Queue hello() {
return new Queue("hello");
}
@Bean
public Tut1Receiver receiver() {
return new Tut1Receiver();
}
@Bean
public Tut1Sender sender() {
return new Tut1Sender();
}
}
运行结果:
[x] Sent ‘Hello World!’
[x] Received ‘Hello World!’
[x] Sent ‘Hello World!’
[x] Received ‘Hello World!’
[x] Sent ‘Hello World!’
…
主要思想是避免 立即执行资源密集型任务,必须等待它要完成。相反,我们将任务安排在以后完成。我们将任务封装为消息并将其发送到队列。正在运行的工作进程 在后台将弹出任务并最终执行工作
生产者:
public class Tut2Sender {
@Autowired
private RabbitTemplate template;
@Autowired
private Queue queue;
AtomicInteger dots = new AtomicInteger(0);
AtomicInteger count = new AtomicInteger(0);
@Scheduled(fixedDelay = 1000, initialDelay = 500)
public void send() {`在这里插入代码片`
StringBuilder builder = new StringBuilder("Hello");
if (dots.incrementAndGet() == 4) {
dots.set(1);
}
for (int i = 0; i < dots.get(); i++) {
builder.append('.');
}
builder.append(count.incrementAndGet());
String message = builder.toString();
template.convertAndSend(queue.getName(), message);
System.out.println(" [x] Sent '" + message + "'");
}
}
消费者:
@RabbitListener(queues = "hello")
public class Tut2Receiver {
private final int instance;
public Tut2Receiver(int i) {
this.instance = i;
}
@RabbitHandler
public void receive(String in) throws InterruptedException {
StopWatch watch = new StopWatch();
watch.start();
System.out.println("instance " + this.instance +
" [x] Received '" + in + "'");
doWork(in);
watch.stop();
System.out.println("instance " + this.instance +
" [x] Done in " + watch.getTotalTimeSeconds() + "s");
}
private void doWork(String in) throws InterruptedException {
for (char ch : in.toCharArray()) {
if (ch == '.') {
Thread.sleep(1000);
}
}
}
}
队列、生产者、消费者注入容器:
@Configuration
public class Tut2Config {
@Bean
public Queue hello() {
return new Queue("hello");
}
private static class ReceiverConfig {
@Bean
public Tut2Receiver receiver1() {
return new Tut2Receiver(1);
}
@Bean
public Tut2Receiver receiver2() {
return new Tut2Receiver(2);
}
}
@Bean
public Tut2Sender sender() {
return new Tut2Sender();
}
}
运行结果:
[x] Sent ‘Hello.1’
instance 1 [x] Received ‘Hello.1’
[x] Sent ‘Hello…2’
instance 2 [x] Received ‘Hello…2’
instance 1 [x] Done in 1.0062309s
[x] Sent ‘Hello…3’
instance 1 [x] Received ‘Hello…3’
instance 2 [x] Done in 2.0085791s
[x] Sent ‘Hello.4’
instance 2 [x] Received ‘Hello.4’
…
异常处理方案:
spring.rabbitmq.listener.retry.max-attempts=5 //重试超过5次,消息丢弃;
默认情况下,RabbitMQ 会将每条消息发送给下一个消费者。平均而言,每个消费者将获得相同数量的 消息。这种分发消息的方式称为轮询。 在这种模式下,调度不一定完全按照我们想要的方式工作。 若是存在两台机器,一台性能好、一台性能差, 而RabbitMQ对此一无所知,仍然会调度 消息均匀。发生这种情况是因为 RabbitMQ 只是在消息时调度消息 进入队列。它不看未确认的数量 面向消费者的消息。它只是盲目地发送每 n 条消息 给第 n 个消费者,这就导致了一台机器特别忙碌、一台机器空闲;
“公平调度”是Spring AMQP的默认配置。Consumer可以向服务器声明一个prefetchCount, 表示轮到自己时、自己可处理多少消息;这样RabbitMQ转发消息给消费者时、会先看Consumer正在处理的消息数量是否达到了prefetchCount, 若已达到该值,则发给其他的Consumer;
特点:一条消息同时会被所有消费者消息;X是交换机(Exchange);交换机和队列进行绑定(Binding)
交换机负责接收生产者发送的消息,再转发消息到队列中;实现了生产者与队列的解耦;
RabbitMQ 中消息传递模型的核心思想是生产者 从不将任何消息直接发送到队列
发送者:
public class Tut3Sender {
@Autowired
private RabbitTemplate template;
@Autowired
private FanoutExchange fanout;
AtomicInteger dots = new AtomicInteger(0);
AtomicInteger count = new AtomicInteger(0);
@Scheduled(fixedDelay = 1000, initialDelay = 500)
public void send() {
StringBuilder builder = new StringBuilder("Hello");
if (dots.getAndIncrement() == 3) {
dots.set(1);
}
for (int i = 0; i < dots.get(); i++) {
builder.append('.');
}
builder.append(count.incrementAndGet());
String message = builder.toString();
template.convertAndSend(fanout.getName(), "", message);
System.out.println(" [x] Sent '" + message + "'");
}
}
消费者:
public class Tut3Receiver {
@RabbitListener(queues = "#{autoDeleteQueue1.name}")
public void receive1(String in) throws InterruptedException {
receive(in, 1);
}
@RabbitListener(queues = "#{autoDeleteQueue2.name}")
public void receive2(String in) throws InterruptedException {
receive(in, 2);
}
public void receive(String in, int receiver) throws InterruptedException {
StopWatch watch = new StopWatch();
watch.start();
System.out.println("instance " + receiver + " [x] Received '" + in + "'");
doWork(in);
watch.stop();
System.out.println("instance " + receiver + " [x] Done in "
+ watch.getTotalTimeSeconds() + "s");
}
private void doWork(String in) throws InterruptedException {
for (char ch : in.toCharArray()) {
if (ch == '.') {
Thread.sleep(1000);
}
}
}
}
交换机、匿名队列、绑定,生产者、消费者注入容器;
public class Tut3Config {
@Bean
public FanoutExchange fanout() {
return new FanoutExchange("tut.fanout");
}
private static class ReceiverConfig {
@Bean
public Queue autoDeleteQueue1() {
return new AnonymousQueue();
}
@Bean
public Queue autoDeleteQueue2() {
return new AnonymousQueue();
}
@Bean
public Binding binding1(FanoutExchange fanout,
Queue autoDeleteQueue1) {
return BindingBuilder.bind(autoDeleteQueue1).to(fanout);
}
@Bean
public Binding binding2(FanoutExchange fanout,
Queue autoDeleteQueue2) {
return BindingBuilder.bind(autoDeleteQueue2).to(fanout);
}
@Bean
public Tut3Receiver receiver() {
return new Tut3Receiver();
}
}
@Bean
public Tut3Sender sender() {
return new Tut3Sender();
}
}
运行结果:
instance 1 [x] Received 'Hello.1'
instance 2 [x] Received 'Hello.1'
instance 2 [x] Done in 1.0057994s
instance 1 [x] Done in 1.0058073s
....
通常情况下,业务开发中,经常会监听该事件做扩展,例如初始化数据, 打印日志等等;
生产者:
public class AppContextSender {
@Autowired
RabbitTemplate rabbitTemplate;
@Scheduled(fixedDelay = 1000, initialDelay = 500)
public void publishContextRefreshEvent() {
rabbitTemplate.convertAndSend("contextRefreshedExchange", "", "publish refreshed event");
}
}
消费者:
@RabbitListener(queues = {"initQueue"})
public class InitContextRefreshedConsumer {
@RabbitHandler
public void consum(String in) {
System.out.println("init :"+in);
}
}
@RabbitListener(queues = "logQueue")
public class LogContextRefreshedConsumer {
@RabbitHandler
public void consum(String in) {
System.out.println("log : "+in);
}
}
交换机、队列、绑定、生产者、消费者注入容器:
@Configuration
public class ContextRefreshedConfig {
@Bean
public FanoutExchange contextRefreshedExchange(){
return new FanoutExchange("contextRefreshedExchange");
}
@Bean
public AppContextSender appContextSender() {
return new AppContextSender();
}
public static class ConsumerConfig {
@Bean
public Queue initQueue() {
return new Queue("initQueue");
}
@Bean
public Queue logQueue() {
return new Queue("logQueue");
}
@Bean
public Binding initBinding(Queue initQueue, FanoutExchange contextRefreshedExchange) {
return BindingBuilder.bind(initQueue).to(contextRefreshedExchange);
}
@Bean
public Binding logBinding(Queue logQueue, FanoutExchange contextRefreshedExchange) {
return BindingBuilder.bind(logQueue).to(contextRefreshedExchange);
}
@Bean
public InitContextRefreshedConsumer initContextRefreshedConsumer() {
return new InitContextRefreshedConsumer();
}
@Bean
public LogContextRefreshedConsumer logContextRefreshedConsumer() {
return new LogContextRefreshedConsumer();
}
}
}
log : publish refreshed event
init :publish refreshed event
log : publish refreshed event
init :publish refreshed event
…
public class BaseServiceSender {
@Autowired
private RabbitTemplate template;
@Autowired
private DirectExchange messageExchange;
AtomicInteger index = new AtomicInteger(0);
AtomicInteger count = new AtomicInteger(0);
private final String[] keys = {"sms", "mail"};
@Scheduled(fixedDelay = 1000, initialDelay = 500)
public void send() {
//短信
String sms = "{userName: xxx; phone:xxx}";
template.convertAndSend(messageExchange.getName(), "sms", sms);
//邮件
String mail = "{userName: xxx; mail:xxx}";
template.convertAndSend(messageExchange.getName(), "mail", mail);
}
}
消费者:
@RabbitListener(queues = "mailQueue")
public class MailConsumer {
@RabbitHandler
public void consum(String in) {
System.out.println("send mail : " + in);
}
}
@RabbitListener(queues = "smsQueue")
public class SmsConsumer {
@RabbitHandler
public void consum(String in) {
System.out.println("send sms : " + in);
}
}
交换机、队列,绑定、消费者,生产者注入容器:
@Configuration
public class DirectConfig {
@Bean
public DirectExchange messageExchange() {
return new DirectExchange("messageExchange");
}
@Bean
public BaseServiceSender baseServiceSender() {
return new BaseServiceSender();
}
public static class ConsumerGroup {
@Bean
public MailConsumer mailConsumer() {
return new MailConsumer();
}
@Bean
public SmsConsumer smsConsumer() {
return new SmsConsumer();
}
@Bean
public Queue mailQueue() {
return new Queue("mailQueue");
}
@Bean
public Queue smsQueue() {
return new Queue("smsQueue");
}
@Bean
public Binding smsBinding(DirectExchange messageExchange, Queue smsQueue){
return BindingBuilder.bind(smsQueue).to(messageExchange).with("sms");
}
@Bean
public Binding mailBinding(DirectExchange messageExchange, Queue mailQueue){
return BindingBuilder.bind(mailQueue).to(messageExchange).with("mail");
}
}
}
运行结果
send mail : {userName: xxx; mail:xxx}
send sms : {userName: xxx; phone:xxx}
send sms : {userName: xxx; phone:xxx}
send mail : {userName: xxx; mail:xxx}
…
若是消息指定的路由键为"xxx.orange.xxx", 则会匹配到Q1, 若是"lazy.xxx.xx"则是Q2;
生产者:
public class Tut5Sender {
@Autowired
private RabbitTemplate template;
@Autowired
private TopicExchange topic;
AtomicInteger index = new AtomicInteger(0);
AtomicInteger count = new AtomicInteger(0);
private final String[] keys = {"quick.orange.rabbit", "lazy.orange.elephant", "quick.orange.fox",
"lazy.brown.fox", "lazy.pink.rabbit", "quick.brown.fox"};
@Scheduled(fixedDelay = 1000, initialDelay = 500)
public void send() {
StringBuilder builder = new StringBuilder("Hello to ");
if (this.index.incrementAndGet() == keys.length) {
this.index.set(0);
}
String key = keys[this.index.get()];
builder.append(key).append(' ');
builder.append(this.count.incrementAndGet());
String message = builder.toString();
template.convertAndSend(topic.getName(), key, message);
System.out.println(" [x] Sent '" + message + "'");
}
}
消费者:
public class Tut5Receiver {
@RabbitListener(queues = "#{autoDeleteQueue1.name}")
public void receive1(String in) throws InterruptedException {
receive(in, 1);
}
@RabbitListener(queues = "#{autoDeleteQueue2.name}")
public void receive2(String in) throws InterruptedException {
receive(in, 2);
}
public void receive(String in, int receiver) throws
InterruptedException {
StopWatch watch = new StopWatch();
watch.start();
System.out.println("instance " + receiver + " [x] Received '"
+ in + "'");
doWork(in);
watch.stop();
System.out.println("instance " + receiver + " [x] Done in "
+ watch.getTotalTimeSeconds() + "s");
}
private void doWork(String in) throws InterruptedException {
for (char ch : in.toCharArray()) {
if (ch == '.') {
Thread.sleep(1000);
}
}
}
}
交换器,队列,绑定、生产者,消费者注入容器:
@Configuration
public class Tut5Config {
@Bean
public TopicExchange topic() {
return new TopicExchange("tut.topic");
}
private static class ReceiverConfig {
@Bean
public Tut5Receiver receiver() {
return new Tut5Receiver();
}
@Bean
public Queue autoDeleteQueue1() {
return new AnonymousQueue();
}
@Bean
public Queue autoDeleteQueue2() {
return new AnonymousQueue();
}
@Bean
public Binding binding1a(TopicExchange topic,
Queue autoDeleteQueue1) {
return BindingBuilder.bind(autoDeleteQueue1)
.to(topic)
.with("*.orange.*");
}
@Bean
public Binding binding1b(TopicExchange topic,
Queue autoDeleteQueue1) {
return BindingBuilder.bind(autoDeleteQueue1)
.to(topic)
.with("*.*.rabbit");
}
@Bean
public Binding binding2a(TopicExchange topic,
Queue autoDeleteQueue2) {
return BindingBuilder.bind(autoDeleteQueue2)
.to(topic)
.with("lazy.#");
}
}
@Bean
public Tut5Sender sender() {
return new Tut5Sender();
}
}
运行结果:
[x] Sent ‘Hello to lazy.orange.elephant 1’
instance 2 [x] Received ‘Hello to lazy.orange.elephant 1’
instance 1 [x] Received ‘Hello to lazy.orange.elephant 1’
[x] Sent ‘Hello to quick.orange.fox 2’
[x] Sent ‘Hello to lazy.brown.fox 3’
instance 1 [x] Done in 2.0110456s
…
RabbitMQ也实现了RPC的功能,但是业务开发中,根本没有使用场景,RPC要么使用Dubbo, 要么使用OpenFeign, 使用RabbitMQ做RPC的信息,目前都没有看到;