springboot-消息中间件:ActiveMQ和RabbitMQ


1 消息中间件介绍

1.两个重要的规范JMS和AMQP

JMS(Java Message Service),java通信的规范,统一java api层面的标准,规范写法。
AMQP,不是从api层面去规范的,是从协议的层面去规范发消息和解析消息,不管什么语言,只要遵守这个规范,就可以使用AMQP。比较有代表的是RabbitMQ。

2.什么是消息中间件

消息中间件是一种跨线程通信的方式

3.优点

能够很好地实现服务的调用者和消费者的解耦
发邮件是一个耗时的操作,是单线程,发完邮件后不可能等着发成功后响应,对于系统而言,有很多个模块都需要发邮件,可以把邮件服务抽取出来,单独做一个项目,从消息中间件中提取数据。哪个模块想发邮件,就往消息中间件发一条信息,另一个模块自动从消息中间件中读取信息发送邮件。
如果采用多线程的话,发邮件就跟服务绑定在了一起,不好扩展。

2 JMS之ActiveMQ

1.准备工作

1.ActiveMQ下载

ActiveMQ下载

2.启动

在bin目录下输入activemq start命令启动

F:\activemq\apache-activemq-5.16.2\bin>activemq start

3.访问网址

8161是管理客户端的端口号,61616是通信的端口号
http://localhost:8161/
默认用户名和密码都是:admin
在这里插入图片描述

2.springboot中使用ActiveMQ

1.创建springboot项目

依赖选择web和ActiveMQ5
在这里插入图片描述

2.在application.yml中配置相关参数

spring:
  activemq:
    broker-url: tcp://127.0.0.1:61616  # 61616是通信端口号
    packages:
      trust-all: true  # 信任所有的包
    user: admin  # 客户端的账户
    password: admin  # 客户端的密码

3.在springboot的main方法所在类定义一个消息队列

package com.ll;

import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import javax.jms.Queue;

@SpringBootApplication
public class JmsApplication {

	public static void main(String[] args) {
		SpringApplication.run(JmsApplication.class, args);
	}

	// 消息队列
	@Bean
	Queue queue(){
		return new ActiveMQQueue("ll-Queue");
	}

}

4.创建JMS的组件

package com.ll.config;

import com.ll.model.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Component;

import javax.jms.Queue;

// 在组件里实现消息的发送和接收
@Component
public class JmsComponent {
    // springboot已经帮我们做了自动化配置,所以在这里面直接注入消息发送模板
    @Autowired
    JmsMessagingTemplate jmsMessagingTemplate;
    @Autowired
    Queue queue;

    // 发信息
    public void sendMessage(Message message){
        // 发到哪里去queue,发送内容message
        jmsMessagingTemplate.convertAndSend(queue,message);
    }

    // 接收信息
    // JmsListener会时刻监听消息队列,一旦,队列里有消息就会打印出来
    @JmsListener(destination = "ll-Queue") // 确定该消息是接收信息的
    public void receive(Message message){
        System.out.println("msg:" +message);
    }
}

4.发送邮件

先运行一个项目
再运行一个发送邮件的单元测试,当发送邮件后,前一个项目的控制台会打印出信息

package com.ll;

import com.ll.config.JmsComponent;
import com.ll.model.Message;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;

@SpringBootTest
class JmsApplicationTests {
	@Autowired
	JmsComponent jmsComponent;

	@Test
	void contextLoads() {
		Message message = new Message();
		message.setContent("哈哈哈哈哈哈哈哈哈哈");
		message.setDate(new Date());
		jmsComponent.sendMessage(message);
	}
}

5.该例子的完整项目代码

jmsgithub链接

3 AMQP之RabbitMQ

1.准备工作

1.使用 Docker 安装 RabbitMQ

网址

2.在linux服务器上安装Docker

# 首先安装 Docker
yum -y install docker

# 然后启动 Docker 服务
service docker start

# 测试安装是否成功
docker -v

3.在安装Docker的linux上,安装RabbitMQ

查看仓库里的RabbitMQ
docker search rabbitmq

安装RabbitMQ
docker pull rabbitmq

