RabbitMQ之延迟队列

2023-11-03

1. 延迟队列概念


  延时队列,队列内部是有序的,最重要的特性就体现在它的延时属性上,延时队列中的元素是希望在指定时间到了以后或之前取出和处理,简单来说,延时队列就是用来存放需要在指定时间被处理的元素的队列

2. 延迟队列使用场景


1️⃣ 订单在十分钟之内未支付则自动取消
2️⃣ 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒
3️⃣ 用户注册成功后,如果三天内没有登陆则进行短信提醒
4️⃣ 用户发起退款,如果三天内没有得到处理则通知相关运营人员
5️⃣ 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议

  这些场景都有一个特点,需要在某个事件发生之后或者之前的指定时间点完成某一项任务,如:发生订单生成事件,在十分钟之后检查该订单支付状态,然后将未支付的订单进行关闭;看起来似乎使用定时任务,一直轮询数据,每秒查一次,取出需要被处理的数据,然后处理不就完事了吗?如果数据量比较少,确实可以这样做,比如:对于“如果账单一周内未支付则进行自动计算”这样的需求,如果对于时间不是严格限制,而是宽松意义上的一周,那么每天晚上跑个定时任务检查一下所有未支付的账单,确实也是一个可行的方案。但对于数据量比较大,并且时效性较强的场景,如:“订单十分钟内未支付则关闭”,短期内未支付的订单数据可能会有很多,活动期间甚至会达到百万甚至千万级别,对这么庞大的数据量仍旧使用轮询的方式显然是不可取的,很可能在一秒内无法完成所有订单的检查,同时会给数据库带来很大压力,无法满足业务要求而且性能低下。

在这里插入图片描述

3. RabbitMQ 中的 TTL


  TTL 是什么呢?TTL 是 RabbitMQ 中一个消息或者队列的属性,表明一条消息或者该队列中的所有消息的最大存活时间,单位是毫秒,换句话说,如果一条消息设置了 TTL 属性或者进入了设置 TTL 属性的队列,那么这条消息如果在TTL设置的时间内没有被消费,则会成为“死信”。如果同时配置了队列的 TTL 和消息的TTL,那么较小的那个值将会被使用,有两种方式设置TTL

3.1 消息设置 TTL


针对每条消息设置 TTL

rabbitTemplate.convertAndSend("X","XC",message,correlationData->{
	correlationData.getMessageProperties().setExpiration(ttlTime);
	return correlationData;
});

3.2 队列设置 TTL


在创建队列的时候设置队列的“x-message-ttl" 属性

// 声明队列的TTL
args.put("x-message-ttl",5000);
return QueueBuilder.durable(Queue_A).withArguments(args).build();

3.3 两者的区别


  如果设置了队列的 TTL 属性,那么一旦消息过期,就会被队列丢弃(如果配置了死信队列被丢到死信队列中),而第二种方式,消息即使过期,也不一定会被马上丢弃,因为消息是否过期是在即将投递到消费者之前判定的,如果当前队列有严重的消息积压情况,则已过期的消息也许还能存活较长时间;另外,还需要注意的一点是,如果不设置 TTL,表示消息永远不会过期,如果将 TTL 设置为 0 ,则表示除非此时可以直接投递该消息到消费者,否则消息将会被丢弃。

4. 整合 SpringBoot

4.1 创建项目


在这里插入图片描述

4.2 添加依赖


<!--RabbitMQ 依赖-->
<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>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!--swagger-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>
<!--RabbitMQ 测试依赖-->
<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit-test</artifactId>
    <scope>test</scope>
</dependency>

4.3 修改配置文件


spring.rabbitmq.host=182.92.234.71
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123

4.4 添加 Swagger 配置类


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket webApiConfig() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .build();
    }

    private ApiInfo webApiInfo() {
        return new ApiInfoBuilder()
                .title("rabbitmq接口文档")
                .description("本文档描述了 rabbitmq 微服务接口定义")
                .version("1.0")
                .contact(new Contact("enjoy6288", "http://atguigu.com", "1873172950@qq.com"))
                .build();
    }
}

5. 队列 TTL

