敲详细的springframework-amqp-rabbit源码解析

看源码时将RabbitMQ的springframework-amqp-rabbit和spring-rabbit的一套区分开,springboot是基于RabbitMQ的Java客户端建立了简便易用的框架。

springboot的框架下相对更多地使用消费者Consumer和监听器Listener的概念,这两个概念不注意区分容易混淆。默认情况下,springboot中消费者为单线程串行消费的模型,体现了队列的特性。


在springboot的框架下使用rabbitmq的一般步骤

  1. 启动rabbitmq服务器,springboot项目引入依赖
  2. 配置信息,有两种方式
    1. 配置文件配置
    2. 配置类配置SimpleMessageListenerContainer
  3. 实现消息处理类ChannelAwareMessageListener处理业务逻辑,或用@RabbitListener注解

这两种方式其实异曲同工,@RabbitListener的方式在实际使用时创建MessagingMessageListenerAdapter,这个对象是ChannelAwareMessageListener接口的实现类,实现了onMessage()方法,这个方法利用了适配器模式,能够调用注解标注的方法,而实现ChannelAwareMessageListener的方式比较直白就是实现onMessage()方法


源码解析

关于SimpleMessageListenerContainer

SimpleMessageListenerContainer是在spring项目中使用RabbitMQ关键的类,用来接收并处理消息的。阅读源码可以从这个类入手。

  1. 首先关注构造器,需要传入ConnectionFactory用于获取连接,这跟原生rabbitmq是一致的,都从Connection连接开始。

  2. 关键属性

    concurrentConsumers:指定要创建的并发消费者的数量。默认值为1。建议增加并发使用者的数量,以便扩展从队列传入的消息的消耗。但是,请注意,一旦注册了多个消费者,将无法保证顺序。一般来说,对于低容量队列,坚持使用1个消费者。同时不能超过maxConcurrentConsumers(如果设置了)。

    maxConcurrentConsumers:设置消费者数量的上限。默认为concurrentConsumers。消费者可以根据需求增加,但不会小于concurrentConsumers。

    acknowledgeMode:消息确认模式

    // 自动确认消息
    container.setAcknowledgeMode(AcknowledgeMode.NONE);
    // 根据情况确认消息
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    // 手动确认消息
    container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
    
  3. 绑定组件:

  4. 设置消费者的Consumer_tag和Arguments:container.setConsumerTagStrategy可以设置消费者的 Consumer_tag, container.setConsumerArguments可以设置消费者的 Arguments

    container.setConsumerTagStrategy(queue -> "order_queue_"+(++count));
    //设置消费者的Arguments
    Map<String, Object> args = new HashMap<>();
    args.put("module","订单模块");
    args.put("fun","发送消息");
    container.setConsumerArguments(args);
    

    在这里插入图片描述


spring的亮点在于用注解简化了很多代码操作,其中最常用的当属@RabbitListener

@RabbitListener(queues = {BiMqConstant.BI_QUEUE_NAME}, ackMode = "MANUAL")
public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag){
}

从@RabbitListener入手

1、从spring开启RabbitMQ的注解模式,@EnableRabbit导入RabbitBootstrapConfiguration配置类。

2、这个配置类定义了RabbitListenerAnnotationBeanPostProcessor和RabbitListenerEndpointRegistry两个bean。前者用来扫描加了@RabbitListener 的类,通过反射找到带注解的类,再找到对应的方法,存为handlerMethods。后者在注册终端后用于构建ListenerContainer(继承了RabbitListener注解内的信息,包括监听的队列和注解所在的类和方法)。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(RabbitBootstrapConfiguration.class)
public @interface EnableRabbit {
}

3、RabbitListenerEndpointRegistry通过创建MethodRabbitListenerEndpoint对象和SimpleRabbitListenerContainerFactory工厂bean,生成SimpleMessageListenerContainer对象。

(RabbitListenerAnnotationBeanPostProcessor中拥有注解信息,如队列名,以及被标注注解的方法,所以endpoint的注册还是在processor类中)

