RabbitMQ-API

news2024/12/28 4:06:42

这里写目录标题

  • Hello word 模式
    • 添加依赖
    • 生产者
    • 消费者
    • 获取信道工具类
  • Work Queues模式
    • 消费者代码 C1
    • 开启多线程运行
    • 启动 消费者代码 C2
    • 生产者代码
  • 消息应答
    • 自动应答
    • 消息应答的方法
    • Multiple 的解释
    • 消息自动重新入队
    • 消息手动应答代码
    • 消费者API
  • 队列持久化
  • 消息持久化
  • 不公平分发
  • 消息预取值
  • 确认发布
    • 单个确认发布
    • 批量确认发布
    • 异步批量确认发布
    • 如何处理异步未确认消息

Hello word 模式

“ P”是我们的生产者,“ C”是我们的消费者。中间的框是一个队列-RabbitMQ 代表使用者保留的消息缓冲区
在这里插入图片描述

添加依赖

<!--rabbitmq 依赖客户端-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.8.0</version>
        </dependency>
        <!--操作文件流的一个依赖-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

生产者

package com.wlj.rabbitmq.one;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.wlj.rabbitmq.util.MQUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
 *@创建人 wlj
 *@创建时间 2023/7/20
 *@描述 消息生产者 Hello World简单队列模式
 */
