RabbitMQ-API
这里写目录标题
- 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 = newConcurrentSkipListMap<>();/*** 确认收到消息的一个回调* 1.消息序列号* 2.true 可以确认小于等于当前序列号的消息* false 确认当前序列号消息*/ConfirmCallback ackCallback = (sequenceNumber, multiple) -> {String message = outstandingConfirms.get(sequenceNumber);System.out.println("发布的消息---------------" + message + "已被确认,序列号" + sequenceNumber);//是否是批量确认if (multiple) {//返回的是小于等于当前序列号的未确认消息 是一个 mapConcurrentNavigableMap<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 与发布线程之间进行消息的传
递.上面代码就是用了此方法,发消息是进行记录,在未确认回调函数里面进行处理
相关文章:

RabbitMQ-API
这里写目录标题 Hello word 模式添加依赖生产者消费者获取信道工具类 Work Queues模式消费者代码 C1开启多线程运行启动 消费者代码 C2生产者代码 消息应答自动应答消息应答的方法Multiple 的解释消息自动重新入队消息手动应答代码消费者API 队列持久化消息持久化不公平分发消息…...

外边距实现居中的写法
1、代码实例 2、默认是贴到左侧对齐的,但我们想要把他贴到中间对齐 3、居中的写法 4、这样就可以保证盒子居中了 5、以上写法仅适于行内元素和行内块元素的写法,有没有什么方法适用于行内块元素:可以添加text-align:center进行添加࿰…...
剑指 Offer 20. 表示数值的字符串 (正则 逐步分解)
文章目录 题目描述题目分析法一:完整代码: 法二:完整代码: 题目描述 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 数值(按顺序)可以分成以下几个部分: 若干空格 一个 小数 或者…...

【深度学习】Transformer,Self-Attention,Multi-Head Attention
必读文章: https://blog.csdn.net/qq_37541097/article/details/117691873 论文名:Attention Is All You Need 文章目录 1、Self-Attention 自注意力机制2、Multi-Head Attention 1、Self-Attention 自注意力机制 Query(Q)表示当…...

CADintosh X for mac CAD绘图软件2D CAD 程序 兼容 M1
CADintosh X for Mac是一个功能强大的2D CAD绘图程序,专为Mac用户设计。它由Lemke Software开发,提供了一套丰富的工具和功能,使用户能够轻松创建高质量的技术图纸,平面图和设计。 CADintosh X for Mac具有直观的用户界面&#x…...

【读书笔记】《厌女》- [日]上野千鹤子 - 2010年出版
不停的阅读,然后形成自己的知识体系。 2023.08. 读 《厌女》- [日]上野千鹤子 - 2010年出版 - 豆瓣读书 文章目录 2023年中文版作者序2015年中文版作者序第一章 喜欢女人的男人的厌女症 2023年中文版作者序 ‘厌女症’的现象本来如‘房间里的大象’,因为…...
Android 从其他xml文件中获取View组件数据
问题 Android Studio 我想在 trace.java 从setting.java绑定的页面activity_setting.xml中 的editview中获取数据 解决方案 仅适用于 在同一应用的不同组件之间共享数据 在 SettingActivity.java 中,当用户准备离开当前活动时,可以将 EditText 中的数…...

java 数组的使用
数组 基本介绍 数组可以存放多个同一类型的数据,数组也是一种数据类型,是引用类型。 即:数组就是一组数据。 数组的使用 1、数组的定义 方法一 -> 单独声明 数据类型[] 数组名 new 数据类型[大小] 说明:int[] a new int…...

Jmeter(一) - 从入门到精通 - 环境搭建(详解教程)
1.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序)。它可以用来测试静态和动态资源的性能,例如:静态文件,Java Servlet,CGI Scripts,Java Object,数据库和FTP服务器…...
外贸企业选择CRM的三大特点
外贸营销管理CRM云平台可以帮助外贸企业实现更高质量的营销管理和客户管理。无论是销售、市场营销或客户服务团队的成员,CRM都可以帮助企业更好地理解客户需求,并提供更好的服务。 1.便捷轻量级 云平台的一大优势是用户可以随时随地访问数据࿰…...
软件测试与游戏测试的区别
软件测试和游戏测试是两种不同领域的测试活动,它们之间存在一些区别,包括以下几个方面: 1. 测试目标 软件测试主要是验证和确认软件功能是否符合预期,通常关注软件的正确性、稳定性和兼容性等方面;而游戏测试则更关注游…...
Programming Abstractions in C阅读笔记:p72-p75
《Programming Abstractions In C》阅读P72-p75,每次阅读其实都有很多内容需要总结,这里摘抄其中一部分。 一、技术总结 1.字符串数组 学习《Programming Abstractions in C》第75页的时候,遇到一段代码: static string bigCitie…...
bash测试test详解
bash测试test详解 概述 任何相对完整的计算机语言都能够测试某个条件,然后根据测试的结果采取不同的动作。对于测试条件, Bash使用test命令、各种方括号和圆括号、if/then结构等来测试条件。 7.1. Test Constructs 一个if/then语句结构测试一个或多个命…...
你来问我来答,ChatGPT对话软件测试!主题互动
你来问我来答,ChatGPT对话软件测试! 大家好,我是聪明而有趣的ChatGPT。作为IT专家,我将竭尽全力为你解答技术问题,并提供适合各个级别人群理解的解决方案。无论你是初学者还是专业人士,我都会用智能、简单…...

无人机巢的作用及应用领域解析
无人机巢作为无人机领域的创新设备,不仅可以实现无人机的自主充电和电池交换,还为无人机提供安全便捷的存放空间。为了帮助大家更好地了解无人机巢,本文将着重解析无人机巢的作用和应用领域。 一、无人机巢的作用 无人机巢作为无人机技术的重…...

面试热题(环形链表II)
给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引…...

策略模式:优雅地实现可扩展的设计
策略模式:优雅地实现可扩展的设计 摘要: 策略模式是一种常用的设计模式,它可以帮助我们实现可扩展的、灵活的代码结构。本文将通过一个计算器案例来介绍策略模式的概念、使用场景以及如何在实际项目中应用策略模式来提高代码的可维护性和可扩…...

从8个新 NFT AMM,聊聊能如何为 NFT 提供流动性
DeFi 的出现,开启了数字金融民主化的革命。其中,通过 AMM 自由创建流动性池极大地增加了 ERC-20 Token 的流动性,并为一些长尾 Token 解锁了价值的发现,因而今天在链上可以看到各种丰富的交易、借贷和杠杆等活动。 而另一方面&am…...

习题1.27
先写代码 (defn square [x] (* x x)) (defn expmod[base exp m](cond ( exp 0) 1(even? exp) (mod (square (expmod base (/ exp 2) m)) m):else (mod (* base (expmod base (- exp 1) m)) m)))(defn fermat-test[n](defn try-it [a](cond ( a n) (println "test end&qu…...

简单游戏截图_可控截取内容2
一个需求 我需要在场景中截取不同层级的截图(如只截模型或只截UI或只截外部相加看到的画面 或全都截或和Shader配合呈现人眼夜视仪热成像的画面切换) 将截图排到列表中,在场景UI中展示出来 如何做 相机要能够看到不同的画面 将当前帧画面存储下来 将存储的画面展示出…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...

STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...