JMS
JMS是Java Message Service的简称,是Java定义的一套消息服务标准,类似于JDBC。只要是符合JMS规范标准的,都是通用的Java消息服务
MOM
MOM是Message Oriented Middleware的简称
RabbitMQ
RabbitMQ是实现了AMQP(Advanced Message Queuing Protocol - 高级消息队列协议)的开源消息代理软件
RabbitMQ服务器是用Erlang语言编写的
1.RabbitMQ的安装
将RabbitMQ部署到CentOS中
-
预装环境
yum install ncurses ncurses-base ncurses-devel ncurses-libs ncurses-static ncurses-term ocaml-curses ocaml-curses-devel -y yum install openssl-devel zlib-devel -y yum -y install make ncurses-devel gcc gcc-c++ unixODBC unixODBC-devel openssl openssl-devel
-
Erlang的安装
# 创建安装目录 mkdir /usr/local/erlang # 进入目录 cd /usr/local/erlang # 下载安装包 wget http://erlang.org/download/otp_src_20.0.tar.gz # 解压 tar -xf otp_src_20.0.tar.gz # 设置安装环境 ./configure --prefix=/opt/erlang --with-ssl -enable-threads -enable-smmp-support -enable-kernel-poll --enable-hipe --without-javac # 编译并安装 make&&make install # 设置软连接 ln -s /opt/erlang/bin/erl /usr/local/bin/erl # 进入erlang应用环境 /usr/local/bin/erl # 测试 EvenN = lists:filter (fun (N) -> N rem 2 == 0 end, lists:seq (1,100)). # 退出erlang应用环境 halt().
# 设置环境变量 vim /etc/profile # 添加内容 # ERLANG_HOME=/opt/erlang # PATH=$ERLANG_HOME/bin:$PATH # export ERLANG_HOME # export PATH # 保存后重载 source /etc/profile # 检查是否加载成功 echo $ERLANG_HOME echo $PATH
-
安装RabbitMQ
# 进入安装目录 cd /usr/local/erlang # 下载安装包 http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.10/rabbitmq-server-generic-unix-3.6.10.tar.xz # 安装xz解压插件 yum -y install xz # 解压xz文件 xz -d rabbitmq-server-generic-unix-3.6.10.tar.xz # 解压tar文件 tar -xvf rabbitmq-server-generic-unix-3.6.10.tar # 解压后移动到/opt/rabbitmq目录 mv rabbitmq_server-3.6.10 /opt/rabbitmq/ # 进入rabbitmq的可执行目录 cd /opt/rabbitmq/sbin # 启动rabbitmq # 直接启动使用 ./rabbitmq-server # 后台启动使用 ./rabbit-server -detached # 开启插件管理页面 ./rabbitmq-plugins enable rabbitmq_management # 创建用户密码 ./rabbitmqctl add user admin 123 # 设置用户角色 ./rabbitmqctl set_user_tags admin administrator # 用户授权 ./rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" # 访问 # 在浏览器输入ip+端口号 http://192.168.54.121:15672 # 关闭服务 ./rabbitmqctl stop
2.MQ的作用
MQ主要的功能是通过将同步转变为异步,实现流量削峰的作用
3.RabbitMQ的原理
- Publisher:出版者,即消息的生产者,也是一个向交换器发布消息的客户端应用程序
- Exchange:交换器,用来接收生产者发送的消息,并将这些消息路由给服务器中的队列。常见类型(direct、topic、fanout)
- Binding:绑定,用于消息队列和交换器之间的关联,一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表
- Queue:消息队列,是消息的容器,也是消息的终点,用来保存消息,直到发送给消费者。一个消息可以投入一个或多个队列;消息一直在队列中,等待消费者链接到队列将其取出
- Virtual Host:虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个虚拟主机本质上就是一个迷你版的RabbitMQ服务器,拥有自己的队列、交换器、绑定和权限机制等。虚拟主机是AMQP概念的基础,必须在链接时指定,RabbitMQ默认的虚拟主机是/
- Broker:表示消息队列服务器实体,也就是RabbitMQ整体应用
- Connection:链接,指RabbitMQ服务器和服务建立的TCP链接
- Channel:信道,是TCP里面的虚拟链接,发布消息、接收消息、订阅队列等动作都是通过信道完成的(使用信道可以降低资源的浪费,提高TCP链接性能)
- Consumer:消费者,即消息的消费者,表示一个从消息队列中取出消息的客户端应用程序
- Routing-key:路由键,队列通过路由键绑定到交换器。消息发送到MQ服务器时,会拥有一个路由键,RabbitMQ会将其和绑定使用的路由键进行匹配,相匹配则投递到该队列;不匹配,则投入黑洞
4.RabbitMQ的使用
- 创建父工程并引入相关依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> <modules> <module>messages</module> <module>consumer</module> <module>publisher</module> </modules> <parent> <artifactId>spring-boot-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.1.10.RELEASE</version> </parent> <groupId>cn.khue</groupId> <artifactId>rabbitmq-test-01</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--web依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--amqp依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <!--测试依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies> </project>
- 创建commons子工程
- 创建一个自定义消息类
package cn.khue.message; import java.io.Serializable; import java.util.Objects; public class MyMessage implements Serializable { private Integer id; private String remark; private Object data; public MyMessage(Integer id, String remark, Object data) { this.id = id; this.remark = remark; this.data = data; } public MyMessage() { } @Override public String toString() { return "MyMessage{" + "id=" + id + ", remark='" + remark + '\'' + ", data=" + data + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyMessage myMessage = (MyMessage) o; return Objects.equals(id, myMessage.id) && Objects.equals(remark, myMessage.remark) && Objects.equals(data, myMessage.data); } @Override public int hashCode() { return Objects.hash(id, remark, data); } }
- 创建一个自定义消息类
- 创建consummer子工程
- 引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>rabbitmq-test-01</artifactId> <groupId>cn.khue</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>consumer</artifactId> <dependencies> <!--message依赖--> <dependency> <groupId>cn.khue</groupId> <artifactId>messages</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
- 创建启动类
@SpringBootApplication //导入spring-boot-starter-amqp,该注解自动生效(可以不写),但不会扫描@RabbitListener和@RabbitHandler等注解,薛涛提供@Component注解 //@EnableRabbit public class ConsumerApp{ public static void main(String[] args){ SpringApplication.run(ConsumerApp.class,args); } }
- 创建配置文件
# 自定义队列、交换器、路由键参数 mq.exchange.name=my-exchange mq.queue.name=my-queue mq.queue.routing-key=my-routing-key # 配置RabbitMQ路径 spring.rabbitmq.host=192.168.54.121 spring.rabbitmq.port=5672 spring.rabbitmq.virtual-host=/ spring.rabbitmq.username=admin spring.rabbitmq.password=123
- 创建消费者类
package cn.khue.consumer; import cn.khue.message.MyMessage; import org.springframework.amqp.rabbit.annotation.*; import org.springframework.stereotype.Component; @RabbitListener(bindings = { @QueueBinding( name = @Queue( name = "${mq.queue.name}", autoDelete = "false"), exchange = @Exchange( name = "${mq.exchange.name}", type = "direct", autoDelete = "false"), key = "${mq.queue.routing-key}") }) @Component public class MyConsumer { //处理消息 @RabbitHandler public void message(MyMessage message){ System.out.println("\033[36m"+message+"\033[m"); } }
- 启动测试
查看RabbitMQ界面管理器【192.168.54.121:15672】
- 引入依赖
- 创建publisher子模块
- 引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>rabbitmq-test-01</artifactId> <groupId>cn.khue</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>publisher</artifactId> <dependencies> <!--messages依赖--> <dependency> <groupId>cn.khue</groupId> <artifactId>messages</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
- 创建启动类
package cn.khue; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class PublisherApp { public static void main(String[] args) { SpringApplication.run(PublisherApp.class,args); } }
- 创建配置文件
# 配置交换器、路由键参数 mq.exchange.name=my-exchange mq.queue.routing-key=my-routing-key # 配置RabbitMQ路径 spring.rabbitmq.host=192.168.54.121 spring.rabbitmq.port=5672 spring.rabbitmq.virtual-host=/ spring.rabbitmq.username=admin spring.rabbitmq.password=123
- 创建自定义消息发送者类
package cn.khue.publisher; import cn.khue.message.MyMessage; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class MyPublisher { @Autowired private AmqpTemplate template; @Value("${mq.exchange.name}") //发送到哪一个交换器 private String exchangeName; @Value("${mq.queue.routing-key}") //路由键是什么 private String routingKey; public void sendMessage(Integer id,String remark,Object data){ //创建消息主体 MyMessage message=new MyMessage(id,remark,data); //发送消息 this.template.convertAndSend(exchangeName,routingKey,message); } }
- 创建测试类
package cn.khue.test; import cn.khue.PublisherApp; import cn.khue.publisher.MyPublisher; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = {PublisherApp.class}) public class PublisherAppTest { @Autowired private MyPublisher publisher; @Test public void sendTest(){ //输出10个消息 for (int i = 0; i <10 ; i++) { this.publisher.sendMessage(i,"消息"+i,"内容是"+i); } } }
- 引入依赖
RabbitMQ交换器
RabbitMQ常用交换器有:direct交换器、topic交换器及fanout交换器
1.direct交换器
direct交换器是一种点对点、实现发布/订阅标准的交换器,根据路由键信息决定消息存储在哪个队列
2.topic交换器
topic交换器也叫主题交换器,或规则匹配交换器,它是通过自定义的模糊匹配规则来决定消息存储在哪些队列
3.fanout交换器
fanout交换器也叫广播交换器,它会将接收的消息发送给绑定的所有队列,不会尝试匹配路由键,所以无需提供路由键信息