C#rabbitmq安装步骤以及使用方法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第⼆步点击下载并且选择环境
注意需要 rabbitmq 的版本与erlang的版本要对应
2. 安装并配置环境
a.安装erlang OTP 环境
b.配置环境变量 Path 添加第⼀步安装的⽂件位置
c.打开cmd 输⼊erl 看是否配置成功
d.安装rabbitmq server
e.在安装⽬录下运⾏cmd 输⼊ rabbitmq-plugins enable rabbitmq_management 启动界⾯管理服务
g.rabbitmq server 默认端⼝是 udp的5672端⼝如果是远程连接需要开启防⽕墙
h.guest ⽤户是本地账号如果不在同⼀台服务器上⽆法连接需要新建⼀个账号并且给这个账号对应的权限
直接点击设置默认权限
现在权限就有了
3. 在vs中引⽤客户端 RabbitMQ.Client 可以在官⽹下载也可以在 vs的nuget中下载
ConnectionFactory Factory1 = new ConnectionFactory();
Factory1.HostName = "127.0.0.1";
erName = "xy";
Factory1.Password = "xy";
Factory1.Port = 5672;
var conn1 = Factory1.CreateConnection();
//创建⼀个channel通道
var channel1 = conn1.CreateModel();
channel1.QueueDeclare("csdata", false, false, false, null);//创建⼀个 csdata的队列
var pro = channel1.CreateBasicProperties();
pro.DeliveryMode = 1; //设置消息不持久保存
Task.Run(() =>
{
while (true)
{
channel1.BasicPublish("", "csdata", pro, Encoding.ASCII.GetBytes(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff"))); //⽣产消息
Thread.Sleep(100);
}
});
ConnectionFactory Factory = new ConnectionFactory();
Factory.HostName = "127.0.0.1";
erName = "xy";
Factory.Password = "xy";
Factory.Port = 5672;
var conn = Factory.CreateConnection();
//创建⼀个channel通道
var channel = conn.CreateModel();
var consumer = new EventingBasicConsumer(channel);//消费者
channel.BasicConsume("csdata", true, consumer);//消费消息
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body.ToArray());
Console.WriteLine($"send {message} rcv {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff")}");
};
Console.ReadLine();
注意如果需要消息持久化需要吧队列设置为持久化并且每次发送消息都需要设置为持久化,重启以后会⾃动去加载队列以及队列的消息⼀不⽤交换机的队列
⽣产者⽰例:
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送10条消息,依次在消息后⾯附加1-10个点
for (int i = 6; i > 0; i--)
{
String message = "helloworld";
channel.basicPublish("", QUEUE_NAME,null, message.getBytes());
}
消费者⽰例:
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
// 指定消费队列
channel.basicConsume(QUEUE_NAME, true, consumer);
while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
doWork(message);
}
API介绍
(1) channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments) ;
可以看到⽣产者和消费者⽤同样的参数声明了队列,官⽅推荐该做法,事实上对于⼀个已经存在的队列即使该⽅法试图⽤不同的参数去创建队列也不会有任何效果,这意味着不会改变队列更不会影响队列现在的⼯作。
queue:队列名字
durable:队列持久化标志,true为持久化队列
exclusive:exclusive:排他队列,如果⼀个队列被声明为排他队列,该队列仅对⾸次声明它的连接可见,并在连接断开时⾃动删除。
这⾥需要注意三点:其⼀,排他队列是基于连接可见的,同⼀连接的不同信道是可以同时访问同⼀个连接创建的排他队列的。
其⼆,“⾸次”,如果⼀个连接已经声明了⼀个排他队列,其他连接是不允许建⽴同名的排他队列的,这个与普通队列不同。
其三,即使该队列是持久化的,⼀旦连接关闭或者客户端退出,该排他队列都会被⾃动删除的。
这种队列适⽤于只限于⼀个客户端发送读取消息的应⽤场景。
autoDelete:⾃动删除,如果该队列没有任何订阅的消费者的话,该队列会被⾃动删除。
这种队列适⽤于临时队列。
arguments:Map类型,关于队列及队列中消息的详细设置
arguments键值意义
x-message-ttl 数字类
型,标志
时间,以
豪秒为单
位
标志队列中的消息存活时间,也就是说队列中的消息超过了制定时间会被删除
x-expires 数字类
型,标志
时间,以
豪秒为单
位
队列⾃⾝的空闲存活时间,当前的queue在指定的时间内,没有consumer、basic.get也就是未被访问,就会被删除。
x-max-length和
x-max-length-
bytes 数字
最⼤长度和最⼤占⽤空间,设置了最⼤长度的队列,在超过了最⼤长度后进⾏插⼊会删除之前插⼊的消息为本次的留出空间,相应的最⼤占⽤⼤⼩也是这个道理,当超过了这个⼤⼩的时候,会删除之前插⼊的消息为本次的留出空间。
x-dead-letter-
exchange和x-dead-letter-routing-key 字符串
消息因为超时或超过限制在队列⾥消失,这样我们就丢失了⼀些消息,也许⾥⾯就有⼀些是我们做需要获知的。
⽽rabbitmq的死信功能则为我们带来了解决⽅案。
设置了dead letter exchange与dead letter routingkey(要么都设
定,要么都不设定)那些因为超时或超出限制⽽被删除的消息会被推动到我们设置的exchange中,再根据routingkey推
到queue中
x-max-priority数字队列所⽀持的优先级别,列如设置为5,表⽰队列⽀持0到5六个优先级别,5最⾼,0最低,当然这需要⽣产者在发送消息时指定消息的优先级别,消息按照优先级别从⾼到低的顺序分发给消费者
(2)channel.basicPublish(exchange, routingKey, mandatory, immediate, basicProperties, body);
exchange: 交换机名
routingKey: 路由键
mandatory:当mandatory标志位设置为true时,如果exchange根据⾃⾝类型和消息routeKey⽆法找到⼀个符合条件的queue,那么会调⽤basic.return⽅法将消息返还给⽣产者, channel.addReturnListener添加⼀个监听器,当broker执⾏basic.return⽅法时,会回调handleReturn⽅法,这样我们就可以处理变为死信的消息了;当mandatory 设为false时,出现上述情形broker会直接将消息扔掉;通俗的讲,mandatory标志告诉broker代理服务器⾄少将消息route到⼀个队列中,否则就将消息return给发送者。
immediate: rabbitMq3.0已经不在⽀持了,3.0以前这个标志告诉服务器如果该消息关联的queue上有消费者,则马上将消息投递给它,如果所有queue都没有消费者,直接把消息返还给⽣产者,不⽤将消息⼊队列等待消费者了。
basicProperties:消息的详细属性,例如优先级别、持久化、到期时间,headers类型的exchange要⽤到的是其中的headers字段。
public BasicProperties(
String contentType,//消息类型如:text/plain
String contentEncoding,//编码
Map<String,Object> headers,
Integer deliveryMode,//1:nonpersistent 2:persistent
Integer priority,//优先级
String correlationId,
String replyTo,//反馈队列
String expiration,//expiration到期时间
String messageId,
Date timestamp,
String type,
String userId,
String appId,
String clusterId)
body:消息实体,字节数组。
(3)QueueingConsumer:这是⼀个已经实现好了的Consumer,相⽐于⾃⼰实现Consumer接⼝,这是个⽐较安全快捷的⽅式。
该类基于jdk的BlockingQueue实
现,handleDelivery⽅法中将收到的消息封装成Delivery对象,并存放到BlockingQueue中,这相当于消费者本地存放了⼀个消息缓存队列。
nextDelivery()⽅法底层调⽤的BlockingQueue的阻塞⽅法take()。
(4)channel.basicConsume(queue, autoAck, consumer);
queue:队列名。
autoAck:⾃动应答标志,true为⾃动应答。
consumer:消费者对象,可以⾃⼰实现Consumer接⼝,建议使⽤QueueingConsumer。
⼆ fanout类型的交换机
1、消息与队列匹配规则:fanout类型交换机会将接收到的消息⼴播给所有与之绑定的队列。
2、现在我们来演⽰⼀下如图所⽰的消息⼴播机制,不难注意到这种情况⽣产者P只关⼼消息发送给哪个交换机,由交换机X决定消息发送到哪些队列,,⽽消费者C只关注订阅哪个队列。
⽣产者⽰例:
// 声明转发器和类型
channel.exchangeDeclare(EXCHANGE_NAME, "fanout" );
String message = "消息1";
// 往转发器上发送消息
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
消费者⽰例:
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
// 创建⼀个⾮持久的、唯⼀的且⾃动删除的队列
String queueName = channel.queueDeclare().getQueue();
// 为转发器指定队列,设置binding
channel.queueBind(queueName, EXCHANGE_NAME, "");
QueueingConsumer consumer = new QueueingConsumer(channel);
// 指定接收者,第⼆个参数为⾃动应答,⽆需⼿动应答
channel.basicConsume(queueName, true, consumer);
while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
doSomething(message);
}
⽰例代码所⽤API介绍:
(1)channel.exchangeDeclare(exchange, type );
exchange: 交换机名。
type:交换机类型,值为fanout、direct、topic、headers其中之⼀。
(2)channel.queueDeclare():创建⼀个⾮持久的、唯⼀的、⾃动删除的队列且队列名称由服务器随机产⽣。
(3)channel.queueBind(queue, exchange, bindingKey);
queue:队列名。
exchange:交换机名。
bindingKey:绑定键。
(4)channel.basicPublish(exchange, routingKey, basicProperties, body)
exchange:交换机名。
routingKey:消息绑定的路由键。
basicProperties:消息属性,详细字段见上⼀节不⽤交换机的队列
body:消息实体,字节数组。
三 direct类型的交换机
1、消息分发规则:消息会被推送⾄绑定键(binding key)和消息发布附带的选择键(routing key)完全匹配的队列。
2、图⽰说明:消息1附带路由键“error”、与绑定键“error”匹配,⽽队列Q4、Q5与交换机X间都存在绑定键“error”所以消息1被分发到Q4、Q5;消息2附带路由键“info”,⽽队列Q4与交换机间存在绑定建“info”,所以消息2被分发到队列Q4。
3、分发到队列的消息不再带有绑定键,事实上分发到队列的消息不再带有发送者的任何信息,当然如果消息实体⾥⾯包含了发送者消息,那么消费者可以获取发送者信息。
⽣产者⽰例:
chanel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 发布消息⾄转发器,指定routingkey
chanel.basicPublish(EXCHANGE_NAME, "error", null, message1.getBytes());
chanel.basicPublish(EXCHANGE_NAME, "info", null, message2.getBytes());
消费者⽰例:
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
// 指定binding_key
channel.queueBind(queueName, EXCHANGE_NAME, "error");
channel.queueBind(queueName, EXCHANGE_NAME, "warning");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer);
while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
doSomething(message);
}
四 topic类型的交换机
1、消息分发规则:⼀个附带特殊的选择键将会被转发到绑定键与之匹配的队列中。
2、routingKey于bindingKey匹配规则:
routingKey必须是由点隔开的⼀系列的标识符组成。
标识符可以是任何东西,但是⼀般都与消息的某些特性相关。
⼀些合法的选择键的例⼦:”d.nyse”,“nyse.vmw”,”quick.orange.rabbit”.你可以定义任何数量的标识符,上限为255个字节。
绑定键和选择键的形式⼀样。
需要注意的是:关于绑定键有两种特殊的情况。
*可以匹配⼀个标识符。
#可以匹配0个或多个标识符。
3、图⽰说明:
消息1附带路由键“fast.orange.*”与绑定键“#”、“*.orange.*”匹配,所以消息1被分发给队列Q6、Q7;消息2附带路由键“lazy.orange.a.b”与绑定键“#”、“lazy.#”匹配,所以消息2被分发给队列Q6、Q8。
代码⽰例与direct类型转发器基本雷同,只是路由键和绑定键格式不⼀样,这⾥不再赘述。
四 headers类型的交换机
1、消息分发规则:headers类型的交换机分发消息不依赖routingKey,是使⽤发送消息时basicProperties对象中的headers来匹配的。
headers是⼀个键值对类型,发送者发送消息时将这些键值对放到basicProperties对象中的headers字段中,队列绑定交换机时绑定⼀些键值对,当两者匹配时,队列就可以收到消息。
匹配模式有两种,在队列绑定到交换机时⽤x-match来指定,all代表定义的多个键值对都要满⾜,⽽any则代码只要满⾜⼀个就可以了。
fanout,direct,topic exchange的routingKey都需要要字符串形式的,⽽headers exchange则没有这个要求,因为键值对的值可以是任何类型。
2、图⽰说明:
消息1附带的键值对与Q9绑定键值对的color匹配、speed不匹配,但是Q9的x-match设置为any,因此只要有⼀项匹配消息1就可以被分发到Q9。
消息1与Q10完全匹配,消息2与Q10部分匹配,由于Q10的x-match设置为all,所以只能接受到消息1。
3、代码⽰例
⽣产者⽰例:
//声明转发器和类型headers
channel.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.HEADERS,false,true,null);
String message = "消息1";
Map<String,Object> headers = new Hashtable<String, Object>();
headers.put("aaa", "01234");
Builder properties = new BasicProperties.Builder();
properties.headers(headers);
// 指定消息发送到的转发器,绑定键值对headers键值对
channel.basicPublish(EXCHANGE_NAME, "",properties.build(),message.getBytes());
消费者⽰例:
//声明转发器和类型headers
channel.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.HEADERS,false,true,null);
channel.queueDeclare(QUEUE_NAME,false, false, true,null);
Map<String, Object> headers = new Hashtable<String, Object>();
headers.put("x-match", "any");//all any
headers.put("aaa", "01234");
headers.put("bbb", "56789");
// 为转发器指定队列,设置binding 绑定header键值对
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME,"", headers);
QueueingConsumer consumer = new QueueingConsumer(channel);
// 指定接收者,第⼆个参数为⾃动应答,⽆需⼿动应答
channel.basicConsume(QUEUE_NAME, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(message);
}
⽰例代码API介绍:
(1) channel.exchangeDeclare(exchange, type, durable, autoDelete, arguments);
exchange:交换机名
type:交换机类型
durable:持久化标志
autoDelete:⾃动删除
arguments:扩展参数,具体如下表
扩展参数键意义
alternate-exchange 下⾯简称AE,当⼀个消息不能被route的时候,如果exchange设定了AE,则消息会被投递到AE。
如果存在AE链,则会按此继续投递,直到消息被route或AE链结束或遇到已经尝试route过消息的AE。
(2)channel.queueBind(queue, exchange, routingKey, arguments);queue:队列名
exchange:交换机名
routingKey:选择键(路由键)
arguments:headers类型交换机绑定时指定的键值对
queueDeclare(String queue,
boolean durable,
boolean exclusive,
Map<String, Object> arguments);
queue: 队列名称
durable:是否持久化, 队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang⾃带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库
exclusive:是否排外的,有两个作⽤,⼀:当连接关闭时connection.close()该队列是否会⾃动删除;⼆:该队列是否是私有的private,如果不是排外的,可以使⽤两个消费者都访问同⼀个队列,没有任何问题,如果是排外的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常:
com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=405, reply-text=RESOURCE_LOCKED - cannot obtain exclusive access to locked queue 'queue_name' in vhost '/', class-id=50, method-id=20)⼀般等于true的话⽤于⼀个队列只能有⼀个消费者来消费的场景
autoDelete:是否⾃动删除,当最后⼀个消费者断开连接之后队列是否⾃动被删除,可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会⾃动删除
arguments:
队列中的消息什么时候会⾃动被删除?
Message TTL(x-message-ttl):设置队列中的所有消息的⽣存周期(统⼀为整个队列的所有消息设置⽣命周期), 也可以在发布消息的时候单独为某个消息指定剩余⽣存时间,单位毫秒, 类似于redis中的ttl,⽣存时间到了,消息会被从队⾥中删除,注意是消息被删除,⽽不是队列被删除,特性Features=TTL, 单独为某条消息设置过期时间AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().builder().expiration(“6000”);
channel.basicPublish(EXCHANGE_NAME, “”, properties.build(), message.getBytes(“UTF-8”));
Auto Expire(x-expires): 当队列在指定的时间没有被访问(consume, basicGet, queueDeclare…)就会被删除,Features=Exp
Max Length(x-max-length): 限定队列的消息的最⼤值长度,超过指定长度将会把最早的⼏条删除掉,类似于mongodb中的固定集合,例如保存最新的100条消
息, Feature=Lim
Max Length Bytes(x-max-length-bytes): 限定队列最⼤占⽤的空间⼤⼩,⼀般受限于内存、磁盘的⼤⼩, Features=Lim B
Dead letter exchange(x-dead-letter-exchange):当队列消息长度⼤于最⼤长度、或者过期的等,将从队列中删除的消息推送到指定的交换机中去⽽不是丢弃
掉,Features=DLX
Dead letter routing key(x-dead-letter-routing-key):将删除的消息推送到指定交换机的指定路由键的队列中去, Feature=DLK
Maximum priority(x-max-priority):优先级队列,声明队列时先定义最⼤优先级值(定义最⼤值⼀般不要太⼤),在发布消息的时候指定该消息的优先级,优先级
更⾼(数值更⼤的)的消息先被消费,
Lazy mode(x-queue-mode=lazy): Lazy Queues: 先将消息保存到磁盘上,不放在内存中,当消费者开始消费的时候才加载到内存中
Master locator(x-queue-master-locator)
⼀不⽤交换机的队列
⽣产者⽰例:
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发送10条消息,依次在消息后⾯附加1-10个点
for (int i = 6; i > 0; i--)
{
String message = "helloworld";
channel.basicPublish("", QUEUE_NAME,null, message.getBytes());
}
消费者⽰例:
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
// 指定消费队列
channel.basicConsume(QUEUE_NAME, true, consumer);
while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
doWork(message);
}
API介绍
(1) channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments) ;
可以看到⽣产者和消费者⽤同样的参数声明了队列,官⽅推荐该做法,事实上对于⼀个已经存在的队列即使该⽅法试图⽤不同的参数去创建队列也不会有任何效果,这意味着不会改变队列更不会影响队列现在的⼯作。
queue:队列名字
durable:队列持久化标志,true为持久化队列
exclusive:exclusive:排他队列,如果⼀个队列被声明为排他队列,该队列仅对⾸次声明它的连接可见,并在连接断开时⾃动删除。
这⾥需要注意三点:其⼀,排他队列是基于连接可见的,同⼀连接的不同信道是可以同时访问同⼀个连接创建的排他队列的。
其⼆,“⾸次”,如果⼀个连接已经声明了⼀个排他队列,其他连接是不允许建⽴同名的排他队列的,这个与普通队列不同。
其三,即使该队列是持久化的,⼀旦连接关闭或者客户端退出,该排他队列都会被⾃动删除的。
这种队列适⽤于只限于⼀个客户端发送读取消息的应⽤场景。
autoDelete:⾃动删除,如果该队列没有任何订阅的消费者的话,该队列会被⾃动删除。
这种队列适⽤于临时队列。
arguments:Map类型,关于队列及队列中消息的详细设置
arguments键值意义
x-message-ttl 数字类
型,标志
时间,以
豪秒为单
位
标志队列中的消息存活时间,也就是说队列中的消息超过了制定时间会被删除
x-expires 数字类
型,标志
时间,以
豪秒为单
位
队列⾃⾝的空闲存活时间,当前的queue在指定的时间内,没有consumer、basic.get也就是未被访问,就会被删除。
x-max-length和
x-max-length-
bytes 数字
最⼤长度和最⼤占⽤空间,设置了最⼤长度的队列,在超过了最⼤长度后进⾏插⼊会删除之前插⼊的消息为本次的留出空间,相应的最⼤占⽤⼤⼩也是这个道理,当超过了这个⼤⼩的时候,会删除之前插⼊的消息为本次的留出空间。
x-dead-letter-
exchange和x-dead-letter-routing-key 字符串
消息因为超时或超过限制在队列⾥消失,这样我们就丢失了⼀些消息,也许⾥⾯就有⼀些是我们做需要获知的。
⽽rabbitmq的死信功能则为我们带来了解决⽅案。
设置了dead letter exchange与dead letter routingkey(要么都设
定,要么都不设定)那些因为超时或超出限制⽽被删除的消息会被推动到我们设置的exchange中,再根据routingkey推
到queue中
x-max-priority数字队列所⽀持的优先级别,列如设置为5,表⽰队列⽀持0到5六个优先级别,5最⾼,0最低,当然这需要⽣产者在发送消息时指定消息的优先级别,消息按照优先级别从⾼到低的顺序分发给消费者
(2)channel.basicPublish(exchange, routingKey, mandatory, immediate, basicProperties, body);
exchange: 交换机名
routingKey: 路由键
mandatory:当mandatory标志位设置为true时,如果exchange根据⾃⾝类型和消息routeKey⽆法找到⼀个符合条件的queue,那么会调⽤basic.return⽅法将消息返还给⽣产者, channel.addReturnListener添加⼀个监听器,当broker执⾏basic.return⽅法时,会回调handleReturn⽅法,这样我们就可以处理变为死信的消息了;当mandatory 设为false时,出现上述情形broker会直接将消息扔掉;通俗的讲,mandatory标志告诉broker代理服务器⾄少将消息route到⼀个队列中,否则就将消息return给发送者。
immediate: rabbitMq3.0已经不在⽀持了,3.0以前这个标志告诉服务器如果该消息关联的queue上有消费者,则马上将消息投递给它,如果所有queue都没有消费者,直接把消息返还给⽣产者,不⽤将消息⼊队列等待消费者了。
basicProperties:消息的详细属性,例如优先级别、持久化、到期时间,headers类型的exchange要⽤到的是其中的headers字段。
public BasicProperties(
String contentType,//消息类型如:text/plain
String contentEncoding,//编码
Map<String,Object> headers,
Integer deliveryMode,//1:nonpersistent 2:persistent
Integer priority,//优先级
String correlationId,
String replyTo,//反馈队列
String expiration,//expiration到期时间
String messageId,
Date timestamp,
String type,
String userId,
String appId,
String clusterId)
body:消息实体,字节数组。
(3)QueueingConsumer:这是⼀个已经实现好了的Consumer,相⽐于⾃⼰实现Consumer接⼝,这是个⽐较安全快捷的⽅式。
该类基于jdk的BlockingQueue实
现,handleDelivery⽅法中将收到的消息封装成Delivery对象,并存放到BlockingQueue中,这相当于消费者本地存放了⼀个消息缓存队列。
nextDelivery()⽅法底层调⽤的BlockingQueue的阻塞⽅法take()。
(4)channel.basicConsume(queue, autoAck, consumer);
queue:队列名。
autoAck:⾃动应答标志,true为⾃动应答。
consumer:消费者对象,可以⾃⼰实现Consumer接⼝,建议使⽤QueueingConsumer。
⼆ fanout类型的交换机
1、消息与队列匹配规则:fanout类型交换机会将接收到的消息⼴播给所有与之绑定的队列。
2、现在我们来演⽰⼀下如图所⽰的消息⼴播机制,不难注意到这种情况⽣产者P只关⼼消息发送给哪个交换机,由交换机X决定消息发送到哪些队列,,⽽消费者C只关注订阅哪个队列。
⽣产者⽰例:
// 声明转发器和类型
channel.exchangeDeclare(EXCHANGE_NAME, "fanout" );
String message = "消息1";
// 往转发器上发送消息
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
消费者⽰例:
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
// 创建⼀个⾮持久的、唯⼀的且⾃动删除的队列
String queueName = channel.queueDeclare().getQueue();
// 为转发器指定队列,设置binding
channel.queueBind(queueName, EXCHANGE_NAME, "");
QueueingConsumer consumer = new QueueingConsumer(channel);
// 指定接收者,第⼆个参数为⾃动应答,⽆需⼿动应答
channel.basicConsume(queueName, true, consumer);
while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
doSomething(message);
}
⽰例代码所⽤API介绍:
(1)channel.exchangeDeclare(exchange, type );
exchange: 交换机名。
type:交换机类型,值为fanout、direct、topic、headers其中之⼀。
(2)channel.queueDeclare():创建⼀个⾮持久的、唯⼀的、⾃动删除的队列且队列名称由服务器随机产⽣。
(3)channel.queueBind(queue, exchange, bindingKey);
queue:队列名。
exchange:交换机名。
bindingKey:绑定键。
(4)channel.basicPublish(exchange, routingKey, basicProperties, body)
exchange:交换机名。
routingKey:消息绑定的路由键。
basicProperties:消息属性,详细字段见上⼀节不⽤交换机的队列
body:消息实体,字节数组。
三 direct类型的交换机
1、消息分发规则:消息会被推送⾄绑定键(binding key)和消息发布附带的选择键(routing key)完全匹配的队列。
2、图⽰说明:消息1附带路由键“error”、与绑定键“error”匹配,⽽队列Q4、Q5与交换机X间都存在绑定键“error”所以消息1被分发到Q4、Q5;消息2附带路由键“info”,⽽队列Q4与交换机间存在绑定建“info”,所以消息2被分发到队列Q4。
3、分发到队列的消息不再带有绑定键,事实上分发到队列的消息不再带有发送者的任何信息,当然如果消息实体⾥⾯包含了发送者消息,那么消费者可以获取发送者信息。
⽣产者⽰例:
chanel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 发布消息⾄转发器,指定routingkey
chanel.basicPublish(EXCHANGE_NAME, "error", null, message1.getBytes());
chanel.basicPublish(EXCHANGE_NAME, "info", null, message2.getBytes());
消费者⽰例:
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
// 指定binding_key
channel.queueBind(queueName, EXCHANGE_NAME, "error");
channel.queueBind(queueName, EXCHANGE_NAME, "warning");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer);
while (true)
{
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
doSomething(message);
}
四 topic类型的交换机
1、消息分发规则:⼀个附带特殊的选择键将会被转发到绑定键与之匹配的队列中。
2、routingKey于bindingKey匹配规则:
routingKey必须是由点隔开的⼀系列的标识符组成。
标识符可以是任何东西,但是⼀般都与消息的某些特性相关。
⼀些合法的选择键的例⼦:”d.nyse”,“nyse.vmw”,”quick.orange.rabbit”.你可以定义任何数量的标识符,上限为255个字节。
绑定键和选择键的形式⼀样。
需要注意的是:关于绑定键有两种特殊的情况。
*可以匹配⼀个标识符。
#可以匹配0个或多个标识符。
3、图⽰说明:
消息1附带路由键“fast.orange.*”与绑定键“#”、“*.orange.*”匹配,所以消息1被分发给队列Q6、Q7;消息2附带路由键“lazy.orange.a.b”与绑定键“#”、“lazy.#”匹配,所以消息2被分发给队列Q6、Q8。
代码⽰例与direct类型转发器基本雷同,只是路由键和绑定键格式不⼀样,这⾥不再赘述。
四 headers类型的交换机
1、消息分发规则:headers类型的交换机分发消息不依赖routingKey,是使⽤发送消息时basicProperties对象中的headers来匹配的。
headers是⼀个键值对类型,发送者发送消息时将这些键值对放到basicProperties对象中的headers字段中,队列绑定交换机时绑定⼀些键值对,当两者匹配时,队列就可以收到消息。
匹配模式有两种,在队列绑定到交换机时⽤x-match来指定,all代表定义的多个键值对都要满⾜,⽽any则代码只要满⾜⼀个就可以了。
fanout,direct,topic exchange的routingKey都需要要字符串形式的,⽽headers exchange则没有这个要求,因为键值对的值可以是任何类型。
2、图⽰说明:
消息1附带的键值对与Q9绑定键值对的color匹配、speed不匹配,但是Q9的x-match设置为any,因此只要有⼀项匹配消息1就可以被分发到Q9。
消息1与Q10完全匹配,消息2与Q10部分匹配,由于Q10的x-match设置为all,所以只能接受到消息1。
3、代码⽰例
⽣产者⽰例:
//声明转发器和类型headers
channel.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.HEADERS,false,true,null);
String message = "消息1";
Map<String,Object> headers = new Hashtable<String, Object>();
headers.put("aaa", "01234");
Builder properties = new BasicProperties.Builder();
properties.headers(headers);
// 指定消息发送到的转发器,绑定键值对headers键值对
channel.basicPublish(EXCHANGE_NAME, "",properties.build(),message.getBytes());
消费者⽰例:
//声明转发器和类型headers
channel.exchangeDeclare(EXCHANGE_NAME, ExchangeTypes.HEADERS,false,true,null);
channel.queueDeclare(QUEUE_NAME,false, false, true,null);
Map<String, Object> headers = new Hashtable<String, Object>();
headers.put("x-match", "any");//all any
headers.put("aaa", "01234");
headers.put("bbb", "56789");
// 为转发器指定队列,设置binding 绑定header键值对
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME,"", headers);
QueueingConsumer consumer = new QueueingConsumer(channel);
// 指定接收者,第⼆个参数为⾃动应答,⽆需⼿动应答
channel.basicConsume(QUEUE_NAME, true, consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(message);
}
⽰例代码API介绍:
(1) channel.exchangeDeclare(exchange, type, durable, autoDelete, arguments);
exchange:交换机名
type:交换机类型
durable:持久化标志
autoDelete:⾃动删除
arguments:扩展参数,具体如下表
扩展参数键意义
alternate-exchange 下⾯简称AE,当⼀个消息不能被route的时候,如果exchange设定了AE,则消息会被投递到AE。
如果存在AE链,则会按此继续投递,直到消息被route或AE链结束或遇到已经尝试route过消息的AE。
(2)channel.queueBind(queue, exchange, routingKey, arguments);queue:队列名
exchange:交换机名
routingKey:选择键(路由键)
arguments:headers类型交换机绑定时指定的键值对。