【RabbitMQ】工作队列和发布/订阅模式的具体实现

建立连接

我们把建立连接时,创建的连接工厂部分创建成常量,方便后面进行使用

  • rabbitmq 包下,再创建一个 constant
package rabbitmq.constant;  
  
public class Constants {  
    static public final String HOST = "localhost";  
    static public final int PORT = 5672;  
    static public final String USER_NAME = "study";  
    static public final String PASSWORD = "study";  
    static public final String VIRTUAL_HOST = "coding ";  
}

工作队列模式实现

和简单模式相比较,工作队列与之不同的就是有多个消费者,其他都一样。所以我们只需要多添加几个消费者即可

创建队列和交换机

Constants 中添加:

// 工作队列模式  
public static final String WORK_QUEUE = "work.queue ";

生产者代码

package rabbitmq.work;  
  
import com.rabbitmq.client.Channel;  
import com.rabbitmq.client.Connection;  
import com.rabbitmq.client.ConnectionFactory;  
import rabbitmq.constant.Constants;  
  
import java.io.IOException;  
import java.util.concurrent.TimeoutException;  
  
public class Producer {  
    public static void main(String[] args) throws IOException, TimeoutException {  
  
        // 1. 建立连接  
        ConnectionFactory connectionFactory = new ConnectionFactory();  
        connectionFactory.setHost(Constants.HOST);  
        connectionFactory.setPort(Constants.PORT);  
        connectionFactory.setUsername(Constants.USER_NAME);  
        connectionFactory.setPassword(Constants.PASSWORD);  
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);  
  
        Connection connection = connectionFactory.newConnection();  
  
        //2. 开启信道  
        Channel channel = connection.createChannel();  
  
        //3. 声明队列  
        channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);  
  
        //4. 发送消息  
        for (int i = 0; i < 10; i++) {  
            String msg = "hello work queue..." + i;  
            channel.basicPublish("", Constants.WORK_QUEUE, null, msg.getBytes());  
        }  
  
        System.out.println("消息发送成功!");  
  
        // 5. 资源释放  
        channel.close();  
        connection.close();  
    }  
}

消费者代码

package rabbitmq.work;  
  
import com.rabbitmq.client.*;  
import rabbitmq.constant.Constants;  
  
import java.io.IOException;  
import java.util.concurrent.TimeoutException;  
  
public class Consumer1 {  
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {  
        // 1. 建立连接  
        ConnectionFactory connectionFactory = new ConnectionFactory();  
        connectionFactory.setHost(Constants.HOST);  
        connectionFactory.setPort(Constants.PORT);  
        connectionFactory.setUsername(Constants.USER_NAME);  
        connectionFactory.setPassword(Constants.PASSWORD);  
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);  
  
        Connection connection = connectionFactory.newConnection();  
  
        //2. 开启信道  
        Channel channel = connection.createChannel();  
  
        //3. 声明队列  
        channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);  
  
        //4. 消费消息  
        DefaultConsumer consumer = new DefaultConsumer(channel){  
            // 从队列中收到消息,就会执行的方法  
            @Override  
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
                //TODO  
                System.out.println("接收到消息: " + new String(body));  
            }  
        };  
        channel.basicConsume(Constants.WORK_QUEUE, true, consumer);  
  
  
        // 等待程序执行完成  
        Thread.sleep(2000);  
  
        // 5. 释放资源  
//        channel.close();  
//        connection.close();  
    }  
}
  • 多个消费者的代码都一样的

运行程序

我们先启动两个消费者,再启动生产者

  • 如果先启动生产者,再启动消费者,由于消息较少,处理较快,那么第一个启动的消费者就会瞬间把 10 条消息消费掉,所以我们先启动两个消费者,再启动生产者

启动消费者

我们将两个消费者启动

  • 我们可以看到 rabbitmq 客户端里面,work.queue 队列已经被创建了出来
  • image.png

启动生产者

在启动消费者之后,我们启动生产者,发送 10 条消息到队列中

  • 我们可以看到,连个该消费者将 10 条消息消费完了image.png

发布/订阅模式实现

在发布/订阅模式中,多了一个 Exchange 角色

