ActiveMQ
kafka性能好
RocketMQ性能高(双十一)
RabbitMQ可靠性、稳定性好(金融)
修改配置文件
vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app
运行:
管控台
查看插件:
rabbitmq-plugins list
启动管理插件
rabbitmq-plugins enable rabbitmq_management
检查是否启动
lsof -i:5672
访问
http://ip:15672
注意放行15672端口。或者直接关闭防火墙,否则无法访问
放行
firewall-cmd --zone=public --add-port=15672/tcp --permanent
重启防火墙
firewall-cmd --reload
命令行
Connection Factory:获取连接工厂
Connection:一个连接
Channel:数据通信信道,可发送和接收消息
Queue:具体的消息存储队列
Producer& Consumer生产和消费者
注意放行,否则会连接不上
放行
firewall-cmd --zone=public --add-port=5672/tcp --permanent
重启防火墙
firewall-cmd --reload
测试:
Procuder
public static void main(String[] args) throws IOException, TimeoutException {
//创建一个Connection工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.75.130");
factory.setPort(5672);
factory.setVirtualHost("/");
//创建连接
Connection connection = factory.newConnection();
//创建一个channel
Channel channel = connection.createChannel();
//通过channel发送
String mesg = "hello world";
for (int i = 0; i < 6; i++) {
channel.basicPublish("", "test001", null, mesg.getBytes());
}
channel.close();
connection.close();
}
对于channel.basicPublish("", “test001”, null, mesg.getBytes());
Consumer
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//创建一个Connection工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setPort(5672);
factory.setHost("192.168.75.130");
factory.setVirtualHost("/");
//创建连接
Connection connection = factory.newConnection();
//创建一个channel
Channel channel = connection.createChannel();
//创建一个队列
String queueName = "test001";
channel.queueDeclare(queueName, true, false, true, null);
//创建一个消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//对channel进行设置
channel.basicConsume(queueName, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg);
}
}
关于basicConsumer()
注意:BasicConsume将信道设置为接收模式,直到取消队列的订阅为止,在接收模式期间,RabbitMQ会不断地推送消息给消费者,当然推送消息的个数还是会收到basicQos()的限制。如果只是想从队列获取单条消息而不是订阅,建议用basicGet()拉模式 。 将BasicGet()放在一个循环中代替BasicConsum()是不可取的
所有发送到 Direct Exchange的消息被转发到 RouteKey中指定的 Queue
注意: Direct模式可以使用 RabbitMQ自带的 Exchange: defaultExchange,所以不需要将 Exchange进行任何绑定。若需要指定自定义exchange则需要绑定( binding操作,消息传递时, Routekey必须完全匹配才会被队列接收,否则该消息会被抛弃。如图所示
实例:
本质上就是一段数据,由 Properties和 Payload(Body)组成
在生产者中:
各种属性含义
在消费者中:
方式1
消息落库,对消息状态进行打标(需要存储数据库,不适合高并发),通过检查数据库消息的状态判断是否成功。发送时status=0,成时status被改为:1;重试3次后则变为2
方式2
消息的延迟投递,做二次确认,回调检查(高效,真正很多互联网公司所使用的方式)
.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。如:扣款只扣一次,发消息只发一次。
消费端实现幂等性,就意味着,我们的消息永远不会消费多次,即使我们收到了多条一样的消息。
业界主流的幂等性操作:
我们一般就是在代码中编写 While循环,进行 consumer. nextDelivery方法进行获取下一条消息,然后进行消费处理!
但是我们使用自定义的Consumer更加的方便,解耦性更加的强,也是在实际工作中最常用的使用方式!
只需要继承一个类然后实现方法就行。
创建一个consumer类继承DefaultConsumer
public class MyConsumer extends DefaultConsumer {
/**
* Constructs a new instance and records its association to the passed-in channel.
*
* @param channel the channel to which this consumer is attached
*/
public MyConsumer(Channel channel) {
super(channel);
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("consumerTag" + consumerTag);
System.out.println("envelope" + envelope);
System.out.println("body" + new String(body));
}
}
如何实现
RabbitMQ提供了一种qos(服务质量保证)功能,即在非自动确认消息的前提下,如果一定数目的消息(通过基于 consume或者 channel设置Qos的值)未被确认前,不进行消费新的消息。(不设置自动签收)
在消费端中设置
实例:
consumer端限制(注意自定签收一定要设置为false)
在自定义consumer中设置,注意在构造器中设置channel
消费端重回队列是为了对没有处理成功的消息,把消息重新会递给Broker
般我们在实际应用中,都会关闭重回队列,也就是设置为 False
消费端的手工ACK和NACK
消息变成死信有一下几种情况
死信设置
-首先需要设置死信队列的 exchange和 queue,然后进行绑定:
Exchange: dlx. exchange
Queue: dlx.queue 监听
Routing key:# 任何routingkey都能路由