spring-boot rabbitmq整合

news2024/12/27 10:02:49

文章请参考:Springboot 整合RabbitMq ,用心看完这一篇就够了

mven依赖

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

配置rabbitmq

配置生产者

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: root
    password: root
    virtual-host: pers-xrb

配置queue

 @Bean
    public Queue TestDirectQueue(){
        return new Queue("direct-queue",true);
    }

Queue的构造函数如下:

Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)

Queue有如下几个属性:

  1. name-the name of the queue - must not be null; set to “” to have the broker generate the name.(不能为空,如果想让服务器自动生成的话,那么设置为"")
  2. durable- true if we are declaring a durable queue (the queue will survive a server restart)(是否持久化,如果想在服务器重启的时候仍然存在,那么设置为true,默认和推荐设置为true)
  3. exclusive-true if we are declaring an exclusive queue (the queue will only be used by the declarer’s connection) (排他性,默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable)
  4. autoDelete-true if the server should delete the queue when it is no longer in use(自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除,默认是false)

所以通常的初始化构造函数是
在这里插入图片描述

配置exchange

  @Bean
    DirectExchange TestDirectExchange() {
        return new DirectExchange("TestDirectExchange", true, false);
    }

绑定

    @Bean
    Binding bindingDirect() {
        return BindingBuilder.bind(TestDirectQueue())
                .to(TestDirectExchange())
                .with("TestDirectRouting");
    }

配置触发器

@RestController("/rabbit-mq-sender")
public class SendMessageController {
    private final RabbitTemplate rabbitTemplate;

    public SendMessageController(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @GetMapping("/sendDirectMessage")
    public String sendDirectMessage() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "test message, hello!";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>(3);
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
        //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
         rabbitTemplate.convertAndSend("TestDirectExchange",
                "TestDirectRouting", map);
        return "ok";
    }
}

发送后,可以看到ui界面的信息
在这里插入图片描述

配置消费者

基本配置和配置生产者没有什么区别,除了这里需要配置的是监听器和指定监听器的处理器,声明消费者类为监听器@DirectReceiver,指定方法为处理器@RabbitHandler

@Service
@Slf4j
@RabbitListener(queues = "TestDirectQueue")     // 指定监听的队列
public class DirectReceiver {
    @RabbitHandler
    public void process(Object testMessage) {
        try {
            TimeUnit.SECONDS.sleep(1);
            log.debug("DirectReceiver[TestDirectQueue]消费者收到消息  : " + testMessage.toString());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

启动服务器后,就可以直接监听和消费mq信息了,如果需要增加一个消费者,那么copy一个DirectReceiver实例,命名为DirectReceiver2就行了。默认的消费策略是轮询策略,这个方法待优化,很明显在现实开发过程中是不可能这么写的。

生产者的confirm和return机制

RabbitMq发消息的流程
在这里插入图片描述
从上面的图,我们可以看到一个mq发送的话,可能会出现如下几种情况

  1. 未找到交换机
  2. 找到交换机,但是未绑定queue
  3. 找到了queue,消息推送成功

confirm机制

confirm机制是对于生产者和服务器broker来说的。

生产者将信道设置成confirm模式,一旦信道进入confirm模式,所有在该信道上面发布的消息都会被指派一个唯一的ID(从1开始,单调递增),一旦消息被投递到所有匹配的队列之后,broker就会发送一个ACK给生产者(包含消息的唯一ID)

在这里插入图片描述

return机制

returnCallback 未投递到queue退回模式

confrim 模式只能保证消息到达 broker,不能保证消息准确投递到目标 queue 里。在有些业务场景下,我们需要保证消息一定要投递到目标 queue 里,此时就需要用到 return 退回模式。这里需要exchange触发无法查找queue的场景,也就是说exchange存在,但是queue不存在

配置confirm和return

  1. 修改yml配置
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: root
    password: root
    virtual-host: pers-xrb
    publisher-confirms: true    #确认消息已发送到交换机(Exchange)
    publisher-returns: true     #确认消息已发送到队列(Queue)
  1. 自定义rabbitMqTemplate
@Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.addAfterReceivePostProcessors(message -> {
            byte[] body = message.getBody();
            log.info("received body is:{}", body);
            return message;
        });
        // set confirm callback
        rabbitTemplate.setConfirmCallback((correlationData, isAck, cause) -> {
            System.out.println("ConfirmCallback:     " + "相关数据:" + correlationData);
            System.out.println("ConfirmCallback:     " + "确认情况:" + isAck);
            System.out.println("ConfirmCallback:     " + "原因:" + cause);
        });
        // set return callback
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            System.out.println("ReturnCallback:     " + "消息:" + message);
            System.out.println("ReturnCallback:     " + "回应码:" + replyCode);
            System.out.println("ReturnCallback:     " + "回应信息:" + replyText);
            System.out.println("ReturnCallback:     " + "交换机:" + exchange);
            System.out.println("ReturnCallback:     " + "路由键:" + routingKey);

        });
        return rabbitTemplate;
    }
  1. 增加触发接口
    private static Map<String, Object> getMqMessage() {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = "test message, hello!";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>(3);
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
        return map;
    }
 /**
     * 4.1 exchange and queue not exist
     */
    @GetMapping("/sendNotExistExchangeMessage")
    public String sendNotExistExchangeMessage() {
        Map<String, Object> map = getMqMessage();
        //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
        rabbitTemplate.convertAndSend("NotExistExchangeExchange",
                "NotExistExchangeExchangeQueue", map);
        return "ok";
    }

    /**
     * 4.2 exchange exist but queue not exist
     */
    @GetMapping("/sendNotExistQueueButExchangeMessage")
    public String sendNotExistQueueButExchangeMessage() {
        Map<String, Object> map = getMqMessage();
        //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
        rabbitTemplate.convertAndSend("NotExistQueueButExchangeExchange",
                "NotExistQueueButExchangeQueue", map);
        return "ok";
    }
  1. 测试
    为了测试两种情形,我们分别为
    1. 交换机exchange不存在
    2. 交换机存在但是队列不存在
    3. exchange和queue都存在
