过度依赖开发框架_过度依赖框架有时会带来问题

过度依赖开发框架

Today when we are thinking of Java, most of the time, we directly think to SpringBoot with Hibernate and all the frameworks helping us to go faster in our coding: spring initializr, module choice, 3 lines of code and 10 annotations and you can test your first application.But this magic side also comes with some drawbacks.The issue is when you delegate everything to a framework, at some point you can lose control and detect problems that will take you days to be understood and fixed. We are going to talk about Java and Spring, just to have a practical example, but the same can happens on different frameworks or languages.

今天,当我们想到Java时,大多数时候,我们会直接想到带有Hibernate的SpringBoot和所有有助于我们加快编码速度的框架: spring initializr ,模块选择,三行代码和10个注释,您可以进行测试您的第一个应用程序。但是这个神奇的一面也有一些缺点。问题是当您将所有内容委派给框架时,在某些时候您可能会失去控制并检测到需要花费几天时间才能理解和解决的问题。 我们将讨论Java和Spring,只是为了提供一个实际的示例,但是在不同的框架或语言上也会发生同样的情况。

In this post, we would like to share with you how on a long live application — developed and maintained over several years from many different developers over time — new features can break everything. Or on the other hand, how with a simple annotation you can couple an external API performance to your application and make it unstable — or completely broken — until the external service comes back.

在本文中,我们想与您分享如何开发一个长期使用的应用程序 -由许多不同的开发人员随时间开发并维护了数年的时间-新功能会破坏一切。 或者,另一方面,如何通过简单的注释 将外部API性能耦合到应用程序,并使其变得不稳定(或完全损坏),直到外部服务返回。

应用环境 (Application Context)

The application we will use for this post is a payment proxy, but it will be the same with any proxy application making an external call and having internal logic too.

我们将在这篇文章中使用的应用程序是付款代理,但是任何进行外部调用并具有内部逻辑的代理应用程序都将相同。

Image for post
Payment Proxy Application - Functional Architecture
付款代理应用程序-功能架构

Imagine something like in the previous image:

想象一下上图中的内容:

  • customers make an API call to the application to make a payment.

    客户对应用程序进行API调用以进行付款。
  • We need to store this information! In case of a network problem, payment outage, or whatever, we need to save the request and its status somewhere. Lets put it in the local database.

    我们需要存储此信息 ! 如果出现网络问题,付款中断或其他原因,我们需要将请求及其状态保存在某处。 让我们将其放在本地数据库中。

  • based on the call, the proxy will trigger the right payment method, which is outside the application stack & infrastructure

    根据调用,代理将触发正确的付款方式,该方式不在应用程序堆栈和基础架构中

  • The answer is provided to the original client.

    答案将提供给原始客户端。

That’s easy, isn’t it?

很简单,不是吗?

Following we propose a story of the LOTR ltd company implementing this payment proxy.

接下来,我们提出一个有关LOTR ltd公司实施此付款代理的故事。

首次实施 (First Implementation)

With the provided information the first dev team started with a simple application with the following main components:

利用所提供的信息,第一个开发团队从一个简单的应用程序开始,该应用程序包含以下主要组件:

  • A controller exposing a /payment API

    控制器公开/ payment API

PaymentController
PaymentController
  • A service storing information into the database

    将信息存储到数据库中的服务
TransactionService
交易服务
  • A rest service to make the external HTTP call

    进行外部HTTP调用的Rest服务
PaymentService
PaymentService

随着时间的推移增强 (The enhancements over time)

Application is used in the LOTR ltd for several years, and developers come over it one after one. In 10 years the LOTR ltd had 50/60 developers working on the project codebase, adding stuff, refactoring, … and for sure sometime some bugs were detected requiring a quick fix.

该应用程序在LOTR ltd中使用了数年,开发人员将其一遍又一遍。 在10年的时间里, LOTR ltd有50/60个开发人员在项目代码库上工作,添加了东西,进行了重构,……并确定有时会发现一些需要快速修复的错误。

Step 1: Save all transactionsThe first version of the application was working but, during the first months, the dev team discovered this was not so useful. All started a week after the go-live; a customer called the LOTR ltd support center:

第1步:保存所有交易该应用程序的第一个版本正在运行,但是在最初的几个月中,开发团队发现这并不是那么有用。 一切都在上线一周后开始; 称为LOTR ltd支持中心的客户:

C: “Good morning, I made an order 5 minutes ago but I had problems with the payment, can you help me, please?”.D: “Good morning, thanks for calling. I can see nothing here about your order, but I will check with our technical team and I call you back in few minutes.”

