工程化与框架系列(11)--Serverless实践

Serverless实践 ☁️

Serverless(无服务器)架构是云计算的一种新范式,它让开发者专注于业务逻辑而无需关心服务器运维。本文将详细介绍前端开发中的Serverless实践方案。

Serverless概述 🌟

💡 小知识:Serverless并不是真的没有服务器,而是将服务器管理的职责转移给了云服务提供商,开发者只需要关注业务代码的编写。

为什么选择Serverless

在现代前端开发中,Serverless带来以下优势:

  1. 降低运维成本

    • 无需管理服务器
    • 自动扩缩容
    • 按使用付费
    • 降低维护成本
  2. 提高开发效率

    • 专注业务逻辑
    • 快速部署上线
    • 简化开发流程
    • 减少基础设施代码
  3. 灵活扩展能力

    • 自动伸缩
    • 高可用性
    • 全球部署
    • 按需使用资源
  4. 成本优化

    • 按量计费
    • 无闲置资源
    • 精确计量
    • 成本可控

函数计算实践 ⚡

基于AWS Lambda的实现

// aws-lambda.ts
import { APIGatewayProxyHandler } from 'aws-lambda';
import * as AWS from 'aws-sdk';const dynamoDB = new AWS.DynamoDB.DocumentClient();export const createUser: APIGatewayProxyHandler = async (event) => {try {const requestBody = JSON.parse(event.body || '{}');const { username, email } = requestBody;const params = {TableName: 'Users',Item: {userId: Date.now().toString(),username,email,createdAt: new Date().toISOString()}};await dynamoDB.put(params).promise();return {statusCode: 201,headers: {'Content-Type': 'application/json'},body: JSON.stringify({message: 'User created successfully',user: params.Item})};} catch (error) {return {statusCode: 500,headers: {'Content-Type': 'application/json'},body: JSON.stringify({message: 'Failed to create user',error: error.message})};}
};export const getUser: APIGatewayProxyHandler = async (event) => {try {const userId = event.pathParameters?.userId;const params = {TableName: 'Users',Key: {userId}};const result = await dynamoDB.get(params).promise();if (!result.Item) {return {statusCode: 404,body: JSON.stringify({message: 'User not found'})};}return {statusCode: 200,headers: {'Content-Type': 'application/json'},body: JSON.stringify(result.Item)};} catch (error) {return {statusCode: 500,headers: {'Content-Type': 'application/json'},body: JSON.stringify({message: 'Failed to get user',error: error.message})};}
};

基于Vercel的实现

// vercel-api.ts
import { VercelRequest, VercelResponse } from '@vercel/node';
import { connectToDatabase } from '../utils/database';export default async function handler(req: VercelRequest,res: VercelResponse
) {if (req.method === 'POST') {try {const { username, email } = req.body;const db = await connectToDatabase();const result = await db.collection('users').insertOne({username,email,createdAt: new Date()});return res.status(201).json({message: 'User created successfully',userId: result.insertedId});} catch (error) {return res.status(500).json({message: 'Failed to create user',error: error.message});}}if (req.method === 'GET') {try {const { userId } = req.query;const db = await connectToDatabase();const user = await db.collection('users').findOne({_id: userId});if (!user) {return res.status(404).json({message: 'User not found'});}return res.status(200).json(user);} catch (error) {return res.status(500).json({message: 'Failed to get user',error: error.message});}}return res.status(405).json({message: 'Method not allowed'});
}

静态网站部署 🚀

Serverless Framework配置

