当前位置: 首页 > news >正文

使用AWS Amplify、Lambda、API Gateway和DynamoDB部署静态Web应用

如何使用AWS Amplify、Lambda、API Gateway和DynamoDB部署静态Web应用

构建现代Web应用通常涉及复杂的设置和服务器管理,但其实不必如此。Amazon Web Services(AWS)提供了一套强大的"无服务器"服务,让您能够构建和部署应用而无需担心底层基础设施。这意味着AWS会为您处理所有繁重的服务器、扩展和维护工作。

在本教程中,我们将引导您使用几个关键的AWS无服务器服务构建一个简单但功能完整的Web应用。您将学习如何将前端(用户看到的界面)与强大的后端(处理数据的部分)连接起来,实现自动高效的扩展。

目录

  • 我们将构建什么:无服务器求和计算器
  • 核心AWS服务介绍
  • 先决条件:需要准备什么
  • 开始构建:如何构建我们的无服务器Web应用
  • 如何测试应用:是否正常工作?
  • 常见问题及解决方案
  • 下一步:增强您的应用
  • 结论

我们将构建什么:无服务器求和计算器

我们将创建一个简单的求和计算器Web应用。该应用将允许用户输入两个数字,将其发送到我们的AWS后端进行计算,存储结果,然后将求和结果返回给用户。

计算器的工作流程如下:

  1. 接收用户输入:在简单网页中输入两个数字
  2. 后端处理:这些数字将发送到运行在AWS Lambda上的代码,进行相加计算
  3. 数据存储:计算详情(两个数字、它们的和以及计算时间)将保存在名为DynamoDB的高速数据库中
  4. 显示结果:求和结果将返回到网页并显示给用户

这个项目是理解无服务器架构核心概念以及不同AWS服务如何协同工作创建动态Web应用的绝佳方式。

核心AWS服务介绍

在深入之前,让我们熟悉将要使用的主要AWS服务。将它们视为专业工具,每个都有特定职责,共同构建我们的应用。

AWS Lambda:想象一下有一个只在收到特定任务时才激活的小机器人。这就是Lambda!它是"无服务器计算"服务,意味着您不需要管理任何服务器。只需上传代码(本例中的计算器逻辑),Lambda只在需要时运行它。

为什么使用它:它处理我们的后端数学计算(数字相加)。当用户请求求和时,Lambda"唤醒",执行计算,然后返回休眠状态。这很高效且成本效益高,因为您只需为代码实际运行时间付费。

Amazon API Gateway:将其视为后端的门卫或接待员。当您的网页想要与Lambda函数通信时,它不会直接对话。而是将请求发送到API Gateway。

为什么使用它:API Gateway安全地接收来自网页的请求(如"请计算这两个数字的和"),然后告诉正确的Lambda函数唤醒并处理它。它充当后端的安全入口点,确保只有授权请求才能通过。

Amazon DynamoDB:这是我们高速、灵活的数据库。与传统数据库不同,DynamoDB是NoSQL数据库,非常适合快速高效处理大量数据而无需固定结构。

为什么使用它:我们将使用DynamoDB存储计算历史(输入的数字及其和)。它设计用于处理大量流量而不会减慢速度,非常适合需要快速存储和检索数据的Web应用。

AWS Amplify:Amplify就像您的Web和移动应用个人施工队。它简化了构建、部署和托管前端应用的过程,并与其他AWS服务无缝集成。

为什么使用它:我们将使用Amplify托管简单的HTML、CSS和JavaScript网页。它提供了将网站上线互联网的简单方法,为我们处理所有复杂的部署步骤。

先决条件:需要准备什么

要跟随本教程,您应该具备:

  • AWS账户:这是访问所有AWS服务所必需的
  • Python基础知识:我们的后端逻辑将使用Python编写
  • REST API理解:了解API是什么以及RESTful API的工作原理会有帮助
  • HTML/CSS/JavaScript熟悉度:我们的前端将使用这些标准Web技术构建
  • NoSQL基础知识:虽然不是严格要求,但理解数据库中的键值对概念会有益

开始构建:如何构建我们的无服务器Web应用

我们将逐步构建应用,从数据库开始,然后是后端代码,通过API连接它们,最后部署前端。

步骤1:使用Amazon DynamoDB设置数据库