public class Producer {
    private static String QUEUE_NAME="hello";
    public static void main(String[] args) {
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //工厂IP连接MQ的队列
        factory.setHost("localhost");
        //设置用户名
        factory.setUsername("guest");
        factory.setPassword("guest");

        try {
            //创建连接
            Connection connection = factory.newConnection();
            //获取信道
            Channel channel = connection.createChannel();
            //生成一个队列
            /**
             * 生成一个队列
             * 1.队列名称
             * 2.队列里面的消息是否持久化 默认消息存储在内存中
             * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
             * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
             * 5.其他参数
             */
            channel.queueDeclare(QUEUE_NAME,false,false,false,null);
            String message="hello world2";
            /**
             * 发送一个消息
             * 1.发送到那个交换机
             * 2.路由的 key 是哪个
             * 3.其他的参数信息
             * 4.发送消息的消息体
             */
            channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
            System.out.println("消息发送完毕");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }

}

消费者

package com.wlj.rabbitmq.one;

import com.rabbitmq.client.*;
/**
 *@创建人 wlj
 *@创建时间 2023/7/20
 *@描述 消息消费者 Hello World简单队列模式
 */
public class Consumer {
    private static String QUEUE_NAME="hello";
    public static void main(String[] args) {
        //创建有一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //工厂IP连接MQ的队列
        factory.setHost("localhost");
        //设置用户名
        factory.setUsername("guest");
        factory.setPassword("guest");
        //创建连接
        try {
            Connection connection = factory.newConnection();
            //获取信道
            Channel channel = connection.createChannel();
            /**
             *消费者消费消息
             * 1.消费那个队列
             * 2. 消费成功之后 是否手动应答 true 自动应答 false 手动应答
             * 3.消费者成功消费的回调
             * 4. 消费者取消消费的回调
             */

            //接收消息
            DeliverCallback deliverCallback=(conusmerTag,message)->{
                System.out.println("消费的消息======="+ new String( message.getBody()));
            };
            //取消接收消息的回调
            CancelCallback cancelCallback =(e)->{
                System.out.println("消费消息被中断=======");
            };
            channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

获取信道工具类

package com.wlj.rabbitmq.util;

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

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

/**
 * @创建人 wlj
 * @创建时间 2023/7/19
 * @描述 连接MQ获取信道工具类
 */
public class MQUtil {
    public static  Channel    getMQ( ) {
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //工厂IP连接MQ的队列
        factory.setHost("localhost");
        //设置用户名
        factory.setUsername("guest");
        factory.setPassword("guest");

        try {
            //创建连接
            Connection connection = factory.newConnection();
            //获取信道
            Channel channel = connection.createChannel();
           return channel;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return null;
    }

}

Work Queues模式

在这里插入图片描述

一个生产者对应多个消费者。每个消费者是竞争关系,一个消息只能被一个消费者消费

消费者代码 C1

package com.wlj.rabbitmq.two;

import com.rabbitmq.client.Channel;
import com.wlj.rabbitmq.util.MQUtil;

import java.io.IOException;

/**
 *@创建人 wlj
 *@创建时间 2023/7/20
 *@描述 这是一个工作线程 也是一个消费者
 */
public class WorkerConsumer {
    private static String QUEUE_NAME="hello";

    public static void main(String[] args) {
        Channel channel = MQUtil.getMQ();
        System.out.println("C1");
        //消息的接收
        try {
            channel.basicConsume(QUEUE_NAME,true,(tag,msg)->{
                System.out.println("消费的消息====="+ new String(msg.getBody()));
            },e->{
                System.out.println("消费取消");
            });
        } catch (IOException e) {


        }
    }
}

开启多线程运行

在这里插入图片描述

启动 消费者代码 C2

只需要更改输出语句即可

System.out.println("C2");

生产者代码

从控制台输入 发送消息

package com.wlj.rabbitmq.two;

import com.rabbitmq.client.*;
import com.wlj.rabbitmq.util.MQUtil;

import java.util.Scanner;

/**
 *@创建人 wlj
 *@创建时间 2023/7/20
 *@描述 消息消费者 工作线程模式
 */
public class WorkerProducer {
    private static String QUEUE_NAME="hello";
    public static void main(String[] args) {
        Channel channel = MQUtil.getMQ();
        try {
 channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //从控制台当中接受信息
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()){
                String message = scanner.next();
                /**
                 * 生成一个队列
                 * 1.队列名称
                 * 2.队列里面的消息是否持久化 默认消息存储在内存中
                 * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
                 * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
                 * 5.其他参数
                 */
                channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
                System.out.println("发送消息完成:"+message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

启动C1、C2和生产者进行测试

消息应答

消费者完成一个任务可能需要一段时间,如果其中一个消费者处理一个长的任务并仅只完成
了部分突然它挂掉了,会发生什么情况。RabbitMQ 一旦向消费者传递了一条消息,便立即将该消
息标记为删除。在这种情况下,突然有个消费者挂掉了,我们将丢失正在处理的消息。以及后续
发送给该消费这的消息,因为它无法接收到。
为了保证消息在发送过程中不丢失,rabbitmq 引入消息应答机制,消息应答就是:消费者在接
收到消息并且处理该消息之后,告诉 rabbitmq 它已经处理了,rabbitmq 可以把该消息删除了。

自动应答

消息发送后立即被认为已经传送成功,这种模式需要在高吞吐量和数据传输安全性方面做权
,因为这种模式如果消息在接收到之前,消费者那边出现连接或者 channel 关闭,那么消息就丢
失了,当然另一方面这种模式消费者那边可以传递过载的消息,没有对传递的消息数量进行限制,
当然这样有可能使得消费者这边由于接收太多还来不及处理的消息,导致这些消息的积压,最终
使得内存耗尽,最终这些消费者线程被操作系统杀死,所以这种模式仅适用在消费者可以高效并
以某种速率能够处理这些消息的情况下使用。

消息应答的方法

  • Channel.basicAck(用于肯定确认)
    RabbitMQ 已知道该消息并且成功的处理消息,可以将其丢弃了
  • Channel.basicNack(用于否定确认)
  • Channel.basicReject(用于否定确认)
    与 Channel.basicNack 相比少一个参数
    不处理该消息了直接拒绝,可以将其丢弃了

Multiple 的解释

手动应答的好处是可以批量应答并且减少网络拥堵
在这里插入图片描述

multiple 的 true 和 false 代表不同意思
true 代表批量应答 channel 上未应答的消息
比如说 channel 上有传送 tag 的消息 5,6,7,8 当前 tag 是 8 那么此时
5-8 的这些还未应答的消息都会被确认收到消息应答
false 同上面相比
只会应答 tag=8 的消息 5,6,7 这三个消息依然不会被确认收到消息应答

消息自动重新入队

如果消费者由于某些原因失去连接(其通道已关闭,连接已关闭或 TCP 连接丢失),导致消息
未发送 ACK 确认,RabbitMQ 将了解到消息未完全处理,并将对其重新排队。如果此时其他消费者
可以处理,它将很快将其重新分发给另一个消费者。这样,即使某个消费者偶尔死亡,也可以确
保不会丢失任何消息。
在这里插入图片描述

消息手动应答代码

默认消息采用的是自动应答,所以我们要想实现消息消费过程中不丢失,需要在接收消息是把自动应答改
为手动应答。之后需要在消息处理完成之后进行手动应答

消费者API

** 首先是消费消息时,取消自动应答**
消费消息完成时,进行手动应答

package com.wlj.rabbitmq.two;

import com.rabbitmq.client.Channel;
import com.wlj.rabbitmq.util.MQUtil;

import java.io.IOException;

/**
 *@创建人 wlj
 *@创建时间 2023/7/20
 *@描述 这是一个工作线程 也是一个消费者
 */
public class WorkerConsumer {
    private static String QUEUE_NAME="hello";

    public static void main(String[] args) {
        Channel channel = MQUtil.getMQ();
        System.out.println("C2");
        //消息的接收
        try {
            /**
             *消费者消费消息
             * 1.消费那个队列
             * 2. 消费成功之后 是否手动应答 true 自动应答 false 手动应答
             * 3.消费者成功消费的回调
             * 4. 消费者取消消费的回调
             */
            //取消自动应答
            channel.basicConsume(QUEUE_NAME,false,(tag,msg)->{
                System.out.println("消费的消息====="+ new String(msg.getBody()));
                //进行手动应答 参数一:消息标记tag ,参数二:false代表只应答接收到的那个传递的消息。true代表为应答所有消息包括传递过来的消息
                channel.basicAck(msg.getEnvelope().getDeliveryTag(),true);
            },e->{
                System.out.println("消费取消");
            });
        } catch (IOException e) {


        }
    }
}

队列持久化

队列为开启持久化时。MQ重启队列就会被删除掉,如果想要队列实现持久化,需要在声明队列的时候吧durable参数设置为持久化(true)

 boolean durable =true;
  channel.queueDeclare(QUEUE_NAME,durable,false,false,null);

需要注意的是,如果之前生声明的队列不是持久化的,需要把原来的队列删除之后,重新声明持久化队列
查看mq队列列表,出现D就是持久化成功
在这里插入图片描述

消息持久化

要想让消息实现持久化需要在消息生产者修改代码,,MessageProperties.PERSISTENT_TEXT_PLAIN 添
加这个属性 可以理解为告诉队列把消息实现持久化保存到磁盘上

 boolean durable =true;
            channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
            String message="hello world2";
            /**
             * 发送一个消息
             * 1.发送到那个交换机
             * 2.路由的 key 是哪个
             * 3.其他的参数信息
             * 4.发送消息的消息体
             */
            channel.basicPublish("",QUEUE_NAME,MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());

将消息标记为持久化并不能完全保证不会丢失消息。尽管它告诉 RabbitMQ 将消息保存到磁盘,但是
这里依然存在当消息刚准备存储在磁盘的时候 但是还没有存储完,消息还在缓存的一个间隔点。此时并没
有真正写入磁盘。持久性保证并不强,但是对于我们的简单任务队列而言,这已经绰绰有余了。如果需要
更强有力的持久化策略,参考MQ的发布确认。

不公平分发

MQ默认是采用轮询的方式分发消息,但是有的消费者处理很慢,就会导致消息积压,可以设置不公平分发,消费者进行应答之后,才会接收下一条消息

生产者代码无变动

package com.wlj.rabbitmq.bugongpingfenfa;

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

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

/**
 *@创建人 wlj
 *@创建时间 2023/7/20
 *@描述 消息生产者 Hello World简单队列模式
 */
public class Producer {
    private static String QUEUE_NAME="hello3";
    public static void main(String[] args) {
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //工厂IP连接MQ的队列
        factory.setHost("localhost");
        //设置用户名
        factory.setUsername("guest");
        factory.setPassword("guest");

        try {
            //创建连接
            Connection connection = factory.newConnection();
            //获取信道
            Channel channel = connection.createChannel();
            //生成一个队列
            /**
             * 生成一个队列
             * 1.队列名称
             * 2.队列里面的消息是否持久化 默认消息存储在内存中
             * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
             * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
             * 5.其他参数
             */
            //从控制台当中接受信息
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()) {
                String message = scanner.next();
               //发布消息
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
                System.out.println("发送消息完成:" + message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }

}

消费者代码 在消息消费之前 channel.basicQos(1); 同时开通了手动应答,手动应答应该先启动消费者,在启动生产者

package com.wlj.rabbitmq.bugongpingfenfa;

import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.wlj.rabbitmq.util.MQUtil;
import com.wlj.rabbitmq.util.ThreadSleep;

import java.io.IOException;

/**
 *@创建人 wlj
 *@创建时间 2023/7/20
 *@描述 进行不公平分发测试,处理慢的消费者接受的消息少,处理快的消费者处理的消息多
 */
public class WorkerConsumer {
    private static String QUEUE_NAME="hello3";

    public static void main(String[] args) {
        Channel channel = MQUtil.getMQ();

        System.out.println("50秒消费的消息=====");        //消息的接收
        try {
             channel.basicQos(1);
            /**
             *消费者消费消息
             * 1.消费那个队列
             * 2. 消费成功之后 是否手动应答 true 自动应答 false 手动应答
             * 3.消费者成功消费的回调
             * 4. 消费者取消消费的回调
             */
            //接收消息
            DeliverCallback deliverCallback=(conusmerTag, message)->{
                System.out.println("消费的消息======="+ new String( message.getBody()));
                ThreadSleep.sleep(50000);
                 channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
                System.out.println("应答结束");
            };
            //取消接收消息的回调
            CancelCallback cancelCallback =(e)->{
                System.out.println("消费消息被中断=======");
            };
            channel.basicConsume(QUEUE_NAME,false,deliverCallback,cancelCallback);
        } catch (IOException e) {


        }
    }
}

进行测试

  • 启动消费者一,获取消息之后,睡眠十秒再应答。
  • 启动消费者二,获取消息之后,睡眠二十秒再应答。
  • 启动生产者
  • 测试发送第一条消息,由第一个消费者消费。再发送第二条消息,此时消费者二是空闲的,所以消费者二消费消息
  • 发送第三条数据,如果第一个消费者十秒结束,进行应答,那么会得到第三条消息。如果没有进行应答,则不能接收到消息
    所以得出结论,不公平分发,是消费者消息处理完应答之后,才会接收到下一条消息

消息预取值

确认发布

这是一种简单的确认方式。他是一种同步确认发布的方式,也就是发布一个消息之后,只有它被确认发布,后续的消息才能被发布。waitForCnfirmsOrDie(long)这个方法只有在消息被确认的时候才返回,如果指定时间内没有被确认,则会抛出异常

单个确认发布

生产者API
需要使用 信道开启发布确认

package com.wlj.rabbitmq.three;

import com.rabbitmq.client.Channel;
import com.wlj.rabbitmq.util.MQUtil;

import java.util.Scanner;

/**
 * @创建人 wlj
 * @创建时间 单个发布确认模式
 * @描述
 */
public class ComfirmSelecr {
    private static String QUEUE_NAME = "confirms";

    public static void main(String[] args) {
        Channel channel = MQUtil.getMQ();
      
        try {
          //开启发布确认
            channel.confirmSelect();
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            long l = System.currentTimeMillis();
            for (int i = 0; i < 1000; i++) {
            	//发送消息
                channel.basicPublish("", QUEUE_NAME, null, (i+"").getBytes());
                System.out.println("发布确认模式:" + i);
                boolean b = channel.waitForConfirms(1000);
                if (b) {
                    System.out.println("发布成功");
                }
            }
            long l1 = System.currentTimeMillis();
            System.out.println("发布时长= === " + (l1 - l));

        } catch (
                Exception e) {
            e.printStackTrace();
        }
    }
}

批量确认发布

package com.wlj.rabbitmq.three;

import com.rabbitmq.client.Channel;
import com.wlj.rabbitmq.util.MQUtil;

/**
 * @创建人 wlj
 * @创建时间 批量发布确认模式
 * @描述
 */
public class BatchComfirmSelecr {
    private static String QUEUE_NAME = "confirms";

    public static void main(String[] args) {
        Channel channel = MQUtil.getMQ();
        //开启发布确认
        try {
            channel.confirmSelect();
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            long l = System.currentTimeMillis();
            for (int i = 0; i < 1000; i++) {


                /**
                 * 生成一个队列
                 * 1.队列名称
                 * 2.队列里面的消息是否持久化 默认消息存储在内存中
                 * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
                 * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
                 * 5.其他参数
                 */
                channel.basicPublish("", QUEUE_NAME, null, (i + "").getBytes());
                System.out.println("发布确认模式:" + i);
                // 每一百个一次确认发布
                if (i % 100 == 0) {
                    boolean b = channel.waitForConfirms(1000);
                    if (b) {
                        System.out.println("发布成功");
                    }
                }

            }
            long l1 = System.currentTimeMillis();
            System.out.println("发布时长= === " + (l1 - l));

        } catch (
                Exception e) {
            e.printStackTrace();
        }
    }
}

异步批量确认发布

需要添加添加一个异步确认的监听器 处理已被处理的消息或者是未被处理的消息 channel.addConfirmListener(ackCallback, nackCallback);

package com.wlj.rabbitmq.three;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmCallback;
import com.wlj.rabbitmq.util.MQUtil;

import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * @创建人 wlj
 * @创建时间 批量发布确认模式
 * @描述
 */
public class SyncBatchComfirmSelecr {
    private static String QUEUE_NAME = "confirms";

    public static void main(String[] args) {
        Channel channel = MQUtil.getMQ();
        //开启发布确认
        try {
            channel.confirmSelect();
            /**
             * 线程安全有序的一个哈希表,适用于高并发的情况
             * 1.轻松的将序号与消息进行关联
             * 2.轻松批量删除条目 只要给到序列号
             * 3.支持并发访问
             */
            ConcurrentSkipListMap<Long, String> outstandingConfirms = new
                    ConcurrentSkipListMap<>();
            /**
             * 确认收到消息的一个回调
             * 1.消息序列号
             * 2.true 可以确认小于等于当前序列号的消息
             * false 确认当前序列号消息
             */
            ConfirmCallback ackCallback = (sequenceNumber, multiple) -> {
                String message = outstandingConfirms.get(sequenceNumber);
                System.out.println("发布的消息---------------" + message + "已被确认,序列号" + sequenceNumber);
                //是否是批量确认
                if (multiple) {
                    //返回的是小于等于当前序列号的未确认消息 是一个 map
                    ConcurrentNavigableMap<Long, String> confirmed =
                            outstandingConfirms.headMap(sequenceNumber, true);
                    //清除该部分未确认消息
                    confirmed.clear();
                } else {
                    //只清除当前序列号的消息
                    outstandingConfirms.remove(sequenceNumber);
                }
            };


            ConfirmCallback nackCallback = (sequenceNumber, multiple) -> {
                String message = outstandingConfirms.get(sequenceNumber);
                System.out.println("发布的消息*************" + message + "未被确认,序列号" + sequenceNumber);
            };
            /**
             * 添加一个异步确认的监听器
             * 1.确认收到消息的回调
             * 2.未收到消息的回调
             */
            //  channel.addConfirmListener(ackCallback, null);
            channel.addConfirmListener(ackCallback, nackCallback);

            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            long l = System.currentTimeMillis();
            for (int i = 0; i < 1000; i++) {
                String message = "消息" + i;
                /**
                 * channel.getNextPublishSeqNo()获取下一个消息的序列号
                 * 通过序列号与消息体进行一个关联
                 * 全部都是未确认的消息体
                 */
                outstandingConfirms.put(channel.getNextPublishSeqNo(), message);

                /**
                 * 生成一个队列
                 * 1.队列名称
                 * 2.队列里面的消息是否持久化 默认消息存储在内存中
                 * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
                 * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
                 * 5.其他参数
                 */
                channel.basicPublish("", QUEUE_NAME, null, (i + "").getBytes());



            }
            long l1 = System.currentTimeMillis();
            System.out.println("异步批量确认发布时长= === " + (l1 - l));

        } catch (
                Exception e) {
            e.printStackTrace();
        }
    }
}

如何处理异步未确认消息

最好的解决的解决方案就是把未确认的消息放到一个基于内存的能被发布线程访问的队列,
比如说用 ConcurrentLinkedQueue 这个队列在 confirm callbacks 与发布线程之间进行消息的传
递.上面代码就是用了此方法,发消息是进行记录,在未确认回调函数里面进行处理

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

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

相关文章

思科模拟器配置静态路由(下一跳使用IP)

Router0配置代码&#xff1a;##端口配置 Router(config)#int fastEthernet 0/0 Router(config-if)#ip address 192.168.10.254 255.255.255.0 Router(config-if)#no shutdown Router(config-if)#int fastEthernet 0/1 Router(config-if)#ip address 192.168.20.1 255.255.255.2…

拼多多海外版Temu商业模式分析

拼多多于2022 年 9 月在美国上线跨境平台 Temu&#xff0c;发布仅2个月就成为北美下载量最高的应用程序&#xff0c;持续霸榜。 这篇文章帮你快速了解下Temu&#xff1a; 商业模式如何竞争情况如何有哪些优势和挑战后期业务如何发展 一、Temu商业模式 Temu平台拥有对商品的最…

如何一目了然地监控远程 Linux 系统

动动发财的小手&#xff0c;点个赞吧&#xff01; Glances 是一款免费的开源、现代、跨平台、实时 top 和类似 htop 的系统监控工具&#xff0c;与同类工具相比&#xff0c;它提供了先进的功能&#xff0c;并且可以在不同的模式下运行&#xff1a;作为独立模式、客户端/服务器模…

32.利用fmincon 解决 最小费用问题(matlab程序)

1.简述 fmincon函数非线性约束下的最优化问题 fmincon函数&#xff0c;既是求最小约束非线性多变量函数 该函数被用于求如下函数的最小值 语法如下: x fmincon(fun,x0,A,b) x fmincon(fun,x0,A,b,Aeq,beq) x fmincon(fun,x0,A,b,Aeq,beq,lb,ub) x fmincon(fun,x0,A,b,Aeq…

软件测试分类总结

目录 1.根据源代码可见度划分 1.1黑盒测试 1.2白盒测试 1.3灰盒测试 2.根据开发阶段划分 2.1单元测试 2.2集成测试 2.3系统测试 2.4验收测试 3.按照实施组织划分 3.1α测试 3.2β测试 3.3第三方测试 4.按照是否运行程序划分 4.1静态测试 4.2动态测试 5.根据软件测试工作的…

centos7 部署Tomcat和jpress应用

目录 一、静态、动态、伪静态 二、Web 1.0 和 Web 2.0 三、centos7 部署Tomcat 3.1 安装、配置jdk 3.2 安装 Tomcat 3.3 配置服务启动脚本 3.3.1 创建用户和组 3.3.2 创建tomcat.conf文件 3.3.3 创建服务脚本(tomcat.service) 3.3.4 重新加载守护进程并且测试 四、部…

P3373 【模板】线段树 2

题目 思路 作为线段树模板题&#xff0c;这题主要考查了对lazytag以及先乘后加的使用&#xff0c; 因为是模板&#xff0c;所以这里证明略 代码 #include<bits/stdc.h> using namespace std; #define int long long const int maxn1e55; int n,m,p; int a[maxn]; stru…

输出菱形的程序填空题补充代码问题

ABBBCCCCCDDDDDDDEEEF 如上图所示&#xff0c;补充以下代码&#xff0c;输出这个图形。 #include<stdio.h> #define N 7//图案共N行 #define L (80-N)/2//整个图案据屏幕左边L列&#xff0c;使其居中 int main() {char cA;int i,j,p;for(i1;i<N;i, ){if(i<(…

(12)理解委托,反射,Type,EvenInfo,插件, 组合枚举,BindingFlags,扩展方法及重载,XML认识

一、复习委托事件 1、委托复习。 private delegate int MyDelegate(int a, int b); //1.定义委托类型private static void Main(string[] args){MyDelegate md new MyDelegate(AddDelegate);//2.声明委托变量int result md(1, 2);//3.调用委托Console.WriteLine(result);Cons…

运维作业4

一.简述静态网页和动态网页的区别。 静态页面资源特征 1. 处理文件类型&#xff1a;如.html、jpg、.gif、.mp4、.swf、.avi、.wmv、.flv等 2. 地址中不含有问号"&#xff1f;"或&等特殊符号。 3. 保存在网站服务器文件系统上的&#xff0c;是实实在在保存在服务器…

十进制、八进制、二进制、十六进制

十进制 每一位都有 0~9 十个数码&#xff0c;逢十进一 二进制 每一位仅有 0 和 1 两个数码&#xff0c;逢二进一 八进制 每一位都有 0~7 八个数码&#xff0c;逢八进一 十六进制 每一位都有 0~9、A(10)、B(11)、C(12)、D(13)、E(14)、F(15)&#xff0c;十六个数码&#…

路由动态选择协议之RIP(路由信息协议)

软件&#xff1a;cicso packet tracer 8.0 拓扑图&#xff1a;路由器&#xff1a;Router-PT、连接线&#xff1a;Serial DTE、连接口&#xff1a;Serial口 1、配置基础ip R1配置&#xff1a;虚拟接口——1.1.1.1&#xff1b;S3/0——192.168.1.1 R1(config)#int s3/0 R1(con…

SQL Server数据库 -- 索引与视图

文章目录 一、索引 聚集索引非聚集索引二、视图三、自定义函数 标量函数表值函数四、游标五、总结 前言 在学习完创建库表、查询等知识点后&#xff0c;为了更加方便优化数据库的存储和内容&#xff0c;我们需要学习一系列的方法例如索引与视图等等&#xff0c;从而使我们更加…

快手头部主播合体,二驴祁天道直播首秀销售额破亿

2023年刚刚过半&#xff0c;直播江湖突然生变。 快手头部娱乐主播「二驴」与快手户外主播第一人「祁天道」宣布“合体”&#xff0c;两者加总的粉丝量接近1亿&#xff0c;又一个“超级网红IP”诞生。 ▲图源&#xff1a;二驴的、祁天道快手截图 从白手起家的草根&#xff0c;…

语义检索系统【四】:基于ERNIE-Gram的Pair-wise和基于RocketQA的CrossEncoder训练的单塔模型实现数据精排

搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术…

启动Flink显示初始化状态怎么解决?

启动Flink显示初始化状态怎么解决&#xff1f; Flink On Yarn模式 问题 flnk任务在跑的过程中&#xff0c; 有时候任务停掉了 &#xff0c;不过我有 定时任务&#xff0c;可以把失败的flink任务拉起来&#xff0c;但是因为最新的checkpoint做失败了&#xff0c;导致脚本无法拉…

【leetcode经典简单题】自食用||删除链表中倒数第k个结点

step by step. 题目&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&a…

C# Blazor 学习笔记(8):row/col布局开发

文章目录 前言相关文章代码row和col组件B_rowB_col结构 使用 前言 可能是我用的element ui和 uView这种第三方组件用的太多了。我上来就希望能使用这些组件。但是目前Blazor目前的生态其实并不完善&#xff0c;所以很多组件要我们自己写。 我们对组件的要求是 我们在组件化一共…

人工智能科研经验——人工智能顶会审稿人科研方法分享

人工智能顶会审稿人科研方法分享 前言 1、本教程主要记录人工智能科研方面的经验积累。 Question&#xff1a;科研是什么&#xff1f; Anwser&#xff1a;科研是【进行观察—>定义问题—>研究问题—>发展假设—>设计实验—>收集和分析结果—>构建结论—>进…