4.1 交换机exchange不存在

调用对应的接口 http://localhost:9876/rabbit-mq-sender/sendNotExistExchangeMessage
在日志台会出现如下信息:
在这里插入图片描述
这个时候只会调用confirmcallback,confirmcallback会告诉你这个exchange是不存在的。

4.2,交换机存在但是queue不存在的场景,

我们调用对应的api接口
http://localhost:9876/rabbit-mq-sender/sendNotExistQueueButExchangeMessage
不过在这之前,我们需要先在声明一个exchange

  /**
     * Direct交换机 起名:NotExistQueueButExchangeExchange
     */
    @Bean
    DirectExchange NotExistQueueButExchange() {
        return new DirectExchange("NotExistQueueButExchangeExchange", true, false);
    }

查看日志台,信息如下:
在这里插入图片描述
我们会发现,confirmcallback和returnCallback两个函数其实都是调用了的,在confirmcallback中,返回结果告诉了我们isAck是true的,但是returnCallback中告诉我们No_route,表示没有找到对应的路由
,在exchange的ui中,我们可以看到我们有两个exchange
在这里插入图片描述

4.3 那如果所有的都是正常的呢?[confirm|return]callback会返回什么呢?我们调用api测试下:

http://localhost:9876/rabbit-mq-sender/sendDirectMessage
日志台结果如下:
在这里插入图片描述
这个时候是只调用confirmCallback的

消费者接收到消息的消费确认机制

如果消费者消费后怎么告诉rabbitmq服务器呢?通常来说消费者消费后会返回一个信息给服务器表达了服务器已经接受并且告诉服务器消费结果和后续的处理,总结来说,消费者要告诉消费者如下消息

  1. 是否消费成功-这决定服务器是否要删除消息
  2. 如果消费失败,那么需要重新放回队列吗?

消费者接收的确认机制主要存在三种模式

  1. 自动确认,这也是默认的消息确认情况。 AcknowledgeMode.NONE

RabbitMQ成功将消息发出(即将消息成功写入TCP Socket)中立即认为本次投递已经被正确处理,不管消费者端是否成功处理本次投递。
所以这种情况如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。
一般这种情况我们都是使用try catch捕捉异常后,打印日志用于追踪数据,这样找出对应数据再做后续处理。

  1. 根据业务来处理,这个根据自己的业务需求来处理,不细说
  2. 手动确认, 这个比较关键,也是我们配置接收消息确认机制时,多数选择的模式。

