RabbitMQ的使用建议

本文探讨了在使用RabbitMQ时的连接和通道管理最佳实践。作者建议每个应用程序实例应保持一个长期开启的连接,并且频繁创建和关闭通道以提高效率。文中还提到了避免连接和通道跨线程重用以防止问题发生。此外,分享了关于RoutingKey和QueueName的命名规范,以促进系统集成和事件订阅。在实践中发现,共享Channel可能导致粘包问题,因此改为每次发送消息时创建新的Channel。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经过4年的使用经验, 发现网上有很多的误解和错误的代码. 为清楚一些错误的观念和使用方式. 特意写了这个文章统一大家的编程规范和命名规范, 方便系统开发和系统集成.
增加了一些其它文章中的常见问题 和使用指南.和命名指南

首先要建立代码连接,然后绑定,然后发送.

代码中与RabbitMQ的连接是保持一打开永久使用的长连接的好呢? 还是每次需要都打开的好?

在数据库操作的时候是建议每次执行查询都打开连接, 然后再关上.
那么在操作RabbitMQ的时候, 是否也应该如此呢?

我的建议如下: 摘抄自 https://www.javaroad.cn/questions/94679

在RabbitMQ中,连接被认为是“昂贵的” - 它们占用TCP / IP端口,需要握手/协商等等 . 虽然这在SQL Server领域似乎微不足道,但当你谈到在RabbitMQ中每秒发送100K消息时,这种开销变得不可行 .

因此,对于RabbitMQ,一般的最佳做法是为每个应用程序实例打开一个连接,并尽可能长时间保持打开 - 如果可以的话最好是跟应用程序实例一样的生命周期,我个人一般设置为static 静态的. .

在app实例中,您可以在RabbitMQ连接之上创建通道(Channels) . 你可以非常快速地创建它们 . 大多数应用程序在RabbitMQ中使用单个通道进行单个操作 . 消息制作人?打开一个 Channels . 从队列中消费?打开一个 Channels . 重新定义队列?打开 Channels 等

另外 - 如果你使用的是具有线程的语言,比如C#,你必须将你的 Channels 限制为一个线程 . 不要跨线程重用通道 . 如果你试图这样做会发生非常糟糕的事情 .

所以根据上面的指南我的消息生产者 发布消息的代码.

package com.qcd.webapi.service;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.*;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
// import com.qcd.webapi.SocketServe;
import com.qcd.webapi.model.*; 
import com.rabbitmq.client.Connection; 
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.MessageProperties;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;

import java.net.URLEncoder;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
// import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
// import java.util;
import java.util.concurrent.TimeoutException;

// import com.weilan.openapi.test.RestTemplate;
// import sun.misc.BASE64Decoder;

@Slf4j
@Service
public class RabbitMQService {
  

    @Value("${app.RabbitMQ.IP}")
    private String AppRabbitMQIP;
    @Value("${app.RabbitMQ.Port}")
    private String AppRabbitMQPort;
    @Value("${app.RabbitMQ.UserName}")
    private String AppRabbitMQUserName;
    @Value("${app.RabbitMQ.Password}")
    private String AppRabbitMQPassword;
    @Value("${app.RabbitMQ.VirtualHost}")
    private String AppRabbitMQVirtualHost;
    @Value("${app.RabbitMQ.ExchangeName}")
    private String AppRabbitMQExchangeName;

    
    /**
     * 通知AI可以开始打分了.
     * 
     * @param msg
     * @return
     */

    static Connection connection = null;
    // static Channel channel = null;
    static Object lockobj = new Object();

    public Connection  getConnection() throws IOException, TimeoutException {
        // 1.创建连接工厂
        if (connection == null) {
            synchronized (lockobj) {
                if (connection == null) {
                    ConnectionFactory factory = new ConnectionFactory( );
                    factory.setAutomaticRecoveryEnabled(true);
                    factory.setHost(AppRabbitMQIP);
                    factory.setPort(Integer.parseInt(AppRabbitMQPort));
                    factory.setUsername(AppRabbitMQUserName);
                    factory.setPassword(AppRabbitMQPassword);
                    factory.setVirtualHost(AppRabbitMQVirtualHost);

                    // 创建与RabbitMQ服务器的TCP连接
                    connection = factory.newConnection();
                }
            }
        }
         return connection;
        // Channel  channel = connection.createChannel(); 
        // return channel;
    }

    // ————————————————
    // 版权声明:本文为优快云博主「卑微的小白」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    // 原文链接:https://blog.youkuaiyun.com/qq_39411354/article/details/109311332

