This is the first article in a series of articles on Serverless best practices. I hope that this series of articles will help you gain a deeper understanding of the mechanism behind Serverless and master related best practices.
Serverless architecture is a distributed, event-driven architecture. In this architecture, the core nodes are functions. In order to be different from ordinary functions, we call them cloud functions.
Cloud functions run on demand, so they are closed when not triggered by an event.
Cloud functions will be started and run only when an event is triggered.
In addition, since frequent startup is not conducive to effective utilization of resources, most cloud service providers will retain the instance of the cloud function for two minutes after the cloud function is started, so that when a similar event is triggered, it can be processed quickly without having to waste time and resources again. Start the instance. If no event of the same type is triggered after two minutes, shut down the instance.
Therefore, the complete process of the cloud function being triggered is:
- Event triggering
- Create and start cloud function instances
- Enter event information
- Execute the cloud function trigger code and return the result
- If there are subsequent similar incidents
- Enter the next event
- Execute the cloud function trigger code and return the result
- Repeat step 5
- Wait for two minutes and there are no new events, then destroy the cloud function instance.
The life cycle converted into a cloud function is divided into three steps:
- Mount start
- Invoke trigger
- Destroy Close
Since the closing of cloud functions is directly controlled by the service provider, it cannot be captured and customized.
In FaasJS, the life cycle is reflected in cloud functions as:
// onMount cloud function instance starts
import { Func } from '@faasjs/func';
let count = 0;
export default new Func({
handler() { // The trigger code of the onInvoke cloud function is in the handler
return count++;
}
});
In the above cloud function example, when triggered, what is returned is the number of times the current cloud function instance has been triggered since it was started.
So what are the best practices for reference regarding the life cycle of cloud functions? The current recommendations include the following two points:
- Put the definition and creation of constants in the Mount stage
- Place the database connection in the Mount stage
- Only place code that is strongly related to input events in the Invoke phase
Examples are as follows:
import { Func } from '@faasjs/func';
import { Sql } from '@faasjs/sql';
//Initialize database objects and connections
const sql = new Sql();
// define constants
const types = {
user: 0,
admin: 1
};
export default new Func({
plugins: [sql],
async handler(){
return await sql.query('SELECT count(*) FROM users WHERE type = ?', [types.user]);
}
});
In the sample code, the database connection is created when the cloud function instance is started and destroyed when the instance is shut down. When a cloud function instance exists, the same database connection will be used every time an event is triggered, without having to wait for extra time to connect to the database each time. In addition, this method can also effectively control the number of database connections, and will not generate a large number of database connections due to excessive triggering of cloud functions.
Declaring constants during the startup phase not only helps improve code readability, but also avoids the problem of repeatedly creating constants when events are triggered (although this usually has little impact on performance).