Exchange 常见有三种类型,分别代表不同的路由规则

  1. Fanout: 广播,将消息交给所有绑定到交换机的队列(Publish/Subscribe
  2. Direct: 定向,将消息交给符合指定 routingKey 的队列 (Routing 模式)
  3. Topic: 通配符,把消息交给符合 routing pattern(路由模式)的队列(Topics 模式)
    也就分别对应不同的工作模式

image.png

创建队列和交换机

Constants 中添加:

// 发布订阅模式  
public static final String FANOUT_EXCHANGE = "fanout.exchange";  
public static final String FANOUT_QUEUE1 = "fanout.queue1";  
public static final String FANOUT_QUEUE2 = "fanout.queue2";

生产者代码

发布/订阅模式的生产者代码和简单模式类似,只是有些变化

  • 需要声明交换机
  • 需要指出交换机和队列之间的关系

创建交换机

相比于生产者代码和简单模式,这一步是关键的一步。我们需要声明一个交换机,而不是使用默认交换机

channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);
  • 我们会使用到 exchangeDeclare() 方法
Exchange.DeclareOk exchangeDeclare(String exchange,  
BuiltinExchangeType type,  
boolean durable,  
boolean autoDelete,  
boolean internal,  
Map<String, Object> arguments) throws IOException;

参数解释:

  1. exchange:交换机名称
  2. type:交换机类型
    • Direct("direct"):定向,直连,routing
    • Fanout("fanout"):扇形(广播),每个队列都能收到消息
    • TOPIC("topic"):通配符
    • HEADERS("headers"):参数匹配(工作时用到的少)
  3. durable:是否持久化
    • true:持久化
    • false:非持久化
    • 持久化可以将交换器存盘,在服务器重启的时候不会丢失相关信息
  4. autoDelete:自动删除
    • 自动删除的前提是至少有一个对类或者交换器与这个交换器绑定,之后所有与这个交换器绑定的对类或交换器都与此解绑
    • 而不是这种理解:当与此交换器连接的客户端都断开时,RabbitMQ 会自动删除本交换器
  5. internal:内部使用,一般 false
    • 如果设置为 true,表示内部使用
    • 客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式
  6. argument:参数

声明两个队列

// 如果没有一个这样的队列,会自动创建;如果有,则不创建
channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);  
channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);

绑定队列和交换机

channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");  
channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");
  • 这里会用到 queueBind() 方法
queueBind(String queue, String exchange, String routingKey)

参数解释:

  1. queue:对类名称
  2. exchange:交换机名称
  3. routingKey:路由 key,路由规则
  • 如果交换机类型为 fanoutroutingKey 设置为 “”,表示每个消费者都能收到全部信息

发送消息

String msg = "hello fanout...";  
// 第二个参数 routingKey 为空。因为这是广播模式,交换机收到消息后需要全部转发(绑定的时候设为空,发送的时候也为空  
channel.basicPublish(Constants.FANOUT_EXCHANGE, "", null, msg.getBytes());  
System.out.println("消息发送成功!");
  • 这里会用到 basicPublish() 方法
basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)

参数解释:

  1. Exchange:交换机名称
  2. routingKey:如果交换机类型为 fanoutroutingKey 设置为 “”,表示每个消费者都能收到全部信息

完整代码

package rabbitmq.fanout;  
  
import com.rabbitmq.client.BuiltinExchangeType;  
import com.rabbitmq.client.Channel;  
import com.rabbitmq.client.Connection;  
import com.rabbitmq.client.ConnectionFactory;  
import rabbitmq.constant.Constants;  
  
import java.io.IOException;  
import java.util.concurrent.TimeoutException;  
  
public class Producer {  
    public static void main(String[] args) throws IOException, TimeoutException {  
  
        // 1. 建立连接  
        ConnectionFactory connectionFactory = new ConnectionFactory();  
        connectionFactory.setHost(Constants.HOST);  
        connectionFactory.setPort(Constants.PORT);  
        connectionFactory.setUsername(Constants.USER_NAME);  
        connectionFactory.setPassword(Constants.PASSWORD);  
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);  
  
        Connection connection = connectionFactory.newConnection();  
  
        //2. 开启信道  
        Channel channel = connection.createChannel();  
  
