春季交易多线程问题
我正在从事付款服务,然后在付款过程中扣除产品的股票扣除问题。我还没有弄清楚如何为整体应用程序和分布式应用程序做到这一点。
我正在使用以下技术堆栈;
- MySQL数据库
- Spring Boot框架
- Hikari连接池
这些是实体。
付款实体,
@Entity
public class Payment {
private static final int HASH = 17;
@Id
@GeneratedValue
private Long id;
private BigDecimal price;
private String bankResponse;
private String integrationId;
产品实体,
public class Product {
private static final int HASH = 29;
@Id
@GeneratedValue
private Long id;
private String name;
private String description;
private Integer remainingStockCount;
private BigDecimal price;
@Version
private Integer version;
就我而言,
要求是
- 产品不应以其股票的价格出售。
- 如果股票耗尽,则同时支付同一产品的客户不应购买该产品。 (即,如果剩下2个股票,并且有3个客户同时付款,那么第一个成功应该购买该产品,而第三个客户应以适当的消息失败。)
- 也有一种异步方法可以在扣除股票之后或之前向银行付款,请考虑。
我在那里有很多问题,
- 对于单层应用,我认为我可以通过春季交易注释来实现它,但是我们需要考虑使用多线程的解决方案,所以我不知道我应该如何扣除特定产品的库存,对于我上面提到的情况(第二种情况)。
- 对于分布式解决方案,我们有很多服务器,并且在类似服务器的容器中运行的许多应用程序,如果我使用春季交易式注释,我是否会错过某些内容,或者如何为分布式系统进行操作?
为了简单起见,我在这里打破了测试方法。
private void runPaymentForProduct(ProcessType processType) {
List<CompletableFuture> futures = new ArrayList<>();
for (int i = 0; i < 15; i++) {
CompletableFuture future = getFuture();
futures.add(future);
}
futures.forEach(f -> CompletableFuture.allOf(f).join());
}
private CompletableFuture getFuture() {
return CompletableFuture.runAsync(() -> {
productPaymentService.pay(ID);
}
);
}
- 我有付款方式并添加了 @transactional注释,但是在这种交易方法中也称为异步方法,因此我无法解决如何和谐地适应这两个过程。
- 我不想使用一些并发收集或锁定机制(读取锁定,同步),因为即使我们使用单片应用程序,我们也可以扩展服务器,这就是为什么如果我们实现了基于单个服务器的解决方案,则不始终如一地与其他服务器一起使用。
- 顺便说一句,如果我们实现此解决方案,我们假设有100个用户称此端点为此,因此我们需要公平对待每个用户,并立即给出响应。
- 当您回答这些问题时,可以将解决方案分为两个部分,例如单片和分发?
- 顺便说一句,我无法更改Hikari CP的设置,例如增加连接的超时或增加池的大小,因为我们需要通过其他方式解决。
- 我正在使用一种乐观的锁定机制,这就是为什么产品实体具有@version注释的原因。
I'm working on Payment Services, then I have an issue with the product's stock deduction during the payment process. I haven't figured out how I will do this for monolithic apps and distributed apps.
I'm using the below technology stacks;
- MySQL Database
- Spring Boot Framework
- Hikari Connection pool
These are entities.
Payment entity,
@Entity
public class Payment {
private static final int HASH = 17;
@Id
@GeneratedValue
private Long id;
private BigDecimal price;
private String bankResponse;
private String integrationId;
Product Entity,
public class Product {
private static final int HASH = 29;
@Id
@GeneratedValue
private Long id;
private String name;
private String description;
private Integer remainingStockCount;
private BigDecimal price;
@Version
private Integer version;
In my case,
Requirement is,
- A product should not be sold for more than its stock.
- Customers paying for the same product at the same time should not buy the product if the stock is depleted. (i.e. if there are 2 stocks left and 3 customers pay at the same time, first 2 successful should buy the product and the 3rd one should fail with an appropriate message.)
- There is an async method to pay to the bank also after or before stock's deduction, please also consider it.
I have a lot of questions in there,
- For a monolithic application, I think I can achieve it with Spring Transactional annotation, but we need to consider the solution with multithreading, so I don't know how I should deduct stock of the specific product, for the case that I mentioned above (second case).
- For the distributed solution, we have a lot of servers, and a lot of applications that run in the server-like container, if I use Spring Transactional annotation, do I miss something or how can I do it for distributed systems?
For the sake of simplicity, I'm breaking my test methods here;
private void runPaymentForProduct(ProcessType processType) {
List<CompletableFuture> futures = new ArrayList<>();
for (int i = 0; i < 15; i++) {
CompletableFuture future = getFuture();
futures.add(future);
}
futures.forEach(f -> CompletableFuture.allOf(f).join());
}
private CompletableFuture getFuture() {
return CompletableFuture.runAsync(() -> {
productPaymentService.pay(ID);
}
);
}
- I have the payment method and added @Transactional annotation, but there is also an async method which is called in this transactional method, so I couldn't solve how I adapt these two processes harmonically.
- I didn't want to use some concurrent collections or locking mechanism (read-write lock, synchronized) because even if we use monolithic applications, we can scale out our servers, that's why if we implement the single server-based solution, then it doesn't work with other servers together consistently.
- By the way, if we implement this solution, let's assume that there are 100 users who call this endpoint, so we need to treat each user fairly, and give a response immediately.
- When you answer these, could you please split the solution into two parts such as monolithic and distributed?
- By the way, I can't change Hikari cp's settings like increasing the connection's timeout or increasing's the pool's size because we need to solve with other ways.
- I'm using an optimistic lock mechanism, that's why Product Entity has @Version annotation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以查看发件箱模式,以防分布式架构以原子方式执行事务并获得最大保证,
请参阅:https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/
对于整体,我觉得你应该有应用程序事件将按顺序运行。
You can have a look at Outbox pattern in case of distributed archiecture to do the transactions atomically and have maximum guarantees
refer: https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/
For monolithic still I feel you should have application events that will be run in a sequence.