5.1 代码架构图


  创建两个队列 QA 和 QB,两者队列 TTL 分别设置为 10S 和 40S,然后在创建一个交换机 X 和死信交换机 Y,它们的类型都是 direct,创建一个死信队列 QD,它们的绑定关系如下:
在这里插入图片描述

5.2 配置文件类代码


@Configuration
public class TtlQueueConfig {

    public static final String X_EXCHANGE = "X";
    public static final String QUEUE_A = "QA";
    public static final String QUEUE_B = "QB";
    public static final String Y_DEAD_LETTER_EXCHANGE = "Y";
    public static final String DEAD_LETTER_QUEUE = "QD";

    // 声明 xExchange
    @Bean("xExchange")
    public DirectExchange xExchange() {
        return new DirectExchange(X_EXCHANGE);
    }

    // 声明 yExchange
    @Bean("yExchange")
    public DirectExchange yExchange() {
        return new DirectExchange(Y_DEAD_LETTER_EXCHANGE);
    }

    // 声明队列 A ttl为10s 并绑定到对应的死信交换机
    @Bean("queueA")
    public Queue queueA() {
        Map<String, Object> args = new HashMap<>(3);
        // 声明当前队列绑定的死信交换机
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        // 声明当前队列的死信路由 key
        args.put("x-dead-letter-routing-key", "YD");
        // 声明队列的 TTL
        args.put("x-message-ttl", 10000);
        return QueueBuilder.durable(QUEUE_A).withArguments(args).build();
    }

    // 声明队列 A 绑定 X 交换机
    @Bean
    public Binding queueBindingX(@Qualifier("queueA") Queue queueA, @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queueA).to(xExchange).with("XA");
    }

    // 声明队列 B ttl 为 40s 并绑定到对应的死信交换机
    @Bean("queueB")
    public Queue queueB() {
        Map<String, Object> args = new HashMap<>(3);
        // 声明当前队列绑定的死信交换机
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        // 声明当前队列的死信路由 key
        args.put("x-dead-letter-routing-key", "YD");
        // 声明队列的 TTL
        args.put("x-message-ttl", 40000);
        return QueueBuilder.durable(QUEUE_B).withArguments(args).build();
    }

    // 声明死信队列 QD
    @Bean("queueD")
    public Queue queueD() {
        return new Queue(DEAD_LETTER_QUEUE);
    }

    // 声明死信队列 QD 绑定关系
    @Bean
    public Binding deadLetterBindingQAD(@Qualifier("queueD") Queue queueD, @Qualifier("yExchange") DirectExchange yExchange) {
        return BindingBuilder.bind(queueD).to(yExchange).with("YD");
    }
}

5.3 消息生产者代码


@Slf4j
@RequestMapping("ttl")
@RestController
public class SendMsgController {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("sendMsg/{message}")
    public void sendMsg(@PathVariable String message) {
        log.info("当前时间: {}, 发送一条信息给两个 TTL 队列: {}", new Date(), message);
        rabbitTemplate.convertAndSend("X", "XA", "消息来自 ttl 为 10s 的队列:" + message);
        rabbitTemplate.convertAndSend("X", "XB", "消息来自 ttl 为 40s 的队列:" + message);
    }
}

5.4 消息消费者代码


@Slf4j
@Component
public class DeadLetterQueueConsumer {
    
    @RabbitListener(queues = "QD")
    public void receiveD(Message message, Channel channel) {
        String msg = new String(message.getBody());
        log.info("当前时间:{},收到死信队列信息{}", new Date().toString(), msg);
    }
}

发起一个请求 http://localhost:8080/ttl/sendMsg/嘻嘻嘻
在这里插入图片描述

  第一条消息在 10s 后变成了死信消息,然后被消费者消费掉,第二条消息在 40s 之后变成了死信消息,然后被消费掉,这样一个延时队列就打造完成了。

  不过,这样使用的话,岂不是每增加一个新的时间需求,就要新增一个队列,这里只有 10s 和 40s 两个时间选项,如果需要一个小时候处理,那么就需要增加 TTL 为一个小时的队列,如果是预定会议室然后提前通知这样的场景,岂不是要增加无数个队列才能满足需求?

6. 延时队列优化

6.1 代码架构图


  在这里新增了一个队列QC,绑定关系如下,该队列不设置 TTL 时间
