RabbitMQ消息队列实战(2)—— Java调用RabbitMQ的三种方式

news2024/11/19 14:40:10

本文主要介绍Java中调用RabbitMQ的三种方式。三种方式实际上对应了三种不同的抽象级别:

首先,通过Java原生代码来访问RabbitMQ。在这种方式下,需要手动创建Connection,创建Channel,然后通过Channel对象可以显式的创建Exchange、Queue和Bind等等。这种方式的好处就是使得我们能够很显式地了解到整个RabbitMQ操作的生命周期,建议新手可以通过这种方式学习RabiitMQ的入门。

spring-boot-starter-amqp对RabbitMQ的使用进行了进一步的封装,通过这种方式使用集成到spring boot中的RabbitMQ时,我们不再关心Connect和Channel的创建,spring boot会替我们创建好。我们索要做的,只是通过注解的方式创建Exchange、Queue和Bind对象,并把他们交给spring ioc进行管理,然后spring boot又会自动生成这些对象对应的交换机、队列和绑定。

Java中操作RabbitMQ的最后一种方法是通过EDA(Event Driven Achitecture,事件驱动架构)框架的spring cloud stream。spring cloud stream对RabitMQ(准确的说应该是消息队列)封装的更加彻底,我们甚至不用关心使用的消息队列是RabbitMQ还是Kafka(spring cloud stream可以配置RabbitMQ和Kafak两种消息队列,并进行无缝切换)。在使用时spring cloud stream时,只需一个标签就能自动创建RabitMQ的Connection、Chanel,甚至你都不用关心Exchange、Queue和Bind这些在spring-boot-starter-amqp中还需要手动创建的对象,他们就被创建好了。spring cloud stream的强大之处就在于它的封装,但是不足之处也在于它的封装,封装的太强,必然增加了学习成本和调试难度,而且类似RabbitMQ和Kafka这种中间件的使用,一般在系统创建之处就一定确定,进行无缝切换就显得有些鸡肋了。

下面,我们就以代码的方式演示这三种调用RabbitMQ的方式:

一、Java原生代码调用RabbitMQ

1.1 交换机和队列的创建

ConnectionFactory factory = new ConnectionFactory();
factory.setHost(this.rabbitMqHost);
factory.setPort(this.rabbitMqPort);
factory.setConnectionTimeout(this.rabbitMqTimeOut);
factory.setUsername(this.rabbitMqUsername);
factory.setPassword(this.rabbitMqPassword);
factory.setVirtualHost("/");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("direct-exchange", "direct", true, false, null);
channel.queueDeclare("test-queue", true, false, false, null);
com.rabbitmq.client.AMQP.Queue.BindOk ok = channel.queueBind("test-queue", "direct-exchange", "test-queue");
channel.basicPublish("direct-exchange", "test-queue", null, msg.getBytes("UTF-8"));

上述的代码,创建了一个直连交换机、一个队列,并进行绑定,最后向交换机中发送了一个"Hello World"的字符串。

1~7行,创建了一个ConnectionFactory 对象并进行配置,配置的参数包括RabbitMQ的ip地址(host),端口号(port)、超时(connectionTimeout)等等。

第8行,通过ConnectionFactory 对象,创建了一个Connection 对象,此时已经完成了对RabbitMQ服务器的连接。如果我们通过RabitMQ Magement Web查看,可以看到这个链接。

第9行,创建用来通信的信道Channel。

第10行,声明和创建交换机。这里exchangeDeclare有五个参数。第1个参数指定了交换机的名称;第2个参数指定了交换机的类型:direct、topic或者fanout;第3个参数,指定交换机是否要持久化,如果设置为true,那么交换机的元数据要持久化到内存中;第4个参数,指定交换机在没有队列与其绑定时,是否删除,设置为false表示不删除;最后一个参数是Map<String, Object>类型,用来指定交换机其它一些结构化的参数,我在这里直接设置为null。

第11行,声明了一个名为test-queue的队列。queueDeclare有5个参数:第1个参数指定了队列的名称;第2个参数表示队列是否要持久化,但是需要注意,这里的持久化只是队列名称等这些队列元数据的持久化,不是队列中消息的持久化。第3个参数,表示队列是不是私有的,如果是私有的,只有创建它的应用程序才能从队列消费消息;第4个参数表示队列在没有消费者订阅时是否自动删除;第5个参数是队列的一些结构化信息,比如声明死信队列、磁盘队列会用到。

第12行,创建了一个bind对象,将交换机和队列进行绑定,queueBind的三个参数中:第1个参数指定了队列名称,第2个参数指定了交换机名称,第3个参数是路由键,在直连模式下为队列名称。

