mongotemplate中save抛出异常_基于 MongoDB 解决微服务设计中的原子写入问题

本文讨论了在使用 Mongotemplate 的 save 方法时遇到的并发异常问题。在微服务设计中,针对单个字段的原子性更新至关重要。通过案例分析,指出在多用户并发操作下,简单的 get 和 set 方式可能导致计数不准确。同时,介绍了 MongoDB 文档更新的原子性特性,如何确保在预定座位场景中实现一致性。最后,通过一个示例展示了如何在文档更新中利用 version 字段防止冲突,当 version 不匹配时,保存操作将失败。
摘要由CSDN通过智能技术生成

3b840f377673702b05b20844a8fbd2e6.png

毫不保留的说,我们正处在一个充满并发计算的世界里。为了保证业务数据的一致性状态不遭受破坏,开发者通常需要对潜在的并发以及异常场景做出估量并采取适当的原子性保护。与此同时,几乎所有主流的编程语言都提供了良好的并发框架支持,例如,Java 中的 concurrent 包就提供了全面的锁特性实现。借由这些能力,我们很容易在单进程应用中解决原子性方面的问题。但是,微服务架构让应用程序处理并发原子性问题变得更加复杂,这是由分布式系统的复杂性所决定的。尤其是对于实例(进程)内施加的锁机制无法解决分布式的问题。如下图所示:

43ff3788ac383b117ded59048becedb8.png


对于 MongoDB 来说,更多的应用实践倾向于利用单文档事务性来解决原子性问题,当然,你也可以使用高版本中的多文档事务实现,但缺点是必须接受多文档事务所带来的性能损失。而关于MongoDB 的文档级原子性,尽管大多数人已经知道这一点,但在一些真实的项目案例中,仍然可以发现各种考虑不周的情形。下面,以案例来说明此类问题。案例一

a1ea2597bf2529b6c4075b0f565d6824.png

为了能了解网站上在售课程的受欢迎程度,我们增加了课程的关注功能,即喜欢该课程的用户可以通过点击关注以获得更新通知。这样,在课程的信息页面上也可以清楚的看到关注的人数。为此,每个课程文档需要增加 favCount 字段用来表示得到的关注数量,如下:

@Data

@Document(collection="Course")

public class Course {

@Id

private String id;

private String courseName;

//收藏数量

private Integer favCount;

...

这里对 Course 类添加了@Document 注解,这表示框架将处理文档和对象之间的关系,这是Spring Data Mongo 提供的 ORM 实现。那么,对于"加关注"这一逻辑功能,很容易实现如下:

@Autowired

private CourseRepository courseRepository;

public boolean incrFavCount(String courseId) {

Assert.hasLength(courseId, "courseId required");

Course course = courseRepository.findById(courseId).orElse(null);

if (course == null) {

return false;

在Java使用`mongoTemplate`调用MongoDB的函数`archive_data`,你需要确保你的MongoDB服务器已经定义了名为`archive_data`的函数,并且该函数接受三个参数。通常,你可以通过`mongoTemplate`的`invoke`方法或者`callFunction`方法来调用存储在MongoDB服务器上的函数。 以下是使用`mongoTemplate`调用名为`archive_data`的MongoDB函数的基本步骤: 1. 首先,你需要构建一个函数调用对象,该对象包含了要调用的函数名以及参数列表。 2. 然后,使用`mongoTemplate`的相关方法执行该调用。 这里提供一个使用`callFunction`方法的例子,假设`archive_data`函数已经定义好,并且接受三个参数(例如:一个日期对象、一个文档对象和一个布尔值): ```java import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.BasicQuery; // ... 其他导入 ... public void callArchiveDataFunction(MongoTemplate mongoTemplate) { // 准备参数 Date dateParam = ...; // 第一个参数,例如日期对象 Document docParam = ...; // 第二个参数,例如文档对象 Boolean boolParam = ...; // 第三个参数,例如布尔值 // 创建函数调用对象 String functionName = "archive_data"; List<BasicDBList> params = new ArrayList<>(); params.add(new BasicDBObject("$date", dateParam)); params.add(new BasicDBObject("$document", docParam)); params.add(boolParam); DBObject functionArgs = new BasicDBObject("args", params); // 调用函数 DBObject result = mongoTemplate.getCollection("function.collection").getDB().command( new BasicDBObject("eval", functionArgs) ); // 处理结果 // ... } ``` 注意,`mongoTemplate`并没有直接的`invoke`或`callFunction`方法,所以通常我们使用`command`方法来执行MongoDB的原生命令,如上述的`eval`命令。在上面的代码,你需要替换`...`占位符为实际的参数值。 这个例子假设你使用的是MongoDB的原生命令来调用函数,而具体的函数实现和参数可能会有所不同。你需要根据实际的函数定义来调整参数和调用方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值