在这里插入图片描述

6.2 配置文件类代码


@Component
public class MsgTtlQueueConfig {
    public static final String Y_DEAD_LETTER_EXCHANGE = "Y";
    public static final String QUEUE_C = "QC";

    // 声明队列 C 死信交换机
    @Bean("queueC")
    public Queue queueB() {
        Map<String, Object> args = new HashMap<>(3);
        // 声明当前队列绑定的死信交换机
        args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
        // 声明当前队列的死信路由 key
        args.put("x-dead-letter-routing-key", "YD");
        // 没有声明 TTL 属性
        return QueueBuilder.durable(QUEUE_C).withArguments(args).build();
    }

    // 声明队列 B 绑定 X 交换机
    @Bean
    public Binding queuecBindingX(@Qualifier("queueC") Queue queueC, @Qualifier("xExchange") DirectExchange xExchange) {
        return BindingBuilder.bind(queueC).to(xExchange).with("XC");
    }
}

6.3 消息生产者代码


@GetMapping("sendExpirationMsg/{message}/{ttlTime}")
public void sendMsg(@PathVariable String message, @PathVariable String ttlTime) {
    rabbitTemplate.convertAndSend("X", "XC", message, correlationData -> {
        correlationData.getMessageProperties().setExpiration(ttlTime);
        return correlationData;
    });
    log.info("当前时间:{},发送一条时长{}毫秒 TTL 信息给队列 C:{}", new Date(), ttlTime, message);
}

发起请求
http://localhost:8080/ttl/sendExpirationMsg/你好 1/20000
http://localhost:8080/ttl/sendExpirationMsg/你好 2/2000
在这里插入图片描述
  看起来似乎没什么问题,但是在最开始的时候,就介绍过如果使用在消息属性上设置 TTL 的方式,消息可能并不会按时“死亡”,因为 RabbitMQ 只会检查第一个消息是否过期,如果过期则丢到死信队列,如果第一个消息的延时时长很长,而第二个消息的延时时长很短,第二个消息并不会优先得到执行。

7. RabbitMQ 插件实现延时队列


  上文中提到的问题,确实是一个问题,如果不能实现在消息粒度上的 TTL,并使其在设置的TTL 时间
及时死亡,就无法设计成一个通用的延时队列。那如何解决呢,接下来我们就去解决该问题。

7.1. 安装延时队列插件


  在官网上下载 https://www.rabbitmq.com/community-plugins.html,下载rabbitmq_delayed_message_exchange 插件,然后解压放置到 RabbitMQ 的插件目录。
  进入 RabbitMQ 的安装目录下的 plgins 目录,执行下面命令让该插件生效,然后重启 RabbitMQ
/usr/lib/rabbitmq/lib/rabbitmq_server-3.8.8/plugins
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
在这里插入图片描述
在这里插入图片描述

7.2 代码架构图


  在这里新增了一个队列delayed.queue,一个自定义交换机 delayed.exchange,绑定关系如下:
在这里插入图片描述

7.3 配置文件类代码


  在我们自定义的交换机中,这是一种新的交换类型,该类型消息支持延迟投递机制 消息传递后并不会立即投递到目标队列中,而是存储在 mnesia(一个分布式数据系统)表中,当达到投递时间时,才投递到目标队列中。

@Configuration
public class DelayedQueueConfig {
    public static final String DELAYED_QUEUE_NAME = "delayed.queue";
    public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
    public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";

    @Bean
    public Queue delayedQueue() {
        return new Queue(DELAYED_QUEUE_NAME);
    }

    // 自定义交换机 我们在这里定义的是一个延迟交换机
    @Bean
    public CustomExchange delayedExchange() {
        Map<String, Object> args = new HashMap<>();
        // 自定义交换机的类型
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false,
                args);
    }

    @Bean
    public Binding bindingDelayedQueue(@Qualifier("delayedQueue") Queue queue,
                                       @Qualifier("delayedExchange") CustomExchange
                                               delayedExchange) {
        return BindingBuilder.bind(queue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
    }
}

7.4 消息生产者代码


public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";