启动RabbitMQ
docker run -d --hostname my-rabbit --name rabbit(名称) -p 15672:15672 -p 5672:5672 rabbitmq:3-management

先执行docker ps 拿到当前的镜像ID
进入容器
安装插件
ctrl+p+q退出当前容器
docker ps 
docker start 镜像ID
docker exec -it 镜像ID /bin/bash
rabbitmq-plugins enable rabbitmq_management  管理页面
docker启动       
    systemctl start docker  centos7.x
    service docker start  centos6.x
重启docker服务
    systemctl restart  docker  centos7.x
    sudo service docker restart  centos6.x
关闭docker    
    systemctl stop docker  centos7.x
    service docker stop  centos6.x
查看是否启动成功
    docker ps -a

在这里插入图片描述

4.客户端window机访问RabbitMQ后台网页

http://linuxip地址:15672,这里的用户名和密码默认都是guest
在这里插入图片描述

2.springboot中使用RabbitMQ

1.创建springboot工程,添加依赖

添加web和RabbitMQ的依赖
在这里插入图片描述

3.配置RabbitMQ

通信端口5672,15672管理页面端口

4.交换机

交换机概念:
所有生产的消息都会交给交换机exchange,exchange根据不同的策略将信息发送到RabbitMQ里面去。
四种策略:
Direct exchange、Fanout exchange、Topic exchange、Headers exchange(使用频率相对低)。
四种策略的完整代码

1.Direct exchange直来直去的交换机

路由策略:把消息队列绑定到Direct exchange上面,当消息到达时,会转发到相应的routing key上面,跟ActiveMQ点对点发送差不多,给一个队列就可以了,跟交换机的关系并不大。

1.创建消息队列

如果用了Direct 模式,可以不使用DirectExchange Bean和Binding Bean。

// DirectConfig.java

package com.ll.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DirectConfig {

    @Bean
    Queue directQueue(){
        return new Queue("ll-directQueue");
    }

    // 如果用了Direct 模式,可以不使用DirectExchange Bean和Binding Bean。
//    @Bean
//    DirectExchange directExchange(){
//        // durable:重启后是否有效  autoDelete:长期未使用是否自动删除
//        return new DirectExchange("ll-direct",true,false);
//    }
//
//    // 将队列Queue和交换机DirectExchange绑定到一起
//    @Bean
//    Binding binding(){
//        return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct");
//    }
}

2.消息接收
// DirectRecevier.java

package com.ll.recevier;

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

@Component
// 消息接收
public class DirectRecevier {
    @RabbitListener(queues = "ll-directQueue")
    public void handlerDirect(String message){
        System.out.println("msg:   "+message);
    }
}


3.单元测试发送信息
package com.ll;

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

@SpringBootTest
class AmqpApplicationTests {

	@Autowired
	RabbitTemplate rabbitTemplate;

	@Test
	void contextLoads() {
		rabbitTemplate.convertAndSend("ll-directQueue","哈哈哈哈哈");
	}

}

2.Fanout exchange点对面

两个队列连接同一个交换机,当交换机收到信息后,两个队列都能收到信息。

1.创建消息队列
package com.ll.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FanoutConfig {
    @Bean
    Queue queue1(){
        return new Queue("q1");
    }
    @Bean
    Queue queue2(){
        return new Queue("q2");
    }

    @Bean
    FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanoutExchange",true,false);
    }

    // 将两个队列与同一个交换机绑定
    @Bean
    Binding binding1(){
        // 不需要设置routing key
        return BindingBuilder.bind(queue1()).to(fanoutExchange());
    }
    @Bean
    Binding binding2(){
        return BindingBuilder.bind(queue2()).to(fanoutExchange());
    }
}

2.消息接收
package com.ll.recevier;

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

@Component
public class FanoutRecevier {
    @RabbitListener(queues = "q1")
    public void handler1(String message){
        System.out.println("1  "+message);
    }
    @RabbitListener(queues = "q2")
    public void handler2(String message){
        System.out.println("2  "+message);
    }
}

3.单元测试发送信息
package com.ll;

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

@SpringBootTest
class AmqpApplicationTests {

