RabbitMQ学习
rabbitmq和kafka选择
1.吞吐量比较:kafka的吞吐量更大
2.可靠性比较:kafka存在丢数据的情况,适合做日志系统; rabbitmq更可靠,适合做数据系统和金融系统.
3.kafka使用分布式,rabbitmq比kafka更适合大文件
linux系统下安装rabbitmq
安装Erlang
rabbitmq是erlang语言编写的,需要先安装erlang环境,kafka是java开发的
# 启动EPEL源
$ sudo yum install epel-release
# 安装erlang
$ sudo yum install erlang
安装RabbitMQ
# 下载rabbitmq安装包
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6-1.el7.noarch.rpm
# 使用yum安装上面的安装包
yum install rabbitmq-server-3.6.6-1.el7.noarch.rpm
基本命令
命令 | 作用 |
---|---|
sudo chkconfig rabbitmq-server on | 添加开机启动RabbitMQ服务 |
sudo /sbin/service rabbitmq-server start | 启动服务 |
sudo /sbin/service rabbitmq-server status | 停止服务 |
sudo rabbitmqctl list_users | 查看所有用户 |
sudo rabbitmqctl list_user_permissions guest | 查看默认用户guest 的权限 |
sudo rabbitmqctl delete_user guest | 删除用户 |
sudo rabbitmqctl add_user username password | 添加用户 |
sudo rabbitmqctl set_user_tags username administrator | 添加用户tag |
sudo rabbitmqctl set_permissions -p / username “." ".” “.*” | 添加所有权限 |
sudo rabbitmqctl list_user_permissions username | 查看用户权限 |
sudo rabbitmq-plugins enable rabbitmq_management | 安装web管理页面 |
web管理页面
//安装web管理插件
sudo rabbitmq-plugins enable rabbitmq_management
//访问url,guest(密码默认也是guest)只能通过localhost访问;
//如果需要远程访问,需要删掉guest,创建新的用户
http://localhost:15672
普通项目java调用rabbitmq
创建生产者
public static void main(String[] args) {
ConnectionFactory connectionFactory= new ConnectionFactory();
//设置连接地址
connectionFactory.setHost("192.168.25.23");
//设置端口
connectionFactory.setPort(5672);
//设置密码用户名
connectionFactory.setUsername("username");
connectionFactory.setPassword("password");
//设置虚拟机,每个虚拟机相当于一个小的mq
connectionFactory.setVirtualHost("/");
Connection connection =null;
try {
//建立连接
connection = connectionFactory.newConnection();
//建立通道,生产着和消费者都是在通道中完成
Channel channel = connection.createChannel();
channel.queueDeclare(Queue,true,false,false,null);
String message="hello maxchen";
channel.basicPublish("",Queue,null,message.getBytes());//注意:第一个参数不能为null
System.out.println("send xiaoxi");
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
//扩展
/*
参数1,声明队列
参数2 是否持久化
参数3 是否排他,是否独战连接,队列只允许该链接中访问,如果连接关闭,队列也就删除了
参数4:是否自动删除,如果将此参数设置true,那么就变成零时队列
参数5 :扩展参数,例如存活时间
channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)*/
//所有的队列需要制定默认的交换机
/*
参数1: 交换机,如果使用默认交换机,那么就为空,不可以设置为null
参数二:路由key.交换机根据key来将消息发送到制定搞得队列,如果私用默认交互机,就应该设置为队列名称
channel.basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)
throws IOException;
*/
创建消费者
private static final String Queue="world";
public static void main(String[] args) {
ConnectionFactory connectionFactory= new ConnectionFactory();
//设置连接地址
connectionFactory.setHost("192.168.25.23");
//设置端口
connectionFactory.setPort(5672);
//设置密码用户名
connectionFactory.setUsername("username");
connectionFactory.setPassword("password");
//设置虚拟机,每个虚拟机相当于一个小的mq
connectionFactory.setVirtualHost("/");
Connection connection =null;
try {
//建立连接
connection = connectionFactory.newConnection();
//建立通道,生产着和消费者都是在通道中完成
Channel channel = connection.createChannel();
channel.queueDeclare(Queue,true,false,false,null);
/*
参数一:队列名称
参数二:自动回复
参数三 消费者方法
String basicConsume(String queue, boolean autoAck, Consumer callback)*/
DefaultConsumer defaultConsumer=new DefaultConsumer(channel) {
//当接受到消息时候,此方法被调用
/**
* @Author cxy
* @Description //TODO
* @Date 2019/3/24
* @Param [consumerTag, envelope, properties, body]
* @return void
*
* consumerTag 用来标识.可以再监听队列时候设置
* envelope 信封,通过envelope可以通过这个获取到很多东西
* properties 额外的消息属性
* body:消息体
**/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//获取交换机
String exchange = envelope.getExchange();
//消息id,用来表示那个消息消费了
long deliveryTag = envelope.getDeliveryTag();
String message=new String(body,"utf-8");
System.out.println("receive:"+message);
}
};
channel.basicConsume(Queue,true ,defaultConsumer);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
springboot整合rabbitmq,并实现持久化
//配置文件
#rabbitmq
spring.rabbitmq.host=192.168.25.23
spring.rabbitmq.port=5672
#默认的v-host
spring.rabbitmq.virtual-host=/
spring.rabbitmq.username=username
spring.rabbitmq.password=password
#生产者手动确认
spring.rabbitmq.publisher-confirms=true
#生产者接受mq返回的信息
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.dynamic=true
#消费者持久化配置
spring.rabbitmq.cache.connection.mode=channel
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual
生产者
@Component
public class Producer implements RabbitTemplate.ReturnCallback,RabbitTemplate.ConfirmCallback{
@Autowired
private RabbitTemplate rabbitTemplate;
@Scheduled(fixedDelay = 10000L)
public void send() {
this.rabbitTemplate.setReturnCallback(this);
this.rabbitTemplate.setConfirmCallback(this);
this.rabbitTemplate.setMandatory(true);
CorrelationData correlationData = new CorrelationData("007");
//exchange有4个类型:direct,topic,fanout,header,路由规则,routingkey需要完全相同才匹配
//rabbitTemplate.convertAndSend("amq.direct","hello","kkk",correlationData);
rabbitTemplate.convertAndSend("world","kkk");
}
@Override
public void confirm(@Nullable CorrelationData correlationData, boolean ack, @Nullable String cause) {
//如果消息没有到exchange,则confirm回调,ack=false
//如果消息到达exchange,则confirm回调,ack=true;但是exchange到queue失败,ack还是true,要注意
//System.out.println("correlationData:"+correlationData.getId());
System.out.println("ack:"+ack);
System.out.println("cause:"+cause);
if (ack){
System.out.println("成功");
}else {
System.out.println("失败");
}
}
@Override
public void returnedMessage(Message message, int replycode, String replytext, String exchange, String routingkey) {
//exchange到queue成功,则不回调return
//exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
System.out.println("message:"+message);
System.out.println("replycode:"+replycode);
System.out.println("replytext:"+replytext);
System.out.println("exchange:"+exchange);
System.out.println("routingkey:"+routingkey);//路由
}
}
消费者
import java.util.concurrent.TimeoutException;
@Component
public class RabbitConsumer {
private static final String Queue="world";
@RabbitHandler
//@RabbitListener(queues = "world")//简单配置,可以消费生产者创建的queue
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value = "direct.hlm2"),
exchange = @Exchange(value = "hlm2.direct", type = ExchangeTypes.DIRECT),
key = "hlm2.*"
)//这样如果mq中不存在该queue,会为消费者创建一个新的queue
)//FANOUT模式,符合amq.*格式的queue都可以消费消息,direct要rountingkey完全匹配才消费
public void process(@Payload String foo) {
System.out.println("消费消息:"+new Date() + ": " + foo);
}
//持久化消费者
//消费者使用手动应答模式,如果没有应答,消息状态变成了unacked,断开消费者客户端链接后,消息重新变成ready;消息处于unacked的时候其他消费者是不能消费的
@RabbitListener(queues = "world")
public void listerQueueR1(Channel channel, String message, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("here:"+message);
/**参数一deliveryTag:该消息的index
参数二multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。*/
channel.basicAck(tag,false);
}
}
配置config类
@Configuration
public class RabbitConfig {
@Bean
public Queue Queue() {
//如果生产者和消费者都没有创建queue,生产者发送的消息会丢到黑洞,消费者会报错
return new Queue("world");//创建queue,默认是持久化的,没有binding任何exchange和routingkey
}
}
通过rabbitmq客户端查看到创建的queue参数druable为true
重启rabbitmq,会发现消息还存在mq中