(processor中有注册员成员变量registrar的registerEndpoint()注册endpoint,registrar有注册处registry成员变量注册利用registerListenerContainer()的createListenerContainer()注册container)

public class RabbitListenerEndpointRegistry  implements SmartLifecycle{private final Map<String, MessageListenerContainer> listenerContainers =new ConcurrentHashMap<String, MessageListenerContainer>();//注册终端public void registerListenerContainer(RabbitListenerEndpoint endpoint, RabbitListenerContainerFactory<?> factory,boolean startImmediately) {String id = endpoint.getId();synchronized (this.listenerContainers) {//创建 listenerContainerMessageListenerContainer container = createListenerContainer(endpoint, factory);this.listenerContainers.put(id, container);……if (startImmediately) {startIfNecessary(container);}}}protected MessageListenerContainer createListenerContainer(RabbitListenerEndpoint endpoint,RabbitListenerContainerFactory<?> factory) {//调用RabbitListener容器工厂的createListenerContainer方法获取RabbitListener容器MessageListenerContainer listenerContainer = factory.createListenerContainer(endpoint);return listenerContainer;}

4、SimpleMessageListenerContainer对象保存了要监听的队列名(可以是configuration时set的也可以是@RabbitListener中标注的),创建了用于处理消息的MessagingMessageListenerAdapter实例(实际上是一个listener)

public class MethodRabbitListenerEndpoint extends AbstractRabbitListenerEndpoint {......@Overrideprotected MessagingMessageListenerAdapter createMessageListener(MessageListenerContainer container) {Assert.state(this.messageHandlerMethodFactory != null,"Could not create message listener - MessageHandlerMethodFactory not set");MessagingMessageListenerAdapter messageListener = createMessageListenerInstance();messageListener.setHandlerMethod(configureListenerAdapter(messageListener));String replyToAddress = getDefaultReplyToAddress();if (replyToAddress != null) {messageListener.setResponseAddress(replyToAddress);}MessageConverter messageConverter = container.getMessageConverter();if (messageConverter != null) {messageListener.setMessageConverter(messageConverter);}if (getBeanResolver() != null) {messageListener.setBeanResolver(getBeanResolver());}return messageListener;}protected MessagingMessageListenerAdapter createMessageListenerInstance() {return new MessagingMessageListenerAdapter(this.bean, this.method);}......
}

5、SimpleMessageListenerContainer的内部类AsyncMessageProcessingConsumer(区分,该类封装了BlockingQueueConsumer,由于该类实现了Runnable接口,可以视为一个线程任务放入线程池中执行)有一个run()方法,调用了receiveAndExecute(),这个方法会获取BlockingQueueConsumer,阻塞读取其消息(一次获取多条),完成消息读取。

6、接着调用listener进行消息处理,这里设置了代理,最终会执行actualInvokeListener所谓实际被执行的listener,溯源最终调用了listener.onMessage(message, channelToUse)。

SimpleMessageListenerContainer {//接受并执行private boolean receiveAndExecute(final BlockingQueueConsumer consumer) throws Throwable {//do接受并执行return doReceiveAndExecute(consumer);}//do接受并执行private boolean doReceiveAndExecute(BlockingQueueConsumer consumer) throws Throwable {Channel channel = consumer.getChannel();for (int i = 0; i < this.txSize; i++) {//txSize为一次事务接受的消息个数//读取消息,这里阻塞的,但是有一个超时时间。Message message = consumer.nextMessage(this.receiveTimeout);if (message == null) {//阻塞超时break;}try {executeListener(channel, message);//消息接收已完成,现在开始处理消息。}catch (Exception e) {}}return consumer.commitIfNecessary(isChannelLocallyTransacted());}//处理消息开始。该方法在其父类中protected void executeListener(Channel channel, Message messageIn) throws Exception {try {Message message = messageIn;if (……) {//批处理信息,这个不研究}else {invokeListener(channel, message);}}catch (Exception ex) {}}//在其父类中protected void invokeListener(Channel channel, Message message) throws Exception {//这里this.proxy.invokeListener最终会调用actualInvokeListener方法。this.proxy.invokeListener(channel, message);}//在其父类中protected void actualInvokeListener(Channel channel, Message message) throws Exception {Object listener = getMessageListener();if (listener instanceof ChannelAwareMessageListener) {doInvokeListener((ChannelAwareMessageListener) listener, channel, message);}else if (listener instanceof MessageListener) {//……doInvokeListener((MessageListener) listener, message)}else{//……}}    protected void doInvokeListener(ChannelAwareMessageListener listener, Channel channel, Message message)throws Exception {Channel channelToUse = channel;try {listener.onMessage(message, channelToUse);}catch (Exception e) {throw wrapToListenerExecutionFailedExceptionIfNeeded(e, message);}}
}

7、关于第6点,根据这个listener实例的不同,有两种处理方式:

如果是前面所说的实现ChannelAwareMessageListener,就直接调用实现类的onMessage()。

如果是@RabbitListener注解,不同在于MessagingMessageListenerAdapter(ChannelAwareMessageListener的实现类,也是listen),基于适配器模式持有@RabbitListener注解的对象和方法(adapter实例中有HandlerMethod属性加入到adapter类中,HandlerMethod调用invoke()就能执行注解标注的方法)。

public class HandlerAdapter {private final InvocableHandlerMethod invokerHandlerMethod;private final DelegatingInvocableHandler delegatingHandler;public Object invoke(Message<?> message, Object... providedArgs) throws Exception                {if (this.invokerHandlerMethod != null) {//InvocableHandlerMethod不为null,就调用invokerHandlerMethod.invoke方法。return this.invokerHandlerMethod.invoke(message, providedArgs);}else if (this.delegatingHandler.hasDefaultHandler()) {//……}else {//……}}
}
public class MessagingMessageListenerAdapter extends AbstractAdaptableMessageListener {private HandlerAdapter handlerMethod;
}

现在就能把整个过程串起来了


关于关于endpoint和register

Endpoint为终端,像电脑、手机都是终端,他们都可以接受外部信息并响应,如手机来短信了就有提示。这里也用了终端的概念,被@RabbitListener注解修饰方法也有终端的特点可以接受外部信息并响应,即接到消息就执行对应方法。

registry姑且成为注册处用Map保存endpoint的id和对应的listenerContainer,注册处registerListenerContainer()利用endpoint和factory实例创建container,实际上是用了containerfactory的createListenerContainer(RabbitListenerEndpoint endpoint)方法

public class RabbitListenerEndpointRegistry implements DisposableBean, SmartLifecycle, ApplicationContextAware,ApplicationListener<ContextRefreshedEvent> {// 检查是否被注册过,注册过就不能注册第二次// 调用createListenerContainer创建消息监听// 关于分组消费的,我们不关心// 是否立即启动,是的话,同步调用startIfNecessary方法public void registerListenerContainer(RabbitListenerEndpoint endpoint, RabbitListenerContainerFactory<?> factory,boolean startImmediately) {Assert.notNull(endpoint, "Endpoint must not be null");Assert.notNull(factory, "Factory must not be null");String id = endpoint.getId();Assert.hasText(id, "Endpoint id must not be empty");synchronized (this.listenerContainers) {Assert.state(!this.listenerContainers.containsKey(id),"Another endpoint is already registered with id '" + id + "'");MessageListenerContainer container = createListenerContainer(endpoint, factory);this.listenerContainers.put(id, container);if (StringUtils.hasText(endpoint.getGroup()) && this.applicationContext != null) {List<MessageListenerContainer> containerGroup;if (this.applicationContext.containsBean(endpoint.getGroup())) {containerGroup = this.applicationContext.getBean(endpoint.getGroup(), List.class);}else {containerGroup = new ArrayList<MessageListenerContainer>();this.applicationContext.getBeanFactory().registerSingleton(endpoint.getGroup(), containerGroup);}containerGroup.add(container);}if (startImmediately) {startIfNecessary(container);}}// 其实就是调用了RabbitListenerContainerFactory的createListenerContainer生成了一个MessageListenerContainer对象protected MessageListenerContainer createListenerContainer(RabbitListenerEndpoint endpoint,RabbitListenerContainerFactory<?> factory) {MessageListenerContainer listenerContainer = factory.createListenerContainer(endpoint);if (listenerContainer instanceof InitializingBean) {try {((InitializingBean) listenerContainer).afterPropertiesSet();}catch (Exception ex) {throw new BeanInitializationException("Failed to initialize message listener container", ex);}}int containerPhase = listenerContainer.getPhase();if (containerPhase < Integer.MAX_VALUE) {  // a custom phase valueif (this.phase < Integer.MAX_VALUE && this.phase != containerPhase) {throw new IllegalStateException("Encountered phase mismatch between container factory definitions: " +this.phase + " vs " + containerPhase);}this.phase = listenerContainer.getPhase();}return listenerContainer;}
}

把endpoint内的信息全部注入到container里。

@Override
public C createListenerContainer(RabbitListenerEndpoint endpoint) {C instance = createContainerInstance();if (this.connectionFactory != null) {instance.setConnectionFactory(this.connectionFactory);}if (this.errorHandler != null) {instance.setErrorHandler(this.errorHandler);}if (this.messageConverter != null) {instance.setMessageConverter(this.messageConverter);}if (this.acknowledgeMode != null) {instance.setAcknowledgeMode(this.acknowledgeMode);}if (this.channelTransacted != null) {instance.setChannelTransacted(this.channelTransacted);}if (this.autoStartup != null) {instance.setAutoStartup(this.autoStartup);}if (this.phase != null) {instance.setPhase(this.phase);}instance.setListenerId(endpoint.getId());// 最重要的一行!!!endpoint.setupListenerContainer(instance);initializeContainer(instance);return instance;
}

关于container和containerFactory

containerFactory也能配置并发消费者等参数。

@Configuration
@EnableAsync
public class ThreadPoolConfig { @Bean("customContainerFactory") public SimpleRabbitListenerContainerFactory containerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();factory.setConcurrentConsumers(10); //设置线程数factory.setMaxConcurrentConsumers(10); //最大线程数configurer.configure(factory, connectionFactory);return factory; }
}

配置containerFactory能够创建container,但一般不在配置类中手动创建。一般是在注解中标记,然后让spring来生产container。

@RabbitListener(queues="demo.queue",containerFactory = "customContainerFactory")

直接配置container效果是相同的,同样可以设置队列,并发消费者等。


细说上面第5步container内的操作。