消费者收到消息后,手动调用basic.ack/basic.nack/basic.reject后,RabbitMQ收到这些消息后,才认为本次投递成功,换句话说,这也是和业务做耦合的。

消费者收到消息后,手动调用对应的方法后,RabbitMQ收到这些消息后,才认为本次投递成功。手动确认分如下情况,消费成功和消费失败。
1. basicAck用于肯定确认
2. basicNack用于否认确定
3. basicReject 用于否认确认,但和basic.nack相比有一个限制,一次只能拒绝单条信息

basic.reject

basicReject 表示否认确认,

 /**
     * Reject a message. Supply the deliveryTag from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}
     * or {@link com.rabbitmq.client.AMQP.Basic.Deliver} method
     * containing the received message being rejected.
     * @see com.rabbitmq.client.AMQP.Basic.Reject
     * @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
     * @param requeue true if the rejected message should be requeued rather than discarded/dead-lettered
     * @throws java.io.IOException if an error is encountered
     */
    void basicReject(long deliveryTag, boolean requeue) throws IOException;

入参有两个

  1. deliveryTag-消息体发送标志,不细说
  2. requeue-是否重新放在队列中,次还会消费这消息。设置false,就是告诉服务器,我已经知道这条消息数据了,因为一些原因拒绝它,而且服务器也把这个消息丢掉就行。 下次不想再消费这条消息了。

使用拒绝后重新入列这个确认模式要谨慎,因为一般都是出现异常的时候,catch异常再拒绝入列,选择是否重入列。

但是如果使用不当会导致一些每次都被你重入列的消息一直消费-入列-消费-入列这样循环,会导致消息积压。

channel.basicNack

也是相当于设置不消费某条消息。
源码如下

 /**
     * Reject one or several received messages.
     *
     * Supply the <code>deliveryTag</code> from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}
     * or {@link com.rabbitmq.client.AMQP.Basic.GetOk} method containing the message to be rejected.
     * @see com.rabbitmq.client.AMQP.Basic.Nack
     * @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
     * @param multiple true to reject all messages up to and including
     * the supplied delivery tag; false to reject just the supplied
     * delivery tag.
     * @param requeue true if the rejected message(s) should be requeued rather
     * than discarded/dead-lettered
     * @throws java.io.IOException if an error is encountered
     */
    void basicNack(long deliveryTag, boolean multiple, boolean requeue)
            throws IOException;

Reject one or several received messages,很显然是否认一个或多个接受到的消息体,重点是第二个参数:是指是否针对多条消息;如果是true,也就是说一次性针对当前通道的消息的tagID小于当前这条消息的,都拒绝确认。

同样使用不确认后重新入列这个确认模式要谨慎,因为这里也可能因为考虑不周出现消息一直被重新丢回去的情况,导致积压。

消费者消费机制case(在消费者项目里)

  1. 首先,我们需要设定一个通用的receiver,这个receiver是通用的receiver(之前的相关监听器可以先注释掉,以免造成多个同类型监听器都监听同一个队列。如果有的话)
public class MyAckMessageReceiver implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        String strictRequired = null;
        try {
            byte[] body = message.getBody();
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(body));
            Map<String, String> msgMap = (Map<String, String>) ois.readObject();
            String messageId = msgMap.get("messageId");
            String messageData = msgMap.get("messageData");
            String createTime = msgMap.get("createTime");
            strictRequired = msgMap.get("strictRequired");

            ois.close();
            System.out.println("  MyAckReceiver  messageId:" + messageId + "  messageData:" + messageData + "  createTime:" + createTime);
            System.out.println("消费的主题消息来自:" + message.getMessageProperties().getConsumerQueue());
            //第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息
            channel.basicAck(deliveryTag, true);
        } catch (IOException | ClassNotFoundException e) {
            boolean isRequeue = strictRequired != null && strictRequired.contains("1");
            channel.basicReject(deliveryTag, isRequeue);
            e.printStackTrace();
        }

    }
}
  1. 配置一个MessageListenerConfig,代码如下:
@Configuration
public class MessageListenerConfig {

    @Bean
    MyAckMessageReceiver messageReceiver() {
        return new MyAckMessageReceiver();
    }

    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer(CachingConnectionFactory connectionFactory,
                                                                         MyAckMessageReceiver myAckMessageReceiver) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setConcurrentConsumers(1);
        container.setMaxConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
        //设置一个队列
        container.setQueueNames("TestDirectQueue");
        //如果同时设置多个如下: 前提是队列都是必须已经创建存在的
        //  container.setQueueNames("TestDirectQueue","TestDirectQueue2","TestDirectQueue3");