# serverless.yml
service: my-static-websiteprovider:name: awsruntime: nodejs14.xstage: ${opt:stage, 'dev'}region: ${opt:region, 'us-east-1'}plugins:- serverless-finch- serverless-single-page-app-plugincustom:client:bucketName: my-website-${self:provider.stage}distributionFolder: buildindexDocument: index.htmlerrorDocument: index.htmlspa:website: truecertificate: ${self:custom.domain.certificate}dns: trueresources:Resources:ClientBucket:Type: AWS::S3::BucketProperties:BucketName: ${self:custom.client.bucketName}WebsiteConfiguration:IndexDocument: ${self:custom.client.indexDocument}ErrorDocument: ${self:custom.client.errorDocument}ClientBucketPolicy:Type: AWS::S3::BucketPolicyProperties:Bucket: !Ref ClientBucketPolicyDocument:Statement:- Effect: AllowPrincipal: '*'Action: s3:GetObjectResource: !Join ['/', [!GetAtt ClientBucket.Arn, '*']]

自动部署配置

# github-actions-deploy.yml
name: Deploy Websiteon:push:branches:- mainjobs:deploy:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Setup Node.jsuses: actions/setup-node@v2with:node-version: '14'- name: Install dependenciesrun: npm ci- name: Build websiterun: npm run build- name: Configure AWS credentialsuses: aws-actions/configure-aws-credentials@v1with:aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}aws-region: us-east-1- name: Deploy to S3run: |aws s3 sync build/ s3://my-website-${GITHUB_REF##*/} \--delete \--cache-control "max-age=31536000"- name: Invalidate CloudFrontrun: |aws cloudfront create-invalidation \--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \--paths "/*"

数据存储方案 💾

基于DynamoDB的实现

// dynamodb-service.ts
import { DynamoDB } from 'aws-sdk';
import { DocumentClient } from 'aws-sdk/clients/dynamodb';export class DynamoDBService {private readonly client: DocumentClient;private readonly tableName: string;constructor(tableName: string) {this.client = new DynamoDB.DocumentClient();this.tableName = tableName;}async create<T extends { id: string }>(item: T): Promise<T> {const params = {TableName: this.tableName,Item: {...item,createdAt: new Date().toISOString()}};await this.client.put(params).promise();return item;}async get<T>(id: string): Promise<T | null> {const params = {TableName: this.tableName,Key: { id }};const result = await this.client.get(params).promise();return (result.Item as T) || null;}async update<T extends { id: string }>(id: string,updates: Partial<T>): Promise<T> {const updateExpressions: string[] = [];const expressionAttributeNames: Record<string, string> = {};const expressionAttributeValues: Record<string, any> = {};Object.entries(updates).forEach(([key, value]) => {if (key !== 'id') {const attributeName = `#${key}`;const attributeValue = `:${key}`;updateExpressions.push(`${attributeName} = ${attributeValue}`);expressionAttributeNames[attributeName] = key;expressionAttributeValues[attributeValue] = value;}});const params = {TableName: this.tableName,Key: { id },UpdateExpression: `SET ${updateExpressions.join(', ')}`,ExpressionAttributeNames: expressionAttributeNames,ExpressionAttributeValues: expressionAttributeValues,ReturnValues: 'ALL_NEW'};const result = await this.client.update(params).promise();return result.Attributes as T;}async delete(id: string): Promise<void> {const params = {TableName: this.tableName,Key: { id }};await this.client.delete(params).promise();}async query<T>(indexName: string,keyCondition: string,expressionAttributeValues: Record<string, any>): Promise<T[]> {const params = {TableName: this.tableName,IndexName: indexName,KeyConditionExpression: keyCondition,ExpressionAttributeValues: expressionAttributeValues};const result = await this.client.query(params).promise();return (result.Items as T[]) || [];}
}// 使用示例
const userService = new DynamoDBService('Users');// 创建用户
const user = await userService.create({id: 'user123',name: 'John Doe',email: 'john@example.com'
});// 查询用户
const result = await userService.query('EmailIndex','email = :email',{ ':email': 'john@example.com' }
);

身份认证实现 🔐

基于Cognito的认证

