AMQP
AMQP:Advanced Message Queuing Protocol高级消息队列协议。
其是一个线路层的协议规范,不是API规范(e.g: JMS)
所以它是天然跨平台的。按照规范的格式发送数据,任何平台都可以通过AMQP进行消息交互。
主流的StormMQ,RabbitMQ都实现了AMQP
RabbitMQ的安装:
安装指南:安装指南 [推荐访问我这个链接,下面的安装步骤就可以跳过]
RabbitMQ是一个实现了AMQP的开源消息中间件,使用高性能的Erlang编写(很古老的语言).RabbitMQ具有可靠性,支持多种协议、高可用、支持消息集群和多语言客户端等特点,分布式系统中存储转发消息,具有不错的性能表现。
例如Go+RabbitMQ实现商品秒杀系统,再配合CDN,实现高并发处理水平扩展。
这里再梳理一下安装步骤:
Erlang安装:
1.先安装Erlang:
wget http://erlang.org/download/otp_src_21.0.tar.gz
2. 解压:
tar -zxvf otp_src_21.0.tar.gz
3.编译:
cd otp_src_21.0
./otp_build autoconf
./configure
make
4.安装:
make install
5.检验:
erl
RabbitMQ安装:
yum仓库中默认的Erlang版本较低,需要将最新的Erlang包添加到yum源中。
vi /etc/yum.repos.d/rabbitmq-erlang.repo
添加:
[rabbitmq-erlang]
name=rabbitmq-erlang
baseurl=https://dl.bintray.com/rabbitmq/rpm/erlang/21/el/7
gpgcheck=l
gpgkey=https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc
repo_gpgcheck=0
enabled=l
添加后清楚原来的缓存并创建新缓存:
yum clean all
yum makecache
安装RabbitMQ:
1.下载文件:
wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.7/rabbitmq-server-3.7.7-1.el7.noarch.rpm
2.安装:
yum install rabbitmq-server-3.7.7-1.el7.noarch.rpm
我安装的过程中说缺少socat依赖,安装即可:
yum install socat
3.启动RabbitMQ并进行用户管理:
# 启动
service rabbitmq-server start
# 查看状态
rabbitmqctl status
# 开启web插件
rabbitmq-plugins enable rabbitmq_management
#重启
service rabbitmq-server restart
# 添加一个用户:yinlei是用户名,yinleilei是密码
rabbitmqctl add_user yinlei yinleilei
#设置yinlei角色为管理员
rabbitmqctl set_user_tags yinlei administrator
#配置yinlei可以远程登录
rabbitmqctl set_permissions -p "/" yinlei ".*" ".*" ".*"
启动成功后,默认是一个guest用户,但是该用户只能本地登录,无法远程登录,所以添加了一个yinlei作为管理员身份,可以远程登录。
利用上面的用户名和密码进行登录:
SpringBoot整合AMQP:
1.添加依赖
2.配置RabbitMQ连接信息:
3.RabbitMQ配置:
Rabbitmq中所有的消息生产者提交的消息都会交给Exchange进行再分配,Exchange会根据不同的策略将消息发布到不同的Queue中。
Rabbitmq共提供了4种不同的Exchange策略:
- Direct
- Fanout
- Topic
- Header(使用频率低)
1.Direct:
DirectExchange的路由策略是将消息队列绑定到一个DirectExchange上,当一条消息到达DirectExchange时候会被转发到与该条消息routing key相同的Queue上。
DirectExchange配置:
package com.yinlei.rabbitmq;
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 RabbitDirectConfig {
public final static String DIRECTINAME="yinlei-direct";
/**
* 提供一个消息队列Queue
* @return
*/
@Bean
Queue queue(){
return new Queue("hello-queue");
}
/**
* 创建DirectExchange对象
* @return
*/
@Bean
DirectExchange directExchange(){
//参数1是名字,参数2是重启后是否以然有效,参数3是长期未使用时是否删除
return new DirectExchange(DIRECTINAME,true,false);
}
/**
* 创建一个Binding对象将Exchange和Queue绑定再一起
* @return
*/
@Bean
Binding binding(){
return BindingBuilder.bind(queue()).to(directExchange()).with("direct");
}
}
DirectExchange和Binding的2个bean配置可以省略,如果使用DirectExchange,只配置一个Queue实例即可。
配置消费者:
测试验证:
2.Fanout
FanoutExchange的数据交换策略是把所有到达FanoutExchange的消息转发给所有与它绑定的Queue,
routingkey不起任何作用了。
FanoutExchange配置方式:
package com.yinlei.rabbitmq;
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 RabbitFanoutExchange {
public final static String FANOUTNAME ="yinlei-fanout";
@Bean
FanoutExchange fanoutExchange(){
return new FanoutExchange(FANOUTNAME,true,false);
}
@Bean
Queue queueOne(){
return new Queue("queue-1");
}
@Bean
Queue queueTwo(){
return new Queue("queue-2");
}
@Bean
Binding bindingOne(){
return BindingBuilder.bind(queueOne()).to(fanoutExchange());
}
@Bean
Binding bindingTwo(){
return BindingBuilder.bind(queueTwo()a).to(fanoutExchange());
}
}
创建2个消费者,分别消费2条消息队列中的消息:
最后进行测试:
这里不再需要routingkey,指定exchange即可。routingkey可以传null
3.Topic
TopcExchange比较复杂和灵活。
Queue中通过routingkey绑定到TopicExchange上,当消息到达TopicExchange后,TopicExchange将消息路由到一个或多个Queue上。
TopicExchange配置:
package com.yinlei.rabbitmq;
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 RabbitTopicConfig {
public final static String TOPICNAME="yinlei-topic";
@Bean
TopicExchange topicExchange(){
return new TopicExchange(TOPICNAME,true,false);
}
@Bean
Queue zhangming(){
return new Queue("zhangming");
}
@Bean
Queue songyang(){
return new Queue("songyang");
}
@Bean
Queue yinwei(){
return new Queue("yinwei");
}
@Bean
Binding zhangmingBinding(){
return BindingBuilder.bind(zhangming()).to(topicExchange()).with("zhangming.#");
}
@Bean
Binding songyangBinding(){
return BindingBuilder.bind(songyang()).to(topicExchange()).with("songyang.#");
}
@Bean
Binding yinweiBinding(){
return BindingBuilder.bind(yinwei()).to(topicExchange()).with("yinwei.#");
}
}
创建了3个queue
分别绑定到topicExchange上,yinwei.#表示消息的routingkey凡是以yinwei开头的,都将被路由到名称为yinwei的queue上。其他同理。
创建3个消费者:
测试:
4.Header
HeaderExchange会根据消息的Header将消息路由到不同的Queue上,也和routingkey无关。
HeaderExcahnge配置如下:
package com.yinlei.rabbitmq;
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;
import java.util.Map;
@Configuration
public class RabbitHeaderConfig {
public final static String HEADNAME = "yinlei-header";
@Bean
HeadersExchange headersExchange(){
return new HeadersExchange(HEADNAME,true,false);
}
@Bean
Queue queueName(){
return new Queue("name-queue");
}
@Bean
Queue queueAge(){
return new Queue("age-queue");
}
@Bean
Binding bindingName(){
Map<String,Object> map = new HashMap<>();
map.put("name","yinlei");;
return BindingBuilder.bind(queueName()).to(headersExchange()).whereAny(map).match();
}
@Bean
Binding bindingAge(){
return BindingBuilder.bind(queueAge()).to(headersExchange()).where("age").exists();
}
}
whereAny表示消息的header中只要有一个Header匹配上map中的key/value,都把消息路由到名为name-queue的Queue上。
而对于age,表示无论age是多少,只要header包含age,都将消息路由到age-queue的Queue上。
2个消费者:
消息的发送也和routingkey无关: