RabbitMQ消息应答与发布

消息应答

RabbitMQ一旦向消费者发送了一个消息,便立即将该消息,标记为删除.

消费者完成一个任务可能需要一段时间,如果其中一个消费者处理一个很长的任务并仅仅执行了一半就突然挂掉了,在这种情况下,我们将丢失正在处理的消息,后续给消费者发送的消息也就无法接收到了.

为了确保消息不丢失,我们引入了消息应答机制.

消息应答就是:消费者在接收到生产者的消息并且处理该消息之后,告诉RabbitMQ已经处理完成了,rabbitMQ可以进行删除了.

一个生产者,两个消费者.

/**
消费者1
*/
public class Consumer1 {public static final String QueueName = "TaskQueueName";public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("127.0.0.1");factory.setUsername("DGZ");factory.setPassword("Dgz@#151");Connection connection = factory.newConnection();Channel channel = connection.createChannel();//声明接收消息System.out.println("消费者1等待接收消息时间较短");DeliverCallback deliverCallback = (consumerTag,message) -> {Util.sleep(1);  //等待1sSystem.out.println("正在处理该消息" + new String(message.getBody()));/*** 手动应答* 1.消息的标记Tag* 2.是否批量应答 false表示不批量应答信道中的消息*/channel.basicAck(message.getEnvelope().getDeliveryTag(),false);};//取消消息时的回调CancelCallback cancelCallback = consumerTag ->{System.out.println("消息消费被中断");};/*** 消费者消费消息* 1.消费哪个队列* 2.消费成功之后是否要自动应答true:代表自动应答      false:代表手动应答* 3.消费者未成功消费的回调* 4.消费者取消消费的回调*/boolean autoAck = false;channel.basicConsume(QueueName,autoAck,deliverCallback,cancelCallback);}
}

/**
消费者2
*/
public class Consumer2 {public static final String QueueName = "TaskQueueName";public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("127.0.0.1");factory.setUsername("DGZ");factory.setPassword("Dgz@#151");Connection connection = factory.newConnection();Channel channel = connection.createChannel();System.out.println("消费者2等待接收消息时间较长");//声明接收消息DeliverCallback deliverCallback = (consumerTag,message) -> {Util.sleep(10); //等待10sSystem.out.println("正在处理该消息" + new String(message.getBody()));/*** 手动应答* 1.消息的标记Tag* 2.是否批量应答 false表示不批量应答信道中的消息*///当该消息还没有被处理的时候,如果此时这个应用挂掉,// 由于这个手动应答的机制,就不会删除该消息,而是将给消息交给其他应用去处理channel.basicAck(message.getEnvelope().getDeliveryTag(),false);};//取消消息时的回调CancelCallback cancelCallback = consumerTag ->{System.out.println("消息消费被中断");};/*** 消费者消费消息* 1.消费哪个队列* 2.消费成功之后是否要自动应答true:代表自动应答     false:代表手动应答* 3.消费者未成功消费的回调* 4.消费者取消消费的回调*/boolean autoAck = false;channel.basicConsume(QueueName,autoAck,deliverCallback,cancelCallback);}
}

消费者1 1s处理一个消息,消费者2 10s处理一个消息

/**
生产者
*/
public class Producer {//队列名称public static final String QueueName = "TaskQueueName";public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {ConnectionFactory factory = new ConnectionFactory();   //创建连接工厂factory.setHost("127.0.0.1");  //主机factory.setUsername("DGZ"); //用户名factory.setPassword("Dgz@#151"); // 密码Connection connection = factory.newConnection();  //通过连接工厂创建一个连接Channel channel = connection.createChannel();  //获取信道/*** 生产一个对列* 1.对列名称* 2.对列里面的消息是否持久化,默认情况下,消息存储在内存中* 3.该队列是否只供一个消费者进行消费,是否进行消息共享,true可以多个消费者消费 false:只能一个消费者消费* 4.是否自动删除,最后一个消费者端开链接以后,该队列是否自动删除,true表示自动删除* 5.其他参数*/channel.queueDeclare(QueueName,false,false,false,null);Scanner scanner = new Scanner(System.in);System.out.println("请输入消息:");while(scanner.hasNext()) {String message = scanner.next();channel.basicPublish("",QueueName,null,message.getBytes());System.out.println("生产者发出消息: " + message);}/*** 发送一个消息* 1.发送到哪个交换机* 2.路由的key值是哪个本次是队列的名称* 3.其他参数信息* 4.发送消息的消息体*/System.out.println("消息发送完毕");}
}