// auth-service.ts
import { CognitoIdentityServiceProvider } from 'aws-sdk';export class AuthService {private readonly cognito: CognitoIdentityServiceProvider;private readonly userPoolId: string;private readonly clientId: string;constructor(userPoolId: string, clientId: string) {this.cognito = new CognitoIdentityServiceProvider();this.userPoolId = userPoolId;this.clientId = clientId;}async signUp(username: string,password: string,email: string): Promise<string> {const params = {ClientId: this.clientId,Username: username,Password: password,UserAttributes: [{Name: 'email',Value: email}]};const result = await this.cognito.signUp(params).promise();return result.UserSub;}async confirmSignUp(username: string,code: string): Promise<void> {const params = {ClientId: this.clientId,Username: username,ConfirmationCode: code};await this.cognito.confirmSignUp(params).promise();}async signIn(username: string,password: string): Promise<{accessToken: string;refreshToken: string;idToken: string;}> {const params = {AuthFlow: 'USER_PASSWORD_AUTH',ClientId: this.clientId,AuthParameters: {USERNAME: username,PASSWORD: password}};const result = await this.cognito.initiateAuth(params).promise();const authResult = result.AuthenticationResult!;return {accessToken: authResult.AccessToken!,refreshToken: authResult.RefreshToken!,idToken: authResult.IdToken!};}async refreshToken(refreshToken: string): Promise<{accessToken: string;idToken: string;}> {const params = {AuthFlow: 'REFRESH_TOKEN_AUTH',ClientId: this.clientId,AuthParameters: {REFRESH_TOKEN: refreshToken}};const result = await this.cognito.initiateAuth(params).promise();const authResult = result.AuthenticationResult!;return {accessToken: authResult.AccessToken!,idToken: authResult.IdToken!};}async forgotPassword(username: string): Promise<void> {const params = {ClientId: this.clientId,Username: username};await this.cognito.forgotPassword(params).promise();}async confirmForgotPassword(username: string,code: string,newPassword: string): Promise<void> {const params = {ClientId: this.clientId,Username: username,ConfirmationCode: code,Password: newPassword};await this.cognito.confirmForgotPassword(params).promise();}
}// 使用示例
const authService = new AuthService('us-east-1_xxxxxx','xxxxxxxxxxxxxxxxxx'
);// 注册用户
const userId = await authService.signUp('johndoe','Password123!','john@example.com'
);// 登录
const tokens = await authService.signIn('johndoe','Password123!'
);

最佳实践建议 ⭐

开发原则

  1. 函数设计

    • 单一职责
    • 无状态设计
    • 适当超时设置
    • 错误处理完善
  2. 性能优化

    • 冷启动优化
    • 资源复用
    • 并发控制
    • 缓存策略
  3. 安全考虑

    • 最小权限原则
    • 密钥管理
    • 输入验证
    • 日志审计

开发流程建议

  1. 本地开发环境
# 安装Serverless Framework
npm install -g serverless# 创建新项目
serverless create --template aws-nodejs-typescript
cd my-serverless-project# 安装依赖
npm install# 本地测试
serverless offline start
  1. 调试和测试
// 本地调试配置
// serverless.yml
custom:serverless-offline:httpPort: 3000lambdaPort: 3002websocketPort: 3001plugins:- serverless-offline- serverless-webpack- serverless-dotenv-plugin# 单元测试示例
describe('User API', () => {it('should create user', async () => {const event = {body: JSON.stringify({username: 'test',email: 'test@example.com'})};const result = await createUser(event as any);expect(result.statusCode).toBe(201);});
});

结语 📝

Serverless架构为前端开发提供了一种全新的开发模式,它能够显著提高开发效率并降低运维成本。通过本文,我们学习了:

  1. Serverless的基本概念和优势
  2. 函数计算的实践方案
  3. 静态网站的部署策略
  4. 数据存储的实现方式
  5. 身份认证的解决方案

💡 学习建议:

  1. 从简单的API开始实践
  2. 熟悉云服务提供商的产品
  3. 注重安全性和性能优化
  4. 建立完善的监控体系
  5. 保持代码的可维护性

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/896682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kan与小波,和不知所云的画图

