RabbitMQ 交换机类型

常用交换机

发布订阅(Publish/Subscribe)交换机

 一个生产者给多个队列发送消息,X 代表交换机。

交换机的作用:类似网络路由器,主要提供转发功能,解决怎么把消息转发到不同的队列中,让消费者从不同队列取然后消费。

绑定:交换机和队列关联起来

发布订阅交换机,队列进行持久化,生产者发布消息,所有消费者都能接收到消息。

生产者代码

public class FanoutProducer{

  private static final String EXCHANGE_NAME = "fanout_exchange";

  public static void main(String[] argv) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    try (Connection connection = factory.newConnection();
         Channel channel = connection.createChannel()) {
        //创建交换机,参数:交换机名称,交换机类型
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNext()){
            String message = scanner.nextLine();
            //第二个参数是路由规则
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
            System.out.println(" [x] Sent '" + message + "'");
        }

    }
  }
}

消费者代码

public class FanoutConsumer {
    private static final String EXCHANGE_NAME = "fanout_exchange";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //绑定交换机,以及设置绑定规则
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        String queueName1 = "xiaowang";
        String queueName2 = "xiaoli";
        channel.queueDeclare(queueName1, true, false, false, null);
        channel.queueDeclare(queueName2, true, false, false, null);
        //创建队列,不指定队列,随机分配
        //String queueName = channel.queueDeclare().getQueue();
        channel.queueBind(queueName1, EXCHANGE_NAME, "");
        System.out.println(" [xiaowang] Waiting for messages. To exit press CTRL+C");
		//交换机绑定队列
        channel.queueBind(queueName2, EXCHANGE_NAME, "");
        System.out.println(" [xiaoli] Waiting for messages. To exit press CTRL+C");
        DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(  " [xiaowang] Received '" + message + "'");
        };
        DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(  " [xiaoli] Received '" + message + "'");
        };
        channel.basicConsume(queueName1, true, deliverCallback1, consumerTag -> {
        });
        channel.basicConsume(queueName2, true, deliverCallback2, consumerTag -> {
        });
    }
}

channel 频道:理解为操作消息队列的 Client,通过 channel 收发消息,提供了和消息对了 server 建立通信的传输方法

channel.queueDeclare 方法参数:

queue:这是一个字符串参数,代表要声明的队列的名称。如果队列不存在,则会自动创建一个新的队列。

durable:这是一个布尔值参数,表示队列是否持久化。如果设置为true,则队列会在服务器重启后仍然存在;如果设置为false,则队列在服务器重启后会被删除。默认值为false。

exclusive:这也是一个布尔值参数,表示队列是否为独占模式。如果设置为true,则只有当前连接可以访问该队列;如果设置为false,则其他连接也可以访问该队列。默认值为false。

autoDelete:这是另一个布尔值参数,表示队列是否自动删除。如果设置为true,则当最后一个消费者取消订阅时,队列将被删除;如果设置为false,则队列将一直存在,直到手动删除或服务器重启。默认值为false。

arguments:这是一个可选参数,用于设置队列的其他属性,比如消息的最大长度、最大优先级等。

channel.basicPublish 参数:

exchange:这是一个字符串参数,代表交换机的名称。如果不需要使用特定的交换机,可以传递一个空字符串("")。交换机是RabbitMQ中用于接收生产者发送的消息并根据绑定规则路由到队列的组件。

routingKey:这也是一个字符串参数,它指定了发布消息的队列。无论通道绑定到哪个队列,最终发布的消息都会包含这个指定的路由键。路由键是用来确定消息应该发送到哪个队列的重要信息。

message:这是要发布的消息本身,通常是字节数组的形式。

properties:这是一个可选参数,用于设置消息的属性,比如消息的优先级、过期时间等。

在使用channel.basicPublish时,需要注意以下几点:

exchange和routingKey不能为空:在AMQImpl类中的实现要求这两个参数都不能为null,否则会抛出异常。

交换机类型:根据不同的需求,可以选择不同类型的交换机,如fanout、direct或topic。每种类型的交换机都有其特定的路由规则。

非命名队列:在某些情况下,比如日志系统,可以使用非命名队列,这样消费者可以接收到所有相关的日志消息,而不是特定的部分。

 channel.basicConsume 参数:

queue:这是一个字符串参数,代表要消费的队列的名称。如果队列不存在,则会抛出异常。
onMessage:这是一个回调函数,当有新的消息到达时会被调用。该函数需要接收两个参数:一个表示消息内容的Delivery对象和一个表示通道的Channel对象。
consumerTag:这是一个可选参数,用于标识消费者。如果没有指定,则会自动生成一个唯一的标识符。
autoAck:这是一个布尔值参数,表示是否自动确认消息。如果设置为true,则在消息被处理后会自动发送确认信息;如果设置为false,则需要手动发送确认信息。默认值为false。
arguments:这是一个可选参数,用于设置消费者的其他属性,比如消息的最大长度、最大优先级等。
在使用channel.basicConsume时,需要注意以下几点:

队列名称:队列名称应该是唯一的,否则会抛出异常。
消息处理:在onMessage回调函数中,需要对消息进行处理,并根据需要发送确认信息。
消费者标识符:可以通过设置consumerTag来标识消费者,以便在后续操作中进行识别和管理。
消费者属性:可以通过设置消费者的其他属性来控制消费者的行为,比如设置消息的最大长度、最大优先级等。

路由交换机 (Direct exchange)

和订阅发布的区别是在交换机和队列之间有一个路由键,用来控制消息发送到哪个队列中供消费者消费。生产者给交换机一个标识,让交换机给指定的队列转发消息。

生产者代码

public class DirectProducer {

    private static final String EXCHANGE_NAME = "direct_exchange";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            //创建交换机,交换机类型是 direct
            channel.exchangeDeclare(EXCHANGE_NAME, "direct");
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()){
                String userInput = scanner.nextLine();
                //输入的时候带着标识,标识就是路由键
                String[] strs = userInput.split(" ");
                if(strs.length<1){
                    continue;
                }
                //消息
                String message = strs[0];
                //路由键
                String severity = strs[1];
                //发送消息时带着路由键
                channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
                System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
            }
        }
    }
}

消费者代码

public class DirectConsumer {

    private static final String EXCHANGE_NAME = "direct_exchange";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        String queueName1 = "xiaohong";
        String queueName2 = "xiaobai";
        channel.queueDeclare(queueName1, true, false, false, null);
        channel.queueDeclare(queueName2, true, false, false, null);
        //交换机使用路由键绑定队列
        channel.queueBind(queueName1, EXCHANGE_NAME, "xiaohong");
        channel.queueBind(queueName2, EXCHANGE_NAME, "xiaobai");
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

        DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [xiaohong] Received '" +
                    delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [xiaobai] Received '" +
                    delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        channel.basicConsume(queueName1, true, deliverCallback1, consumerTag -> {
        });
        channel.basicConsume(queueName2, true, deliverCallback2, consumerTag -> {
        });
    }
}

主题交换机 (Topic exchange)

在路由交换机的基础上,消息会具有一个模糊的路由键转发给指定的对俄(一系列的路由键、一类的路由键)

1. (*)标识匹配一个单词,比如 *.orange 表示 a.orange b.orange 都能匹配

2. (#)表示 0 个或多个单词,比如 a,#, a.a, a.b 都可以

生产者代码

public class TopicProduce {

  private static final String EXCHANGE_NAME = "topic_exchange1";

  public static void main(String[] argv) throws Exception {
      ConnectionFactory factory = new ConnectionFactory();
      factory.setHost("localhost");
      try (Connection connection = factory.newConnection();
           Channel channel = connection.createChannel()) {
          channel.exchangeDeclare(EXCHANGE_NAME, "topic");

          Scanner scanner = new Scanner(System.in);
          while (scanner.hasNext()) {
              String userInput = scanner.nextLine();
              String[] strs = userInput.split(" ");
              if (strs.length < 1) {
                  continue;
              }
              //消息
              String message = strs[0];
              //路由键
              String severity = strs[1];

              channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
              System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
          }
      }
  }
}

消费者代码

public class TopicConsumer {

    private static final String EXCHANGE_NAME = "topic_exchange1";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        String queueName1 = "xiaohei";
        String queueName2 = "xiaolv";
        String queueName3 = "xiaohuang";
        channel.queueDeclare(queueName1, true, false, false, null);
        channel.queueDeclare(queueName2, true, false, false, null);
        channel.queueDeclare(queueName3, true, false, false, null);
        //交换机使用路由键绑定队列,路由键绑定在第三个参数
        channel.queueBind(queueName1, EXCHANGE_NAME, "#.前端.#");
        channel.queueBind(queueName2, EXCHANGE_NAME, "#.后端.#");
        channel.queueBind(queueName2, EXCHANGE_NAME, "#.产品.#");
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
        //收到消息后如何处理
        DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [xiaohei] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [xiaolv] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        DeliverCallback deliverCallback3 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [xiaohuang] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        channel.basicConsume(queueName1, true, deliverCallback1, consumerTag -> {
        });
        channel.basicConsume(queueName2, true, deliverCallback2, consumerTag -> {
        });
        channel.basicConsume(queueName3, true, deliverCallback3, consumerTag -> {
        });


    }
}