  1. container的启动入口是star()方法,然后进入doStart(),在该方法中会初始化consumer(BlockingQueueConsumer),每一个并发需要对应一个consumer,consumer的数量是根据前面所说的concurrentConsumers确定

    consumer = new BlockingQueueConsumer(getConnectionFactory(), getMessagePropertiesConverter(),this.cancellationLock, getAcknowledgeMode(), isChannelTransacted(), actualPrefetchCount,isDefaultRequeueRejected(), getConsumerArguments(), isNoLocal(), isExclusive(), queues);
    // 带有连接信息,数据转换器,确认模式,预取值,consumerArgs,监听的队列(可多个)等信息传入
    

区分一下consumer和listener,consumer是接收消息的消费者,listener是实际处理业务的执行者,consumer接收的每个消息都需要调用listener内的onMessage()方法来处理实际业务。

int newConsumers = initializeConsumers();
  1. 然后将consumer封装成AsyncMessageProcessingConsumer线程任务类型,然后就可以放入线程池中执行。
AsyncMessageProcessingConsumer processor = new AsyncMessageProcessingConsumer(consumer);
processors.add(processor);
getTaskExecutor().execute(processor);
  1. 这里的线程池是SimpleAsyncTaskExecutor(也可以自定义传入),默认是不限制并发量的。每个container都有一个线程池,线程不足以支持consumer并发时就会超时报错。