文章目录 小波应用范围与pde小波的名字 画图图(a)&#xff1a;数值解向量 \( u \)图(b)&#xff1a;数值解向量 \( v \)结论图4 小波 在你提供的代码中&#xff0c;小波变换&#xff08;Wavelet Transform&#xff09;被用于 KANLinear 类中。具体来说&#xff0c;小波变换在 …

算法-二叉树篇22-二叉搜索树的最近公共祖先

二叉搜索树的最近公共祖先 力扣题目链接 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且…

细说STM32F407单片机RS485收发通信实例及调试方法

目录 一、硬件配置 1、RCC、DEBUG、CodeGenerator 2、USART3 3、 RS485_DIR 4、NVIC 二、软件设计 1、RS485的收发控制 2、main.c 三、运行调试 1、修改RS485_DIR为SET后需要延迟 2、向串口助手发送的数据不能太长 MCU上的串口UART&#xff08;USART&#xff09;是…

PDF工具 Candy Desktop(安卓)

PDF Candy Desktop&#xff08;安卓&#xff09; 今天给大家分享一个电脑端的PDF工具&#xff0c;里面的功能很多&#xff0c;主要涉及PDF编辑、转换等&#xff0c;不仅超级好用&#xff0c;而且免费&#xff01;剩下就不说了&#xff0c;兄弟们自行下载体验吧&#xff01; 「…

基于javaweb的SSM+Maven幼儿园管理系统设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

golang安装(1.23.6)

1&#xff0e;切换到安装目录 cd /usr/local 2&#xff0e;下载安装包 wget https://go.dev/dl/go1.23.6.linux-amd64.tar.gz 3&#xff0e;解压安装包 sudo tar -C /usr/local -xzf go1.23.6.linux-amd64.tar.gz 4&#xff0e;配置环境变量 vi /etc/profile export PATH$…

【新手入门】SQL注入之盲注

一、引言 在我们的注入语句被带入数据库查询但却什么都没有返回的情况我们该怎么办? 例如应用程序返回到一个"通用的"的页面&#xff0c;或者重定向一个通用页面(可能为网站首页)。这时&#xff0c;我们之前学习的SQL注入的办法就无法使用了。这种情况我们称之为无…

2024年12月中国电子学会青少年软件编程(Python)等级考试试卷(六级)答案 + 解析