核心机制

消息过期机制

官方文档:Preventing Unbounded Buffers with RabbitMQ | RabbitMQ

每个消息指定一个有效期,一段时间内没有被消费者处理,就过期了。

比如消费者挂了,消息一直不被处理,订单就会失效。

可以清理过期的数据,模拟延迟队列的实现。

给每条消息都设置过期时间:

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 60000);
channel.queueDeclare(QUEUE_NAME, false, false, false, args);

给队列设置过期时间,设置在生产者中

AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
        .expiration("1000")
        .build();

生产者代码

public class TtlProducer {

  private final static String QUEUE_NAME = "Tll_queue";
  public static void main(String[] argv) throws Exception {
    //创建连接
    ConnectionFactory factory = new ConnectionFactory();
    //设置了本地连接,如果修改了用户名和密码,需要设置
    /*factory.setPassword();
    factory.setUsername();*/
    factory.setHost("localhost");
    //建立连接、创建频道
    //频道,类似客户端,用于调用server
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    String message = "Hello World!";
    //发消息设置过期时间
    AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
            .expiration("1000")
            .build();
    //发送消息
    channel.basicPublish("",QUEUE_NAME,properties,message.getBytes(StandardCharsets.UTF_8));
    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

  }
}

消费者代码

public class TtlConsumer {

  private final static String QUEUE_NAME = "Tll_queue";

  public static void main(String[] argv) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    //声明队列,同一个消息队列参数必须一致
    Map<String, Object> args = new HashMap<String, Object>();
    args.put("x-message-ttl", 60000);
    channel.queueDeclare(QUEUE_NAME, false, false, false, args);
    //定义了如何处理消息
    DeliverCallback deliverCallback = (consumerTag, delivery) -> {
      String message = new String(delivery.getBody(), "UTF-8");
      System.out.println(" [x] Received '" + message + "'");
    };
    //接收、消费消息 第二个参数 autoAck
    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

  }
}

死信队列

官方文档:Dead Letter Exchanges | RabbitMQ

为了保证消息的可靠性,比如每条消息都成功消费,需要提供一个容错机制,即:失效的消息怎么办?

死信:过期的消息、拒收的消息、消息队列满了、处理失败的消息的统称。

死信队列:处理死信的队列。

死信交换机:专门给死信队列转发消息的,存在路由绑定关系

实际就是设置一个普通的队列,专门将死信发送到这个队列中处理。

1. 创建死信交换机和死信队列

//声明死信交换机
String queueName = "laoban_dlx_queue";
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, EXCHANGE_NAME, "laoban");

2. 给失败之后需要容错处理的队列绑定死信交换机

args2.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);

3. 绑定交换机到死信队列

args2.put("x-dead-letter-routing-key", "waibao");

生产者代码

public class DLXDirectProducer {

    private static final String EXCHANGE_NAME = "direct2_exchange";
    private static final String DEAD_EXCHANGE_NAME = "dlx_direct2_exchange";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            //声明死信交换机
            channel.exchangeDeclare(DEAD_EXCHANGE_NAME, "direct");
            String queueName = "laoban_dlx_queue";
            channel.queueDeclare(queueName, true, false, false, null);
            channel.queueBind(queueName, DEAD_EXCHANGE_NAME, "laoban");