    /**
     * 通知AI可以开始打分了.
     * 
     * @param msg
     * @return
     * @throws IOException
     * @throws TimeoutException
     */
    public boolean SendMsg(String routing_key, String msg) {
        Channel  channel =null;
        try {
            connection = getConnection();
            channel = connection.createChannel();
            // 创建一个通道,
            // channel = connection.createChannel();
            channel.exchangeDeclare(AppRabbitMQExchangeName, "topic", true);
            channel.basicPublish(AppRabbitMQExchangeName, routing_key, null, msg.getBytes());
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        } 
        finally {
            if (channel != null) {
                try {
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    protected void finalize() {
        if (connection != null) {
            try {
                connection.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

关于RoutingKey的命名规则.我建议是

E.系统.事件

例如:

E.WebAPI.DataReceived //意思是WebAPI系统触发了,数据已接收事件
E.WebAPI.OrderIsCreate //意思是WebAPI系统触发了,订单已创建事件
E.WebAPI.OrderIsDelete //意思是WebAPI系统触发了,订单已删除事件
E.WebAPI.OrderIsCheck //意思是WebAPI系统触发了,订单已审核事件
E.WebAPI.OrderIsInStock //意思是WebAPI系统触发了,订单已入库事件

在我的概念里, RoutingKey 约等于 事件名. 这个事件名一定要能够区分系统, 区分业务操作.
越大的概念越要放左边. 容易被topic模式统一订阅. 例如我要订阅WebAPI的所有业务日志, 可以在队列绑定的时候 订阅成 Event.WebAPI.*

关于QuneName的命名规则.我的建议

Q.系统.方法.on.系统.事件

Q.ERP.CreateWorkOrder.on.WebAPI.OrderIsCheck
意思是当WebAPI数据已接收, ERP系统开始创建生产工单

Q.ERP.CancelWorkOrder.on.WebAPI.OrderIsDelete
意思是WebAPI系统触发了,ERP系统开始撤销生产工单

Q.ERP.CompleteWorkOrder.on.WebAPI.OrderIsInStock
意思是WebAPI系统触发了,订单已入库事件,ERP系统修改工单为的已完成状态.

2021-12-31
经过使用了一段时间, 发现Channel 如果共用同一个, 似乎会发生粘包的现象. 至于是否由它引起, 目前不可知. 暂时改回 每次发送消息都创建一个通道 Channel, 上文代码已修正.

### 如何在 Windows 上使用 RabbitMQ #### 安装 RabbitMQ Server 为了能够在 Windows 平台上运行 RabbitMQ,首先需要安装 Erlang 和 RabbitMQ 服务器。可以通过 Chocolatey 或者手动下载安装包来完成这一步骤。 对于通过 Chocolatey 进行安装的方法如下: 打开命令提示符(以管理员身份),输入以下命令: ```powershell choco install erlang choco install rabbitmq ``` 如果选择手动安装,则需前往官方网站分别下载并执行对应的安装文件[^2]。 #### 启动与配置 RabbitMQ Service 成功安装之后,可以利用 `rabbitmq-service.bat` 脚本来启动服务,并确认其状态是否正常工作。同样地,在 PowerShell 中键入下面的内容来进行操作: ```powershell Start-Service RabbitMQ rabbitmqctl status ``` 上述指令会显示有关当前节点的状态信息,表明 RabbitMQ 已经正确部署完毕。 #### 配置管理控制台 为了让管理和监控变得更加直观便捷,建议启用官方提供的 Web 界面——Management Plugin。此插件允许用户查看队列详情、发布消息以及调整参数设置等功能。激活方式十分简单,只需一条简单的命令即可实现: ```powershell rabbitmq-plugins enable rabbitmq_management ``` 随后访问浏览器中的 http://localhost:15672 地址登录,默认账户名为 guest 密码也为 guest (注意生产环境中应更改默认凭证)。 #### 开发 Android 应用连接RabbitMQ 当考虑从移动设备发送或接收数据时,Android 应用程序能够借助 AMQP 协议同远程的消息代理建立联系。此时可选用 Spring Boot 提供的支持库简化开发流程;另外还需确保应用程序属性文件内包含了必要的网络配置项以便顺利连通目标实例[^3]。 例如,在项目的 `application.properties` 文件里添加如下定义: ```properties spring.rabbitmq.host=<your_server_ip> spring.rabbitmq.port=5672 spring.rabbitmq.username=<username> spring.rabbitmq.password=<password> ``` 以上即是在 Windows 操作系统环境下搭建和应用 RabbitMQ 的基本指南。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值