我们的计算器需要存储计算结果的地方。为此,我们将使用Amazon DynamoDB。

  1. 导航到DynamoDB控制台
    首先登录AWS管理控制台。在顶部搜索栏中输入"DynamoDB"并从结果中选择"DynamoDB"。这将带您进入DynamoDB仪表板。

  2. 创建新表
    在DynamoDB仪表板上,找到并点击"Create table"按钮。

  3. 配置表
    需要为表添加以下信息:

    • 表名:输入myTable
    • 分区键:输入ID

    分区键是DynamoDB中每个唯一项的主要标识符。将其视为每个记录的唯一ID号。它帮助DynamoDB快速查找和分布数据。

    保留所有其他设置为默认值。

  4. 完成表创建
    点击页面底部的"Create table"按钮。DynamoDB现在将创建您的表,通常需要几秒钟。

  5. 重要:记下表ARN
    表创建后,点击列表中的名称(myTable)进入其详细信息页面。在"Summary"选项卡下,您将找到显示"ARN"的部分。这是在AWS中唯一标识DynamoDB表的标识符。
    复制整个ARN并安全保存(如记事本)。稍后设置Lambda函数权限时需要它。

步骤2:使用AWS Lambda创建后端逻辑

现在我们有了数据库,让我们创建操作的核心:执行加法并保存结果的Lambda函数。

  1. 导航到Lambda控制台
    在AWS管理控制台搜索栏中输入"Lambda"并从结果中选择"Lambda"。这将带您进入Lambda仪表板。
    然后点击仪表板上的"Create function"按钮创建新函数。

  2. 配置函数
    配置您的函数:

    • 选择"Author from scratch"
    • 函数名:给函数起有意义的名字,例如SumCalculatorFunction
    • 运行时:选择"Python 3.9"(或可用的最新Python 3.x运行时)
    • 架构:保留为x86_64(默认)
    • 权限:暂时保留默认执行角色
  3. 创建函数
    点击底部的"Create function"按钮创建函数。

  4. 编写Lambda函数代码
    函数创建后,您将进入其配置页面。向下滚动到"Code source"部分。这是编写或粘贴Python代码的地方。
    您将看到默认的lambda_function.py文件。将其内容替换为以下Python代码:

import json
import boto3
import time
from botocore.exceptions import ClientError# 创建DynamoDB客户端
dynamodb = boto3.resource('dynamodb')
# 指定要交互的DynamoDB表
table = dynamodb.Table('myTable')def lambda_handler(event, context):# 从event数据中提取数字num1 = event.get('num1')num2 = event.get('num2')# 基本验证:检查是否提供了两个数字if num1 is None or num2 is None:return {'statusCode': 400,'body': json.dumps({'message': 'Both num1 and num2 are required'})}# 计算两个数字的和sum_result = num1 + num2# 为DynamoDB项生成唯一IDpartition_key = str(int(time.time() * 1000))sort_key = str(int(time.time()))# 准备要存储在DynamoDB表中的数据item = {'ID': partition_key,'Timestamp': sort_key,'num1': num1,'num2': num2,'sum': sum_result}# 尝试将项存储在DynamoDB表中try:table.put_item(Item=item)except ClientError as e:return {'statusCode': 500,'body': json.dumps({'message': f'Error storing data in DynamoDB: {e.response["Error"]["Message"]}'})}# 如果一切成功,返回成功消息和计算详情return {'statusCode': 200,'body': json.dumps({'message': 'Sum calculated and stored successfully','result': {'ID': partition_key,'Timestamp': sort_key,'num1': num1,'num2': num2,'sum': sum_result}})}
  1. 部署代码
    粘贴代码后,点击代码编辑器上方的"Deploy"按钮。这将保存更改并使其生效。

  2. 测试Lambda函数
    代码部署后,测试Lambda函数(可选但推荐)。点击"Deploy"按钮旁边的"Test"按钮。系统将提示配置测试事件。
    选择"New event",对于"Event template",选择"hello-world"。将"Event JSON"框中的示例JSON替换为以下测试数据:

    { "num1": 5, "num2": 10 }
    

    给测试事件命名(例如testSum)。然后点击"Save"和"Test"。您应该看到"Status: Succeeded"消息,在"Execution result"选项卡中,您将看到statusCode: 200和计算的和。这确认了Lambda函数正常工作!

更新Lambda函数权限(IAM角色策略)

