对于我目前正在进行的项目,我已经了解了 AWS 提供的一系列服务。我们将着手在 AWS Lambda 和 Api Gateway 中创建无服务器 Web API。有几种方法可以实现这一点,我们需要评估哪种方法最适合我们的解决方案。

Lambda 函数

Amazon.Lambda.Templates 提供的 AWS 无服务器项目模板之一具有用 C# 编写的直接 lambda 函数,如下所示:

public class Functions
{
    public APIGatewayProxyResponse Get(
        APIGatewayProxyRequest request,
        ILambdaContext context
    )
    {
        context.Logger.LogLine("Get Request\n");
        var response = new APIGatewayProxyResponse
        {
            StatusCode = (int)HttpStatusCode.OK,
            Body = "Hello AWS Serverless",
            Headers = new Dictionary<string, string>
            {
                { "Content-Type", "text/plain"}
            }
        };


        return response;
    }
}

进入全屏模式 退出全屏模式

然后将其与 SAM 模板文件放在一起以定义 AWS API Gateway:

 "GetLambda": {
   "Type": "AWS::Serverless::Function",
   "Properties": {
     "Handler": "AwsServerless.Lambda::AwsServerless.Lambda.Functions::Get",
     "Runtime": "dotnetcore3.1",
     "CodeUri": "",
     "MemorySize": 256,
     "Timeout": 30,
     "Role": null,
     "Policies": [
       "AWSLambdaBasicExecutionRole"
     ],
     "Events": {
       "RootGet": {
         "Type": "Api",
         "Properties": {
           "Path": "/",
           "Method": "GET"
         }
       }
     }
   }
 }

进入全屏模式 退出全屏模式

这将创建一个 API 端点,当我们的 API 路径“/”上发生 GET 请求事件时,该端点会调用 Function 类的 Get() 方法。

对于本地开发,它带有一个 Mock Lambda 测试工具。因此,如果您在 Visual Studio 上按 F5,它会在 localhost:5050 上打开它:

[Mock Lambda 测试工具截图](https://res.cloudinary.com/practicaldev/image/fetch/s--rplZOe4C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to-uploads.s3.amazonaws.com/uploads/articles/ez3bhpkdx91wkp34g4w4.png)

该工具将请求转发到 lambda,以便可以在本地开发环境中使用测试 JSON 数据执行/调试它。您还可以在没有 UI 的情况下从命令行调用该工具并传入有效负载文件。

这里要说的一件事是,我对无法将 lambda 作为 API 服务进行调试,以便将对 localhost:port 端点的请求转发到指定的 lambda 感到非常不满意。作为一名全栈开发人员,对我来说常见的工作流程之一是在 Visual Studio 中打开 API 项目,在 VS Code 中打开客户端应用程序项目并一起运行整个堆栈。对于 .net 的 lambda 项目,我很难找到任何方法来做到这一点。可以使用 AWS 为 nodejs、python 和 golang 提供的 sam 命令行工具进行调试,但 .net 缺少文档。

这里的另一个问题是,当我们启动一个基本的 asp.net 核心项目时,我们需要将大量样板代码放在一起,用于我们认为开箱即用的服务——CORS、JWT Auth、授权、HTTPS 重定向,模型绑定。这些都不存在。当然,在基类中将它连接在一起有多难,但仍然需要在这里完成工作。

直接使用 lambda 模板还意味着您的代码库与 AWS Lambda 紧密相关,而不是与平台无关。

Asp.net 核心

Amazon.Lambda.Templates 提供的另一个项目模板是一个配置为作为一个 lambda 运行的 Asp.net 核心项目。

[项目大纲Monolity](https://res.cloudinary.com/practicaldev/image/fetch/s--Lkg53YAb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/9wi3k8sjtyzyxpid6m4v.png)

这里有两个值得注意的文件:LocalEntryPoint.cs 和 LambdaEntryPoint.cs。 LocalEntyPoint 具有典型的 asp.net 核心主要功能,当您想在本地运行它时:

 public class LocalEntryPoint
 {
     public static void Main(string[] args)
     {
         CreateHostBuilder(args).Build().Run();
     }


     public static IHostBuilder CreateHostBuilder(string[] args) =>
         Host.CreateDefaultBuilder(args)
             .ConfigureWebHostDefaults(webBuilder =>
             {
                 webBuilder.UseStartup<Startup>();
             });
 }

进入全屏模式 退出全屏模式

LambdaEntryPoint 派生自 APIGatewayProxyFunction,它确保对于每个 API 网关调用,都会调用 web-api 的启动并将 API 网关请求编组到 Asp.net 核心的请求中。

 public class LambdaEntryPoint : 
    Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction
 {
     protected override void Init(IWebHostBuilder builder)
     {
         builder
             .UseStartup<Startup>();
     }


     protected override void Init(IHostBuilder builder)
     {
     }
 }

进入全屏模式 退出全屏模式

然后 SAM 模板使用/{proxy+}路径来确保对 Api 网关根目录的所有请求都只调用 Asp.net 核心应用程序。

 "AspNetCoreFunction": {
   "Type": "AWS::Serverless::Function",
   "Properties": {
     "Handler": "AWSServerless.Aspnet::AWSServerless.Aspnet.LambdaEntryPoint::FunctionHandlerAsync",
     "Runtime": "dotnetcore3.1",
     "CodeUri": "",
     "MemorySize": 256,
     "Timeout": 30,
     "Role": null,
     "Policies": [
       "AWSLambda_FullAccess"
     ],
     "Events": {
       "ProxyResource": {
         "Type": "Api",
         "Properties": {
           "Path": "/{proxy+}",
           "Method": "ANY"
         }
       },
       "RootResource": {
         "Type": "Api",
         "Properties": {
           "Path": "/",
           "Method": "ANY"
         }
       }
     }
   }
 }

进入全屏模式 退出全屏模式

在此配置中,路由由 asp.net 核心处理,而不是 Api 网关。

[Monolyth Lambda 图](https://res.cloudinary.com/practicaldev/image/fetch/s--kWipYINH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/zr0lln5rw39yqmswdbh3.png)

任何 asp.net 开发人员都熟悉此配置,这意味着分配到该项目的新开发人员可以开始运行。我之前提到的全栈调试工作流程也是一个很大的优势。附带 Asp.net 核心、微软驱动的标准框架、可用的中间件、库、社区支持和所有其他方面的所有好处。

该解决方案也与平台无关。我们需要做的就是为 Azure Functions 或 Google Cloud Function 编写一个入口点,并且该解决方案可能可以部署在这些平台上。

这里的问题是这是一个 lambda 单体,而不是我们选择的微服务。微服务的好处在这里消失了。加载 lambda 单体也会对性能产生一些影响。

这里要注意的另一点是,这不是传统的 asp.net 核心应用程序,不同的环境,不同的生命周期,因此某些组件将无法工作。例如,您将无法像在传统的 asp.net 核心应用程序上那样托管 SignalR 或调度程序组件。但是,如果您正在投资 AWS,您可能正在考虑用 AWS 提供的服务替换这些组件,但这只是需要牢记的事情。

其他框架

我们确实查看了其他一些可以使用的类似框架。

南希

一个类似于 Asp.net 核心的 web 框架,可能需要更少的管道。尽管它缺乏像 Asp.net 核心那样的支持和熟悉度,但它确实是一个非常有前途的微服务框架,我一直在关注它。

无服务器框架

另一个非常有前途的框架,可以以与平台无关的方式编写无服务器微服务。乍一看,我发现大多数示例和库都是针对 nodejs 的。我确信它的库提供了足够的中间件来创建 Restful API,但乍一看,它并没有给我们对 .net 开发的信心。

我们的选择

我们决定坚持使用 Asp.net 核心框架,但我们决定将其拆分为多个 web-api 项目,而不是拥有一个整体 web-api,每个项目都有自己的 Startup。

[项目大纲 - 微服务](https://res.cloudinary.com/practicaldev/image/fetch/s--1pCwTCnx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/uploads/articles/fizp5jfky90l3lwo6ptg.png)

有一个共享的基础架构,我们可以在其中封装所有服务共有的启动行为。有一个 LocalDev 项目,这是我们唯一拥有 LocalEntryPoint 的地方。这被设置为仅用于本地开发的启动项目,不部署到 AWS。这个项目没有任何控制器。 Starup 可能有一些额外的配置来设置本地开发环境。例如,您可能想在生产环境中使用 Lambda Authorizer,但对于 local-dev,您可以启用 Auth 中间件。

其他项目,每个都专注于特定的服务:客户、订单和产品。这些项目中的每一个都可能处理一种类型的资源(或相关的子资源)。这些项目都有一个 Startup 和一个 LambdaEntryPoint,因为它们将被部署为在 API Gateway 后面作为 Lambda 运行。

[微服务 Asp.net 图](https://res.cloudinary.com/practicaldev/image/fetch/s--UKdFVES2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to-uploads.s3.amazonaws.com/uploads/articles/ta4c96z51v2nrlia8mox.png)

然后,SAM 模板确保对于给定的基本路径,我们调用正确的 LambdaEntryPoint。下面以产品服务的定义为例:

 "ProductsServiceLambda": {
   "Type": "AWS::Serverless::Function",
   "Properties": {
     "Handler": "AWSServerlessDemo.Aspnet::AWSServerlessDemo.Products.LambdaEntryPoint::FunctionHandlerAsync",
     "Runtime": "dotnetcore3.1",
     "CodeUri": "./AWSServerlessDemo.Products/",
     "MemorySize": 256,
     "Timeout": 30,
     "Role": null,
     "Policies": [
       "AWSLambda_FullAccess"
     ],
     "Events": {
       "ProductsProxyResource": {
         "Type": "Api",
         "Properties": {
           "Path": "/products/{proxy+}",
           "Method": "ANY"
         }
       },
       "ProductsRootResource": {
         "Type": "Api",
         "Properties": {
           "Path": "/products/",
           "Method": "ANY"
         }
       }
     }
   }
}

进入全屏模式 退出全屏模式

请注意,我们设置的是"CodeUri": "./AWSServerlessDemo.Products/"。这确保了 dotnet lambda 工具在构建和部署AWSServerlessDemo.Products的源代码时,将部署的大小保持在最小,这是 lambda 性能的一个影响因素。

Asp.net 作为微服务框架的未来

.Net Core 5 引入了一些很有前景的新特性:

  • C# 源代码生成器

  • 会员级修整

这些在现阶段是实验性的,但具有真正的潜力。目前asp.net核心生命周期对反射有很大的依赖,减慢了启动时间。但是想象一下源生成器替换了所有依赖于反射的代码,然后成员级别的修剪将应用程序的大小缩小到只有被调用的方法!我很高兴看到它是如何展开的!

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