RabbitMq学习
RabbitMq学习
简介
不同MQ特点
1.ActiveMQ
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。它是一个完全支持JMS规范的的消息中间件。丰富的API,多种集群架构模式让ActiveMQ在业界成为老牌的消息中间件,在中小型企业颇受欢迎!
2.Kafka
Kafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache顶级项目。Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。
3.RocketMQ
RocketMQ是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。
4.RabbitMQ
RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。
RabbitMQ比Kafka可靠,Kafka更适合IO高吞吐的处理,一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)要求稍低的场景使用,比如ELK日志收集。
安装步骤
自行百度,略
代码
pom依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.2</version>
</dependency>
代码
模板代码
public static Connection createFactory() throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/admin");
return connectionFactory.newConnection("testRabbitMq");
}
public static Channel createChannel() throws IOException, TimeoutException {
Connection connection = RabbitMqConnectFactory.createFactory();
return connection.createChannel();
}
模型
直连
// publish
public void testMqPublish() throws IOException, TimeoutException {
//"hello"->queue name ;是否持久化 ;是否独占 ;是否自动删除; 其他
AMQP.Queue.DeclareOk hello = this.channel.queueDeclare("hello", true, false, false, null);
int consumerCount = hello.getConsumerCount();
channel.basicPublish("","hello",null,"hello rabbitmq".getBytes());
channel.close();
}
// consume
public void testConsume() throws IOException, TimeoutException {
// "hello"-> consume queue ; 是否自动ACK ;消费函数
channel.basicConsume("hello",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body));
}
});
}
工作队列
// publish
public void test2MqPublish() throws IOException {
AMQP.Queue.DeclareOk work = this.channel.queueDeclare("work", true, false, false, null);
for (int i = 0; i < 20; i++) {
channel.basicPublish("", "work", null, (i+"====>:我是消息").getBytes());
}
}
public void test2Consume() throws IOException {
//一次只接受一条未确认的消息
channel.basicQos(1);
// 关闭自动确认
channel.basicConsume("work", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1: " + new String(body));
// 手动确认消息
channel.basicAck(envelope.getDeliveryTag(),false);
}
});
}
public void test2AConsume() throws IOException {
channel.basicConsume("work", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000); //处理消息比较慢 一秒处理一个消息
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者2: " + new String(body));
}
});
}
广播模式+路由模式
注意此模式下为推送模式,即消息不会再publish时暂存至队列,队列由消费者创建并消费
// DIRECT路由 FANOUT 广播
// 路由:按照路由去找对应的通道以及队列 广播:所有通道以及队列都可以接受到消息
public void test2MqPublish() throws IOException, TimeoutException {
channel.exchangeDeclare("exchange2", BuiltinExchangeType.DIRECT);
for (int i = 0; i < 40; i++) {
channel.basicPublish("exchange2", "", null, (i + "====>:我是消息").getBytes());
}
channel.close();
}
public void test2MqConsume() throws IOException {
String queue = channel.queueDeclare().getQueue();
channel.queueBind(queue, "exchange2", "");
channel.basicQos(1);
channel.basicConsume(queue, false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:" + new String(body));
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
// 路由模式下无法接受到消息 广播模式下可以接受到
public void test2AMqConsume() throws IOException {
String queue = channel.queueDeclare().getQueue();
channel.queueBind(queue, "exchange2", "key");
channel.basicQos(1);
channel.basicConsume(queue, false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:" + new String(body));
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
TOPIC订阅模式(路由模式+通配符)
例子略
# 统配符
* (star) can substitute for exactly one word. 匹配不多不少恰好1个词
# (hash) can substitute for zero or more words. 匹配一个或多个词
# 如:
audit.# 匹配audit.irs.corporate或者 audit.irs 等
audit.* 只能匹配 audit.irs
SpringBoot中使用rabbitmq
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
配置文件
spring:
profiles:
active: local
application:
name: test-learn
rabbitmq:
host: 127.0.0.1
port: 5672
username: admin
password: admin
virtual-host: /admin
模型
普通模型略
广播模型
@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class TestLearnDemoApplication {
@Test
public void testFanout() throws InterruptedException {
rabbitTemplate.convertAndSend("logs","","这是日志广播");
}
}
// 自动触发
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
exchange = @Exchange(name="logs",type = ExchangeTypes.FANOUT)
))
public void receive1(String message){
System.out.println("message1 = " + message);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue, //创建临时队列
exchange = @Exchange(name="logs",type = ExchangeTypes.FANOUT) //绑定交换机类型
))
public void receive2(String message){
System.out.println("message2 = " + message);
}
路由模型
@Test
public void testFanout() throws InterruptedException {
rabbitTemplate.convertAndSend("route","error","这是日志路由");
}
// 如果同时匹配多个路由,默认会消费多次
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
key = {"error","info"},
exchange = @Exchange(name = "route",type = ExchangeTypes.DIRECT)
))
public void routeModel1(String message){
System.out.println("route message1 = " + message);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
key = {"error","warn"},
exchange = @Exchange(name = "route",type = ExchangeTypes.DIRECT)
))
public void routeModel2(String message){
System.out.println("route message2 = " + message);
}
订阅模型略(简单示例)
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
key = {"user.*"},//route is user.save.findAll
exchange = @Exchange(type = "topic",name = "topics")
)
})
public void receive1(String message){
System.out.println("message1 = " + message);
}
后续补充
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name ="test-final" ,durable = "true",autoDelete = "false",exclusive = "true"),
key = {"error","info"},
exchange = @Exchange(name = "route",type = ExchangeTypes.DIRECT)
),ackMode = "MANUAL")
public void routeModel1(String message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) throws IOException {
// RabbitMQ的ack机制中,第二个参数返回true,表示需要将这条消息投递给其他的消费者重新消费
System.out.println(deliveryTag);
// 第三个参数true,表示这个消息会重新进入队列
channel.basicAck(deliveryTag,false);
// channel.basicNack(deliveryTag,false,true);
System.out.println("route message1 = " + message);
}

浙公网安备 33010602011771号