自动应答和手动应答:

自动应答就是MQ只要把消息发出去,不会管消息是否收到,都会立刻把这个消息进行删除.

手动应答就是MQ把消息发送出去之后,消费者在接收到生产者的消息并且处理该消息之后,告诉RabbitMQ已经处理完成了,rabbitMQ可以进行删除了.

RabbitMQ持久化

当RabbitMQ服务突然挂掉之后,消息生产者发送过来的消息如何保证不丢失.

默认情况下当RabbitMQ服务挂掉之后,它会忽略队列和消息,这时刚刚发送的消息和队列都会丢失.

确保消息不丢失我们需要干两件事,就是将队列和消息标记为持久化.

队列持久化

之前创建的队列都是非持久化的,如果RabbitMQ重启的话,队列就是丢失,如果需要实现持久化队列,那么就需要在声明队列的时候把durable参数设置为true.表示代表开启持久化.

public class Producer {public static final String QueueName = "CJ_QUEUE";public static void main(String[] args) throws IOException, TimeoutException {/*** RabbitMQ工具类,用来创建连接等信息*/Channel channel = RabbitMQUtil.RabbitMQ_getChannel();boolean durable = true;//第二个参数为队列持久化的参数,设置为true,表示队列开启持久化,false表示不开启持久化channel.queueDeclare(QueueName,durable,false,false,null);}
}

此时web管理端就会看见:

 表示队列持久化

