node.js批量拷贝文件
This article covers how to deliver messages to app users automatically and in bulk with a Node.js runtime service. These messages will be formatted with Markdown, a popular lightweight markup language that is ideal for formatting bulks of text.
本文介绍如何使用Node.js运行时服务自动向应用程序用户批量发送消息。 这些消息将使用Markdown格式化, Markdown是一种流行的轻量级标记语言,非常适合格式化大量文本。
A GitHub repository coinciding with this article has been set up here that outlines the project structure and acts as boilerplate for the reader to build their own runtime service.
此处已建立了与本文相符的GitHub存储库,该存储库概述了项目结构,并为读者提供了构建自己的运行时服务的样板。
The message delivery terminology used throughout this piece can be interchanged with message generation, as this service will essentially be generating messages from templates that are personalised with parameters that we’ll define. The generated messages are then stored in a database, ready for the user to read. There will be an opportunity to notify the user of their new message using services (such as APN or email) that will be briefly covered also.
贯穿本文使用的消息传递术语可以与消息生成互换,因为该服务本质上将从根据我们将定义的参数进行个性化的模板生成消息。 然后将生成的消息存储在数据库中,以供用户阅读。 有机会使用服务(例如APN或电子邮件)将新消息通知用户,这也将简要介绍。
Notably, there must be some requirements met before generating a message for a user, such as recently registering an account in order to receive an account welcome message. We also must ensure that the user has not already received the message as to not send any duplicates. Concretely, each type of message has different sending requirements that we can represent as metadata in any given database, that must be tied directly into the runtime service logic.
值得注意的是,在为用户生成消息之前必须满足一些要求,例如最近注册一个帐户以接收帐户欢迎消息。 我们还必须确保用户尚未收到该消息,以便不发送任何重复项。 具体来说,每种消息都有不同的发送要求,我们可以将其表示为任何给定数据库中的元数据,这些元数据必须直接绑定到运行时服务逻辑中。
This piece acts as a natural follow-on to my piece on creating an in-app inbox for React Native. By combining the solution in that article with the delivery mechanism from this one, you will have a solid automated messaging service to build upon. Read that article here: React Native: Creating an In-App Inbox.
这是我为React Native创建应用内收件箱文章的自然后续。 通过将该文章中的解决方案与本文中的传递机制结合起来,您将可以构建可靠的自动消息传递服务。 在此处阅读该文章: React Native:创建应用内收件箱 。
邮件类型及其传递要求 (Message types and their delivery requirements)
To make the most of this automated communication between your app and the user, focus on particular message types that will compliment the user experience. Send welcoming and helpful messages to the user, as well as messages to keep them up to date with the state of the app.
为了充分利用您的应用程序和用户之间的这种自动通信,请专注于特定的消息类型,这些消息类型将有助于改善用户体验。 向用户发送欢迎和有用的消息,以及使消息与应用程序状态保持最新的消息。
As mentioned above, each message to be sent will coincide with a trigger, or a set of requirements that need to be met in order for the message to be sent. These requirements are persisted as metadata in your database and will vary depending on the message being sent. With this in mind, consider the following use cases and their delivery requirements:
如上所述,每个要发送的消息将与触发器或一组要满足的条件一致,以便发送消息。 这些要求将作为元数据保留在数据库中,并且将根据发送的消息而有所不同。 考虑到这一点,请考虑以下用例及其交付要求:
Welcome message: A welcome message adds a human touch to your app, that will be triggered once a new user registers a new account and signs in for the first time. Another requirement may be email verification to prove that the account is indeed human and reachable via external communication. A “how-to” message explaining how to use your app can also be sent at this time.
欢迎消息:欢迎消息为您的应用程序增添了人情味,一旦新用户注册了新帐户并首次登录,便会触发该消息。 另一个要求可能是电子邮件验证,以证明该帐户确实是人类的,并且可以通过外部通信来访问。 此时也可以发送“操作方法”消息,说明如何使用您的应用程序。
Critical updates: Automated payment receipts, trade breakdowns and portfolio analysis give users confidence in the field of finance, where key tasks directly related to the user’s finance are effectively bought to the user’s attention. This type of messaging are more akin to detailed notifications than personalised messages, but are a great way to utilise our automated delivery service. The requirements for this message type to be delivered will be key executions such as payments being made or trades executed.
重要更新:自动付款收据,交易明细和资产组合分析使用户对财务领域充满信心,在该领域中,与用户财务直接相关的关键任务被有效地吸引了用户的注意。 这种类型的消息传递比个性化消息更类似于详细的通知,但是是利用我们的自动传递服务的一种好方法。 传递此消息类型的要求将是关键执行,例如付款或执行交易。
Key service additions: We all get excited when rolling out a new app feature, and it’s fun to share that excitement with your users. Our message delivery service will make it possible to deliver such messages to all users automatically and effortlessly. There are no requirements to delivering this message type — it would have to be triggered via your admin panel — coding the CMS side of your ecosystem is outside the scope of this piece.
关键服务新增功能:推出新的应用程序功能时,我们都会感到兴奋,并且与您的用户分享这种乐趣非常有趣。 我们的消息传递服务将使自动将此类消息传递给所有用户成为可能。 传递此消息类型没有要求-必须通过管理面板触发-对生态系统的CMS端进行编码不在本文的讨论范围之内。
Reminders: Messages reminding the user that they have to do something within the app will draw their attention to the task at hand. This is more effective when the user has pre-configured reminder settings, ensuring that they agreed to receive such reminders before allowing messages to be sent that would otherwise be seen as spamming.
提醒:提醒用户必须在应用程序中执行某些操作的消息将引起他们对当前任务的注意。 当用户预先配置了提醒设置时,这种方法会更有效,以确保在允许发送邮件之前,他们同意接收此类提醒,否则这些邮件将被视为垃圾邮件。
Message duplication (as to not send the same message twice) is a global requirement that we’ll address further down with meta data of key value pairs to persist whether a certain message type has been delivered to a particular user.
消息重复(不要重复发送同一条消息)是一项全局要求,我们将进一步处理键值对的元数据,以持久保存某种消息类型是否已交付给特定用户。
These key use cases are by no means an exhaustive list, but should give the reader some intuition on how to further leverage the service with their own custom message types.
这些关键用例绝不是详尽的清单,但应使读者对如何通过自己的自定义消息类型进一步利用服务有一些直觉。
引入不确定的运行时过程 (Introducing the Indefinite Runtime Process)
A Node.js runtime service is designed to run indefinitely and with its logic to be executed at a set interval — a suitable timeframe of which could be every minute to every hour for non-critical message delivery.
Node.js运行时服务旨在无限期运行,并且其逻辑将按设定的时间间隔执行-对于非关键的消息传递,其合适的时间范围可能是每分钟到每小时。
For some initial context and clarity as to how the service functions, check out the following screencast of the service running in a Terminal window. The “new account welcome” message is delivered to 7 newly registered accounts when the service is executed:
为了初步了解该服务的功能,请查看以下在终端窗口中运行的服务的截屏视频。 执行该服务时,“欢迎新帐户”消息传递到7个新注册的帐户:
The runtime service delivering new account welcome messages in one interval execution. 运行时服务在一个间隔执行中传递新帐户欢迎消息。This service run repeatedly, each time checking whether accounts are eligible to receive a particular message. This is achieved in Javascript with the setInterval
function, that allows us to provide a duration between each execution, as well as the execution logic as a function.
该服务反复运行,每次检查帐户是否有资格接收特定消息。 这是通过Java中的setInterval
函数实现的,该函数使我们能够提供每次执行之间的持续时间,以及执行逻辑作为一个函数。
This indefinite loop is simply achieved in JavaScript with setInterval
:
这个不确定的循环很setInterval
在JavaScript中使用setInterval
实现:
// `setInterval` loop executing service every 30 minutessetInterval(() => {
sendPendingMessages();
}, 1000 * 60 * 30);
with the service logic itself being embedded in sendPendingBulkMessages()
.
服务逻辑本身嵌入在sendPendingBulkMessages()
。
We can also see how setInterval
has been used in the GitHub project within the auto-messages.js
service module:
我们还可以在auto-messages.js
服务模块中查看GitHub项目中如何使用setInterval
:
// calling module service function from its `init` functioninit: async function () {
module.exports.generateWelcomeMessages();
setInterval(async function () {
module.exports.generateWelcomeMessages();
}, interval);
}
By design, setInterval
carries out its first execution after the defined interval duration. For this reason, the above example calls generateWelcomeMessages()
before setInterval
is defined to immediately kick off the service.
根据设计, setInterval
在定义的间隔持续时间之后执行其第一次执行。 因此,上面的示例在定义setInterval
之前调用generateWelcomeMessages()
,以立即启动服务。
Once the interval and service is defined, its entry file can then be run indefinitely as a PM2 process either on your local machine or server. As long as the this process is active, the interval will continue indefinitely.
定义间隔和服务后,即可在本地计算机或服务器上将其条目文件作为PM2进程无限期运行。 只要此过程处于活动状态,时间间隔将无限期继续。
// starting service as a background process with pm2pm2 start service.js --name 'auto-messages'
This Node.js as a service paradigm will be leveraged in this article to generate and deliver messages reliably. Before we delve into the code itself, let’s briefly cover some message types and general use cases to more effectively leverage the service.
本文将利用此Node.js即服务范例来可靠地生成和传递消息。 在深入研究代码本身之前,让我们简要介绍一些消息类型和一般用例,以更有效地利用服务。
项目设置和文件结构 (Project Setup and File Structure)
This section outlines the file structure and setup surrounding the indefinite loop that will be calling the auto messaging service every 30 minutes. To conform to a modular style of coding, the message delivery service will be split into three folders: data/
, services/
and templates/
:
本节概述了围绕不确定循环的文件结构和设置,该无限循环将每30分钟调用一次自动消息传递服务。 为了符合模块化的编码风格,邮件传递服务将分为三个文件夹: data/
, services/
和templates/
:
The above diagram is reflected in the file structure of the project. This can be seen on GitHub from the repository’s top level:
上图反映在项目的文件结构中。 这可以在GitHub上从存储库的最高层看到:
// file structure of runtime servicedata/
Accounts.js
services/
auto-messages.js
templates/ welcome.js
init.js
mongo.js
package.json
...
These 3 folders to keep modules logically organised. Breaking them down in more detail:
这3个文件夹用于按逻辑组织模块。 将它们更详细地分解:
The
data/
folder stores modules that fetch data from a database. This project adopts MongoDB as its database. TheAccounts
module, for example, will host functions relating to user data management, such as fetching the accounts that are eligible for messages, and updating the account metadata to signify that messages have been sent.data/
文件夹存储了从数据库中获取数据的模块。 该项目采用MongoDB作为数据库。 例如,“Accounts
模块将托管与用户数据管理有关的功能,例如获取符合消息条件的帐户,并更新帐户元数据以表示已发送消息。The
services/
folder holds the actual services that will be running, each of which will have their own indefinite loop and thus being subject to their own durations. These modules can be seen as the controllers of the service.services/
文件夹包含将要运行的实际服务,每个服务都有其自己的不确定循环,因此要受其自身持续时间的影响。 这些模块可以看作是服务的控制器。The
templates/
folder houses each message template, formatted in Markdown. For consistency, each template exists as a module export, just like the other components of the service. We will personalise these templates by passing arguments to the module that will in turn populate template placeholders.templates/
文件夹包含每个邮件模板,格式为Markdown。 为了保持一致性,每个模板都作为模块导出存在,就像服务的其他组件一样。 我们将通过向模块传递参数来个性化这些模板,该模块又将填充模板占位符。
These Data, Service and Template building blocks are closely aligned to the well-known Model View Controller (MVC) design pattern, so this setup should feel quite natural to the reader.
这些数据,服务和模板构建块与众所周知的 模型视图控制器(MVC) 设计模式 紧密结合 ,因此对于读者来说,这种设置应该是很自然的。
In addition, the mongo.js
file simply provides a static connection that each module can use to connect to a singular database instance.
另外, mongo.js
文件仅提供了一个静态连接,每个模块都可以使用该连接来连接到单个数据库实例。
使用init.js初始化服务 (Initialising the service with init.js)
Services will be imported into init.js
, that acts as the entry file of the runtime. It is in this file that services are imported and initialised, along with a single database instance. For this walkthrough init.js
will only include one service, being our automated message service:
服务将被导入init.js
,它充当运行时的入口文件。 正是在此文件中,服务和单个数据库实例一起被导入和初始化。 对于本演练, init.js
将仅包含一项服务,即我们的自动消息服务:
// `init.js` service entry fileconst Service_AutoMessages = require('./services/auto-messages');async function init () { // start services
Service_AutoMessages.init(); // more services could be started here...
}init();
This entry file is the file run by node or nodemon, or a process manager such as pm2, when starting the process. It is the single point of entry into the program.
此条目文件是 在启动进程时 由node或 nodemon 或进程管理器(例如 pm2) 运行的文件 。 这是 进入程序 的 单点 。
The sections to follow will implement the welcome message type by walking through each of the 3 types of files— the template file, data file and service file respectively.
接下来的部分将通过逐步浏览三种文件类型(分别是模板文件,数据文件和服务文件)来实现欢迎消息类型。
服务实施 (Service Implementation)
With the project structure understood, reviewing the three sections should now be a breeze. Let’s explore the modules defined for the welcome message.
在了解了项目结构之后,现在轻而易举地回顾这三个部分。 让我们探索为欢迎消息定义的模块。
模板模块 (Template Modules)
There are actually two messages that’ll be generated for new users — a welcome message and a how-to message. This will demonstrate further down how the service module can loop through templates and generate the corresponding messages.
实际上,将为新用户生成两条消息-欢迎消息和操作方法消息。 这将进一步说明服务模块如何循环遍历模板并生成相应的消息。
Inside the templates/welcome.js
template, we can see one module export that structures the message and provides placeholders for data passed into it:
在templates/welcome.js
模板内部,我们可以看到一个模块导出,该模块构造消息并为传递到其中的数据提供占位符:
// templates/welcome.jsmodule.exports = {
generate: (name) => ({
subject: `Welcome!`,
message: `Hello ${name !== '' ? ` ${name},` : `!`}## Thank you for signing up!If you have any queries or concerns about the app, if something does not work, or if you have [feedback](https://my-domain.com/contact) for improvements, please feel free to contact us.### From Everyone at App Team`
})
};
These messages are by no means complete. Be creative and use language that will appeal to your user base.
这些消息绝不是完整的。 具有创造力,并使用会吸引您的用户群的语言。
Note that we’ve used a generate()
export function throughout all the templates, a convention used to make the code more predictable. Whitespace is taken into consideration. The message is defined with string literals and is markdown formatted. This is why each new paragraph is not indented.
请注意,我们在所有模板中都使用了generate()
导出函数,该约定用于使代码更具可预测性。 空白被考虑在内。 该消息是用字符串文字定义的,并采用降价格式。 这就是为什么每个新段落都没有缩进的原因。
generate()
can take an arbitrary number of arguments that can be formatted within the message by embedding conditionals within string literal placeholders. This has been done to account for the event where the name
string is empty. Following this template pattern ensures you can manage a large number of templates as module functions.
generate()
可以采用任意数量的参数,可以通过在字符串文字占位符中嵌入条件来在消息内设置格式。 这样做是为了解决name
字符串为空的事件。 遵循此模板模式可确保您可以将大量模板作为模块功能进行管理。
Let’s next check the data module.
接下来让我们检查数据模块。
数据模组 (Data Module)
The data/Accounts.js
file contains two functions that carry out MongoDB queries. The accountsToWelcome()
function fetches all accounts that are eligible to receive the welcome messages, and returns an empty array if none are present. We rely on an account’s meta.sentWelcome
property to determine whether they are eligible for the message.
data/Accounts.js
文件包含执行MongoDB查询的两个函数。 accountsToWelcome()
函数将提取所有有资格接收欢迎消息的帐户,如果不存在任何返回值,则返回一个空数组。 我们依靠帐户的meta.sentWelcome
属性来确定他们是否有资格接收消息。
accountsWelcomeSent()
updates this metadata to signal that they’ve received the message, with the following query:
accountsWelcomeSent()
使用以下查询更新此元数据以表示他们已收到消息:
// updating account metadata to prevent message duplicationaccountsWelcomeSent: async (_id) => {
await Connection.db
.collection('users')
.updateOne({
_id: _id
}, {
$set: {
'meta.sentWelcome': true,
}
});
}
The MongoDB queries in this demo could be refactored to support bulk insertion and bulk updating, with updateMany
or insertMany
. I have opted not to add complexity here as to keep the service structure easily understandable for the reader.
可以使用 updateMany
或 insertMany
重构此演示中的MongoDB查询以支持批量插入和批量更新 。 我选择在此处不添加复杂性,以使服务结构易于读者理解。
accountsToWelcome()
contains a simple requirement check pertaining to one piece of metadata, but more complex requirements could indeed be coded within these functions. Reminder messages for example would need to rely on time-based metrics and activity monitoring to determine whether a user should be reminded to do something within the app.
accountsToWelcome()
包含与一个元数据有关的简单需求检查,但实际上可以在这些函数中编码更复杂的需求。 例如,提醒消息将需要依靠基于时间的指标和活动监视来确定是否应提醒用户在应用程序内执行某些操作。
服务模块 (Service Module)
The services/auto-messages.js
file acts as the controller of the program. The required data and template modules are imported and used to execute account legibility and message generated respectively.
services/auto-messages.js
文件充当程序的控制器。 导入所需的数据和模板模块,并使用它们执行帐户的易读性和分别生成的消息。
The generateWelcomeMessages()
function carries out the following logic:
generateWelcomeMessages()
函数执行以下逻辑:
- Fetches the accounts to generate messages for, and simply returns the function if none are present. 提取帐户以为其生成消息,如果不存在,则仅返回该函数。
Prepares the arguments to be passed into each message. In this case, the user’s first name is taken from a
name
field.准备要传递给每个消息的参数。 在这种情况下,用户的名字来自
name
字段。Messages are generated from the templates are iterated through to insert into a
messages
collection via a MongoDB insertion query. Note that metadata such asprocessed
,read
andarchived
are also added that may be useful for inbox functionalities in-app.从模板生成的
messages
通过MongoDB插入查询进行迭代以插入到messages
集合中。 请注意,还添加了诸如processed
,read
和archived
元数据,这些元数据对于应用程序内的收件箱功能很有用。accountsWelcomeSent()
is called for each user account to update their metadata and prevent message duplication.为每个用户帐户调用
accountsWelcomeSent()
来更新其元数据并防止消息重复。Any further processing can be added from here, such as sending an email or notification to the user that they have received a new message.
可以从此处添加任何进一步的处理,例如向用户发送电子邮件或通知他们已经收到新消息。
As we discovered earlier, this module’s init()
method will initiate the setInterval
loop, and the above process will be executed continuously every 30 minutes.
正如我们之前发现的,此模块的init()
方法将启动setInterval
循环,并且上述过程将每30分钟连续执行一次。
This now covers the whole service, where the reader will hopefully now understand the project execution flow.
现在,它涵盖了整个服务,希望读者现在能够理解项目执行流程。
If there are any queries about this solution please post a comment and I will be sure to get back to you.
如果对此解决方案有任何疑问,请发表评论,我们将尽快与您联系。
在React Native中显示消息 (Displaying Messages in React Native)
After walking though the message delivery service itself, we can now display the delivered messages in React Native using a markdown parser package react-native-markdown-display
. Doing so has not been a focus of this article, but any future articles focusing on this aspect will be embedded here.
经过消息传递服务本身之后,我们现在可以使用markdown解析器包react-native-markdown-display
在React Native中显示传递的消息。 这样做并不是本文的重点,但是以后将重点讨论此方面的任何文章。
综上所述 (In Summary)
This article has walked through a simple Node.js runtime project (hosted here on GitHub) that the reader can use to kick-start their automated message delivery services. We have covered the project structure in depth by adhering to the CommonJS module conventions.
本文通过一个简单的Node.js运行系统项目(托管走到这里在GitHub上),读者可以用它来启动他们的自动消息传递服务。 我们通过遵循CommonJS模块约定深入介绍了项目结构。
This setup also enabled the Data, Service and Template structure to closely resemble the MVC design pattern, so there should be a high level of familiarity for developers who wish to build upon this demo.
此设置还使数据,服务和模板的结构与MVC设计模式极为相似,因此,希望基于此演示的开发人员应具有很高的熟悉度。
Automated message delivery is a required tool for apps that wish to efficiently communicate to their users, where a manual process of individual message sending would be tedious or simply not viable for large user bases. The demo discussed here will hopefully alleviate such bottlenecks and improve the overall prospects on an app project.
对于希望与用户进行有效通信的应用程序来说,自动传递消息是必需的工具,在这种情况下,手动发送单个消息的过程将很繁琐,或者对于大型用户群而言根本不可行。 此处讨论的演示有望缓解此类瓶颈并改善应用程序项目的总体前景。
翻译自: https://medium.com/@rossbulat/node-js-bulk-message-delivery-as-a-runtime-service-70ccb31ecca4
node.js批量拷贝文件