            String queueName2 = "waibao_dlx_queue";
            channel.queueDeclare(queueName2, true, false, false, null);
            channel.queueBind(queueName2, DEAD_EXCHANGE_NAME, "waibao");
            channel.exchangeDeclare(EXCHANGE_NAME, "direct");
            DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [laoban] Received '" +
                        delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
            };
            DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), "UTF-8");
                System.out.println(" [waibao] Received '" +
                        delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
            };
            channel.basicConsume(queueName, false, deliverCallback1, consumerTag -> {
            });
            channel.basicConsume(queueName2, false, deliverCallback2, consumerTag -> {
            });
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()){
                String userInput = scanner.nextLine();
                String[] strs = userInput.split(" ");
                if(strs.length<1){
                    continue;
                }
                //消息
                String message = strs[0];
                //路由键
                String severity = strs[1];

                channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));
                System.out.println(" [x] Sent '" + severity + "':'" + message + "'");
            }

        }
    }
}

消费者代码

public class DLXDirectConsumer {

    private static final String EXCHANGE_NAME = "direct2_exchange";
    private static final String DEAD_EXCHANGE_NAME = "dlx_direct2_exchange";


    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        //死信交换机绑定工作队列,当信息错误就从工作队列发送到死信交换机
        Map<String, Object> args1 = new HashMap<String, Object>();
        //指定绑定哪个交换机
        args1.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
        //死信要发送到哪个队列
        args1.put("x-dead-letter-routing-key", "laoban");
        String queueName1 = "doghuang";
        channel.queueDeclare(queueName1, true, false, false, args1);
        channel.queueBind(queueName1, EXCHANGE_NAME, "doghuang");

        //绑定cat 队列

        String queueName2 = "catbai";
        Map<String, Object> args2 = new HashMap<String, Object>();
        args2.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
        args2.put("x-dead-letter-routing-key", "waibao");
        channel.queueDeclare(queueName2, true, false, false, args2);
        channel.queueBind(queueName2, EXCHANGE_NAME, "catbai");


        //交换机使用路由键绑定队列
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

        DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            channel.basicNack(delivery.getEnvelope().getDeliveryTag(),false,false);
            System.out.println(" [doghuang] Received '" +
                    delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [catbai] Received '" +
                    delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
        };
        channel.basicConsume(queueName1, false, deliverCallback1, consumerTag -> {
        });
        channel.basicConsume(queueName2, false, deliverCallback2, consumerTag -> {
        });

    }
}

项目实战

项目中使用可以选择两种方法

1. 官方的客户端,兼容性好,灵活,需要自己维护管理

2. 使用封装好的客户端,比如 Spring Boot RabbitMQ Starter

优点:简单易用

缺点:不够灵活,被框架限制

小项目使用封装好的足够

1. 依赖引入

引入和自己 Spring Boot 版本相同的依赖,避免出现不能运行的错误

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    <version>2.6.13</version>
</dependency>

2. 引入配置

rabbitmq:
  host: localhost
  port: 5672
  password: guest
  username: guset

3. 创建交换机和消息队列,这个只需要启动一次创建即可

/**
 * 只启动一次,创建交换机和消息队列
 */
public class MqInitMain {

    private static final String EXCHANGE_NAME = "code_exchange";
    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");

        //绑定一个队列
        String queueName = "code_queue";
        channel.queueDeclare(queueName,true,false,false,null);
        channel.queueBind(queueName,EXCHANGE_NAME,"BI_routingKey");
    }
}

4. 生产者

/**
 * 生产者
 */
@Component
public class MyMessageProducer {


    @Resource
    private RabbitTemplate rabbitTemplate;

    //1.交换机名称2. 交换机路由键3.发送的消息
    public void sendMessage(String exchange, String routingKey,String message){
        rabbitTemplate.convertAndSend(exchange,routingKey,message);
    }

}

5. 消费者

/**
 * 消费者
 */
@Component
@Slf4j
public class MessageConsumer {

