java中的RabbitMq

1、创建连接到RabbitMq服务器的连接

     在RabbitMq所有的操作都是在信道中完成的。信道是建立在真实的TCP连接内的虚拟连接,应用程序通过TCP连接到RabbitMQ,在TCP没有关闭的情况下再通过创建信道发送AMQP命令。每条信道都会有专门的唯一ID进行标记,这点RabbitMq会帮你记住。RabbitMq使用信道的原因(摘抄网上的,因为写的不错)

1. TCP的创建和销毁,开销大,创建需要三次握手,销毁需要四次分手

2. 如果不使用信道,那么引用程序就会使用TCP的方式连接到rabbitmq,高峰时每秒成千上万条连接会造成资源的巨大浪费(一条tcp消耗资源
,成千上万的tcp会非常消耗资源),而且操作系统每秒处理TCP连接数量也是有限的,必定会造成性能瓶颈

3.信道的原理是一条线程一条信道,多条线程多条信道共同使用一条TCP连接。一条TCP连接可以容纳无限的信道,及时每秒造成成千上万的
请求也不会造成性能瓶颈

所以要使用RabbitMq,首先的两个步骤就是创建TCP连接,然后再创建信道,只有在信道中才能发送AMQP命令

1-1)创建TCP连接到RabbitMq,其实这一部分主要是进行登陆认证,RabbitMq默认采用的是工厂模式进创建TCP连接

ConnectionFactory connectionFactory = new ConnectionFactory();//创建连接工厂
connectionFactory.setHost("127.0.0.1");//RabbitMq的Host
connectionFactory.setPort(5672);//RabbitMq的端口
connectionFactory.setPassword("guest");//可以登录RabbitMq的账号
connectionFactory.setUsername("guest");//可以登录RabbitMq的密码
Connection connection = connectionFactory.newConnection();//创建一个TCP连接

当然也可以通过配置文件外部导入(创建外部文件进行导入rabbitMq-config.properties,推荐用这种方便移植,我还没翻译完)

rabbitmq.username=guest
rabbitmq.password=guest
rabbitmq.host=127.0.0.1
rabbitmq.port=5672
#rabbitmq.connection.channel.max=
#rabbitmq.connection.frame.max=
#rabbitmq.connection.heartbeat=
#rabbitmq.connection.timeout=
#rabbitmq.handshake.timeout=
#rabbitmq.shutdown.timeout=
#rabbitmq.connection.recovery.enabled=
#rabbitmq.topology.recovery.enabled=
#rabbitmq.connection.recovery.interval=
#rabbitmq.channel.rpc.timeout=
#rabbitmq.channel.should.check.rpc.response.type=
#rabbitmq.use.nio=
#rabbitmq.nio.read.byte.buffer.size=
#rabbitmq.nio.write.byte.buffer.size=
#rabbitmq.nio.nb.io.threads=
#rabbitmq.nio.write.enqueuing.timeout.in.ms=
#rabbitmq.nio.write.queue.capacity=
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.load(ConsumerMain.class.getClassLoader().getResource("rabbitMq-config.properties").getPath());
Connection connection = connectionFactory.newConnection();

1-2)创建完TCP认证连接之后,就是创建信道Channel,所有的AMQP命令都是用信道发送的,这里我认为所有与RabbitMq的交互都是采用AMQP命令,也是因为RaabbitMq就是遵循AMQP的服务器。

try{
    Channel channel = connection.createChannel();//创建一个信道,而且是自动分配ID
}finally{  //最好把清理工作放在finally中
    channel.close();//关闭信道
    connection.close();//关闭连接
}

2、交换器的存在

     生产者并非直接把消息投递到队列上去,而是把消息发送到交换器上,然后再交换器根据某些特定的规则发送到相应队列上去。并被消费者接受。而这个特定的规则就是路由键(routingkey),交换器通过路由键绑定到队列。

                                               

这里可以看到生产者发布消息并非直接发送到队列上去,而是先提交个交换器,再由交换器传到队列上去。交换器的目的我个人认为其实还是为了解耦和复用。假如说没有交换器,生产者直接与队列进行交互,那么生产者就不得不知道队列的名字。这样做确实可以,但是在某些场景就不是那么实用。假如说队列不是由生产者创建的,而是由消费者创建,那么生产者还是得知道队列的名字。你可能会说生产者还是得知道交换器的名字,这点交换器可以通过特定的规则连接一个(direct)或者多个队列(Fanout和Topic)那么生产者只需知道一个交换器的名字,而不用知道每一条队列的名字。换句话说,如果你需要复杂的使用案例,通过交换器可以实现发布/订阅或者多播。

                                                     

这里交换器通过路由键(也就是图中的*.server logs.* api_call)绑定到队列上。可以看到交换器上由Topic,Fanout,Direct,注意这里是交换器的类型,交换器的类型可以是direct,topic,fanout,headers(这里headers性能较差就不提了,以后会补上)。