    private Executor taskExecutor = new SimpleAsyncTaskExecutor();
    
  2. 进入AsyncMessageProcessingConsumer这个Runnable类的run()方法,如果consumer有监听的队列,就初始化initialize并开启mainloop()

    if (this.consumer.getQueueCount() < 1) {...
    }
    try {initialize();while (isActive(this.consumer) || this.consumer.hasDelivery() || !this.consumer.cancelled()) {mainLoop();}
    }
    
  3. initialize()会创建exchangequeuebindings等实例,设置Qos,实现consumer与broker之间的对接,完成消息的订阅,并且会根据tag不同在每个BlockingQueueConsumer中再划分出internalConsumer,再放入BlockingQueueConsumer的queue中逐一处理。

    说明Qos流控指令包括prefetch-sizeprefetch-count参数。

    //该参数是设置在channel上的
    int prefetchCount = 1;
    channel.basicQos(prefetchCount);
    

    broker的delivery指令在客户端会先打包成一个Envelope,所以consumertag是对应consumer一个,而deliveryTag是对应broker中的一条消息一个。

    Envelope envelope = new Envelope(m.getDeliveryTag(),m.getRedelivered(),m.getExchange(),m.getRoutingKey());
    

    当然在broker执行delivery指令将消息推送到客户端Consumer之前还有channel,一个BlockingQueueConsumer对应一个channel,对应一个线程的调用。内部的consumer共用channel,channel会根据tag在dispatcher将消息推送至对应的consumer。