@GetMapping("sendDelayMsg/{message}/{delayTime}")
public void sendMsg(@PathVariable String message, @PathVariable Integer delayTime) {
    rabbitTemplate.convertAndSend(DELAYED_EXCHANGE_NAME, DELAYED_ROUTING_KEY, message,
            correlationData -> {
                correlationData.getMessageProperties().setDelay(delayTime);
                return correlationData;
            });
    log.info(" 当前时间: {}, 发送一条延迟 {} 毫秒的信息给队列 delayed.queue:{}", new Date(), delayTime, message);
}

7.5 消息消费者代码


public static final String DELAYED_QUEUE_NAME = "delayed.queue";

@RabbitListener(queues = DELAYED_QUEUE_NAME)
public void receiveDelayedQueue(Message message) {
    String msg = new String(message.getBody());
    log.info("当前时间:{},收到延时队列的消息:{}", new Date().toString(), msg);
}

发起请求:
http://localhost:8080/ttl/sendDelayMsg/come on baby1/20000
http://localhost:8080/ttl/sendDelayMsg/come on baby2/2000

在这里插入图片描述
第二个消息被先消费掉了,符合预期

8. 总结

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RabbitMQ之延迟队列 的相关文章

  • yard 0.7.3 无法在 Markdown 和 Textile 中构建我的自述文件

    我决定将我的项目中的 README 文件转换为 Markdown 并一直使用yard 验证文档是否正确呈现 所以我安装了 rdiscount 将 README 更改为 README md 并尝试 yard doc README md 这给了
  • Ruby 中的任务/未来

    代表潜在延迟的异步计算并且可以订阅其完成的模式的惯用 Ruby 模拟是什么 即类似于 NET 的东西System Threading Task 或Python 3 xconcurrent futures future 请注意 这并不一定意味
  • 将 sensu-client 连接到服务器时 AMQP 连接的 bad_header

    我已经安装了 sensu 和厨师社区食谱 但是 sensu客户端无法连接到服务器 导致rabbitmq连接错误 尝试连接时消息超时 这是详细的客户端日志 来自 sensu client log 的日志 timestamp 2014 07 0
  • 如何从 Ruby 程序发送邮件?

    我想从 Ruby 应用程序发送电子邮件 核心语言中是否有调用来执行此操作 或者是否有我应该使用的库 最好的方法是什么 如果你不想使用行动邮递员 http wiki rubyonrails org rails pages ActionMail
  • Rails 控制台无法运行

    rbenv 红宝石版本 2 6 6 导轨版本 5 1 4 我正在较旧的代码库中工作 ruby 2 6 6 rails 5 4 1 这是我每天使用的代码库 我必须通过 rbenv 下载一个新的 ruby 版本作为单独的存储库 这样做在我的旧代
  • 如何使用本地安装的gems执行Ruby程序?

    我已经使用安装了我的依赖项 bundle package 然后将它们传输到离线服务器并运行 gt bundle install local Using mime types 1 19 Using rest client 1 6 7 Usin
  • ruby 中的 #encode 和 #force_encoding 有什么区别?

    我真的不明白之间的区别 encode and force encoding在 Ruby 中String班级 我明白那个 kam force encoding UTF 8 将迫使 kam 是UTF 8编码 但是怎么样 encode encod
  • 如何从 ruby​​ 中的字符串中删除所有非数字?

    用户输入数字的形式如下 1 800 432 4567 800 432 4567 800 432 4566 800 432 4567 1 800 432 4567 800 432 4567 我希望所有这些都变成没有特殊字符的剥离版本 例如18
  • Rails:named_scope、lambda 和块

    我认为以下两个是等效的 named scope admin lambda company id conditions gt company id company id named scope admin lambda do company
  • rvm gem 安装错误?

    我正在摆弄 ruby gems 和 rvm 它工作得很好 但现在当我尝试安装 gem 时出现错误 gem install Rails错误 同时 执行 gem Errno EACCES 权限被拒绝 Users da rvm gems ruby
  • 没有要加载的文件 - ffi_c (LoadError)

    这个问题困扰了我几天 每当我使用 bring to front 方法时 require rubygems require watir browser Watir Browser new browser bring to front 我收到此
  • 如何在 Linux 中编写文本模式 GUI? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 当我编写脚本 程序时 我经常想弹出一个简单的文本 gui 来提示输入 我该怎么做 例如 来自 Shel
  • RoR - Rails 中的大文件上传

    我有一个 Rails Web 应用程序 允许用户上传视频 视频存储在 NFS 安装的目录中 当前的设置适用于较小的文件 但我也需要支持大文件上传 最多 4GB 当我尝试上传 4GB 文件时 它最终会发生 但从用户体验的角度来看很糟糕 上传开
  • 如何从 Ruby 中的特定相对路径加载文件?

    我正在制作一颗供内部使用的宝石 在其中 我从另一个目录加载一些 YAML in
  • Ruby 中的 url_encode

    I read 的文档url encode http rdoc info stdlib erb 1 9 3 ERB Util 3Aurl encode 是否有一个表可以准确地告诉我哪个字符被编码为什么 使用url encode ERB s u
  • 我可以让这个 Ruby 代码更快并且/或使用更少的内存吗?

    我有一个Array of StringRuby 中的对象由如下单词组成 animals cat horse dog cat dog bird dog sheep chicken cow 我想将其转换为另一个Array of String对象
  • 如何使用 ruby​​ 和命令行工具在 mavericks 中正确安装 cocoapod?

    这是我的设置 小牛队10 9 1 Xcode 5 0 2 哪个红宝石返回这个 Users quique123 rvm rubies ruby 1 9 3 p194 bin ruby 但 dvm install ruby 返回 Already
  • '回应?'与“定义?”

    如果我想检查是否定义了给定名称的方法 使用哪个更好 respond to or defined 从效率的角度来看 可以有理由使用defined 因为defined 是一个内置关键字 而respond to 是一种方法 因此前者可能更快 但另
  • 从 Ruby on Rails 应用程序运行 phantomjs

    我有兴趣使用幻影 http www phantomjs org 我想从我的 Ruby on Rails 应用程序运行它 然而 这是一个命令行工具 即我需要运行类似phantomjs rasterize js http raphaeljs c
  • 更改 en.yml 文件中的属性名称不起作用

    我更改了 ruby on Rails 项目中 en yml 文件中的属性名称 按钮工作正常 但字段属性并没有改变 这是我的模型 class Enr AffordableWarmth lt ActiveRecord Base self tab

