练习 - 将整体内部的服务重构为微服务

已完成

现在,Fabrikam 已完成分析其应用程序,他们已准备好开始重构过程,将服务从整体体系结构移动到微服务中。 让我们修改应用程序,将包处理服务移动到微服务中。

无人机交付应用程序的资源的可视化效果。

重构应用程序

在部署更新的应用程序之前,让我们看看它是如何更新的。 单体应用包含一个处理包的服务,PackageProcessor.cs。 分析应用程序的性能后,此服务被确定为性能瓶颈。 随着客户增加无人机交付的需求,这项服务在处理无人机交付的计划和物流时变得负载很大。 专用团队完全管理此服务,因此将其迁移到微服务有助于提高性能,并提供改进的开发敏捷性。

让我们进一步了解所做更改。

无人机配送之前

PackageProcessor 类处理 PackageProcessor.cs 文件中包处理的核心功能。 在此示例中,它执行一些资源密集型工作。 实际方案可能包括计算传递时间和传递路由,以及使用此信息更新数据源。

public class PackageProcessor : IPackageProcessor
    {
        public Task<PackageGen> CreatePackageAsync(PackageInfo packageInfo)
        {
            //Uses common data store e.g. SQL Azure tables
            Utility.DoWork(100);
            return Task.FromResult(new PackageGen { Id = packageInfo.PackageId });
        }
    }

随着此服务请求的增加,资源利用率会增加,并限制分配给整体应用程序的物理资源。 如果此服务部署在 Azure 应用服务上,我们可以将其纵向扩展和横向扩展。理想情况下,你希望这种大量使用的资源能够独立缩放,以优化性能和成本。 在此方案中,我们使用 Azure Functions 来执行此作。

无人机配送之后

在部署它之前,让我们看看 DroneDelivery-after 应用代码。 可以看到该 PackageProcessor 类已更改为类 PackageServiceCaller 。 它仍然实现 IPackageProcessor 接口,而是对微服务进行 HTTP 调用。

public class PackageServiceCaller : IPackageProcessor
    {
        private readonly HttpClient httpClient;

        public static string FunctionCode { get; set; }

        public PackageServiceCaller(HttpClient httpClient)
        {
            this.httpClient = httpClient;
        }

        public async Task<PackageGen> CreatePackageAsync(PackageInfo packageInfo)
        {
            var result = await httpClient.PutAsJsonAsync($"{packageInfo.PackageId}?code={FunctionCode}", packageInfo);
            result.EnsureSuccessStatusCode();

            return new PackageGen { Id = packageInfo.PackageId };
        }
    }

微服务部署在 Azure 函数上。 可在 PackageServiceFunction.cs 中找到其代码,并包含以下代码。

public static class PackageServiceFunction
    {
        [FunctionName("PackageServiceFunction")]
        public static Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "put", Route = "packages/{id}")] HttpRequest req,
            string id, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            //Uses common data store e.g. SQL Azure tables
            Utility.DoWork(100);
            return Task.FromResult((IActionResult)new CreatedResult("http://example.com", null));
        }
    }

将此代码放在 Azure Functions 上时,随着用户负载的增加,此服务可以独立缩放。 可以保留针对应用程序其余部分优化的剩余应用程序代码的服务。 随着更多无人机配送请求进入系统,包服务会横向扩展。

现在,让我们重新部署应用程序。 首先,我们在 Azure Functions 上部署重构服务。 然后,我们在应用服务上部署重构的应用程序,并将其指向函数。

部署函数应用

  1. 运行以下命令,设置指向我们的服务的环境变量。

    APPSERVICENAME="$(az webapp list \
                        --resource-group "<rgn>[sandbox resource group]</rgn>" \
                        --query '[].name' \
                        --output tsv)"
    FUNCTIONAPPNAME="$(az functionapp list \
                        --resource-group "<rgn>[sandbox resource group]</rgn>" \
                        --query '[].name' \
                        --output tsv)"
    
  2. 让我们生成并压缩函数应用的应用程序代码。

    cd ~/mslearn-microservices-architecture/src/after
    dotnet build ./PackageService/PackageService.csproj -c Release
    cd PackageService/bin/Release/netcoreapp2.2
    zip -r PackageService.zip .
    
  3. 运行以下命令将代码推送到函数应用。

    az functionapp deployment source config-zip \
        --resource-group "<rgn>[sandbox resource group]</rgn>" \
        --name $FUNCTIONAPPNAME \
        --src PackageService.zip
    