 消息持久化

消息持久化是指消息生产者发布消息的时候,开启消息持久化,

public class Producer {public static final String QueueName = "CJ_QUEUE";public static void main(String[] args) throws IOException, TimeoutException {/*** RabbitMQ工具类,用来创建连接等信息*/Channel channel = RabbitMQUtil.RabbitMQ_getChannel();boolean durable = true;//第二个参数为队列持久化的参数,设置为true,表示队列开启持久化,false表示不开启持久化channel.queueDeclare(QueueName,durable,false,false,null);Scanner scanner = new Scanner(System.in);System.out.println("请输入要发送的消息");while (scanner.hasNext()) {String message = scanner.next();/*** 将发布消息的basicPublish方法的第三个参数设置为MessageProperties.PERSISTENT_TEXT_PLAIN* 表示开启消息持久化*/channel.basicPublish("",QueueName, MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes("UTF-8"));System.out.println("生产者发出消息" + message);}}
}

不公平分发

在最开始的时候RabbitMQ采用的是轮询分发,但是在某种场景下这种策略并不是很好,比如说两个消费者在处理消息,此时消费者1处理消息比较慢,而消费者2处理消息比较快,如果这个时候还是采用轮询分发的方式,那么处理慢的消费者就会一直在处理消息,而处理快的消费者就会有很大时间处于空闲状态.

为了解决这个问题,引入了不公平分发

不公平分发: 如果一个工作队列(消费者)还没有处理完一个消息或者没有应答签收一个消息,则RabbitMQ不会分配新的消息给该队列.

如果所有的消费者都没有完成手上的消息,生产者还在不停地生产消息,队列还在不停地添加新任务,这是不会给消费者分发消息,就有可能导致队列被撑爆.

这时就只能添加新的工作队列或者改变存储策略了.

设置不公平分发:

public class Consumer1 {public static final String QueueName = "CJ_QUEUE";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtil.RabbitMQ_getChannel();System.out.println("消费消息时间较长-----------");DeliverCallback deliverCallback = ((consumerTag,message)->{RabbitMQUtil.sleep(10);System.out.println("消费消息:" + new String(message.getBody()));channel.basicAck(message.getEnvelope().getDeliveryTag(),false);});CancelCallback cancelCallback = consumerTag ->{System.out.println("消息消费被中断");};//设置不公平分发int prefetchCount = 1;channel.basicQos(prefetchCount);//手动应答channel.basicConsume(QueueName,false,deliverCallback,cancelCallback);}
}

public class Consumer2 {public static final String QueueName = "CJ_QUEUE";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtil.RabbitMQ_getChannel();System.out.println("消费消息时间较短-----------");DeliverCallback deliverCallback = ((consumerTag,message)->{RabbitMQUtil.sleep(1);System.out.println("消费消息:" + new String(message.getBody()));channel.basicAck(message.getEnvelope().getDeliveryTag(),false);});CancelCallback cancelCallback = consumerTag ->{System.out.println("消息消费被中断");};//设置不公平分发int prefetchCount = 1;channel.basicQos(prefetchCount);//采取手动应答channel.basicConsume(QueueName,false,deliverCallback,cancelCallback);}
}

预期值分发

带权的消息分发

默认的消息发送是异步的,所以在任何时候,channel中不止一个来自消费者收到确认的消息,因此这里就存在一个未确认的消息缓冲区.

因此我们希望限制这里的缓冲区的大小,避免缓冲区中无休止的未确认消息.

这时我们就可以通过basicqos()方法来设置(预取计数来完成).

basicqos方法里面设置通道上允许的未确认消息的最大数量,一旦数据达到配置的数量,RabbitMQ将停止在通道上传递更多的消息.除非有未确认的消息被确认.

例如:假设此时通道上未确认的消息有 4,6,7,9,5,10,并且通道上设置预取计数值为6,这时RabbitMQ将不会在该通道上传递消息.除非这里的消息被确认一个,RabbitMQ将感知到这一变化,并且在发送一条信息.

消息应答和Qos预取值对用户的吞吐量有着重大影响.

不公平分发和预取值分发都用到 basic.qos 方法,如果取值为 1,代表不公平分发,取值不为1,代表预取值分发 

public class Consumer2 {public static final String QueueName = "CJ_QUEUE";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtil.RabbitMQ_getChannel();System.out.println("消费消息时间较短-----------");DeliverCallback deliverCallback = ((consumerTag,message)->{RabbitMQUtil.sleep(1);System.out.println("消费消息:" + new String(message.getBody()));channel.basicAck(message.getEnvelope().getDeliveryTag(),false);});CancelCallback cancelCallback = consumerTag ->{System.out.println("消息消费被中断");};//设置不公平分发/*int prefetchCount = 1;channel.basicQos(prefetchCount);*///设置预期值分发 值为4int prefetchCount = 4;channel.basicQos(prefetchCount);//采取手动应答channel.basicConsume(QueueName,false,deliverCallback,cancelCallback);}
}

发布确认

生产者发送消息到RabbitMQ后,需要RabbitMQ返回一个ack,表示RabbitMQ已经收到生产者发送的消息.这样生产者就知道自己发送的信息成功了.

发布确认逻辑

如果消息和队列是可持久化的,那么确认消息会在消息写入磁盘之后发出.

broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置 basic.ack 的 multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。

confirm 模式最大的好处在于是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息, 生产者应用程序同样可以在回调方法中处理该 nack 消息。

发布确认默认是没有开启的,如果要开启需要调用方法confirmSelect(),

channel.confirmSelect();

单个发布确认

这是一种简单的确认发布,他是一种同步确认发布的方式,也就是发布一个消息之后,只有它被确认,后续的消息才能被发布出去.

waitforconfirms()这个方法只有在消息被确认的时候才返回.

如果在指定的时间范围内没有返回,则抛出异常.

这种确认的方式就是发布速度特别慢,因为如果没有确认发布的消息,那么其他消息就只能阻塞等待.

这种方式最多每秒不超过百条的发送量.


/*** Created with IntelliJ IDEA.** @Author: DongGuoZhen* @Date: 2024/01/05/10:28* @Description: 单个确认发布*/
//确认发布指的是成功发送到了队列,并不是消费者消费了消息。
public class Producer1 {public static final int MESSAGE_MAX = 1000;public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {pushMessage();}private static void pushMessage() throws IOException, TimeoutException, InterruptedException {Channel channel = RabbitMQUtil.RabbitMQ_getChannel();String QueueName = UUID.randomUUID().toString();channel.queueDeclare(QueueName,false,true,false,null);//开启发布确认channel.confirmSelect();//开始时间long start = System.currentTimeMillis();//依次发送1000个消息for (int i = 0; i < 1000; i++) {String message = i+"";channel.basicPublish("",QueueName,null,message.getBytes());//发送单个消息立马进行发布确认boolean flag = channel.waitForConfirms();if(flag) {  //如果成功 trueSystem.out.println("消息: "+ message + " 成功发送到队列:" + QueueName);}}long end = System.currentTimeMillis();System.out.println("发布: " + MESSAGE_MAX + "条消息共耗时:"+ (end-start)+ "ms");}
}

 

批量发布确认

单个确认发布的速度非常慢,与单个确认等待相比,如果发送一批然后在一起确认,这样就大大提高了消息的发送速度和吞吐量.

当然这种方式的缺点就是,当有一个消息出现问题时,我们无法知道是那个消息没有发布出去.

/*** Created with IntelliJ IDEA.** @Author: DongGuoZhen* @Date: 2024/01/05/10:28* @Description: 批量确认发布*/
//确认发布指的是成功发送到了队列,并不是消费者消费了消息。
public class Producer2 {public static final int MESSAGE_MAX = 1000;public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {pushMessage();}private static void pushMessage() throws IOException, TimeoutException, InterruptedException {Channel channel = RabbitMQUtil.RabbitMQ_getChannel();String QueueName = UUID.randomUUID().toString();channel.queueDeclare(QueueName,false,true,false,null);//开启发布确认channel.confirmSelect();int bachSize = 100;  //批量发布确认的条数//开始时间long start = System.currentTimeMillis();//依次发送1000个消息for (int i = 0; i < 1000; i++) {String message = i+"";channel.basicPublish("",QueueName,null,message.getBytes());if((i+1) % bachSize == 0) {  //当发送的消息达到100条,进行确认发布一次channel.waitForConfirms();   //发布确认System.out.println("第"+ i+"条消息被确认");}}long end = System.currentTimeMillis();System.out.println("发布: " + MESSAGE_MAX + "条消息共耗时:"+ (end-start)+ "ms");}
}

 

异步发布确认

异步确认虽然编程上逻辑比较复杂,但是性价比最高,可靠性和效率都很好,利用了回调函数来达到消息的可靠性传递.


/*** Created with IntelliJ IDEA.** @Author: DongGuoZhen* @Date: 2024/01/05/11:02* @Description: 异步确认发布*/
public class Producer3 {public static final int MESSAGE_MAX = 1000;public static void main(String[] args) throws IOException, TimeoutException {pushMessage();}private static void pushMessage() throws IOException, TimeoutException {Channel channel = RabbitMQUtil.RabbitMQ_getChannel();String QueueName = UUID.randomUUID().toString();channel.queueDeclare(QueueName,false,true,false,null);channel.confirmSelect();long start = System.currentTimeMillis();/*** deliveryTag 消息的标记* multiple 是否为批量确认*/ConfirmCallback ackCallback = (deliveryTag,multiple) ->{System.out.println("确认的消息:" + deliveryTag);};ConfirmCallback nackCallback = (deliveryTag,multiple) ->{System.out.println("未确认的消息:" + deliveryTag);};//消息监听器  监听那些消息成功了,那些消息失败了channel.addConfirmListener(ackCallback,nackCallback);//批量发送消息for (int i = 0; i < 1000; i++) {String message = i+"消息";channel.basicPublish("", QueueName,null,message.getBytes());}long end = System.currentTimeMillis();System.out.println("异步确认发布: " + MESSAGE_MAX + "条消息共耗时:"+ (end-start)+ "ms");}
}
  • 单独发布消息

同步等待确认,简单,但吞吐量非常有限。