我们的Lambda函数当前具有默认权限,通常不包括访问DynamoDB的权限。我们需要明确授予它写入myTable的权限。我们将通过IAM角色策略实现。

  1. 导航到权限
    在Lambda函数配置页面上,点击"Configuration"选项卡。然后选择"Permissions"子选项卡。
    您将看到带有"Role name"的"Execution role"部分。点击此"Role name"(它将是一个链接)。这将带您进入IAM控制台,特别是与Lambda函数关联的角色详细信息。

  2. 添加权限
    在IAM角色页面上,您将看到标题为"Permissions policies"的部分。点击"Add permissions"然后选择"Create inline policy"。

  3. 配置策略
    您将看到"Policy editor"。点击"Visual editor"选项卡(如果尚未选择)。

    • 服务:点击"Choose a service"并搜索选择"DynamoDB"
    • 操作:在"Actions"部分,展开"Write"。搜索PutItem并选择复选框
    • 资源:点击"Specific",点击"Add ARN",粘贴之前从步骤1复制的DynamoDB表ARN
  4. 审查并创建策略
    点击"Next: Review policy"。给策略起有意义的名字,例如DynamoDBPutItemPolicy。
    审查策略JSON确保它授予对特定myTable ARN的dynamodb:PutItem权限。然后点击"Create policy"。

现在您的Lambda函数具有写入DynamoDB表所需的权限!

步骤3:使用Amazon API Gateway连接前端和后端

现在我们有了Lambda函数,我们需要让网页能够与之通信。这就是API Gateway的作用。

在AWS管理控制台搜索栏中输入"API Gateway"并从结果中选择"API Gateway"。

  1. 创建新API
    在API Gateway仪表板上,在"REST API"下,点击"Build"按钮。

  2. 配置API设置

    • 选择协议:选择REST
    • 创建新API:选择New API
    • API名称:给API起清晰的名字,例如SumCalculatorAPI
    • 端点类型:选择Regional(默认)
      然后点击"Create API"。
  3. 创建资源
    创建API后,您将进入其仪表板。从"Actions"下拉菜单中,选择"Create Resource"。

    • 资源名称:可以保留为calculator或任何有意义的名称
    • 路径部分:保留为/(根路径)
      然后点击"Create Resource"。
  4. 创建方法(POST)
    在左导航面板中选择根资源(/),从"Actions"下拉菜单中,选择"Create Method"。
    从出现的下拉列表中选择"POST"并点击旁边的复选标记按钮。
    设置详情:

    • 集成类型:选择Lambda Function
    • 使用Lambda代理集成:勾选此框
    • Lambda区域:选择创建Lambda函数的AWS区域
    • Lambda函数:开始输入Lambda函数名称并从下拉列表中选择
      然后点击"Save"。可能会弹出确认添加Lambda权限的窗口。点击"OK"。
  5. 启用CORS
    这是关键步骤!我们的前端(将托管在AWS Amplify上,不同的域)需要权限与API Gateway端点通信。
    仍然选择根资源(/),从"Actions"下拉菜单中,选择"Enable CORS"。将出现对话框。可以接受此简单应用的默认设置。确保POST在"Methods"下被选中,并且"Access-Control-Allow-Origin"设置为'*'。
    然后点击"Enable CORS and replace existing CORS headers"。提示时点击"Yes, replace existing values"。

  6. 部署API
    对API Gateway方法的更改在API"部署"到"阶段"之前不会生效。从"Actions"下拉菜单中,选择"Deploy API"。

    • 部署阶段:选择"[New Stage]"
    • 阶段名称:给阶段起名字,例如dev
    • 阶段描述:(可选)添加描述
      然后点击"Deploy"。
  7. 记下调用URL
    部署后,您将进入"Stages"视图。在左导航面板中点击新创建的阶段(例如dev)。
    您将看到"Invoke URL"。这是API端点的公共URL。
    复制整个调用URL并安全保存。这是我们的前端将用于向Lambda函数发送数字的URL!

步骤4:创建前端Web应用

现在我们的后端已准备好接收请求和处理计算,让我们构建用户将交互的简单网页。我们将使用基本的HTML、CSS和JavaScript。

您需要在计算机上的新文件夹中创建三个文件:index.html、app.js和style.css。