	@Autowired
	RabbitTemplate rabbitTemplate;

	// 发消息发一次,两个队列都能收到,因为两个队列都在同一个交换机
	@Test
	public void test1(){
		rabbitTemplate.convertAndSend("fanoutExchange",null,"hello fanoutExchange");
	}

}
3.Topic exchange可订阅自己想要的消息

消息队列是通过routing key绑定到Topic exchange上的,当消息到达交换机时,交换机根据routing key把它路由到一个或多个消息队列上,最终被一个或多个消息队列所消费。
相当于给消息一个标签,发信息时指定消息的标签,消息到达交换机时,交换机根据消息的标签,分发给不同队列。

1.创建消息队列
package com.ll.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TopicConfig {
    @Bean
    Queue queuea(){
        return new Queue("qa");
    }
    @Bean
    Queue queueb(){
        return new Queue("qb");
    }
    @Bean
    Queue queuec(){
        return new Queue("qc");
    }

    @Bean
    TopicExchange topicExchange(){
        return new TopicExchange("TopicExchange",true,false);
    }

    @Bean
    Binding bindinga(){
        // qa开头
        return BindingBuilder.bind(queuea()).to(topicExchange()).with("qa.#");
    }
    @Bean
    Binding bindingb(){
        // qb开头
        return BindingBuilder.bind(queueb()).to(topicExchange()).with("qb.#");
    }
    @Bean
    Binding bindingc(){
        // 只要中间是qc就可以
        return BindingBuilder.bind(queuec()).to(topicExchange()).with("#.qc.#");
    }
}

2.消息接收
package com.ll.recevier;

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

@Component
public class TopicRecevier {
    @RabbitListener(queues = "qc")
    public void handler3(String message){
        System.out.println("msg3:  "+message);
    }
    @RabbitListener(queues = "qa")
    public void handler1(String message){
        System.out.println("msg1:  "+message);
    }
    @RabbitListener(queues = "qb")
    public void handler2(String message){
        System.out.println("msg2:  "+message);
    }
}

3.单元测试发送信息
package com.ll;

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

@SpringBootTest
class AmqpApplicationTests {
	@Autowired
	RabbitTemplate rabbitTemplate;
	@Test
	public void test2(){
		rabbitTemplate.convertAndSend("TopicExchange","qa.hhh","这是qa");
//		rabbitTemplate.convertAndSend("TopicExchange","qa.qc","这是qa qc");
	}
}
4.Headers exchange

根据消息的header将消息路由到不同的队列上去。

1.创建消息队列
package com.ll.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.HeadersExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;

@Configuration
public class HeadersConfig {
    @Bean
    Queue queueAge(){
        return new Queue("queueAge");
    }
    @Bean
    Queue queueName(){
        return new Queue("queueName");
    }
    @Bean
    HeadersExchange headersExchange(){
        return new HeadersExchange("headersExchange",true,false);
    }
    @Bean
    Binding bindingAge(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("age",90);
        // age要90才路由
        return BindingBuilder.bind(queueAge()).to(headersExchange()).whereAny(map).match();
    }
    @Bean
    Binding bindingName(){
        // 只要存在name属性就可以
        return BindingBuilder.bind(queueAge()).to(headersExchange()).where("name").exists();
    }
}

2.消息接收
package com.ll.recevier;

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

@Component
public class HeadersRecevier {
    @RabbitListener(queues = "queueAge")
    public void handler1(String message){
        System.out.println("age  "+message);
    }
    @RabbitListener(queues = "queueName")
    public void handler2(String message){
        System.out.println("msg  "+message);
    }
}

3.单元测试发送信息
package com.ll;

import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class AmqpApplicationTests {

	@Autowired
	RabbitTemplate rabbitTemplate;

	@Test
	public void test3(){
		Message message = MessageBuilder.withBody("hello queueName".getBytes()).setHeader("name", "ll").build();
		rabbitTemplate.send("headersExchange",null,message);
	}

	@Test
	public void test4(){
		Message message = MessageBuilder.withBody("hello queueAge".getBytes()).setHeader("age", 90).build();
		rabbitTemplate.send("headersExchange",null,message);
	}
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值