  • 批量发布消息

批量同步等待确认,简单,合理的吞吐量,一旦出现问题但很难推断出是哪条消息出现了问题。

  • 异步处理

最佳性能和资源使用,在出现错误的情况下可以很好地控制,但是实现起来稍微难些

注意:应答和发布的区别:

应答功能属于消费者,消费者消费完消息后告诉RabbitMQ已经消费成功

发布功能属于生产者,生产者生产消息到RabbitMQ,RabbitMQ需要告诉生产者已经收到消息

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

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

相关文章

OpenHarmony驱动消息机制管理

驱动消息机制管理 当用户态应用和内核态驱动需要交互时&#xff0c;可以使用HDF框架的消息机制来实现。 消息机制的功能主要有以下两种&#xff1a; 用户态应用发送消息到驱动。 用户态应用接收驱动主动上报事件。 配置管理 HCS&#xff08;HDF Configuration Source&…

深入分析 Linux 网络丢包问题

热门IT课程【视频教程】-华为/思科/红帽/oraclehttps://xmws-it.blog.csdn.net/article/details/134398330 所谓丢包&#xff0c;是指在网络数据的收发过程中&#xff0c;由于种种原因&#xff0c;数据包还没传输到应用程序中&#xff0c;就被丢弃了。这些被丢弃包的数量&#…

C++进阶:多态(下)

1、多态的原理 多态之所以可以实现&#xff0c;主要是因为虚函数表的存在&#xff0c;虚函数表用于记录虚函数的地址&#xff0c;他是一个函数指针数组&#xff0c;在类中用一个函数指针数组指针来指向数组&#xff0c;子类继承了父类的虚函数表&#xff0c;当有重写的情况发生…

基于SAM的视频标注

在本文中&#xff0c;我们将演示基础模型的应用&#xff0c;例如 Meta 的 Segment Anything 和 YOLOv8&#xff0c;以自动检测、分类和绘制视频中感兴趣对象的蒙版。这是之前指南的后续&#xff1a;使用 Meta 的 Segment Anything 和 YOLOv8 自动分类掩码。在本指南中&#xff…

【RabbitMQ】快速入门及基本使用

一、引言 1、、消息队列 Ⅰ、什么是消息队列&#xff1f; 消息队列是一种进程间通信或同一进程的不同线程间的通信方式&#xff0c;软件的贮列用来处理一系列的输入&#xff0c;通常是来自用户。消息队列提供了异步的通信协议&#xff0c;每一个贮列中的纪录包含详细说明的数据…

PySimpleGUI:让spin支持循环

需求 自己用PySimpleGUI写了个小工具&#xff0c;但是发现它的spin不支持循环。 Tkinter本身的Spinbox有wrap这个开关可以觉得是否支持循环&#xff0c;但是没看到PySimpleGUI也支持这个特性。 代码实现 所谓spin的循环&#xff0c;是指当值变换到最大最小值时&#xff0c;可…

移动开发行业——鸿蒙OS NEXT开出繁花

1月18日&#xff0c;华为宣布HarmonyOS NEXT开发者预览版开放申请&#xff0c;根据官方注解&#xff0c;这个版本的鸿蒙系统有个更通俗易懂的名字——“星河版”&#xff0c;也被称为“纯血”鸿蒙。 根据官方解释&#xff0c;之所以取名星河版&#xff0c;寓意鸿蒙OS NEXT就像…

Screen 简介

目录 1. screen 简介2. screen 基本命令 1. screen 简介 screen 是一个在 Unix 和类 Unix 系统上的 终端复用 工具。它允许用户在单个终端窗口中运行多个终端会话&#xff0c;并提供了一些其他功能&#xff0c;如会话断开后的恢复和远程连接的分离。以下是 screen 的一些主要特…

【C语言】扫雷游戏完整代码实现

目录 1.game.h 2.game.c 3.progress.c 4.运行结果 1.game.h #define _CRT_SECURE_NO_WARNINGS#include <string.h> #include <stdio.h> #include <time.h> #include<stdlib.h>#define ROW 9 #define COL 9 #define ROWS 11 #define COLS 11 #defin…

SpringBoot 中配置处理

1、简介 本文介绍一些 springboot 配置管理相关的内容&#xff0c;如何自定义配置&#xff0c;导入配置&#xff0c;配置绑定和基于 profiles 的配置。 2、springboot 配置管理 2.1、自定义配置类 在 spring 中使用 Configuration 注解定义配置类&#xff0c;在 springboot 中…

进程线程知识

一 初识linux线程 1 线程由来 我们之前说创建一个进程&#xff0c;要创建进程控制块pcb&#xff0c;进程地址空间&#xff0c;页表&#xff0c;而且我之前的博客中都有意无意的说明这个pcb是描述进程的&#xff0c;是os用来管理进程的&#xff0c;而有了线程后&#xff0c;就要…

Python基础第八篇(Python异常处理,模块与包)

文章目录 一、了解异常二、捕获异常&#xff08;1&#xff09;.异常案例代码&#xff08;2&#xff09;.读出结果 三、异常的传递&#xff08;1&#xff09;.异常传递案例代码&#xff08;2&#xff09;.读出结果 四、Python模块&#xff08;1&#xff09;.模块的导入&#xff…

大模型+自动驾驶

论文&#xff1a;https://arxiv.org/pdf/2401.08045.pdf 大型基础模型的兴起&#xff0c;它们基于广泛的数据集进行训练&#xff0c;正在彻底改变人工智能领域的面貌。例如SAM、DALL-E2和GPT-4这样的模型通过提取复杂的模式&#xff0c;并在不同任务中有效地执行&#xff0c;从…

k8s使用ingress实现应用的灰度发布升级

v1是1.14.0版本nginx ,实操时候升级到v2是1.20.0版本nginx&#xff0c;来测试灰度发布实现过程 一、方案&#xff1a;使用ingress实现应用的灰度发布 1、服务端&#xff1a;正常版本v1&#xff0c;灰度升级版本v2 2、客户端&#xff1a;带有请求头versionv2标识的请求访问版…

【MySQL】一文总结MVCC多版本并发控制

目录 MVCC 介绍当前读和快照读当前读快照读 MVCC 原理解析隐式字段Undo Log版本链Read ViewRead View 可见性原则 RC 和 RR 下的 Read ViewRC 下的 Read ViewRR 下的 Read View小结RR 级别下能否防止幻读总结 MVCC 介绍 在当今高度并发的数据库环境中&#xff0c;有效的并发控…

超级管理员权限绕过windows登录windows命令

文章目录 一、设置超级管理员权限二、绕过windows登录界面三、windows命令 一、设置超级管理员权限 查看本机的用户列表&#xff1a;net user激活超级用户&#xff1a;net user administrator /active:yes为超级用户设置密码&#xff1a;net user administrator 123(password)…

Redis 高可用之主从复制

1、简介 在 Redis 中&#xff0c;主从复制就是多个节点进行数据同步&#xff0c;在这些节点中&#xff0c;有 Master 和 slave 两个角色&#xff0c;Master 以写为主&#xff0c;slave 以读为主&#xff0c;当 Master 数据变化的时候&#xff0c;会自动将新的数据同步到其他的 …

StableDiffusion新版汉化

新旧版不同&#xff0c;这里以新版为例&#xff0c;用的是带链接&#xff0c;可以更新的方法。 步骤&#xff1a; 1.找到这个位置&#xff0c;依次点击&#xff0c;注意选项。 2.点击加载&#xff0c;等待刷新。 ctrlF搜索 zh_CN Localization 右边点击install&#xff0c…

外包干了4个月,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

GAMES101-Assignment8

一、总览 1.1 连接绳子的约束 在rope.cpp 中, 实现Rope 类的构造函数。这个构造函数应该可以创建一个 新的绳子(Rope) 对象&#xff0c;该对象从start 开始&#xff0c;end 结束&#xff0c;包含num_nodes 个节点。也就是如下图所示&#xff1a; 每个结点都有质量&#xff…