        //3. 声明交换机  
        /*  
        Exchange.DeclareOk exchangeDeclare(String exchange,        BuiltinExchangeType type,        boolean durable,        boolean autoDelete,        boolean internal,        Map<String, Object> arguments) throws IOException;        参数解释:  
        exchange:交换机名称  
        type:交换机类型  
            DIRECT("direct"),定向,直连,routing  
            FANOUT("fanout"),扇形(广播),每个队列都能收到消息  
            TOPIC("topic"),通配符  
            HEADERS("headers"),参数匹配(工作中用的少)  
        durable:是否持久化  
        autoDelete:自动删除  
        internal:内部使用(一般false)  
        arguments:参数  
         */        channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);  
  
        // 4. 声明队列  
        channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);  
        channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);  
  
        // 5. 绑定交换机和队列  
        channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");  
        channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");  
  
        // 6. 发布消息  
        String msg = "hello fanout...";  
        // 第二个参数 routingKey 为空。因为这是广播模式,交换机收到消息后需要全部转发(绑定的时候设为空,发送的时候也为空  
        channel.basicPublish(Constants.FANOUT_EXCHANGE, "", null, msg.getBytes());  
        System.out.println("消息发送成功!");  
  
        // 7. 释放资源  
        channel.close();  
        connection.close();  
    }  
}

消费者代码

主要的步骤为:

  1. 创建 Channel
  2. 接收消息,并处理

完整代码

package rabbitmq.fanout;  
  
import com.rabbitmq.client.*;  
import rabbitmq.constant.Constants;  
  
import java.io.IOException;  
import java.util.concurrent.TimeoutException;  
  
public class Consumer1 {  
    public static void main(String[] args) throws IOException, TimeoutException {  
  
        // 1. 建立连接  
        ConnectionFactory connectionFactory = new ConnectionFactory();  
        connectionFactory.setHost(Constants.HOST);  
        connectionFactory.setPort(Constants.PORT);  
        connectionFactory.setUsername(Constants.USER_NAME);  
        connectionFactory.setPassword(Constants.PASSWORD);  
        connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);  
        Connection connection = connectionFactory.newConnection();  
  
        // 2.建立信道  
        Channel channel = connection.createChannel();  
  
        // 3.声明队列(如果队列已经存在,就不会创建;不存在就会创建)  
        channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);  
  
        // 4. 消费信息  
        DefaultConsumer consumer = new DefaultConsumer(channel) {  
          // 从队列中收到消息,就会执行的方法  
            @Override  
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {  
                System.out.println("接收到消息:" + new String(body));  
            }  
        };  
        channel.basicConsume(Constants.FANOUT_QUEUE1, true, consumer);  
    }  
  
}

运行程序

启动生产者

  1. 消息全转发

image.png|424

  • 我们可以看到两个队列中分别有了一条消息
  • 这就是发布订阅模式,他会把收到的消息都转发
  1. 交换机绑定了队列
    image.png|374
  • 这里,我们可以看到交换机和队列之间的绑定关系

启动消费者

消费者 1:

接收到消息:hello fanout...

消费者 2:

接收到消息:hello fanout...
要在银河麒麟桌面操作系统V10 SP1上安装字体,你可以按照以下步骤进行操作: 1. 首先,将字体文件放置在适当的目录中。根据引用\[1\],你可以将字体文件放置在以下目录之一:/usr/share/fonts、~/.fonts/或~/.local/share/fonts。第一个目录是系统所有用户共享的,需要管理员权限进行安装。后两个目录是当前登录用户所有的,不需要管理员权限。你可以从网页下载所需的字体包,通常为ttf格式,或者从Windows系统的字体文件保存目录中获取字体文件。 2. 如果你下载的字体文件是RPM格式的,而银河麒麟桌面V10 SP1需要deb格式的安装包,你需要将RPM转换为deb包。根据引用\[2\],你可以使用以下命令进行转换:sudo alien XXX.rpm。如果你的系统没有安装alien包,你需要在线安装。 3. 打开存放字体包的文件夹,在文件夹中右键点击并选择"在终端打开"。 4. 如果你想将字体安装到/usr/share/fonts目录下,可以使用以下命令:sudo cp *.ttf /usr/share/fonts,然后使用sudo fc-cache -fv命令刷新系统字体缓存。 5. 如果你想将字体安装到~/.fonts/目录下(或者安装到~/.local/share/fonts的原理相同),可以使用以下命令:mkdir -p ~/.fonts,然后使用cp *.ttf ~/.fonts命令将字体文件复制到该目录,最后使用fc-cache -vf命令刷新系统字体缓存。 通过按照以上步骤进行操作,你就可以在银河麒麟桌面操作系统V10 SP1上成功安装字体了。 #### 引用[.reference_title] - *1* *3* [银河麒麟桌面操作系统V10 SP1 如何安装字体](https://blog.csdn.net/weixin_40579171/article/details/123852594)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [银河麒麟桌面操作系统V10SP1(X86)安装SQL Developer](https://blog.csdn.net/weixin_54752007/article/details/125361801)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值