青少年软件编程(Python)等级考试试卷(六级) ↓↓↓↓↓↓ 真题模拟测试 分数:100 题数:38 一、单选题(共25题,共50分) 下面代码的输出结果正确的是?( )import json json_str =’ [ “Alice”, “girl", 17,“New York”]’ data = json.loads(json_str) prin…

wordpress按不同页调用不同的标题3种形式

在WordPress中&#xff0c;可以通过多种方式根据不同的页面调用不同的标题。这通常用于实现SEO优化、自定义页面标题或根据页面类型显示不同的标题内容。 使用wp_title函数 wp_title函数用于在HTML的title标签中输出页面标题。你可以通过修改主题的header.php文件来实现自定义…

DeepSeek-R1 大模型实战:腾讯云 HAI 平台 3 分钟极速部署指南

引言&#xff1a;为什么选择 DeepSeek-R1&#xff1f; 近期&#xff0c;国产大模型 DeepSeek-R1 因其低成本、高性能的特点在全球 AI 领域引发热议。根据 Sensor Tower 数据&#xff0c;其发布仅 18 天便斩获 1600 万次下载量&#xff0c;远超 ChatGPT 同期表现。而腾讯云推出…

【Pytest】setup和teardown的四个级别

文章目录 1.setup和teardown简介2.模块级别的 setup 和 teardown3.函数级别的 setup 和 teardown4.方法级别的 setup 和 teardown5.类级别的 setup 和 teardown 1.setup和teardown简介 在 pytest 中&#xff0c;setup 和 teardown 用于在测试用例执行前后执行一些准备和清理操…

使用Docker将ros1自定义消息通过rosjava_bootstrap生成jar包

文章目录 预准备环境rosjava_bootstrap坏消息好消息 环境安装docker安装rosjava_bootstrap仓库rosjava_center仓库修改rosjava_bootstrap代码拉取docker镜像放置自己的自定义消息 启动docker编译 预准备环境 rosjava_bootstrap rosjava_bootstrap是将自定义的ROS消息生成java…

《Python实战进阶》No 7: 一个AI大模型聊天室的构建-基于WebSocket 实时通信开发实战

第7集&#xff1a; 一个AI大模型聊天室的构建-基于WebSocket 实时通信开发实战 在现代 Web 开发中&#xff0c;实时通信已经成为许多应用的核心需求。无论是聊天应用、股票行情推送&#xff0c;还是多人协作工具&#xff0c;WebSocket 都是实现高效实时通信的最佳选择之一。本…

轮式机器人在复杂地形中如何选择合适的全局路径规划算法?

已思考完成 收起 嗯&#xff0c;用户问的是轮式机器人在复杂地形中如何选择合适的全局路径规划算法。首先&#xff0c;我需要理解复杂地形可能带来的挑战&#xff0c;比如崎岖的路面、动态障碍物、非结构化环境等等。轮式机器人在这里的运动控制需要考虑地形通过性、稳定性&…

Mysql100道高频面试题

一、基础概念 1. 什么是数据库&#xff1f;DBMS的作用是什么&#xff1f; 数据库&#xff1a;是按照数据结构来组织、存储和管理数据的仓库。它允许用户高效地访问和管理数据。 DBMS&#xff08;数据库管理系统&#xff09;&#xff1a;是一种软件系统&#xff0c;用于创建和…

React底层原理详解

React中Element&Fiber对象、WorkInProgress双缓存、Reconcile&Render&Commit、第一次挂载过程详解 在面试中介绍React底层原理时&#xff0c;需遵循逻辑清晰、层次分明、重点突出的原则&#xff0c;结合技术深度与实际应用场景。以下是结构化回答模板&#xff1a;…

qt5的中文乱码问题,QString、QStringLiteral 为 UTF-16 编码

qt5的中文乱码问题一直没有很明确的处理方案。 今天处理进程间通信时&#xff0c;也遇到了qt5乱码问题&#xff0c;一边是设置的GBK&#xff0c;一边设置的是UTF8&#xff0c;单向通信约定采用UTF8。 发送端保证发的是UTF8字符串&#xff0c;因为UTF8在网络数据包中没有字节序…

解锁浏览器内置API,助力跨标签/跨页面数据通信

1 BrodcastChanner 概念 BroadcastChannel接口表示给定源的任何浏览上下文都可以订阅的命名频道。它允许同源的不同浏览器窗口、标签页、frame 或者 iframe 下的不同文档之间相互通信。消息通过message事件进行广播&#xff0c;该事件在侦听该频道的所有BroadcastChannel对象上…

Mysql-如何理解事务?

一、事务是什么东西 有些场景中&#xff0c;某个操作需要多个sql配合完成&#xff1a; 例如&#xff1a; 李四这个月剩下的前不够交房租了&#xff0c;找张三借1000元急用&#xff1a; &#xff08;1&#xff09;给张三的账户余额 减去1000元 updata 账户表 set money money -…

《deepseek FlashMLA :高效的 MLA 解码内核》:此文为AI自动翻译

FlashMLA GitHub - deepseek-ai/FlashMLA FlashMLA 是适用于 Hopper GPU 的高效 MLA 解码内核&#xff0c;针对可变长度序列服务进行了优化。 当前发布&#xff1a; BF16、FP16块大小为 64 的分页 kvcache 快速开始 安装 python setup.py install 基准 python tests/test_fl…