随机推荐

  • 闭包Closure

    x xxx 概念 控制台打断点 闭包的作用 return外部函数使用内部变量 当闭包形成时下的写法才有用 闭包的经典样式
  • 微信订单管理软件列表支持小程序(逻辑猫订货系统)

    订货信息管理系统 订货系统的作用 在线订单管理系统 订货系统又名 订单管理系统 订单管理系统分为销售订单和订货订单 这里的订货系统信息管理用于厂家的客户进行订货 并非商城 客户可通过订货端进行订货 订货系统支持的版本 订货系统 订单信息管理
  • gcc与g++的使用

    1 gcc编译器的基本语法格式如下 gcc 选项 准备编译的文件 选项 目标文件 例如 编译名为 test c 的c程序 gcc test c o test 2 若使用gcc编译器编译c 程序 与编译c程序略有不同 若cpp文件中未使用任何
  • 面具卡米怎么删模块_面具magisk ROOT如何更新到最新版本两种实用方法彻底解决...

    面具magisk作者会不定期的更新维护magisk 对magisk的升级维护 以便支持更多的安卓设备 目前已知最新版magisk已支持安卓11系统 对于已经刷入面具root的小伙伴 怎么最快速的升级最新版本呢 ROM乐园小编两种方法教你快速
  • Go 项目

    1 如果想要删除旧的包 直接在go mod注释一下 2 想要指定分支 自己运行 不要使用它提示的sync 方式一 执行以下命令 go get git地址 分支名 如 go get github com golang go master 方式
  • input type="file" 上传文件

    input type file 上传文件 显示上传图片 并替换上传按钮的位置 限制上传格式可以用 accept 比如 accept doc docx 限制上传文件为doc docx 其他的都不可以
  • 思睿嘉得数据泄露防护系统DLP如何卸载?(无需卸载口令的方法)

    之前在某公司实习的时候 带自己的电脑去需要安装这个软件 后面怎么都卸载不掉 也找了网上的很多方法 都行不通 研究了一阵子 终于找到了绕过密码口令卸载的方法 具体步骤如下 启动安全模式 windows10的方法见链接 https suppor
  • 数据结构——堆

    堆 堆的概念 堆 heap 是计算机科学中一类特殊的数据结构的统称 堆通常是一个可以被看做一棵树的数组对象 即是一种顺序储存结构的完全二叉树 1 提示 完全二叉树 完全二叉树 对一棵深度为k 有n个结点二叉树编号后 各节点的编号与深度为k的
  • 2021.11.3-11.4总结

    1 将C语言的单链表看完 简单了解了一下typedef的用法 2 继续数据结构的学习 将线性表的基础知识已学完 因为将C语言的结构体学习了 所以再次重看线性表的顺序表和单链表 比以前更容易理解 更加理解指针 懂了线性表的存储结构 顺序表 单
  • 为什么重写equals()方法时必须重写hashCode()方法【详解】

    目录 一 为什么重写equals 方法时必须重写hashCode 方法 1 关于hashCode 的约定 hashCode 方法源码注释 2 equals 方法和hashCode 方法的关系 3 为什么一定要使用 hashcode 方法 举
  • HTML小白入门学习1

    目录 一 什么是HTML 二 HTML的语法 2 1 基本结构 2 1 1 基本结构的解释 三 实体 3 1 什么是实体 3 2 实体的语法 四 常用的标签 4 1 常用标签 目录 一 什么是HTML 二 HTML的语法 2 1 基本结构
  • C# 调用可执行exe文件几种方法小结

    1 利用进程池方式启动 string exefile xxx exe if File Exists exefile Process process new Process params 为 string 类型的参数 多个参数以空格分隔 如果
  • snprintf和strcpy和strncpy的区别

    概述 snprintf strcpy strncpy这几个函数的功能都是将原字符串拷贝到目的字符串中 但是在细节部分还是存在着一些细微的差别 主要参考man说明 snprintf 格式 int snprintf char str size
  • 最长公共子序列 (LCS) 详解+例题模板(全)

    欢迎访问https blog csdn net lxt Lucia 宇宙第一小仙女 o 萌量爆表求带飞 o dalao们点个关注呗 我只是一条可爱哒分界线 1 摘要 继上篇最长上升子序列后 本篇主要讲述最长公共子序列 LCS 2 LCS定义
  • HMC管理IBM小机

    IBM P5小机的HMC1和HMC2的IP地址默认为192 168 2 147和192 168 3 147 如果对此默认IP做过改动 后来又遗忘的情况下 通过进入P5小机液晶控制面板上的功能30 就能读出HMC端口的IP地址 具体步骤如下
  • WSI图像分割

    0 介绍 Whole Slide Image WSI 图像非常的大 处理起来比较麻烦 在深度学习中的病理切片图像大多数在 10万x10万分辨率 用平常的图像处理库没有办法读取 openslide 提供了一个很好的接口 这里介绍一个可用于处理
  • void*强制类型转换的应用(自己看吧)

    int main int num 20 void value NULL value num printf d n int value return 0 int test void p return int p int main int nu
  • Java—面向对象设计—类和对象

    理解面向对象程序设计 面向对象程序 Object oriented programming OOP 设计是继面向过程又一具有里程碑意义的编程思想 是现实世界模型的自然延伸 下面从结构化程序设计说起 逐步展示面向对象程序设计 结构化程序设计简
  • 深度学习的异构加速技术(一):AI 需要一个多大的“心脏”?

    欢迎大家前往腾讯云社区 获取更多腾讯海量技术实践干货哦 作者 kevinxiaoyu 高级研究员 隶属腾讯TEG 架构平台部 主要研究方向为深度学习异构计算与硬件加速 FPGA云 高速视觉感知等方向的构架设计和优化 深度学习的异构加速技术
  • RabbitMQ之延迟队列

    RabbitMQ之延迟队列 1 延迟队列概念 2 延迟队列使用场景 3 RabbitMQ 中的 TTL 3 1 消息设置 TTL 3 2 队列设置 TTL 3 3 两者的区别 4 整合 SpringBoot 4 1 创建项目 4 2 添加依