第13行,发送消息,在直连模式下需要指定:直连交换机名称(参数1);路由键(参数2,也就是目标队列名称);参数3类型为BasicProperties,可以为消息附带一些额外的附件,比如在使用RabbitMQ远程RPC调用模式发送消息时可以用到,这里直接设置为null。参数4就是要发送的消息转换成的二进制数组。

上面就是一个创建direct exchange和queue并发送消息的例子,如果要使用topic exchange或者fanout exchange只需要一些小小的改动即可。

比如创建topic exchange要明确指明交换机的类型为topic:

 channel.exchangeDeclare("topic-exchange", "topic", true, false, null);

绑定时指定主题为路由键:

 channel.queueBind("test-queue", "topic-exchange", "fruit");

发送消息时指定主题为路由键:

channel.basicPublish("topic-exchange", "fruit", null, msg.getBytes("UTF-8"));

再比如创建fanout exchange要明确指明交换机的类型为fanout:

channel.exchangeDeclare("fanout-exchange", "fanout", true, false, null);

绑定时指定路由键为空:

channel.queueBind("test-queue", "fanout-exchange", "");

发布消息时指定路由键为空:

channel.basicPublish("fanout-exchange", "", null, msg.getBytes("UTF-8"));

1.2 消费者订阅队列的消息

在上面的例子中,我们演示了创建direct、topic和fanout三种类型的exchange以及关联好了队列,现在,我们创建一个消费者来订阅队列里面的消息。首先实现Consumer接口:

public class TestConsumer implements Consumer {

    @Override
    public void handleConsumeOk(String s) {
        System.out.println(s);
    }

    @Override
    public void handleCancelOk(String s) {}

    @Override
    public void handleCancel(String s) throws IOException {}

    @Override
    public void handleShutdownSignal(String s, ShutdownSignalException e) {}

    @Override
    public void handleRecoverOk(String s) {}

    @Override
    public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
        String str = new String(bytes);
        System.out.println("接受到的字符串是:" + str);
    }
}
              

Consumer接口的方法有6个,在这里我们只用到了2个,handleConsumeOk在消费者获取到消息后调用,而handleDelivery是在调用handleConsumeOk后调用。我们业务的主要逻辑在handleDelivery中,因为在这个方法之中,我们可以获取到消息,并进行相应的处理。实现了自己的消费者,接下来需要用该消费者订阅队列:

// 创建连接和信道
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(this.rabbitMqHost);
factory.setPort(this.rabbitMqPort);
factory.setConnectionTimeout(this.rabbitMqTimeOut);
factory.setUsername(this.rabbitMqUsername);
factory.setPassword(this.rabbitMqPassword);
factory.setVirtualHost("/");
Connection connection = null;
try {
    connection = factory.newConnection();
    Channel channel = connection.createChannel();
    Consumer consumer = new TestConsumer();
    channel.basicConsume("test-queue", true, consumer);
    while (true) {
        Thread.sleep(3600000);
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (connection != null && connection.isOpen()) {
        connection.close();
    }
}

第1~12行,和生产者创建Connection的过程一致。

第13行,创建了一个TestConsumer对象。

第14行,订阅队列test-queue中的消息。basicConsume方法有三个参数:第1个参数是指明要订阅的通道的名称;第2个参数指明是否自动ack,如果是true,这个方法结束后会自动进行ack,如果是false,需要额外手动的ack;第3个参数就是装配的消费者。

第15~17行,没有业务上的功能,只是单纯不让程序结束。

当运行了消费者以后,就可以看到,消费者消费了队列中的消息。

消费者打印消息:

队列中的消息已经清空:

以上就是通过Java原生的代码调用RabbitMQ的例子,接下来,我们学习下另外一种调用RabbitMQ的方式,通过 spring-boot-starter-amqp调用。

二、 spring-boot-starter-amqp调用RabbitMQ

2.1 生产者

首先,先要创建spring boot代码工程,并且pom文件中引入spring-boot-starter-amqp的依赖。

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

创建RabbitMQ的配置:

@Configuration
public class RabbitConfig {

    @Value("${spring.rabbitmq.host}")
    private String host;

    @Value("${spring.rabbitmq.port}")
    private int port;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPublisherConfirms(true);
        return connectionFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(connectionFactory());
        return template;
    }
    //队列 起名:TestDirectQueue
    @Bean
    public Queue TestDirectQueue() {
        // durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
        // exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
        // autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
        //   return new Queue("TestDirectQueue",true,true,false);
        //一般设置一下队列的持久化就好,其余两个就是默认false
        return new Queue("TestDirectQueue", true, false, false);
    }
    //Direct交换机 起名:TestDirectExchange
    @Bean
    DirectExchange TestDirectExchange() {
        //  return new DirectExchange("TestDirectExchange",true,true);
        return new DirectExchange("TestDirectExchange", true, false);
    }
    //绑定  将队列和交换机绑定, 并设置用于匹配键:TestDirectQueue
    @Bean
    Binding bindingDirect() {
        return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectQueue");
    }
}