C:“早上好,我5分钟前下了订单,但是付款有问题,请您帮我吗?” D:“早上好,谢谢您的来电。 我在这里看不到有关您的订单的任何信息,但是我会与我们的技术团队联系,并在几分钟后给您回电。”

Unfortunately even the technical team was not able to see anything about the payment transaction. But after a few hours, they got it: the production code is saving the payment only if the REST service is returning a success response. In any other case, the payment proxy is answering with an error (or at least something that is interpreted as an error by the caller) but the status gone.

不幸的是,即使是技术团队也无法看到有关付款交易的任何信息。 但是几个小时后,他们就明白了:仅当REST服务返回成功响应时,生产代码才保存付款。 在任何其他情况下,付款代理都在回答错误(或至少被呼叫者解释为错误的某件事),但状态消失了。

After a few hours the dev team came up with a fix:

几个小时后,开发团队提出了一个解决方案:

Right now the transaction is stored with status before to make a call and then updated with the payment provider response. Great job guys.

现在,该交易将存储状态,然后再进行呼叫,然后使用付款服务提供商的响应进行更新。 干得好。

Step 2: Last transaction status not updatedDuring the first hours after the fix the support center called back the technical team:

步骤2:上次交易状态未更新在修复后的最初几个小时内,支持中心致电技术团队:

C: Good morning, we are checking the latest orders but it seems payment are not processed. All the payment are in PROCESSING state. Is there any problem on your side?

C:早上好,我们正在检查最新的订单,但似乎付款没有处理。 所有付款都处于处理中状态。 你这边有什么问题吗?

Indeed, the dev team made a quick fix to the application but something was not correctly tested.After a while, they understood where was the problem: the transaction status was changed outside a Transactional method and nothing was persisted on the database. So, new fix was provided just some minutes after and all was finally working as expected.

的确,开发团队对应用程序进行了快速修复,但未对某些内容进行正确的测试。一段时间后,他们了解问题出在哪里:事务状态在Transactional方法之外进行了更改,并且数据库上没有任何内容持久化。 因此,几分钟后就提供了新的修复程序,所有修复程序均按预期工作。

世界末日 (The Armageddon Day)

In the LOTR ltd all worked well for years and the dev team goes ahead adding new functions, new payment methods for the customer, improving the code, ... But one day, with no reason, the application started to crash, reboot loop, … and no way to understand for the dev team how to make it stable.

LOTR ltd中,所有设备都运作了好几年,开发团队继续为客户添加新功能,新付款方式,改进代码,但是……有一天,应用程序无缘无故崩溃,重新启动循环, ……而且没有办法让开发团队了解如何使其稳定。

Are you able to point out where the LOTR ltd dev team made an error in the previous Gist?

您是否可以指出LOTR ltd开发人员团队在先前的Gist中出错的地方?

Years after the first line of code there is no dev from the first team to help knowing why some things were added and how everything is working. Fixing this problem could take very long.

在第一行代码发布多年之后,第一团队就再也没有开发人员来帮助您了解为什么添加了某些内容以及一切工作方式。 解决此问题可能需要很长时间。

问题与解决方案 (Problem and solution)

We can now get out of our story and I’m pretty sure that anyone of you found something already seen in real life. That’s the life of almost all the IT projects and the reason we need proper documentation, a tested and readable code. But this is another story…

现在我们可以摆脱我们的故事了,我可以肯定的是,你们当中任何人都发现了现实生活中已经看到的东西。 那就是几乎所有IT项目的生命,也是我们需要适当的文档,经过测试的可读代码的原因。 但这是另一个故事……

The problem comes from the Transactional annotation. We moved it on the method calling the external payment service which is causing to keep the database connection until the end of the method.This is automatically coupling the performance of our payment proxy to the ones of the external services: when a payment provider starts to answer slower to our HTTP calls, the database connection is kept for a longer time and the connection pool will be exhausted due to the high traffic.Usually, the database should provide a response in only a few milliseconds, up to seconds for complex queries; for this reason, in a “standard” architecture, the configuration allows creating a database connection pool with only 10 or 20 connections, that should be enough for hundreds of concurrent API calls per seconds.

问题来自事务注释。 我们将其移动到调用外部支付服务的方法上,该方法导致保持数据库连接直到方法结束。 这自动我们的付款代理的性能与外部服务的性能结合 :当付款提供商开始较慢地响应我们的HTTP调用时,数据库连接将保留更长的时间,并且由于通常,数据库应仅在几毫秒内提供响应,对于复杂的查询,响应时间最多为几秒钟; 因此,在“标准”体系结构中,该配置允许创建仅包含10个或20个连接的数据库连接池 ,这应该足以每秒执行数百次并发API调用。