index.html(网页结构)

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Sum Calculator</title><link rel="stylesheet" href="style.css">
</head>
<body><h1>SUM CALCULATOR</h1><form id="calculatorForm"><label for="num1">Number 1:</label><input type="number" id="num1" required><label for="num2">Number 2:</label><input type="number" id="num2" required><button type="submit" id="calculateBtn"><span id="btnText">CALCULATE</span><span id="loadingSpinner" class="spinner d-none"></span> </button></form><div id="errorAlert" class="alert d-none"><strong style="color: red;">Error!</strong> <span id="errorMessage"></span></div><div id="resultSection" class="result-box d-none"><h2>Calculation Result:</h2><p><strong>Transaction ID:</strong> <span id="resultId"></span></p><p><strong>Timestamp:</strong> <span id="resultTimestamp"></span></p><p><strong>Numbers:</strong> <span id="resultNum1"></span> + <span id="resultNum2"></span></p><p><strong>Sum:</strong> <span id="resultSum"></span></p></div><script src="app.js"></script>
</body>
</html>

app.js(前端逻辑)
记住将替换为从步骤3复制的实际调用URL!

document.addEventListener('DOMContentLoaded', function() {const API_ENDPOINT = 'https://YOUR_API_GATEWAY_INVOKE_URL_HERE.execute-api.us-east-1.amazonaws.com/dev';const calculatorForm = document.getElementById('calculatorForm');const num1Input = document.getElementById('num1');const num2Input = document.getElementById('num2');const calculateBtn = document.getElementById('calculateBtn');const btnText = document.getElementById('btnText');const loadingSpinner = document.getElementById('loadingSpinner');const errorAlert = document.getElementById('errorAlert');const errorMessage = document.getElementById('errorMessage');const resultSection = document.getElementById('resultSection');const resultId = document.getElementById('resultId');const resultTimestamp = document.getElementById('resultTimestamp');const resultNum1 = document.getElementById('resultNum1');const resultNum2 = document.getElementById('resultNum2');const resultSum = document.getElementById('resultSum');calculatorForm.addEventListener('submit', function(event) {event.preventDefault();errorAlert.classList.add('d-none');resultSection.classList.add('d-none');const num1 = parseFloat(num1Input.value);const num2 = parseFloat(num2Input.value);if (isNaN(num1) || isNaN(num2)) {showError('Please enter valid numbers');return;}setLoadingState(true);const requestData = {num1: num1,num2: num2};fetch(API_ENDPOINT, {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(requestData)}).then(response => {if (!response.ok) {return response.json().then(errData => {throw new Error(errData.message || 'Server error');});}return response.json();}).then(data => {setLoadingState(false);if (data.statusCode && data.statusCode === 200 && data.body) {try {const resultData = typeof data.body === 'string' ? JSON.parse(data.body) : data.body;displayResult(resultData);} catch (err) {showError('Error parsing the result: ' + err.message);}} else {showError('Unexpected API response format');}}).catch(error => {setLoadingState(false);showError(error.message || 'An error occurred while communicating with the API');});});function showError(message) {errorMessage.textContent = message;errorAlert.classList.remove('d-none');}function setLoadingState(isLoading) {if (isLoading) {btnText.textContent = 'Calculating...';loadingSpinner.classList.remove('d-none');calculateBtn.disabled = true;} else {btnText.textContent = 'CALCULATE';loadingSpinner.classList.add('d-none');calculateBtn.disabled = false;}}function displayResult(data) {if (!data.result) {showError('No result data in the API response');return;}const result = data.result;let timestampDisplay = result.Timestamp;if (result.Timestamp && !isNaN(result.Timestamp)) {const date = new Date(parseInt(result.Timestamp) * 1000);timestampDisplay = date.toLocaleString();}resultId.textContent = result.ID || 'N/A';resultTimestamp.textContent = timestampDisplay || 'N/A';resultNum1.textContent = result.num1;resultNum2.textContent = result.num2;resultSum.textContent = result.sum;resultSection.classList.remove('d-none');}
});

style.css(页面样式)