可以看到,在上面的代码中我们创建了connectionFactory、rabbitTemplate、TestDirectQueue、TestDirectExchange和bindingDirect,我想从bean name大家也都能猜到,这是在创建RabbitMq的连接工厂、交换机、队列和绑定等等。其中rabbitTemplate我们在上文中没有接触过,实际上这就是spring boot对RabbitMQ根据bean创建信道、交换机等基本组件的封装,利用模板方法模式,将创建过程进行了隐藏,也对消息的发布和订阅过程进行了隐藏。

完成了上述配置之后,运行程序,是不是就自动创建了这些交换机、通道等等呢?也许你会回答是,但是笔者在运行程序后,查看RabbitMQ的web,发现还是一片空白:

是我们没有创建成功?其实不然,是因为rabbitTemplate在创建这些组件时,是采用的懒加载模式,只有在发送消息之前,才会去真正创建这些交换机、通道等等。所以,接下来,我们创建一个工具类,并在单元测试中通过该工具类发送消息:

@Service
public class RbmqServiceImpl implements RbmqService {

    @Autowired
    RabbitTemplate rabbitTemplate;


    @Override
    public void sendMsg(String str) {
        Message message=new Message(str.getBytes());
        rabbitTemplate.send("TestDirectExchange","TestDirectQueue",message);
    }
}

在单元测试中测试消息的发送:

@Test
public void sendMsgTest() {
    rbmqService.sendMsg("Hello World");
}
              

此时,再去看RabbitMQ的管理Web,发现交换机和队列都创建完成,而且队列中也缓存了消息:

2.2 消费者

前面介绍了使用spring boot集成的RabbitMQ组件创建生产者的方法,下面我们介绍下,消费者的创建。

配置类RabbitConfig的定义,基本和生产者一致,这里不再赘述,我们重点介绍下使用@RabbitListener标签监听队列的方法。首先,还是创建一个工具类:

@Service
public class RbmqServiceImpl implements RbmqService {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @RabbitListener(queues = "TestDirectQueue")
    @Override
    public void consumer(String msg) {
        System.out.println("接受到的消息是:" + msg);
    }
}

可以看到,通过RabbitListener标签,我们直接实现了订阅TestDirectQueue队列。此时运行程序,会接受并打印我们用生产者发送的消息:

@RabbitListener标签除了指定监听的队列之外,还可以创建交换机和队列,并进行绑定,然后再开启监听:

@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "TestTopicQueue", durable = "true"),
        exchange = @Exchange(value = "orderTopicExchange", type = ExchangeTypes.TOPIC),
        key = "TestTopicRouting")
)

以上就是使用spring-boot-starter-amqp集成RabbitMQ的方法,最后,我们学习下使用spring cloud stream来操作RabbitMQ。

三、spring cloud stream调用RabbitMQ

3.1 生产者

新建一个spring-boot工程,pom文件中引入以下依赖:

<dependencyManagement>
    <dependencies>
        <!-- spring-cloud-dependencies start-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- spring-cloud-dependencies end-->
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
</dependencies>

同样在配置文件application.yml中进行RabbitMQ链接信息的配置:

server:
  port: 8022
spring:
  #配置rabbitMq 服务器
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    #虚拟host 可以不设置,使用server默认host
    #virtual-host: JCcccHost

使用spring cloud stream方式调用RabbitMQ,首先要创建绑定接口:

 public interface OutputMessageBinding {
    /** Topic 名称*/
    String OUTPUT = "message-center";

    @Output(OUTPUT)
    MessageChannel output();
}

这里需要说明一下:

第5行通过Output标签指定了消息的输出exchange,这里会创建一个名称为message-center-out的exchange,而且类型为topic。通过Spring cloud stream创建的exchange默认的类型都是topic。

接下来,我们需要再次创建一个工具类:

@Service
@EnableBinding(OutputMessageBinding.class)
public class RbmqServiceImpl implements RbmqService {

    @Resource
    private OutputMessageBinding outputMessageBinding;

