RocketMQ快速入门
2.1 消息生产和消费介绍
使用RocketMQ可以发送普通消息、顺序消息、事务消息,顺序消息能实现有序消费,事务消息可以解决分布式事务实现数据最终一致。
RocketMQ有2种常见的消费模式,分别是DefaultMQPushConsumer和DefaultMQPullConsumer模式,这2种模式字面理解一个是推送消息,一个是拉取消息。这里有个误区,其实无论是Push还是Pull,其本质都是拉取消息,只是实现机制不一样。
DefaultMQPushConsumer其实并不是broker主动向consumer推送消息,而是consumer向broker发出请求,保持了一种长链接,broker会每5秒会检测一次是否有消息,如果有消息,则将消息推送给consumer。使用DefaultMQPushConsumer实现消息消费,broker会主动记录消息消费的偏移量。
DefaultMQPullConsumer是消费方主动去broker拉取数据,一般会在本地使用定时任务实现,使用它获得消息状态方便、负载均衡性能可控 ,但消息的及时性差,而且需要手动记录消息消费的偏移量信息 ,所以在工作中多数情况推荐使用Push模式。
RocketMQ发送的消息默认会存储到4个队列中,当然创建几个队列存储数据,可以自己定义。
在这里插入图片描述
RocketMQ作为MQ消息中间件,ack机制必不可少,在RocketMQ中常见的应答状态如下:
LocalTransactionState:主要针对事务消息的应答状态
public enum LocalTransactionState {
COMMIT_MESSAGE,//消息提交
ROLLBACK_MESSAGE,//消息回滚
UNKNOW, //未知状态,一般用于处理超时等现象
}
ConsumeConcurrentlyStatus:主要针对消息消费的应答状态
public enum ConsumeConcurrentlyStatus {
//消息消费成功
CONSUME_SUCCESS,
//消息重试,一般消息消费失败后,RocketMQ为了保证数据的可靠性,具有重试机制
RECONSUME_LATER;
}
重发时间是:(broker.log中有)
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
2.2 RocketMQ普通消息生产者
2.2.1 工程创建
我们先实现一个最基本的消息发送,先创建一个springboot工程,工程名字叫rocketmq-demo1
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 " target="_blank">http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<groupId>org.mentu</groupId>
<artifactId>rocketmq-demo1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<rocketmq.version>4.4.0</rocketmq.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>${rocketmq.version}</version>
</dependency>
</dependencies>
</project>
2.2.2 消息发送
消息发送有这么几个步骤:
创建DefaultMQProducer
设置Namesrv地址
开启DefaultMQProducer
创建消息Message
发送消息
关闭DefaultMQProducer
我们创建一个Producer类,按照上面步骤实现消息发送,代码如下:
在这里插入图片描述
public class Producer {
//指定namesrv地址
private static String NAMESRV_ADDRESS = "192.168.211.143:9876";
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//创建一个DefaultMQProducer,需要指定消息发送组
DefaultMQProducer producer = new DefaultMQProducer("Test_Quick_Producer_Name");
//指定Namesvr地址
producer.setNamesrvAddr(NAMESRV_ADDRESS);
//启动Producer
producer.start();
//创建消息
Message message = new Message(
"Test_Quick_Topic", //主题
"TagA", //标签,可以用来做过滤
"KeyA", //唯一标识,可以用来查找消息
"hello rocketmq".getBytes() //要发送的消息字节数组
);
//发送消息
SendResult result = producer.send(message);
//关闭producer
producer.shutdown();
}
}
我们可以在控制台查看到对应的消息,控制台地址:http://localhost:8080/#/message 我们可以看到如下消息:在这里插入图片描述
注意:这里时间查询以消息存储时间为准,注意服务器的时间有可能不准确。
2.3 RocketMQ普通消息消费者
2.3.1 消息消费
消费者消费消息有这么几个步骤:
创建DefaultMQPushConsumer
设置namesrv地址
设置subscribe,这里是要读取的主题信息
创建消息监听MessageListener
获取消息信息
返回消息读取状态
创建Consumer类,按照上面步骤实现消息消费,代码如下:
在这里插入图片描述
上图代码如下:
public class Consumer {
//指定namesrv地址
private static String NAMESRV_ADDRESS = "192.168.211.143:9876";
public static void main(String[] args) throws MQClientException {
//创建DefaultMQPushConsumer
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("Test_Quick_Consumer_Name");
//设置namesrv地址
consumer.setNamesrvAddr(NAMESRV_ADDRESS);
//设置要读取的topic
consumer.subscribe(
"Test_Quick_Topic", //指定要读取的消息主题
"TagA"); //指定要读取的消息过滤信息,多个标签数据,则可以输入"tag1 || tag2 || tag3"
//创建消息监听
consumer.setMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
//获取第1个消息
MessageExt message = msgs.get(0);
//获取主题
String topic = message.getTopic();
//获取标签
String tags = message.getTags();
//获取消息
String result = new String(message.getBody(),"UTF-8");
System.out.println("topic:"+topic+",tags:"+tags+",result:"+result);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
//消息重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
//消息消费成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费监听
consumer.start();
}
}
控制台运行结果
topic:Test_Quick_Topic,tags:TagA,result:hello rocketmq
2.4 RocketMQ顺序消息
消息有序指的是可以按照消息的发送顺序来消费。 RocketMQ可以严格的保证消息有序。但这个顺序,不是全局顺序,只是分区(queue)顺序。要全局顺序只能一个分区。
如何保证顺序
在MQ的模型中,顺序需要由3个阶段去保障:
消息被发送时保持顺序
消息被存储时保持和发送的顺序一致
消息被消费时保持和存储的顺序一致
发送时保持顺序意味着对于有顺序要求的消息,用户应该在同一个线程中采用同步的方式发送。存储保持和发送的顺序一致则要求在同一线程中被发送出来的消息A和B,存储时在空间上A一定在B之前。而消费保持和存储一致则要求消息A、B到达Consumer之后必须按照先A后B的顺序被处理。
在这里插入图片描述
2.4.1 消息生产者
我们创建一个消息生产者OrderProducer,这里每次发消息都会发到同一个队列中,代码如下:
在这里插入图片描述
上图代码如下:
public class OrderProducer {
//nameserver地址
private static String namesrvaddress="192.168.211.143:9876;";
public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException, RemotingException, MQClientException, MQBrokerException {
//创建DefaultMQProducer
DefaultMQProducer producer = new DefaultMQProducer("order_producer_group_name");
//设置namesrv地址
producer.setNamesrvAddr(namesrvaddress);
//启动Producer
producer.start();
//创建消息
Message message = new Message(
"Topic_Order_Demo",
"TagOrder",
"KeyOrder",
"hello order message!".getBytes(RemotingHelper.DEFAULT_CHARSET));
//发送消息
SendResult result = producer.send(
message, //要发送的消息
new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
return mqs.get((Integer) arg);
}
},
1);//设置存入第几个队列中,这里是下标,从0开始
//关闭Producer
producer.shutdown();
}
}
2.4.2 消息消费者
创建一个消息消费者OrderConsumer,消息监听用MessageListenerOrderly来实现顺序消息,代码如下:
在这里插入图片描述
上图代码如下:
public class OrderConsumer {
//nameserver地址
private static String namesrvaddress="192.168.211.143:9876;";
public static void main(String[] args) throws MQClientException {
//创建消息消费对象DefaultMQConsumer
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_consumer_group_name");
//设置nameserver地址
consumer.setNamesrvAddr(namesrvaddress);
//设置消费顺序
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//设置消息拉取最大数
consumer.setConsumeMessageBatchMaxSize(5);
//设置消费主题
consumer.subscribe("Topic_Order_Demo","TagOrder");
//消息监听
consumer.setMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
try {
for (MessageExt msg : msgs) {
String topic = msg.getTopic();
String tags = msg.getTags();
String keys = msg.getKeys();
String body = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
System.out.println("topic:"+topic+",tags:"+tags+",keys:"+keys+",body:"+body);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
//启动Consumer
consumer.start();
}
}
我们打开控制台,可以看到消息发送到了第2个队列中了。
在这里插入图片描述
提示:大家在测试顺序消息的时候,可以将上面消息生产者连续发送10个或者更多来测试顺序。
2.5 RocketMQ事务消息
在RocketMQ4.3.0版本后,开放了事务消息这一特性,对于分布式事务而言,最常说的还是二阶段提交协议。
2.5.1 RocketMQ事务消息流程
RocketMQ的事务消息,主要是通过消息的异步处理,可以保证本地事务和消息发送同时成功执行或失败,从而保证数据的最终一致性,这里我们先看看一条事务消息从诞生到结束的整个时间线流程:
在这里插入图片描述
事务消息的成功投递是需要经历三个Topic的,分别是:
Half Topic:用于记录所有的prepare消息
Op Half Topic:记录已经提交了状态的prepare消息
Real Topic:事务消息真正的Topic,在Commit后会才会将消息写入该Topic,从而进行消息的投递
2.5.2 事务消息生产者
我们创建一个事务消息生产者TransactionProducer,事务消息发送消息对象是TransactionMQProducer,为了实现本地事务操作和回查,我们需要创建一个监听器,监听器需要实现TransactionListener接口,实现代码如下:
监听器TransactionListenerImpl,代码如下:在这里插入图片描述
上图代码如下:
public class TransactionListenerImpl implements TransactionListener {
//存储当前线程对应的事务状态
private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();
/***
* 发送prepare消息成功后回调该方法用于执行本地事务
* @param msg:回传的消息,利用transactionId即可获取到该消息的唯一Id
* @param arg:调用send方法时传递的参数,当send时候若有额外的参数可以传递到send方法中,这里能获取到
* @return
*/
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
//获取线程ID
String transactionId = msg.getTransactionId();
//初始状态为0
localTrans.put(transactionId,0);
try {
//此处执行本地事务操作
System.out.println("....执行本地事务");
Thread.sleep(70000);
System.out.println("....执行完成本地事务");
} catch (InterruptedException e) {
e.printStackTrace();
//发生异常,则回滚消息
localTrans.put(transactionId,2);
return LocalTransactionState.UNKNOW;
}
//修改状态
localTrans.put(transactionId,1);
System.out.println("executeLocalTransaction------状态为1");
//本地事务操作如果成功了,则提交该消息,让该消息可见
return LocalTransactionState.UNKNOW;
}
/***
* 消息回查
* @param msg
* @return
*/
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
//获取事务id
String transactionId = msg.getTransactionId();
//通过事务id获取对应的本地事务执行状态
Integer status = localTrans.get(transactionId);
System.out.println("消息回查-----"+status);
switch (status){
case 0:
return LocalTransactionState.UNKNOW;
case 1:
return LocalTransactionState.COMMIT_MESSAGE;
case 2:
return LocalTransactionState.ROLLBACK_MESSAGE;
}
return LocalTransactionState.UNKNOW;
}
}
创建消息发送对象TransactionProducer,代码如下:
在这里插入图片描述
上图代码如下:
public class TransactionProducer {
//nameserver地址
private static String namesrvaddress="192.168.211.143:9876;";
public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, InterruptedException {
//创建事务消息发送对象
TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_group_name");
//设置namesrv地址
producer.setNamesrvAddr(namesrvaddress);
//创建监听器
TransactionListener transactionListener = new TransactionListenerImpl();
//创建线程池
ExecutorService executorService = new ThreadPoolExecutor(
2,
5,
100,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(
2000),
new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setName("client-transaction-msg-check-thread");
return thread;
}
}
);
//设置线程池
producer.setExecutorService(executorService);
//设置监听器
producer.setTransactionListener(transactionListener);
//启动producer
producer.start();
//创建消息
Message message = new Message(
"TopicTxt_Demo",
"TagTx",
"KeyTx1",
"hello".getBytes(RemotingHelper.DEFAULT_CHARSET));
//发送事务消息,此时消息不可见
TransactionSendResult transactionSendResult = producer.sendMessageInTransaction(message, "发送消息,回传所需数据!");
System.out.println(transactionSendResult);
//休眠
Thread.sleep(120000);
//关闭
producer.shutdown();
}
}
2.5.3 事务消息
事务消息的消费者和普通消费者一样,这里我们就不做介绍了,直接贴代码:
public class TransactionConsumer {
//nameserver地址
private static String namesrvaddress="192.168.211.143:9876;";
public static void main(String[] args) throws MQClientException {
//创建DefaultMQPushConsumer
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction_consumer_group_name");
//设置nameserver地址
consumer.setNamesrvAddr(namesrvaddress);
//设置每次拉去的消息个数
consumer.setConsumeMessageBatchMaxSize(5);
//设置消费顺序
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//设置监听的消息
consumer.subscribe("TopicTxt_Demo","TagTx");
//消息监听
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
for (MessageExt msg : msgs) {
String topic = msg.getTopic();
String tags = msg.getTags();
String keys = msg.getKeys();
String body = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
System.out.println("topic:"+topic+",tags:"+tags+",keys:"+keys+",body:"+body);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费
consumer.start();
}
}
事务消息参考地址:http://rocketmq.apache.org/docs/transaction-example/
2.5.4 RocketMQ实现分布式事务流程
MQ事务消息解决分布式事务问题,但第三方MQ支持事务消息的中间件不多,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中间件为例,其思路大致为:
第一阶段Prepared消息,会拿到消息的地址。
第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
在这里插入图片描述
2.6 消息广播/批量发送
上面发送消息,我们测试的时候,可以发现消息只有一个消费者能收到,如果我们想实现消息广播,让每个消费者都能收到消息也是可以实现的。而且上面发送消息的时候,每次都是发送单条Message对象,能否批量发送呢?答案是可以的。
2.6.1 消息生产者
创建消息生产者BroadcastingProducer,代码如下:
在这里插入图片描述
上图代码如下:
public class BroadcastingProducer {
//nameserver地址
private static String namesrvaddress="192.168.211.143:9876;";
public static void main(String[] args) throws UnsupportedEncodingException, MQClientException, RemotingException, InterruptedException, MQBrokerException {
//创建DefaultMQProducer
DefaultMQProducer producer = new DefaultMQProducer("broadcasting_producer_group");
//指定nameserver地址
producer.setNamesrvAddr(namesrvaddress);
//启动
producer.start();
//创建消息
List<Message> messages = new ArrayList<Message>();
for (int i = 0; i <20 ; i++) {
Message message = new Message(
"Topic_broadcasting",
"TagBroad",
"KeyBroad"+i,
(i+"--hello brodcasting").getBytes(RemotingHelper.DEFAULT_CHARSET));
//将消息添加到集合中
messages.add(message);
}
//批量发送消息
producer.send(messages);
//关闭
producer.shutdown();
}
}
2.6.2 消息消费者
广播消费模式其实就是每个消费者都能读取到消息,我们这里只需要将消费者的消费模式设置成广播模式即可。consumer.setMessageModel(MessageModel.BROADCASTING);,代码如下:
在这里插入图片描述
上图代码如下:
public class BroadcastingConsumerDemo1 {
//广播模式
private static String namesrvaddress="192.168.211.143:9876;";
public static void main(String[] args) throws MQClientException {
//创建DefaultMQConsumer
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("broadcasting_consumer_group");
//指定nameserver地址
consumer.setNamesrvAddr(namesrvaddress);
//指定要消费的消息主体
consumer.subscribe("Topic_broadcasting","*");
//指定消费顺序
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//指定一次拉取条数
consumer.setConsumeMessageBatchMaxSize(2);
//指定消费模式 集群模式/广播模式
consumer.setMessageModel(MessageModel.BROADCASTING);
//创建监听,监听消息
consumer.setMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
try {
String topic = msg.getTopic();
String tags = msg.getTags();
String keys = msg.getKeys();
String body = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
System.out.println("demo1 topic:"+topic+",tags:"+tags+",keys:"+keys+",body:"+body);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动
consumer.start();
}
}
运行测试,可以发现每个节点都消费了20条消息。效果如下图:
在这里插入图片描述
学习:
https://blog.csdn.net/Same_Liu/article/details/89517571?spm=1001.2014.3001.5506
相关文章:
RocketMQ快速入门
2.1 消息生产和消费介绍使用RocketMQ可以发送普通消息、顺序消息、事务消息,顺序消息能实现有序消费,事务消息可以解决分布式事务实现数据最终一致。RocketMQ有2种常见的消费模式,分别是DefaultMQPushConsumer和DefaultMQPullConsumer模式,这…...
【虚拟仿真】Unity3D实现从浏览器拉起本地exe程序并传参数
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 最近有项目需求,从浏览器调起来本地的exe程序&…...
Intel中断体系(1)中断与异常处理
文章目录概述中断与异常中断可屏蔽中断与不可屏蔽中断(NMI)异常异常分类中断与异常向量中断描述符表中断描述符中断与异常处理中断与异常处理过程堆栈切换错误码64位模式下的中断异常处理64位中断描述符64位处理器下的堆栈切换相关参考概述 中断是现代计…...
财报解读:四季度营收超预期,优步却越来越“不务正业”了
“公司第四季度的业绩表现将是强劲的”。 公布2022年第三季度财报时,优步的高管给出了这样的预告,给资本市场打了一针“强心剂”。然而有人对此表示质疑,后疫情时代,带着新模式、新车型的全新网约车公司层出不穷,车企…...
C语言-程序环境和预处理(14.2)
目录 预处理详解 1.预定义符号 2. #define 2.1 #define定义标识符 2.2 #define 定义宏 2.3 #define 替换规则 注意事项: 2.4 #和## 2.5 带副作用的宏参数 2.6 宏和函数对比 3. #undef 4. 条件编译 4.1 单分支条件编译 4.2 多分支条件编译 4.3 判断是…...
VHDL语言基础-时序逻辑电路-计数器
目录 计数器的设计: 计数器的作用: 计数器的实现: 1、用“”函数描述: 用T触发器级联构成的串行进位的二进制加法计数器的仿真波形: 计数器的仿真: 计数器的设计: 计数是一种最简单基本的…...
MySQL数据库07——高级条件查询
前面一章介绍了基础的一个条件的查询,如果多条件,涉及到逻辑运算,and or 之类的。就是高级一点的条件查询。本章来介绍复杂的条件搜索表达式。 AND运算符 AND运算符只有当两边操作数均为True时,最后结果才为True。人们使用AND描述…...
《Terraform 101 从入门到实践》 第四章 States状态管理
《Terraform 101 从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看。 军书十二卷,卷卷有爷名。 为什么需要状态管理 Terraform的主要作用是管理云平台上的资源ÿ…...
数据结构之二叉树
🎈一.二叉树相关概念 1.树 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合,树结构通常用来存储逻辑关系为 "一对多" 的数据。例如: 关于树的几个重要概念&…...
上海亚商投顾:三大指数集体调整 消费板块逆市活跃
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。市场情绪三大指数今日集体调整,沪指全天弱势震荡,创业板指盘中跌超1%。旅游、食品、乳业等大消费板块…...
【2023unity游戏制作-mango的冒险】-开始画面API制作
👨💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:游戏制作 ⭐mango的冒险-开始画面制作⭐ 文章目录⭐mango的冒险-开始画面制作⭐👨&…...
【微服务】Nacos配置管理
🚩本文已收录至专栏:微服务探索之旅 👍希望您能有所收获 Nacos除了可以做配置管理,同样可以当作注册中心来使用。 了解注册中心用法点击跳转👉【微服务】Nacos注册中心 一.引入 当微服务部署的实例越来越多࿰…...
【C++】类与对象理解和学习(上)
专栏放在【C知识总结】,会持续更新,期待支持🌹类是什么?类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它(实例化后才…...
Pyqt5小案例,界面与逻辑分离的小计算器程序
直接看下最终效果: 使用技术总结 使用Designer设计界面 使用pyuic5命令导出到python文件 新建逻辑处理文件,继承pyuic5导出的文件的类,在里面编写信号与槽的处理逻辑 使用Designer设计界面 要使用Designer,安装一个Python库即…...
leaflet加载KML文件,显示图形(方法2)
第049个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中加载KML文件,将图形显示在地图上。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,请加载其他来练习 文章目录 示例效果配置方式示例源代码(共66…...
Mysql 部署 MGR 集群
0. 参考文章 官方文档: MySQL :: MySQL 8.0 Reference Manual :: 18.2 Getting Started 博客: MGR 单主模式部署教程(基于 MySQL 8.0.28) - 墨天轮 (modb.pro) mysql MGR单主模式的搭建 - 墨天轮 (modb.pro) MySQL 5.7 基于…...
迁移至其他美国主机商时需要考虑的因素
网站的可访问性是关系业务的关键因素之一。一个稳定、快速且优化良好的主机上的网站更有可能享受不间断的流量,并在谷歌的SERP中获得更好的排名。因此,在构建企业网站时,选择合适的主机商相当重要。不过就以美国主机为例,由于每个…...
【数据结构】第二章 线性表
文章目录第二章 知识体系2.1 线性表的定义和基本操作2.1.1 线性表的定义2.1.2 线性表的基本操作2.2 线性表的顺序表示2.2.1 顺序表的定义2.2.2 顺序表的基本操作的实现2.3 线性表的链式表示2.3.1 单链表的定义2.3.2 单链表的基本操作实现2.3.3 双链表2.3.4 循环链表2.3.5 静态链…...
RESTful API 为何成为顶流 API 架构风格?
作者孙毅,API7.ai 技术工程师,Apache APISIX Committer 万物互联的世界充满着各式各样的 API ,如何统筹规范 API 至关重要。RESTful API 是目前世界上最流行的 API 架构风格之一,它可以帮助你实现客户端与服务端关注点分离&#x…...
Python基础知识点汇总(列表)
列表的含义 列表由一系列按特定顺序排列的元素组成,是Python中内置的可变序列。 **注:**列表的所有元素放在中括号[]中,相邻的两个元素用逗号分隔; 可将整数、实数、字符串、列表、元组等任何类型的内容放到列表中,且同一列表的元素类型可以不同。 列表的创建和删除 1.…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
触发DMA传输错误中断问题排查
在STM32项目中,集成BLE模块后触发DMA传输错误中断(DMA2_Stream1_IRQHandler进入错误流程),但单独运行BLE模块时正常,表明问题可能源于原有线程与BLE模块的交互冲突。以下是逐步排查与解决方案: 一、问题根源…...
