我有一个关于对象实例化的问题。现在,我建立了一个相当大的应用程序,在后端实现了RESTful端点。
我想知道对象实例化的“最佳”方法是什么,以及我所做的工作如何影响垃圾收集器。
例如以下内容:
const MyClass = require('../controllers/myClassController');
const router = require('express').Router();
router.get('/', (req, res) => {
console.log('GET request at api/someEndPoint');
const myClass = new MyClass();
myClass.doSomePromise()
.then(() => {
res.end();
})
.catch((err) => {
res.status(500).end();
});
});
在此,在路由路径实现内部实例化一个对象。
现在,以这个为例:
const MyClass = require('../controllers/myClassController');
const router = require('express').Router();
const myClass = new MyClass();
router.get('/', (req, res) => {
console.log('GET request at api/someEndPoint');
myClass.doSomePromise()
.then(() => {
res.end();
})
.catch((err) => {
res.status(500).end();
});
});
在此,对象在路由路径实现之外实例化。在我看来,这将在应用程序的生命周期中无限期持续,而在第一个示例中,变量
myClass
将被清除。所以我真的很想知道:
我在想这是正确的方法吗?
我为什么应该选择一种方法而不是另一种方法?好像我选择了选项2,我不妨只对类本身做
static
方法...每种方法有哪些一般优点/缺点?
我觉得自己正在为未正确清理事物而战,其中一种方法在交通量大,避免比赛条件等方面要比另一种方法好!
最佳答案
这真的很简单。
如果在整个应用程序期间仅需要一个对象,并且可以将该对象用于恰好需要使用该对象的任何请求,则它是“应用程序周期”类型的对象。在初始化时创建一个实例,存储其实例(可能作为模块级变量)并从那时起使用它。例如,如果您设计了某种缓存对象,供许多请求处理程序使用,那么您将需要创建一次该缓存对象,然后保留该缓存对象,并在每次请求时都使用同一对象使用它的处理程序。作为另一个示例,某些应用程序在启动时会创建与其主数据库的一个连接,然后所有其他请求都可以使用该连接。
如果每个可能使用它的单个请求都需要一个新对象,则在请求处理程序内创建它,并让普通垃圾回收在完成请求后负责清理它。例如,如果您需要创建一个Date()
对象以测量请求期间的持续时间,则最简单的实现将在请求处理程序中创建一个new Date()
对象,将其用于需要使用的对象,然后请求完成后,让GC负责。
如果每个请求都需要一个独立的对象,以便同时“进行中”多个请求处理程序时,则需要确保每个请求处理程序都有自己的对象,并且没有一个共享或冲突的。
还有一些混合模型(通常通过这种方式作为性能优化来完成),您可以在其中创建一个对象池,然后每个需要一个对象的请求都可以“签出”,使用一段时间,然后在有条件时将其返回到池中完成。当一小部分对象通常可以满足服务器的需求时,这通常可以节省从头开始重建对象的时间。某些数据库使用此“池”模型进行数据库连接。该请求可能只是从池中获取数据库连接,进行查询,然后将连接返回到池中,而不是为每个要查询的请求创建与数据库的新连接。
在node.js内部,它为磁盘I / O实现的某些部分使用线程池。类似的概念。
我想知道对象实例化的“最佳”方法是什么,以及我所做的工作如何影响垃圾收集器。
最好的方法是选择与用法匹配的实例化模型。在上面的选项1中,对象可能会在整个应用程序周期中存储,并且根本不会被存储。在选项2和3中,将为每个请求创建对象并对其进行GC处理。您不必担心创建和使用对象,然后让其成为GC。这就是该语言旨在用于没有持久持久状态的事物的方式,并且应该保留更长的时间。
我为什么应该选择一种方法而不是另一种方法?好像我选择了选项2一样,我不妨只对类本身进行静态方法...
如果您在对象中没有特定于请求的状态,并且如果有多个请求处理程序尝试同时使用该对象,则对象本身的状态也不会遇到麻烦,那么您实际上只有一个全局服务,即任何请求想要使用它的处理程序可以使用它。如果您将其实现为创建的对象,存储该对象的实例然后在该实例上调用传统方法,或者将其实现为实例化自身的更多单例,并且仅在其上调用函数或静态方法,则取决于您。那只是编码风格上的差异,而不是功能上的差异。
每种方法有哪些一般优点/缺点?
通常,您需要尽可能多的封装和本地状态。无需将特定于请求的内容放入共享全局变量中。针对特定请求进行计算或计算出的内容属于请求处理程序逻辑的内部。因此,只需按照上面的四个问题来确定对象是属于请求处理程序本地对象还是在更高范围内共享。如果您可以通过将事情留在本地来实现目标,那么通常它会更清洁,更安全,更灵活。
我觉得自己正在为未正确清理事物而战,其中一种方法在交通量大,避免比赛条件等方面要比另一种方法好!
GC会为您进行清理。您可以依靠它。只要您不将对对象的引用永久存储在具有持久作用域的变量中,垃圾回收将为您很好地进行清理。这就是设计使用该语言的方式。您不应该担心它。如果,也许您来自诸如C或C ++之类的非GC语言,那么您可能会考虑过度。 GC在那里有你的朋友。非常非常非常偶尔地,您可能需要进行一些性能调整,以更加了解您要求GC进行多少操作,但这几乎永远不会成为您担心的事情,直到您真正发现自己拥有某些东西为止需要以这种方式进行特殊优化。
避免请求处理程序之间出现竞争状态的最简单方法是不在请求处理程序之间共享任何状态。如果处理给定请求所需的一切都在本地创建和使用,并且从未对其他任何请求处理程序可用,那么您将绝对避免在多个请求处理程序之间发生争斗或共享条件。通常,从那里开始,因为那是最简单和最安全的。然后,如果发现需要对性能进行优化,则可以探索某些类型对象的共享或缓存,并且您将必须谨慎进行以免引起共享问题。但是,您很少会开始尝试这样做,因为大多数时候不需要额外的复杂性。
关于javascript - 路由端点内部/外部的Node.js,Express和对象实例化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47664079/