    @Override
    public void sendMsg(String msg) {
        outputMessageBinding.output().send(MessageBuilder.withPayload(msg).build());
    }
}

第2行中的EnableBinding就是为了激活绑定类OutputMessageBinding。OutputMessageBinding被激活之后会产生一个名称为outputMessageBinding的bean托管到IOC中,然后在第6行获取到了这个bean。在第10行中,获取outputMessageBinding的output对象进行消息的发送。

在单元测试中对发送信息的接口进行调用:

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class RbmqTest {

    @Autowired
    RbmqService rbmqService;

    @Test
    public void sendMsgTest() {
        rbmqService.sendMsg("Hello World");
    }
}

然后在web上可以看到新建的topic类型的exchange:

3.1 消费者

消费者的创建和使用过程其实和生产者比较类似,首先也是需要创建一个绑定接口:

public interface InputMessageBinding {

    String INPUT = "message-center";

    @Input(INPUT)
    SubscribableChannel input();
}

在消费者的绑定接口中,使用@Input标签用来表明该对象为消费者对象。接下来,同样也需要创建一个工具类并注入到IOC容器中:

@Service
@EnableBinding({InputMessageBinding.class})
public class RbmqServiceImpl implements RbmqService {

    @StreamListener(InputMessageBinding.INPUT)
    @Override
    public void consume(String msg) {
        System.out.println("接受到的消息是:" + msg);
    }
}

可以看到,在第8行,我们使用了StreamListener标签监听了创建的InputMessageBinding的INPUT字段,StreamListener会在内部进行处理,实际上监听的是名名为message-center+随机字符串的队列,而队列和message-center也自动进行了绑定:

看下上面红色圈出部分的队列和交换机的绑定,你会发现,绑定的路由键为'#',表示路由匹配任意规则,也就是说从名为message-center的exchange发出的消息都会路由到该队列上。

至此,Java中三种操作RabbitMQ的方式都已经介绍完毕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot+vue的问卷调查系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 功能分析…

学校机房高效稳定,一招见效

校园安全作为公共安全领域重要的一部分&#xff0c;一直以来都格外受到重视。近年来&#xff0c;各地区陆续发布了多项加强校园安全管理的政策、法规及标准规范&#xff0c;旨在贯彻落实构建“平安校园”的宗旨&#xff0c;不断完善校园的人防、物防、技防建设。 学校机房常见四…

AutoLisp演练(二)

一、自动绘制出多个等半径圆相切 1.输入基准点baspt 2.输入小圆半径rad 3. 输入欲相切的圆的数量num 4.自动绘制出多个等半径圆相切 5. 涉及到相关变量&#xff0c;设定为baspt、rad、num、midpt、cenpt、kk、ang1、ang2 二、程序代码实现 三、测试及效果 测试一 四、…

盘点一些惊艳一时的 CSS 属性

✨ 个人主页&#xff1a;山山而川~xyj ⚶ 作者简介&#xff1a;前端领域新星创作者&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油&#xff01; &#x1f386; 系列专栏&#xff1a; web 大前端 &#x1f680; 学习格言&#xff1a;与其临…

2023爬虫学习笔记 -- 某狗网站爬取数据

一、爬取某狗网站的首页1、导入需要的库文件import requests2、指定我们要访问的网址网页"https://www.sogou.com"3、获取服务器的返回的所有信息响应requests.get(网页)4、通过text属性&#xff0c;从返回信息中读取字符串内容响应内容响应.text5、查看读取到的内容…

唐宇迪机器学习实战课程笔记(全)