部署更新的无人机交付应用程序

现在,我们的服务在 Azure Functions 上运行,我们需要将无人机应用程序指向该函数应用。

  1. 首先需要获取函数应用的访问代码,以便我们可以从应用程序成功调用它。 运行以下命令以检索此代码。 你将显示函数应用名称和代码,以便在后续步骤中使用。

    RESOURCEGROUPID=$(az group show \
                        --resource-group "<rgn>[sandbox resource group]</rgn>" \
                        --query id \
                        --output tsv)
    FUNCTIONCODE=$(az rest \
                        --method post \
                        --query default \
                        --output tsv \
                        --uri "https://management.azure.com$RESOURCEGROUPID/providers/Microsoft.Web/sites/$FUNCTIONAPPNAME/functions/PackageServiceFunction/listKeys?api-version=2018-02-01")
    echo "FunctionName - $FUNCTIONAPPNAME"
    echo "FunctionCode - $FUNCTIONCODE"
    
  2. 在 Azure Cloud Shell 中运行以下命令,在代码编辑器中打开 appsettings.json

    cd ~/mslearn-microservices-architecture/src/after
    code ./DroneDelivery-after/appsettings.json
    
  3. 在代码编辑器中,替换值 PackageServiceUriPackageServiceFunctionCode。 在PackageServiceUri中,将<FunctionName>替换为您的函数应用程序的名称。

    PackageServiceFunctionCode 中,将 <FunctionCode> 替换为检索到的函数代码。 appsettings.json 文件应类似于以下示例:

    {
        "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
        },
        "AllowedHosts": "*",
        "PackageServiceUri": "https://packageservicefunction-abc.azurewebsites.net/api/packages/",
        "PackageServiceFunctionCode": "SvrbiyhjXJUdTPXrkcUtY6bQaUf7OXQjWvnM0Gq63hFUhbH2vn6qYA=="
    }
    
  4. Ctrl+S 保存文件,然后按 Ctrl+Q 关闭代码编辑器。

  5. 运行以下命令,将更新的应用程序部署到应用服务。

    zip -r DroneDelivery-after.zip . -x \*/obj/\* \*/bin/\*
    az webapp deploy \
        --resource-group "<rgn>[sandbox resource group]</rgn>" \
        --name $APPSERVICENAME \
        --src-path DroneDelivery-after.zip
    
  6. 重新部署网站后,刷新页面。 现在应该已经更新了。

    重新部署的无人机交付网站的屏幕截图。

测试新体系结构的性能

现在,资源约束服务已移动到在 Azure Functions 上运行的微服务,让我们看看此更改如何影响应用程序性能。

  1. 在网站的主页上,选择“ 发送请求”。 此操作将您的整体应用的请求提交到运行在 Azure 函数上的微服务。

  2. 第一次尝试可能会给出与整体应用程序类似的结果。 刷新页面,并在出现提示时重新提交请求。 执行此步骤多次,应看到 1 秒内发送的 100 条消息

    移动到微服务体系结构后无人机交付站点性能的屏幕截图。

启动函数应用时初始尝试速度较慢。 启动并运行后,响应时间优于此代码在整体体系结构中运行时。

现在,此体系结构几乎可以无限扩展,同时仍提供相同的性能。 通过将此应用程序代码移动到微服务,我们提高了 5 到 10 倍的性能。 由于 Fabrikam 拥有此服务的专用开发团队,他们能够对这个微服务进行迭代开发,从而实现更高的敏捷性和更频繁的功能发布带来的好处。