        //另一种设置队列的方法,如果使用这种情况,那么要设置多个,就使用addQueues
        //container.setQueues(new Queue("TestDirectQueue",true));
        //container.addQueues(new Queue("TestDirectQueue2",true));
        //container.addQueues(new Queue("TestDirectQueue3",true));
        container.setMessageListener(myAckMessageReceiver);

        return container;
    }

在container中指定消息队列和对应的receiver处理器

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

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

相关文章

张大大直播爆火?跨境电商直播能从中学到什么

最近这个张大大的直播间确实很火哈&#xff0c;连龙哥这个不怎么看娱乐新闻的也经常刷到。龙哥专门去看了几次张大大的这个直播&#xff0c;确实有点东西。每场都可以做到这么高流量着实是不容易。龙哥就凭借自己一些电商直播经验&#xff0c;给大家总结一下&#xff0c;我们可…

年薪20W软件测试工程师必备的6大技能(建议收藏)

软件测试 随着软件开发行业的日益发展&#xff0c;岗位需求量和行业薪资都不断增长&#xff0c;想要入行的人也是越来越多&#xff0c;但不知道从哪里下手&#xff0c;今天&#xff0c;就给大家分享一下&#xff0c;软件测试行业都有哪些必会的方法和技术知识点&#xff0c;作…

Java知识补充

ArrayList 继承结构 为什么要先继承AbstractList&#xff0c;而让AbstractList先实现List&#xff1f;而不是让ArrayList直接实现List&#xff1f; 这里是有一个思想&#xff0c;接口中全都是抽象的方法&#xff0c;而抽象类中可以有抽象方法&#xff0c;还可以有具体的实现方…

无聊小知识.03 Springboot starter配置自动提示

1、前言Springboot项目配置properties或yaml文件时候&#xff0c;会有很多spring相关的配置提示。这个是如何实现的&#xff1f;如果我们自己的配置属性&#xff0c;能否也自动提示&#xff1f;2、Springboot配置自动提示其实IDE是通过读取配置信息的元数据而实现自动提示的。S…

HCIE-Cloud Computing LAB备考第二步:逐题攻破--第一题:FusionCompute--合并小节

FusionCompute 三大要求&#xff1a;扩容对接共享存储windows版本的FC操作 FCWindows题目思维导图 扩容一台CNA节点&#xff0c;配置管理地址设置为&#xff1a;192.168.100.212。密码设置为&#xff1a;Cloud12#$。主机名设置为CNA2。【安装CNA 】【ctrlaltdel之后按esc选择…

双向链表+循环链表