1. 线性回归1.1线性回归理论1.2线性回归实战2.分类模型评估(Mnist实战SGD_Classifier)2.1 K折交叉验证K-fold cross validation2.2 混淆矩阵Confusion Matrix2.3 准确率accuracy、精度precision、召回率recall、F12.4 置信度confidence2.5 ROC曲线3.训练调参基本功(LinearRegre…

1612_PC汇编语言_条件以及控制结构

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 这一次简单看看条件分支以及控制结构&#xff0c;感觉看完这部分之后&#xff0c;汇编的大部分框架已经有个差不多了。我的目的并不是成为汇编高手&#xff0c;因此…

数据处理——增删改

文章目录插入数据方式一&#xff1a;values方式2&#xff1a;将查询结果插入到表中更新数据删除数据MySQL8新特性&#xff1a;计算列综合案例插入数据 用INSERT插入数据 方式一&#xff1a;values 使用这种语法一次只能向表中插入一条数据。 情况1&#xff1a;为表的所有字段…

1月,不要跳槽

新年结束了&#xff0c;一些不满现状&#xff0c;被外界的“高薪”“好福利”吸引的人&#xff0c;一般就在这时候毅然决然地跳槽了。 在此展示一套学习笔记 / 面试手册&#xff0c;年后跳槽的朋友可以好好刷一刷&#xff0c;还是挺有必要的&#xff0c;它几乎涵盖了所有的软件…

通信原理笔记—码间串扰与波形传输无失真的条件(奈奎斯特第一准则)

目录 波形传输的无失真条件&#xff1a; 码间串扰问题&#xff1a; 奈奎斯特第一准则&#xff1a; 在抽样判决时刻没有码间串扰的信号波形示例&#xff1a; 具有最窄频带的无码间串扰基带传输系统&#xff1a; 无码间串扰基带系统的主要参数&#xff1a; 一种典型的滚降…

golang中的图像image处理详解

常用的图像格式有png&#xff0c;jpeg&#xff0c;gif&#xff0c;对应的文件后缀为png&#xff0c;jpg&#xff0c;gif&#xff0c;当然文件的具体内容编码才能证明存放的是哪种文件&#xff0c;图像文件的头部都存储了具体标志&#xff0c;比如jpeg前缀\xffd8&#xff0c;png…

Redis实现消息队列

7、Redis消息队列 7.1 Redis消息队列-认识消息队列 什么是消息队列&#xff1a;字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色&#xff1a; 消息队列&#xff1a;存储和管理消息&#xff0c;也被称为消息代理&#xff08;Message Broker&#xff09;生产者&…

定位tcp连接或端口是属于哪个进程

首先要知道tcp连接的端口号&#xff0c;要么是本地端口号要是么remote端口号。有的端口号我们已知&#xff0c;有的端口号可以通过日志获取&#xff0c;也可以抓包获取&#xff0c;如然后是用netstat命令获取pidlinux下使用netstat -anpt&#xff08;a是输出所有n是展示端口号&…

【✓基础算法 2.4】KMP(完结)

当模式串和主串的子串有不匹配时&#xff0c;便往后退一步&#xff0c;看是否能走通&#xff0c;如果不能&#xff0c;则进行退—— KMP 目录 一、KMP算法简介 二、手算求next数组 三、next数组实现代码 四、完整代码实现 1、java 2、c 一、KMP算法简介 当主串的子串…

软件测试刚入职,很茫然怎么办~

毕业后能直接到公司开始工作&#xff0c;是多少人都想要却得不到的机遇&#xff0c;你现在茫然无非是因为对软件测试是做什么的不了解&#xff0c;也不知道软件测试的职业规划到底是怎么样的~ 既然已经进入了公司&#xff0c;那就要珍惜机会&#xff0c;多学习&#xff0c;多问…

redis7 Cluster模式 集群

1.Redis集群模式介绍 Cluster模式是Redis3.0开始推出的Redis Cluster属于AP模型采用无中心结构&#xff0c;每个节点保存数据和整个集群状态, 每个节点都和其他所有节点连接官方要求&#xff1a;至少6个节点才可以保证高可用&#xff0c;即3主3从&#xff1b;扩展性强、更好做到…

[GUET-CTF2019]re

于尘世中携一支笔&#xff0c;绘春风十里。 1.查壳 64bit加了UPX壳 2.使用Kali Linux脱壳 re脱壳3.静态分析 shift加F12打开string窗口 发现可疑字符&#xff0c;点击跟进 光标放在aCorrect&#xff0c;点击快捷键X查看引用这个字符串的地方 点击跟进 按下F5反编译 将函数重命…

为什么AI爆炸式增长会对元宇宙产生巨大影响

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;9分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 想想你梦想中的房子。也许它有高高的拱形天花板、温暖的壁炉和宽阔的窗户&…

Allegro如何打开丝印位号的飞线操作指导

Allegro如何打开丝印位号的飞线操作指导 用Allegro做PCB设计的时候,移动器件的位号,会有根飞线实时提示位号是属于哪个器件的,如下图 但是只有在临时移动丝印位号的时候才会显示飞线,如何让所有的器件位号的全部显示,具体操作如下 选择Setup选择User Preferences

如何通过Zabbix Docker配置HTTPS访问系统?

概述 前面文章曾介绍过如果使用docker-compose快速部署一个Zabbix系统&#xff0c;但是部署的Zabbix系统是使用http协议进行访问的。有时候为了保证安全。我们需要配置使用https协议进行访问。 下面就讲述如何使用自签名的ssl证书配置https访问。&#xff08;注&#xff1a;若是…