    一个channel对应了多个consumer

    在这里插入图片描述

    多个AsyncMessageProcessingConsumer对应不同的线程来处理

    在这里插入图片描述

    一个container可能监听多个队列。

    在这里插入图片描述

  4. mainLoop()相较于如何利用consumer接收消息,更侧重于最终的listener来进行业务处理。前面已经知道客户端会将消息存到Consume的queue中,简单来说,mainloop就是只要客户端正常启动就会无限循环来处理业务的,它主要就是完成从queue中提取消息数据然后经过一系列操作最终传递给业务逻辑处理MessageListener中。

    mainLoop()方法中就会从queue中提取消息,根据**batchSize**确定每次提取消息数量,最后回调MessageListener,实现将消息传递到业务逻辑进行处理;

    多个AsyncMessageProcessingConsumer对应一个listener(一个container对应一个listener即是一套处理业务,共用一个线程池,因为它们只是对应不同的并发, 处理的业务逻辑应是相同的。

    在这里插入图片描述

增加RabbitMQ并发的方法

  1. 增加并发消费者数量。并保障能提供充足的线程资源,虽然默认的线程池不设线程并发上线。示例:Redis与RabbitMQ配合使用多线程(多消费者)处理消息_多线程 处理 rabbitmq消息-CSDN博客

  2. 在listener方法上加上@Async(),这样会在异步的子线程下执行,如果提供线程池,就能实现并发。示例:线程池解决RabbitMQ消息堆积_rabbitmq线程池-CSDN博客

  3. 增大prefetchCount,prefetchCount是BlockingQueueConsumer内部维护的一个阻塞队列LinkedBlockingQueue的大小,其作用就是如果某个消费者队列阻塞,就无法接收新的消息

  4. 配置container的自定义线程池,但这个方法不推荐,示例:【RabbitMQ-9】自定义配置线程池(线程池资源不足-MQ初始化队列&&MQ动态扩容影响) - 简书 (jianshu.com)

  5. 当并发量确实无法短时间内提高时,也应尽可能提高消息队列的容量,并开启持久化。如设置惰性队列

    RabbitMQ 从 3.6.0 版本开始引入了惰性队列的概念。惰性队列会尽可能的将消息存入磁盘,而在消费者消费到相应的消息时才会被加载到内存中,它的一个重要的设计目标是能够支持更长的队列,即支持更多的消息存储。当消费者由于各种各样的原因(比如消费者下线宕机亦或者是由于维护而关闭等)而致使长时间内不能消费消息造成消息堆积时,惰性队列就很有必要了。

    正常的队列会尽可能存储在内存中。

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

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

相关文章

Vatee万腾平台:智慧金融的创新引擎,驱动金融服务升级

在数字化浪潮席卷全球的今天&#xff0c;金融行业正经历着前所未有的变革与升级。Vatee万腾平台&#xff0c;作为智慧金融领域的创新先锋&#xff0c;正以其独特的技术优势、前瞻的战略布局和卓越的服务能力&#xff0c;成为推动金融服务升级的重要引擎。 智慧金融&#xff1a;…

一个关于STM32的DAC输出的遗忘点

众所周知熟练使用HAL库可以帮你解决不少stm32的开发难题&#xff0c;但是是谁让你陷入了这些难题&#xff0c;别问。 如上图所示&#xff0c;正常初始化这个模块后生成代码如下图所示&#xff1b; * DAC init function */ void MX_DAC_Init(void) {/* USER CODE BEGIN DAC_Ini…

2024年计算机软考中级【硬件工程师】面试题目汇总(附答案)

硬件工程师面试题汇总分析 1、解释一下同步电路和异步电路 解题思路 同步电路和异步电路是指同步时序电路和异步时序电路。由于存储电路中触发器的动作特点不同&#xff0c;因此可以把时序电路分为同步时序电路和异步时序电路两种。同步时序电路所有的触发器状态的变化都是在同…

【单目3D检测】smoke(1):模型方案详解

纵目发表的这篇单目3D目标检测论文不同于以往用2D预选框建立3D信息&#xff0c;而是采取直接回归3D信息&#xff0c;这种思路简单又高效&#xff0c;并不需要复杂的前后处理&#xff0c;而且是一种one stage方法&#xff0c;对于实际业务部署也很友好。 题目&#xff1a;SMOKE&…

Java 中的异常

异常&#xff1a;就是出现的问题。 在Java中异常被当成对象进行处理&#xff0c;所有的异常类都继承于Throwable类&#xff0c;如果Java提供的异常类并不能满足需求&#xff0c;用户还可以自己定义一个异常类。 下面是异常体系结构&#xff1a; Throwable又分成了Error和Exce…

vmware配置centos+配置静态ip联网+更换镜像

centos7配置参考【实战】VMware17虚拟机以及Centos7详细安装教程-CSDN博客 ip配置步骤&#xff1a; 先更改编辑虚拟网络编辑器中的内容 就按照还原默认设置来&#xff0c;设定后就是以上内容&#xff0c;然后一定要记住子网ip和子网掩码 接下来就是NAT设置&#xff1a; 网关…

PE安装系统

前些天客户的电脑坏了,需要重装系统,我们的恢复光盘安装的时候,由于主板的原因,导致进入windows安装界面,鼠标键盘没有响应,自然也就无法正常安装了. 那我们只能换个方法,PE安装试试看,那么我们需要做哪些准备工作呢? 1.制作PE启动盘,网上很多制作工具,如""U启动,…

关于 Docker Registry (镜像仓库)

什么是镜像仓库 概念 镜像仓库&#xff08;Docker Registry&#xff09;负责存储、管理和分发镜像&#xff0c;并提供了登录认证能力&#xff0c;建立了仓库的索引。 镜像仓库管理多个 Repository&#xff0c;Repository 通过命名来区分。每个 Repository 包含一个或多个镜像…

P1-AI产品经理--九五小庞

产品经理的定位 AI基于现有业务挖掘AI应用场景&#xff0c;服务提供商选择及算法定制等&#xff0c;配合已有产品完成整体产品工工资基于从事医疗行业的考虑&#xff0c;我们走的应该是AI产品经理&#xff08;软件型&#xff09; AI产品经理&#xff08;行业型&#xff09; AI…

Python爬虫(1) --基础知识

爬虫 爬虫是什么&#xff1f; spider 是一种模仿浏览器上网过程的一种程序&#xff0c;可以获取一些网页的数据 基础知识 URL 统一资源定位符 uniform resource locator http: 超文本传输协议 HyperText Transfer Protocol 默认端口 80 https: 安全的超文本传输协议 security…

通过HTML/CSS 实现各类进度条的功能。

需求&#xff1a;我们在开发中会遇到使用各式各样的进度条&#xff0c;因为当前插件里面进度条各式各样的&#xff0c;为了方便我们定制化的开发和方便修改样式&#xff0c;我们这里使用HTML和CSS样式来进行开发进度条功能。 通过本文学习我们会明白如何使用 HTML/CSS 创建各种…

复旦微核心板:基于复旦微FMQL45T900 全国产化核心板

近期开发的一款搭载复旦微FMQL45T900的全国产核心板。FMQL45T900这款是一款高度集成的国产化芯片&#xff0c;它在一个单芯片中融合了多种功能&#xff0c;特别强调的是它的国产化特性&#xff0c;即其设计、制造和知识产权完全属于中国。 处理器性能&#xff1a; 处理器架构&a…

Python和C++行人轨迹预推算和空间机器人多传感融合双图算法模型

&#x1f3af;要点 &#x1f3af;双图神经网络模型&#xff1a;最大后验推理和线性纠错码解码器 | &#x1f3af;重复结构和过约束问题超图推理模型 | &#x1f3af;无向图模型变量概率计算、和积消息传播图结构计算、隐马尔可夫模型图结构计算、矩阵图结构计算、图结构学习 |…

QT--多页面、定时器和日期时间数据

多页面 widget.ui 负责实现一个页面; mian.cpp创建窗体对象,并监控上面的事件 窗口的显示和隐藏: 一个窗体,比如logpage widget,继承QWidget QWidget::show(); 显示该窗体 QWidget::hide(); 隐藏该窗体 页面间通信采用在main.cpp中创建connect槽函数。 代码如下 //main.c…

【ML练习】决策树

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、决策树算法概述 二、代码实现 代码目标&#xff1a;我们希望通过鸢尾花数据&#xff0c;训练一个决策树模型&#xff0c;之后应用该模型&#xff0c;可以…

STM32 CAN外设(基于STMF103C8T6)

STM32内置bxCAN外设&#xff08;CAN控制器)&#xff0c;支持CAN2.0A和2.0B&#xff0c;可以自动发送CAN报文和按照过滤器自动接收指定CAN报文&#xff0c;程序只需处理报文数据而无需关注总线的电平细节 波特率最高可达1兆位/秒3个可配置优先级的发送邮箱2个3级深度的接…

Java后端开发(十五)-- Ubuntu 开启activemq开机自启动功能

目录 1. 修改Wrapper.conf文件配置内容 2. 在/etc/systemd/system目录下创建activemq.service文件 3. 重启服务器,验证是否生效 4. 系统启动目标问题 操作环境: 1、Ubuntu 22.04.4 LTS (GNU/Linux 6.5.0-28-generic x86_64) 2、jdk17.0.11 3、apache-activemq-6.0.1 1. 修…

学习测试10-1自动化 python基础

下载python 要配置环境变量 进入Python的官方下载页面 http://www.python.org/download/安装PyCharm工具&#xff0c;网上可以下载&#xff0c;很多资源&#xff0c;也有免安装的版本&#xff0c;在网上找激活码 链接: https://pan.baidu.com/s/1Y6S_n3KbnjOdE9EDZ5nixw?pwdd…

CentOS 7 yum官方源失效

问题 2024年7月&#xff0c;官方对centos 7停止了维护&#xff0c;yum的源网址mirror.centos.org也已经无法访问。 在此情况下&#xff0c;无法正常使用yum进行安装和更新工具。 在尝试了更换阿里源之后&#xff0c;仍然有部分工具库无法访问。 通用解决方案 1. 打开/etc/y…

AWS Certified Developer Associate备考笔记

AWS Certified Developer Associate备考笔记 缓慢更新中&#xff0c;如果你也正在关注该考试&#xff0c;请点赞后评论感兴趣的章节&#xff0c;可加快我的更新速度 &#x1f603; 文章目录 AWS Certified Developer Associate备考笔记一、IAM二、EC2三、EC2 Instance Storage…