循环链表双向链表 循环链表 循环链表是头尾相接的链表(即表中最后一个结点的指针域指向头结点&#xff0c;整个链表形成一个环)(circular linked list) **优点&#xff1a;**从表中任一结点出发均可访问全部结点 循环链表与单链表的主要差别当链表遍历时&#xff0c;判别当前…

精准测试对于覆盖率技术的全新诠释

对于白盒测试有深入研究的技术人员可能会问到&#xff0c;精准测试还是很多用到了覆盖率技术&#xff0c;这些本来不就是有开源的工具吗?下面我们来比较一下&#xff1a; 开源的覆盖率工具&#xff1a; 1、 将所有的测试产生的覆盖率混在一起&#xff0c;不具备快速定位缺陷与…

Pycharm补丁包使用教程

虽然社区版在大多情况下已经够用&#xff0c;但是有很多功能都是没有的&#xff0c;对照起一些教程之类的就很不方便 现在直接教一种简单中的简单的补丁包使用方法 我这里用的是 pycharm 19.2.6 注意右下角的configure 一般别的方法都是 打开&#xff0c;然后添加路径&#…

关于提高PX4抗风性

滚转角速率控制器&#xff1a;&#xff08;MC_ROLLRATE_P&#xff0c; MC_ROLLRATE_I&#xff0c; MC_ROLLRATE_D&#xff09; 滚转角速率控制器&#xff1a;&#xff08;MC_PITCHRATE_P&#xff0c; MC_PITCHRATE_I&#xff0c;MC_PITCHRATE_D&#xff09; 滚转角速率控制器…

php宝塔搭建部署实战CSM会议室预约系统源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套基于fastadmin开发的CSM会议室预约系统的源码。感兴趣的朋友可以自行下载学习。 技术架构 PHP7.2 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码&#xff0…

5年软测,女朋友跑了俩,2年外包感觉自己废了一半,怎么办?

17年毕业&#xff0c;校招毕业就进入一家软件公司&#xff0c;干了2年的点工&#xff0c;随后进入一家外包公司工作至今&#xff0c;安逸使人堕落不知进取&#xff0c;加之随着近年的环境不景气&#xff0c;谈了多年将要结婚的女朋友也因为我的心态和工资要跟我闹分手我想改变现…

nodejs安装和卸载超详细步骤

安装程序①下载完成后&#xff0c;双击安装包&#xff0c;开始安装&#xff0c;使用默认配置安装一直点next即可&#xff0c;安装路径默认在C:\Program Files下&#xff0c;也可以自定义修改②安装路径默认在C:\Program Files下面&#xff0c;也能够自定义修改&#xff0c;而后…

力扣-2020年最后一次登录

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1890. 2020年最后一次登录二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.…

ThreadLocal学会了这些,你也能和面试官扯皮了!

前言 我们都知道,在多线程环境下访问同一个共享变量,可能会出现线程安全的问题,为了保证线程安全,我们往往会在访问这个共享 变量的时候加锁,以达到同步的效果,如下图所示。 对共享变量加锁虽然能够保证线程的安全,但是却增加了开发人员对锁的使用技能,如果锁使用不当…

MySql主键id不推荐使用UUID

前言 昨天在某个技术群中&#xff0c;有个老哥发送了一个技术视频&#xff1a;讲的是一个毕业生面试被问&#xff0c;前后端的交互ID是使用自增的吗&#xff1f;为什么不使用UUID&#xff1f;最后的解释是说性能问题&#xff0c;这个引起了我的兴趣&#xff0c;查了一下资料总…

计算机网络安全基础知识4:命令执行漏洞,危害极大,DVWA演示命令注入漏洞攻击网站,防御命令注入执行漏洞,low,medium,high,impossible

计算机网络安全基础知识4&#xff1a;命令执行漏洞&#xff0c;危害极大&#xff0c;DVWA演示命令注入漏洞攻击网站&#xff0c;防御命令注入执行漏洞&#xff0c;low&#xff0c;medium&#xff0c;high&#xff0c;impossible 2022找工作是学历、能力和运气的超强结合体&…

CHAPTER 3 Web HA集群部署 - RHCS

Web HA集群部署 - RHCS1. RHCS介绍2. RHCS的核心功能2.1 负载均衡2.2 高可用2.3 存储3. RHCS集群的组成部分3.1 集群管理器CMAN3.2 资源组管理器rgmanager3.3 集群配置文件管理CCS3.4 保护设备Fencs3.5 分布式锁管理器DLM3.6 集群文件系统GFS3.7 集群配置管理工具Conga4. RHCS历…

白盒测试复习重点

白盒测试白盒测试之逻辑覆盖法逻辑覆盖用例设计方法1.语句覆盖2.判定覆盖(分支覆盖)3.条件覆盖4.判定条件覆盖5.条件组合覆盖6.路径覆盖白盒测试之基本路径测试法基本路径测试方法的步骤1.根据程序流程图画控制流图2.计算圈复杂度3.导出测试用例4.准备测试用例5.例题白盒测试总…

扬帆优配“机器人+”方案加码产业发展,这些股有望高增长

“机器人”发明新需求&#xff0c;2022年中国机器人市场规模约为174亿美元。 美国时刻3月1日&#xff0c;特斯拉在得克萨斯州超级工厂举办投资者日活动&#xff0c;展示了人形机器人Optimus的视频&#xff0c;更夸大的是&#xff0c;视频中的机器人好像在制作另一个机器人&…

更改linux时区、时间

问题描述&#xff1a; MINIO上传失败&#xff0c;错误信息如下&#xff1a; The difference between the request time and the servers time is too large. 原因分析&#xff1a; 系统时区与硬件时区不一致导致的 解决方案&#xff1a; 第一种情况&#xff0c;Time zone时区…