2-1)direct交换器(类似于1对1投递)

channel.exchangeDeclare("HELLO_EXCHANGE", BuiltinExchangeType.DIRECT);//第一个参数是交换器名,第二个参数是交换器类型

direct交换器顾名思义就是路由键的匹配规则为一对一完全匹配,一个direct交换器可以连接多个队列。每个队列都有自己专属的路由键。与direct交换器发布信息的时候,只有同时传递正确的路由键,消息才会被发送到自己想要的队列上去。注意不同队列可以由相同的路由键,消息会同时发送给路由键相同的队列。上面声明了一个叫HELLO_EXCHANGE的direct交换器

                                              è¿éåå¾çæè¿°

RabbitMq的服务器会有默认的direct交换器,也是direct交换器。如果你创建了一个队列,但是没有绑定到交换器的时候,服务器会先自动的把队列连接到该默认的direct交换器,同时将队列的名称作为路由键。下面暂时列出队列声明和发送消息的做法。

channel.queueDeclare("HELLO_QUEUE1", false, false, false, null);//声明队列
channel.basicPublish("", "HELLO_QUEUE1",null, message.getBytes());//发布消息

Channel.basicPublish()方法第一个参数是交换器的名称,这里我们选择为空的情况下就是指定默认交换器,第二个参数是路由键,通过传递队列名作为路由键。而且可以发现队列创建后没有明显连接任何交换器,RabbitMq内部是偷偷连接了默认交换器。

2-2)Fanout交换器(类似于1对N的情况)

channel.exchangeDeclare("HELLO_EXCHANGE", BuiltinExchangeType.FANOUT);

fanout交换器作用就是把消息发送给连接到该交换器的所有队列上。也就是广播的作用。fanout交换器并不会去思考任何的路由键,因为它只会把所有的信息发送到队列上,所以因为这点,fanout并不需要路由键。而且fanout交换器的速度是四种交换器最快的。

                        è¿éåå¾çæè¿°

fanout交换器可以允许你对一条信息进行不同的反应,例如说用户提交了评论,需要写入数据库和及时显示给用户上。这时我们就可以创建两条队列,一个负责写入数据库,一条立刻发送给用户。除此之外你可以不断添加队列连接到Fanout交换器上,让一条信息可以有多种处理。

由于使用fanout交换区不需要路由键,所以我们可以在投递信息的时候不用写明路由键,只是需要写明交换器的名称就可以了

channel.exchangeDeclare("HELLO_EXCHANGE", BuiltinExchangeType.FANOUT);
channel.queueDeclare("HELLO_QUEUE1", false, false, false, null);
channel.queueDeclare("HELLO_QUEUE2", false, false, false, null);
channel.queueBind("HELLO_QUEUE1", "HELLO_EXCHANGE", "");
channel.queueBind("HELLO_QUEUE2", "HELLO_EXCHANGE", "");
channel.basicPublish("HELLO_EXCHANGE", "",null, message.getBytes());//第二个参数为路由键保持为空

这里要记住一点,即使你传递了路由键到fanout交换器,依然是会把所有信息发送到绑定到交换器的全部队列上。

2-3)topic交换器(类似于N对1的情况)

channel.exchangeDeclare("HELLO_EXCHANGE2", BuiltinExchangeType.TOPIC);

topic允许根据特定的规则将多条信息投递到一条队列上去。其实你可以这么理解topic交换器可以绑定一个能匹配多个路由键(重申一遍,路由键就是队列绑定交换器的规则)的队列。一个队列如何适应多个路由键呢,答案就是通配符模式。路由键支持两种通配符模式,* 通配符和 # 通配符

         *通配符,表示匹配1个或者多个字符,例如*.log可以匹配 a.log,b.log,c.log

         #通配符,匹配所有的规则,也就是说全部的信息都会发送到该队列上.(这种情况要特别注意)

                  è¿éåå¾çæè¿°

channel.queueBind("HELLO_QUEUE1", "HELLO_EXCHANGE2", "*.first");//绑定到topic交换器
channel.basicPublish("HELLO_EXCHANGE2", "A.first",null, message.getBytes());

topic交换器的应用场景可以这样去考虑,一个程序可能会针对日志有多种输出,error,info,warn,这时可能后面突然要添加一个收集全部日志的情况,这时就可以创建topic交换器.

这里要注意一点,就是创建了交换器之后,你就不能再去修改类型了

channel.exchangeDeclare("HELLO_EXCHANGE2", BuiltinExchangeType.TOPIC);//ok
channel.exchangeDeclare("HELLO_EXCHANGE2", BuiltinExchangeType.DIRECT);//error

还有一点,如果路由键不匹配怎么办,这是你的信息就不见了(这点还需要考证,因为路由键不匹配但是认证却投递成功)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值