    @RabbitListener(queues = {"code_queue"},ackMode = "MANUAL")
    public void receiveMessage(String message, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long delivery){
        log.info("receiveMessage message={}",message);
        try {
            channel.basicAck(delivery,false);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

6. 测试

@SpringBootTest
class MyMessageProducerTest {

    @Resource
    private MyMessageProducer myMessageProducer;
    @Test
    void sendMessage() {
        myMessageProducer.sendMessage("code_exchange","BI_routingKey","你好吗");
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

国家数据局发布《数字中国发展报告 (2023年)》

2023年数字中国建设总体呈现发展基础更加夯实、赋能效应更加凸显、数字安全和治理体系更加完善、数字领域国际合作更加深入等四方面特点。 为贯彻落实党中央、国务院关于建设数字中国的重要部署&#xff0c;国家数据局会同有关单位系统总结2023年数字中国建设重要进展和工作成效…

【UE5.1 角色练习】08-传送技能

前言 在上一篇&#xff08;【UE5.1 角色练习】07-AOE技能&#xff09;基础上继续实现人物通过鼠标点击然后传送技能的功能。 效果 步骤 1. 首先需要显示鼠标光标&#xff0c;我们可以在玩家控制器中勾选“显示鼠标光标” 2. 在项目设置中添加一个操作映射&#xff0c;设置按…

2-Django项目进阶--继续学生管理系统

目录 项目框架: urls.py views.py modules.py class_data.html add_and_modify.html add_stu.html 笔记: 继承语法 模板继承总结&#xff1a; 班级添加 add_and_modify.html 修改添加公用一个页面即可 views.py 班级修改 views.py url.py 班级删除 views.py…

嵌入式UI开发-lvgl+wsl2+vscode系列:2、label(标签)+button(按钮)+slider(滑块)控件熟悉及其示例demo运行

文章目录 一、前言二、常见控件示例demo模拟环境运行及接口熟悉&#xff08;重要&#xff09;如何修改示例main函数测试各种示例1、label示例1.1、label示例1&#xff08;标签基础示例&#xff09;1.2、label示例2&#xff08;标签带阴影效果&#xff09;1.3、label示例3&#…

Textual for Mac:轻量级IRC客户端

在寻找一款高效、轻量级的IRC客户端时&#xff0c;Textual for Mac无疑是你的不二之选。它集成了众多现代技术&#xff0c;如本机IPv6、最新的IRCv3规范&#xff0c;以及客户端证书身份验证&#xff0c;让你的聊天体验更加顺畅和安全。 Textual for Mac v7.2.2免激活版下载 Tex…

代码随想录算法训练营第五十九天|503.下一个更大元素II、42. 接雨水

503. 下一个更大元素 II 思路&#xff1a; 这道题和739. 每日温度 (opens new window)也几乎如出一辙。 不过&#xff0c;本题要循环数组了。 本篇我侧重与说一说&#xff0c;如何处理循环数组。 相信不少同学看到这道题&#xff0c;就想那我直接把两个数组拼接在一起&…

MySQL导入SQL脚本---超详细介绍

1.新建xxx数据库&#xff0c;字符集选对。 2.在mysql安装目录下cmd进入小黑窗 3.执行mysql -uroot -p123456 --default-character-setutf8命令 4.use xxx; 5.source xxx.sql 执行完上面的命令等待结束就可以了 需要注意的是--default-character-setutf8&#xff0c;要不然可…

如何把学浪的视频保存到手机

你是不是还在为无法将学浪的视频保存到手机而烦恼&#xff1f;别担心&#xff0c;接下来我将为大家分享一个非常实用的方法&#xff0c;让你轻松实现这一目标&#xff01; 下载学浪的工具我已经打包好了&#xff0c;有需要的自己下载一下 学浪下载工具打包链接&#xff1a;百…

网络工程师备考2——vlan

vlan 1、什么是VLAN&#xff1f; VLAN&#xff08;Virtual LAN&#xff09;&#xff0c;翻译成中文是“虚拟局域网”。LAN可以是由少数几台家用计算机构成的网络&#xff0c;也可以是数以百计的计算机构成的企业网络。VLAN所指的LAN特指使用路由器分割的网络——也就是广播域…

DAOS: A Scale-Out High Performance Storage Stack for Storage Class Memory——论文泛读

Supercomputing Frontiers 2020 Paper 分布式元数据论文阅读笔记整理 问题 企业、政府和学术界出现的数据密集型应用程序将现有的I/O模型扩展到了极限。现代I/O工作负载的特点是元数据与未对齐和碎片化数据的结合比例越来越高。传统的存储堆栈为这些工作负载提供了较差的性能…

OrangePi AIPro:次世代嵌入式边缘AI计算与智能机器人应用开发平台

近年来,随着物联网(IoT)和人工智能(AI)技术的快速发展,嵌入式边缘计算板卡在智能设备中的应用越来越广泛。OrangePi AIpro作为一款轻量化高性能的嵌入式边缘人工智能计算SoC,在硬件配置、AI性能和使用便利性方面都有着突出的表现。本文将详细评测OrangePi AIpro的各个方…

项目文章 |NC揭示真菌中A-to-I mRNA编辑机制及其调控和演化

A-to-I mRNA编辑是一种重要的基因表达调控方式&#xff0c;它通过将mRNA中的腺苷(A)转变为肌苷(I)&#xff0c;从而可能改变蛋白质的编码信息。在动物中&#xff0c;这一过程由ADAR家族酶介导&#xff0c;然而在真菌中&#xff0c;由于缺乏ADARs的同源物&#xff0c;其背后的机…

python onnx 推理yolov10

python onnx 推理yolov10 import onnxruntime as ort import cv2 import numpy as np# Class names for the COCO dataset CLASSES = ["person", "bicycle", "car", "motorcycle", "airplane"

[猫头虎分享21天微信小程序基础入门教程]第19天:小程序的插件开发与使用

[猫头虎分享21天微信小程序基础入门教程]第19天&#xff1a;小程序的插件开发与使用 第19天&#xff1a;小程序的插件开发与使用 &#x1f527; 自我介绍 大家好&#xff0c;我是猫头虎&#xff0c;一名全栈软件工程师。今天我们继续微信小程序的学习&#xff0c;重点了解如…

StackExchange.Redis跑起来,为什么这么溜?

StackExchange.Redis 是一个高性能的 Redis 客户端库&#xff0c;主要用于 .NET 环境下与 Redis 服务器进行通信&#xff0c;大名鼎鼎的stackoverflow 网站就使用它。它使用异步编程模型&#xff0c;能够高效处理大量请求。支持 Redis 的绝大部分功能&#xff0c;包括发布/订阅…

SwiftUI 5.0(iOS 17)进一步定制 TipKit 外观让撸码如虎添翼

概览 在之前 SwiftUI 5.0&#xff08;iOS 17&#xff09;TipKit 让用户更懂你的 App 这篇博文里&#xff0c;我们已经初步介绍过了 TipKit 的基本知识。 现在&#xff0c;让我们来看看如何进一步利用 SwiftUI 对 TipKit 提供的细粒度外观定制技巧&#xff0c;让 Tip 更加“明眸…

灯塔工厂产业数字化平台解决方案(50页PPT)

方案介绍&#xff1a; 随着工业4.0和智能制造的快速发展&#xff0c;传统工厂正面临着转型升级的迫切需求。为了提升生产效率、优化资源配置、增强市场竞争力&#xff0c;我们推出了灯塔工厂产业数字化平台解决方案。该方案旨在通过先进的信息技术手段&#xff0c;将传统工厂转…

springboot发送短信验证码,结合redis 实现限制,验证码有效期2分钟,有效期内禁止再次发送,一天内发送超3次限制

springboot结合redis发送短信验证码,实现限制发送操作 前言(可忽略)实现思路正题效果图示例手机号不符合规则校验图成功发送验证码示例图redis中缓存随机数字验证码&#xff0c;2分钟后失效删除redis缓存图验证码有效期内 返回禁止重复发送图验证码24小时内发送达到3次&#xf…

Https自签名证书

openSSL下载 https://slproweb.com/products/Win32OpenSSL.html 1_整体流程 &#xff08;1&#xff09;https介绍 HTTPS 是 Hypertext Transfer Protocol Secure 的简称&#xff0c;是基于 SSL 加密方式的 HTTP 协议 &#xff08;2&#xff09;CA机构介绍 介绍&#xff1a…

2024年,游戏行业还值得进入吗?

来自知乎问题“2024年&#xff0c;游戏行业还值得进入吗&#xff1f;”的回答。 ——原问题描述&#xff1a;从超小厂执行策划做起&#xff0c;未来有前途吗&#xff1f; 展望2024年&#xff0c;国内外的游戏市场环境或将变得更加复杂&#xff0c;曾经那个水大鱼大的时代过去了…