敲详细的springframework-amqp-rabbit源码解析
看源码时将RabbitMQ的springframework-amqp-rabbit和spring-rabbit的一套区分开,springboot是基于RabbitMQ的Java客户端建立了简便易用的框架。
springboot的框架下相对更多地使用消费者Consumer和监听器Listener的概念,这两个概念不注意区分容易混淆。默认情况下,springboot中消费者为单线程串行消费的模型,体现了队列的特性。
在springboot的框架下使用rabbitmq的一般步骤
- 启动rabbitmq服务器,springboot项目引入依赖
- 配置信息,有两种方式
- 配置文件配置
- 配置类配置SimpleMessageListenerContainer
- 实现消息处理类ChannelAwareMessageListener处理业务逻辑,或用@RabbitListener注解
这两种方式其实异曲同工,@RabbitListener的方式在实际使用时创建MessagingMessageListenerAdapter,这个对象是ChannelAwareMessageListener接口的实现类,实现了onMessage()方法,这个方法利用了适配器模式,能够调用注解标注的方法,而实现ChannelAwareMessageListener的方式比较直白就是实现onMessage()方法
源码解析
关于SimpleMessageListenerContainer
SimpleMessageListenerContainer是在spring项目中使用RabbitMQ关键的类,用来接收并处理消息的。阅读源码可以从这个类入手。
-
首先关注构造器,需要传入
ConnectionFactory
用于获取连接,这跟原生rabbitmq是一致的,都从Connection连接开始。 -
关键属性
concurrentConsumers
:指定要创建的并发消费者的数量。默认值为1。建议增加并发使用者的数量,以便扩展从队列传入的消息的消耗。但是,请注意,一旦注册了多个消费者,将无法保证顺序。一般来说,对于低容量队列,坚持使用1个消费者。同时不能超过maxConcurrentConsumers(如果设置了)。maxConcurrentConsumers
:设置消费者数量的上限。默认为concurrentConsumers。消费者可以根据需求增加,但不会小于concurrentConsumers。acknowledgeMode
:消息确认模式// 自动确认消息 container.setAcknowledgeMode(AcknowledgeMode.NONE); // 根据情况确认消息 container.setAcknowledgeMode(AcknowledgeMode.AUTO); // 手动确认消息 container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
-
绑定组件:
-
设置消费者的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内的操作。
-
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();
- 然后将consumer封装成AsyncMessageProcessingConsumer线程任务类型,然后就可以放入线程池中执行。
AsyncMessageProcessingConsumer processor = new AsyncMessageProcessingConsumer(consumer);
processors.add(processor);
getTaskExecutor().execute(processor);
-
这里的线程池是SimpleAsyncTaskExecutor(也可以自定义传入),默认是不限制并发量的。每个container都有一个线程池,线程不足以支持consumer并发时就会超时报错。
private Executor taskExecutor = new SimpleAsyncTaskExecutor();
-
进入AsyncMessageProcessingConsumer这个Runnable类的run()方法,如果consumer有监听的队列,就初始化initialize并开启mainloop()
if (this.consumer.getQueueCount() < 1) {... } try {initialize();while (isActive(this.consumer) || this.consumer.hasDelivery() || !this.consumer.cancelled()) {mainLoop();} }
-
initialize()会创建
exchange
、queue
、bindings
等实例,设置Qos
,实现consumer与broker之间的对接,完成消息的订阅,并且会根据tag不同在每个BlockingQueueConsumer中再划分出internalConsumer,再放入BlockingQueueConsumer的queue中逐一处理。说明Qos流控指令包括
prefetch-size
、prefetch-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可能监听多个队列。
-
mainLoop()相较于如何利用consumer接收消息,更侧重于最终的listener来进行业务处理。前面已经知道客户端会将消息存到Consume的queue中,简单来说,mainloop就是只要客户端正常启动就会无限循环来处理业务的,它主要就是完成从
queue
中提取消息数据然后经过一系列操作最终传递给业务逻辑处理MessageListener
中。mainLoop()
方法中就会从queue
中提取消息,根据**batchSize
**确定每次提取消息数量,最后回调MessageListener
,实现将消息传递到业务逻辑进行处理;多个AsyncMessageProcessingConsumer对应一个listener(一个container对应一个listener即是一套处理业务,共用一个线程池,因为它们只是对应不同的并发, 处理的业务逻辑应是相同的。
增加RabbitMQ并发的方法
-
增加并发消费者数量。并保障能提供充足的线程资源,虽然默认的线程池不设线程并发上线。示例:Redis与RabbitMQ配合使用多线程(多消费者)处理消息_多线程 处理 rabbitmq消息-CSDN博客
-
在listener方法上加上@Async(),这样会在异步的子线程下执行,如果提供线程池,就能实现并发。示例:线程池解决RabbitMQ消息堆积_rabbitmq线程池-CSDN博客
-
增大prefetchCount,prefetchCount是BlockingQueueConsumer内部维护的一个阻塞队列LinkedBlockingQueue的大小,其作用就是如果某个消费者队列阻塞,就无法接收新的消息
-
配置container的自定义线程池,但这个方法不推荐,示例:【RabbitMQ-9】自定义配置线程池(线程池资源不足-MQ初始化队列&&MQ动态扩容影响) - 简书 (jianshu.com)
-
当并发量确实无法短时间内提高时,也应尽可能提高消息队列的容量,并开启持久化。如设置惰性队列。
RabbitMQ 从 3.6.0 版本开始引入了惰性队列的概念。惰性队列会尽可能的将消息存入磁盘,而在消费者消费到相应的消息时才会被加载到内存中,它的一个重要的设计目标是能够支持更长的队列,即支持更多的消息存储。当消费者由于各种各样的原因(比如消费者下线、宕机亦或者是由于维护而关闭等)而致使长时间内不能消费消息造成消息堆积时,惰性队列就很有必要了。
正常的队列会尽可能存储在内存中。
相关文章:
敲详细的springframework-amqp-rabbit源码解析
看源码时将RabbitMQ的springframework-amqp-rabbit和spring-rabbit的一套区分开,springboot是基于RabbitMQ的Java客户端建立了简便易用的框架。 springboot的框架下相对更多地使用消费者Consumer和监听器Listener的概念,这两个概念不注意区分容易混淆。…...
Telegram Bot、小程序开发(三)Mini Apps小程序
文章目录 一、Telegram Mini Apps小程序二、小程序启动方式三、小程序开发小程序调试模式初始化小程序Keyboard Button Mini Apps 键盘按钮小程序【依赖具体用户信息场景,推荐】**Inline Button Mini Apps内联按钮小程序**initData 的自动传递使用内联菜单时候哪些参数会默认传…...
Django F()函数
F()函数的作用 F()函数在Django中是一个非常强大的工具,主要用于在查询表达式中引用模型的字段。它允许你在数据库层面执行各种操作,而无需将数据加载到Python内存中。这不仅提高了性能,还允许你利用数据库的优化功能。 字段引用 在查询表达…...
GraphRAG的实践
好久没有体验新技术了,今天来玩一下GraphRAG 顾名思义,一种检索增强的方法,利用图谱来实现RAG 1.配置环境 conda create -n GraphRAG python3.11 conda activate GraphRAG pip install graphrag 2.构建GraphRAG mkdir -p ./ragtest/i…...
自动驾驶三维车道线检测系列—LATR: 3D Lane Detection from Monocular Images with Transformer
文章目录 1. 概述2. 背景介绍3. 方法3.1 整体结构3.2 车道感知查询生成器3.3 动态3D地面位置嵌入3.4 预测头和损失 4. 实验评测4.1 数据集和评估指标4.2 实验设置4.3 主要结果 5. 讨论和总结 1. 概述 3D 车道线检测是自动驾驶中的一个基础但具有挑战性的任务。最近的进展主要依…...
守护动物乐园:视频AI智能监管方案助力动物园安全与秩序管理
一、背景分析 近日,某大熊猫参观基地通报了4位游客在参观时,向大熊猫室外活动场内吐口水的不文明行为。这几位游客的行为违反了入园参观规定并可能对大熊猫造成严重危害,已经被该熊猫基地终身禁止再次进入参观。而在此前,另一熊猫…...
FairGuard游戏加固入选《嘶吼2024网络安全产业图谱》
2024年7月16日,国内网络安全专业媒体——嘶吼安全产业研究院正式发布《嘶吼2024网络安全产业图谱》(以下简称“产业图谱”)。 本次发布的产业图谱,共涉及七大类别,127个细分领域。全面展现了网络安全产业的构成和重要组成部分,探…...
数据仓库事实表
数据仓库中的三种常见事实表类型:事务事实表、周期快照事实表和累积快照事实表 事务事实表: 事务事实表是记录事务级别数据的事实表。它记录了每个事务发生的具体度量指标,如销售金额、数量等。事务事实表的优势在于能够提供详细的事务级别…...
LeetCode题练习与总结:两数之和Ⅱ-输入有序数组--167
一、题目描述 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 < index1 < index…...
在 Java 中,怎样设计一个可扩展且易于维护的微服务架构?
在Java中设计一个可扩展且易于维护的微服务架构,可以考虑以下几个方面: 模块化设计:将应用拆分为多个小的、独立的模块,每个模块负责处理特定的业务逻辑。每个模块可以独立开发、测试和部署,增加或替换模块时不会影响其…...
零基础入门鸿蒙开发 HarmonyOS NEXT星河版开发学习
今天开始带大家零基础入门鸿蒙开发,也就是你没有任何编程基础的情况下就可以跟着石头哥零基础学习鸿蒙开发。 目录 一,为什么要学习鸿蒙 1-1,鸿蒙介绍 1-2,为什么要学习鸿蒙 1-3,鸿蒙各个版本介绍 1-4࿰…...
Chromium CI/CD 之Jenkins实用指南2024-在Windows节点上创建任务(九)
1. 引言 在现代软件开发流程中,持续集成(CI)和持续交付(CD)已成为确保代码质量和加速发布周期的关键实践。Jenkins作为一款广泛应用的开源自动化服务器,通过其强大的插件生态系统和灵活的配置选项…...
ceph进程网卡绑定逻辑
main() //如osd进程,是ceph_osd.cc文件的main函数;mon进程,是ceph_mon.cc文件的main函数 -->pick_addresses() // 会读取"cluster_network_interface"和"public_network_interface"这两个配置项来过滤ip ---->fill…...
学习opencv
初步学习可以参考: OpenCV学习之路(附加资料分享)_opencv资料-CSDN博客 【OpenCV】OpenCV常用函数合集【持续更新】_opencv函数手册-CSDN博客 整体框架可以参考: OpenCV学习指南:从零基础到全面掌握(零…...
利用双端队列 实现二叉树的非递归的中序遍历
双端队列:双向队列:支持插入删除元素的线性集合。 java官方文档推荐用deque实现栈(stack)。 pop(): 弹出栈中元素,也就是返回并移除队头元素,等价于removeFirst(),如果队列无元素,则…...
昇思25天学习打卡营第18天 | 基于MindSpore的GPT2文本摘要
昇思25天学习打卡营第18天 | 基于MindSpore的GPT2文本摘要 文章目录 昇思25天学习打卡营第18天 | 基于MindSpore的GPT2文本摘要数据集创建数据集数据预处理Tokenizer 模型构建构建GPT2ForSummarization模型动态学习率 模型训练模型推理总结打卡 数据集 实验使用nlpcc2017摘要数…...
科研绘图系列:R语言circos图(circos plot)
介绍 Circos图是一种数据可视化工具,它以圆形布局展示数据,通常用于显示数据之间的关系和模式。这种图表特别适合于展示分层数据或网络关系。Circos图的一些关键特点包括: 圆形布局:数据被组织在一个或多个同心圆中,每个圆可以代表不同的数据维度或层次。扇区:每个圆被划…...
追踪Conda包的踪迹:深入探索依赖关系与管理
追踪Conda包的踪迹:深入探索依赖关系与管理 Conda作为Python和其他科学计算语言的包管理器,不仅提供了安装、更新和卸载包的功能,还有一个强大的包跟踪功能,帮助用户理解包之间的依赖关系和管理环境。本文将详细解释如何在Conda中…...
苹果电脑pdf合并软件 苹果电脑合并pdf 苹果电脑pdf怎么合并
在数字化办公日益普及的今天,pdf文件因其跨平台兼容性强、格式稳定等特点,已经成为工作、学习和生活中不可或缺的文件格式。然而,我们常常面临一个问题:如何将多个pdf文件合并为一个?这不仅有助于文件的整理和管理&…...
axios(ajax请求库)
json-server(搭建http服务) json-server用来快速搭建模拟的REST API的工具包 使用json-server 下载:npm install -g json-server创建数据库json文件:db.json开启服务:json-srver --watch db.json axios的基本使用 <!doctype html>…...
Ideal窗口中左右侧栏消失了
不知道大家在工作过程中有没有遇到过此类问题,不论是Maven项目还是Gradle项目,突然发现Ideal窗口右侧图标丢失了,同事今天突然说大象图标不见了,不知道怎样刷新gradle。 不要慌张,下面提供一些解决思路: 1…...
麦芒30全新绽放,中国电信勾勒出AI手机的新方向
高通总裁兼CEO克里斯蒂亚诺阿蒙曾在媒体采访时表示:2024年将成为全球AI手机元年,生成式AI正在“非常快”的进入手机。 把大模型装进手机,由此成了智能终端演进的新方向。三星、华为、OPPO、小米等品牌动作频频,纷纷抢滩AI手机市场…...
数据结构之初始二叉树(3)
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏:数据结构(Java版) 二叉树的基本操作 通过上篇文章的学习,我们简单的了解了二叉树的相关操作。接下来就是有…...
egret 白鹭的编译太慢了 自己写了一个
用的swc 他会检测git的改变 const simpleGit require(simple-git); const fs require(fs); const path require(path); // 初始化 simple-git const swc require(swc/core); const baseDir D:\\project; const gameDir game\\module\\abcdefg; const gitDir D:\\projec…...
<数据集>pcb板缺陷检测数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:693张 标注数量(xml文件个数):693 标注数量(txt文件个数):693 标注类别数:6 标注类别名称:[missing_hole, mouse_bite, open_circuit, short, spurious_copper, spur…...
实验四:图像的锐化处理
目录 一、实验目的 二、实验原理 1. 拉普拉斯算子 2. Sobel算子 3. 模板大小对滤波的影响 三、实验内容 四、源程序和结果 (1) 主程序(matlab) (2) 函数GrayscaleFilter (3) 函数MatrixAbs 五、结果分析 1. 拉普拉斯滤波 2. Sobel滤波 3. 不同大小模板的滤波…...
【Linux】权限的管理和Linux上的一些工具
文章目录 权限管理chgrpchownumaskfile指令sudo指令 目录权限粘滞位Linux中的工具1.软件包管理器yum2.rzsz Linux开发工具vim 总结 权限管理 chgrp 功能:修改文件或目录的所属组 格式:chgrp [参数] 用户组名 文件名 常用选项:-R 递归修改文…...
ES6 字符串的新增方法(二十)
1. String.prototype.startsWith(searchString, position) 特性:判断字符串是否以指定的子字符串开始。 用法:检查字符串的开始部分。 const str "Hello World"; console.log(str.startsWith("Hello")); // 输出:true…...
如何将MP3或WAV文件解码成PCM文件
文章目录 概要整体架构流程技术细节 概要 本文介绍使用 FFmpeg,将MP3或WAV文件解码成PCM文件的方法。 整体架构流程 首先,使用的 FFmpeg 库要支持 MP3/WAV 解码功能,即编译的时候要加上(编译 FFmpeg 库可以参考:Win…...
OpenAI 推出 GPT-4o mini,一种更小、更便宜的人工智能模型
OpenAI 最近推出了新型人工智能模型 GPT-4o mini,以其较小体积和低成本受到关注。这款模型在文本和视觉推理任务上性能优越,且比现有小型模型更快、更经济。GPT-4o mini 已向开发者和消费者发布,企业用户将在下周获得访问权限。 喜好儿网 在…...
中山网站关键词排名/企业网站优化价格
概念(来源于网络): clientX 设置或获取鼠标指针位置相对于窗口客户区域的 x 坐标,其中客户区域不包括窗口自身的控件和滚动条。 clientY 设置或获取鼠标指针位置相对于窗口客户区域的 y 坐标,其中客户区域不包括窗口自…...
wordpress顶踩仿织梦/在线搜索资源
思路1:使用回溯法,同时剪掉不合理分支 // 产生不同的括号队列,选用回溯法 class Solution { public:vector<string> res;vector<string> generateParenthesis(int n) {string str "";backtrack(0,0,n,str);return re…...
论网站建设情况/百度怎么投放广告
一、基本原理 1.数据源无关的数据统一操作,LINQ语句做了一个程序语句到各种数据源之间的抽象统一中间件。where开始,select或group结束. order 和group,select new等各种语句 。 2.用了拓展方法,在generic枚举类型来做࿰…...
合肥做公司网站一般多少钱/营销软文范例大全
原标题:【沙发管家】苹果手机,,iPad连接安卓智能电视投屏方法现在很多人对于安卓智能电视的投屏功能已经非常熟悉,使用安卓手机的用户,有很多办法可以直接连接电视进行投屏,但是,使用苹果设备直连安卓智能电…...
海洋cms做电影网站好做吗/推广优化网站排名
昨天在项目中需要对日志的查询结果进行导出功能。 日志导出功能的实现是这样的,输入查询条件,然后对查询结果进行导出。由于日志数据量比较大。多的时候,有上亿条记录。 之前的解决方案都是多次查询,然后使用limit 限制每次查询的…...
清廉企业建设/seo怎么搞
前文说到使用统计学习方法进行文本分类就是让计算机自己来观察由人提供的训练文档集,自己总结出用于判别文档类别的规则和依据。理想的结果当然是让计算机在理解文章内容的基础上进行这样的分类,然而遗憾的是,我们所说的“理解”往往指的是文…...