To fix the problem we just need to manage differently the Transaction object update and the HTTP Call.

为了解决这个问题,我们只需要不同地管理事务对象更新和HTTP调用。

Image for post
Gatling Load Test
加特林负荷测试

In the previous image, we can see what is happening during a high load period when one of the external services became slower, on the left, is before the fix, and on the right is after.Most of the calls in the application are waiting to have a database connection to create the payment transaction and then call the provider. Due to the fact database connection is not released until the end of the process, requests are queued and wait until the HTTP connection timeout. The result is an error on the client-side with most of the payments in error.After the fix, the application works normally: calls are coming, transaction created and stored into the database — with the connection released just a few milliseconds after the operation — and then the HTTP call is made waiting for the response. As the service is slower we need to wait longer and, with this implementation, we are blocking an HTTP thread until the final client answer. But on this part of the architecture, there are usually more resources available than on the database side.

在上图中,我们可以看到高负载期间发生的情况是当外部服务之一变慢时,左侧是修复之前,右侧是之后。应用程序中的大多数调用都在等待建立数据库连接以创建付款交易,然后致电提供商。 由于事实上直到过程结束才释放数据库连接,因此请求排队并等待HTTP连接超时。 结果是客户端出现错误,大部分付款都出错了。修复后,该应用程序正常运行:调用即将来临,创建了事务并将其存储到数据库中-连接在操作后仅几毫秒就被释放—然后进行HTTP调用以等待响应。 由于服务速度较慢,我们需要等待更长的时间,并且通过此实现,我们将阻塞HTTP线程,直到最终的客户端应答为止。 但是在体系结构的这一部分,通常有比数据库方面更多的可用资源。

Image for post
Database connection usage time
数据库连接使用时间

This is what is happening in the application before and after the fix: the mean connection usage time is drastically decreased. And it is coming to the normal values we should except for a database connection.

这是修复前后应用程序中发生的事情:平均连接使用时间大大减少了。 除了数据库连接,它已经达到正常值。

结论 (Conclusion)

This is a very simple application we created to show you a possible problem. It was easy to understand with few lines of code where was the problem and how to fix it, but in the real-life, this kind of application can have several services linked to each other, and very long classes that will take a long time to be debugged.There are also several other improvements you can add here and I know you are thinking of them. ASYNC HTTP calls? Yes sure, a proxy should always use async… even if this will consume much memory. At a time resources will be limited somewhere :) Immutability of the object? Too! When objects can be modified everywhere in your code, it will be difficult to debug.

这是我们创建的一个非常简单的应用程序,用于向您显示可能的问题。 只需几行代码就可以容易地理解问题所在和解决方法,但是在现实生活中,这种应用程序可以将多个服务相互链接,而且很长的类将花费很长时间您还可以在此处添加其他一些改进,我知道您正在考虑这些改进。 ASYNC HTTP调用? 是的,代理应该始终使用异步…即使会消耗大量内存。 一次资源将被限制在某处:)对象的不变性? 太! 当可以在代码中的任何地方修改对象时,将很难调试。

The lesson learned here is that even when things seem magical, developers should anywhere take care of what is globally happening. With a simple overview, we can understand that the application is crashing due to the missing database connections, but we have to go further to be able to understand what and then find the why and where. This can take a very long time.

从这里吸取的教训是,即使事情看起来很神奇,开发人员也应该在任何地方照顾全球正在发生的事情 。 通过简单的概述,我们可以了解由于缺少数据库连接而导致应用程序崩溃,但是我们必须走得更远才能了解什么,然后找到原因和位置。 这可能需要很长时间。

If you are interested in playing with what described here, a sample application is available over this GitHub repository:

如果您有兴趣使用此处描述的内容,可以在以下GitHub存储库上找到示例应用程序:

  • The persist_problem branch contains the application after the first fix: storing transaction on every call and then update the status (which is not stored in the database).

    第一次修复后, persist_problem分支包含应用程序:在每次调用时存储事务,然后更新状态(未存储在数据库中)。

  • The fix_transactional branch contains the working but BSODed version

    fix_transactional分支包含工作但已蓝屏的版本

  • The main branch contains a working final version

    分支包含工作中的最终版本

翻译自: https://medium.com/decathlondevelopers/how-relying-too-much-on-a-framework-can-sometimes-introduce-issues-9d7fca107543

过度依赖开发框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值