body {background-color: #222629;color: #FFFFFF;font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;padding: 20px;display: flex;flex-direction: column;align-items: center;min-height: 100vh;margin: 0;
}h1 {color: #86C232;margin-bottom: 30px;
}label {font-size: 18px;margin-right: 10px;display: inline-block;width: 80px;text-align: right;
}input[type="number"] {background-color: #333;border: 1px solid #444;color: #FFFFFF;padding: 8px 12px;font-size: 16px;border-radius: 5px;margin-bottom: 15px;width: 150px;outline: none;
}input[type="number"]:focus {border-color: #86C232;box-shadow: 0 0 0 3px rgba(134, 194, 50, 0.5);
}button {background-color: #86C232;border: none;color: #FFFFFF;font-size: 18px;font-weight: bold;padding: 10px 20px;border-radius: 5px;cursor: pointer;transition: background-color 0.3s ease;display: flex;align-items: center;justify-content: center;gap: 10px;margin-top: 20px;width: 180px;
}button:hover:not(:disabled) {background-color: #6a9b2b;
}button:disabled {background-color: #555;cursor: not-allowed;
}#calculatorForm {background-color: #333;padding: 30px;border-radius: 8px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);display: flex;flex-direction: column;align-items: flex-start;margin-bottom: 30px;
}.spinner {border: 3px solid rgba(255, 255, 255, 0.3);border-top: 3px solid #FFFFFF;border-radius: 50%;width: 20px;height: 20px;animation: spin 1s linear infinite;
}@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }
}.alert {background-color: #330d0d;border: 1px solid #ff4d4d;color: #ff4d4d;padding: 10px 15px;border-radius: 5px;margin-top: 20px;width: 100%;max-width: 400px;text-align: center;
}.result-box {background-color: #333;border: 1px solid #86C232;padding: 20px;border-radius: 8px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);width: 100%;max-width: 400px;margin-top: 20px;animation: fadeIn 0.5s ease-in-out;
}.result-box h2 {color: #86C232;margin-top: 0;border-bottom: 1px solid #444;padding-bottom: 10px;margin-bottom: 15px;
}.result-box p {margin-bottom: 8px;
}.result-box strong {color: #99EE33;
}.d-none {display: none !important;
}@keyframes fadeIn {from { opacity: 0; transform: translateY(10px); }to { opacity: 1; transform: translateY(0); }
}

步骤5:使用AWS Amplify部署前端

最后,让我们使用AWS Amplify将前端Web应用上线。Amplify使托管静态网站变得异常简单。

  1. 准备前端文件
    确保所有三个文件(index.html、app.js、style.css)在计算机上的单个文件夹中。
    将此文件夹压缩为ZIP文件。确保index.html文件位于ZIP文件的根目录(不在ZIP内的另一个子文件夹中)。

  2. 导航到AWS Amplify控制台
    在AWS管理控制台搜索栏中输入"Amplify"并从结果中选择"AWS Amplify"。

  3. 无Git提供商部署
    在Amplify控制台上,您将看到连接到Git提供商的选项。为简化本教程,我们选择手动部署。
    在"Deploy without Git provider"下,点击"Deploy"。

  4. 上传文件

    • 应用名称:给应用起名字,例如SumCalculatorWebApp
    • 环境名称:可以使用dev或main
    • 拖放或浏览文件:点击"drag and drop"区域或"Choose files"按钮
    • 选择在步骤5.1中创建的ZIP文件
  5. 审查并部署
    现在点击"Save and Deploy"。Amplify将获取ZIP文件,提取其内容,并将静态网站部署到由AWS CloudFront提供的全球内容分发网络。这使您的网站在世界任何地方都能快速加载。此过程可能需要几分钟。

  6. 访问实时应用
    部署完成后,Amplify将提供"Domain" URL。点击此URL。这是您的实时公共Web应用!

如何测试应用:是否正常工作?

现在是最激动人心的部分——通过以下步骤测试我们完全无服务器的Web应用:

  1. 打开Amplify应用URL:导航到AWS Amplify提供的"Domain" URL
  2. 输入数字:在"Number 1"和"Number 2"输入字段中,输入任意两个数字(例如5和10)
  3. 点击"CALCULATE":点击"CALCULATE"按钮。您应该短暂看到按钮上显示"Calculating..."
  4. 观察结果:如果一切设置正确,您应该看到"Calculation Result"框出现在表单下方,显示交易ID、时间戳、数字及其和!
  5. 在DynamoDB中验证(可选):
    • 返回DynamoDB控制台
    • 点击myTable
    • 点击"Explore table items"选项卡
    • 您现在应该看到新条目,对应于在Web应用上执行的每个计算,存储了ID、时间戳、num1、num2和sum!

常见问题及解决方案

有时事情不会第一次就完美工作。以下是一些常见问题及解决方法:

CORS错误

  • 症状:Web浏览器开发者控制台显示错误,如"Access to fetch at '...' from origin '...' has been blocked by CORS policy"
  • 解决方案:返回步骤3第5部分(启用CORS)并确保已为API启用CORS。确保POST被选中且Access-Control-Allow-Origin正确配置

Lambda超时

  • 症状:Web应用持续显示"Calculating..."或出现通用错误,在Lambda函数日志中看到"Task timed out"
  • 解决方案:增加Lambda函数的"Timeout"设置

DynamoDB权限错误

  • 症状:Lambda函数执行失败,日志中看到错误如"User: ... is not authorized to perform: dynamodb:PutItem on resource: ..."
  • 解决方案:确保正确添加了dynamodb:PutItem操作并指定了myTable的ARN作为资源

不正确的API Gateway调用URL

  • 症状:前端发出请求但立即失败或浏览器控制台中出现网络错误
  • 解决方案:仔细检查app.js文件中的API_ENDPOINT常量是否与从API Gateway复制的调用URL完全一致

下一步:增强您的应用

这个计算器是一个很好的起点,但您可以显著扩展它:

  • 添加身份验证:实现用户登录和注册,使只有授权用户才能使用计算器
  • 实现错误处理:根据后端发送的内容显示特定错误消息,使前端更健壮
  • 创建计算历史视图:扩展前端以获取和显示DynamoDB中存储的所有过去计算列表
  • 添加输入验证:在前端和后端实现更健壮的验证以处理非数字输入或其他边缘情况
  • 实现实时更新:使用AWS AppSync或带有API Gateway的WebSocket将新计算结果实时推送到前端,无需页面刷新

结论

恭喜!您已成功在AWS上构建并部署了功能完整的无服务器Web应用。您已经了解了如何利用强大的服务如AWS Lambda、Amazon API Gateway、Amazon DynamoDB和AWS Amplify来创建可扩展、成本效益高且维护量低的应用。

这种架构非常强大,因为它可以自动扩展以处理数千甚至数百万用户,而您无需管理单个服务器。请记住在完成实验后清理AWS资源以避免不必要的费用。

进一步学习资源

  • AWS Lambda文档:深入了解无服务器函数
  • Amazon DynamoDB开发指南:了解更多关于NoSQL数据库的知识
  • Amazon API Gateway教程:探索构建各种类型的API
  • AWS Amplify文档:发现构建和部署全栈应用的更多功能
    更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
    公众号二维码
http://www.hskmm.com/?act=detail&tid=14630

相关文章:

  • vscode的ssh-remote插件经常掉线
  • 记录第一次CCPC(2025)网络赛前后
  • 第四周课前思考
  • 声像新境:东芝电视以火箭炮SOUND重塑家庭艺术馆新标准
  • c语言数组与指针
  • 开发微信机器人/微信协议/个人微信api接口
  • 深入解析:frp实现内网穿透,公网服务器或云服务器配置frps,本地内网配置frpc
  • 【五行】根据天干、地支、生肖起姓名(9月出生的宝宝可参考)
  • 全差分放大器(FDA)电路设计计算问题及电压范围估算[原创www.cnblogs.com/helesheng]
  • 使用WTAPI开发智能微信机器人文档
  • [Android]自定义view - 详解
  • 不定高元素动画实现方案(下)
  • 详细介绍:C 语言:第 20 天笔记:typedef(类型重命名规则、应用场景与实战案例)
  • Screaming Architecture:让架构自己说话
  • BOE(京东方)携手UNESCO联合主办WCBR“科学十年”分会 彰显中国科技企业可持续发展实力
  • 使用Cyclops.PdfKit根据pdf模板生成pdf文件
  • 一款文本编辑器的介绍
  • 随笔-决战保研篇
  • 科研人必知:293F与HEK293细胞在蛋白表达中的不同“超能力”
  • Redis Cluster
  • 如何使用C语言实现Vigenre密码加解密
  • 【F#学习】列表 List
  • Trae与Gitee MCP深度集成:AI编程工具链迎来重大升级
  • 【2025-09-22】加班感悟
  • OpenAI Codex 使用 智谱 API
  • 嵌入式ARM架构学习9——IIC - 教程
  • Day04---数据类型及面试题详解
  • 记-一次H3C交换机版本升级
  • 客服系统中的定时任务设计与实现
  • 使用Go语言实现高效定时任务功能