View a markdown version of this page

使用部署清单运行多个应用程序和 ASP.NET 核心应用程序 - AWS Elastic Beanstalk

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用部署清单运行多个应用程序和 ASP.NET 核心应用程序

您可以使用部署清单告知 Elastic Beanstalk 如何部署您的应用程序。通过使用此方法,您无需使用MSDeploy为在网站根路径上运行的单个 ASP.NET 应用程序生成源包。相反,您可以使用清单文件在不同路径上运行多个应用程序。或者,你可以让 Elastic Beanstalk 使用 Core 部署和运行应用程序。 ASP.NET 您也可以使用部署清单配置一个应用程序池,在其中运行您的应用程序。

部署清单向 Elastic Beanstalk 添加了对 .NET Core 应用程序的支持。您可以在不使用部署清单的情况下部署 .NET Framework 应用程序。但是,.NET Core 应用程序需要在 Elastic Beanstalk 上运行部署清单。使用部署清单时,请为每个应用程序创建一个站点存档,然后将该站点存档捆绑在包含部署清单的另一个 ZIP 存档中。

部署清单还增加了在不同路径上运行多个应用程序的能力。一个部署清单定义了一组部署目标,每个部署目标有一个站点存档和一个 IIS 应在其上运行部署清单的路径。例如,您可以在 /api 路径上运行 Web API,以服务异步请求,以及使用 API 的根路径上的 Web 应用程序。

您可以使用部署清单来配置使用自定义绑定和物理路径的 IIS 网站。这样,您便可在部署应用程序之前设置用于侦听特定端口或主机名的网站。

您也可以使用部署清单通过在 IIS 或 Kestrel 中的应用程序池运行多个应用程序。您可以将应用程序池配置为定期重启应用程序、运行 32 位应用程序或使用特定版本的 .NET 框架运行时。

要实现完全自定义,你可以在 Windows 中编写自己的部署脚本, PowerShell 并告诉 Elastic Beanstalk 运行哪些脚本来安装、卸载和重启应用程序。

部署清单和相关功能需要 Windows Server 平台版本 1.2.0 或更新版本。

有关所有可用配置选项、属性和高级功能(例如跳过 IIS 重置)的详细信息,请参阅部署清单架构参考。

.NET Core 应用程序

您可以使用部署清单在 Elastic Beanstalk 上运行 .NET Core 应用程序。.NET Core 是 .NET 的跨平台版本,它附带一个命令行工具 (dotnet)。您可以使用它生成一个应用程序、在本地运行该应用程序并做好发布该应用程序的准备。

要在 Elastic Beanstalk 上运行 .NET Core 应用程序,您可以运行 dotnet publish 并将 ZIP 存档中的输出打包,而不包括任何包含的目录。将具有部署清单的源包中的站点存档与类型为 aspNetCoreWeb 的部署目标放在一起。

以下部署清单将在根路径上运行一个来自名为 dotnet-core-app.zip 的站点存档的 .NET 内核应用程序。

例 aws-windows-deployment-manifest.json - .NET Core
{ "manifestVersion": 1, "deployments": { "aspNetCoreWeb": [ { "name": "my-dotnet-core-app", "parameters": { "archive": "dotnet-core-app.zip", "iisPath": "/" } } ] } }

将清单和站点存档捆绑在一个 ZIP 文档中,以创建源包。

例 dotnet-core-bundle.zip
. |-- aws-windows-deployment-manifest.json `-- dotnet-core-app.zip

该站点存档包含已编译的应用程序代码、依赖项和 web.config 文件。

例 dotnet-core-app.zip
. |-- Microsoft.AspNetCore.Hosting.Abstractions.dll |-- Microsoft.AspNetCore.Hosting.Server.Abstractions.dll |-- Microsoft.AspNetCore.Hosting.dll |-- Microsoft.AspNetCore.Http.Abstractions.dll |-- Microsoft.AspNetCore.Http.Extensions.dll |-- Microsoft.AspNetCore.Http.Features.dll |-- Microsoft.AspNetCore.Http.dll |-- Microsoft.AspNetCore.HttpOverrides.dll |-- Microsoft.AspNetCore.Server.IISIntegration.dll |-- Microsoft.AspNetCore.Server.Kestrel.dll |-- Microsoft.AspNetCore.WebUtilities.dll |-- Microsoft.Extensions.Configuration.Abstractions.dll |-- Microsoft.Extensions.Configuration.EnvironmentVariables.dll |-- Microsoft.Extensions.Configuration.dll |-- Microsoft.Extensions.DependencyInjection.Abstractions.dll |-- Microsoft.Extensions.DependencyInjection.dll |-- Microsoft.Extensions.FileProviders.Abstractions.dll |-- Microsoft.Extensions.FileProviders.Physical.dll |-- Microsoft.Extensions.FileSystemGlobbing.dll |-- Microsoft.Extensions.Logging.Abstractions.dll |-- Microsoft.Extensions.Logging.dll |-- Microsoft.Extensions.ObjectPool.dll |-- Microsoft.Extensions.Options.dll |-- Microsoft.Extensions.PlatformAbstractions.dll |-- Microsoft.Extensions.Primitives.dll |-- Microsoft.Net.Http.Headers.dll |-- System.Diagnostics.Contracts.dll |-- System.Net.WebSockets.dll |-- System.Text.Encodings.Web.dll |-- dotnet-core-app.deps.json |-- dotnet-core-app.dll |-- dotnet-core-app.pdb |-- dotnet-core-app.runtimeconfig.json `-- web.config

运行多个应用程序

您可以定义多个部署目标,从而使用一个部署清单运行多个应用程序。

以下部署清单配置两个 .NET Core 应用程序。WebApiSampleApp 应用程序实现一个简单的 Web API 并在 /api 路径提供异步请求。DotNetSampleApp 应用程序是在根路径上服务请求的 Web 应用程序。

例 aws-windows-deployment-manifest.json - 多个应用程序
{ "manifestVersion": 1, "deployments": { "aspNetCoreWeb": [ { "name": "WebAPISample", "parameters": { "appBundle": "WebApiSampleApp.zip", "iisPath": "/api" } }, { "name": "DotNetSample", "parameters": { "appBundle": "DotNetSampleApp.zip", "iisPath": "/" } } ] } }

此处提供了一个具有多个应用场合的示例应用程序:

配置 IIS 网站

您可以使用部署清单来配置使用自定义绑定和物理路径的 IIS 网站。当您需要设置用于侦听特定端口、使用自定义主机名或提供来自特定目录内容的网站时,这很有用。

以下部署清单配置了使用特定端口号和自定义物理路径来侦听 HTTP 的自定义 IIS 网站:

例 aws-windows-deployment-manifest.json – IIS 网站配置
{ "manifestVersion": 1, "iisConfig": { "websites": [ { "name": "MyCustomSite", "physicalPath": "C:\inetpub\wwwroot\mysite", "bindings": [ { "protocol": "http", "port": 8080, "hostName": "mysite.local" } ] } ] }, "deployments": { "aspNetCoreWeb": [ { "name": "my-dotnet-core-app", "parameters": { "appBundle": "dotnet-core-app.zip", "iisWebSite": "MyCustomSite", "iisPath": "/" } } ] } }

在本示例中:

  • 使用自定义物理路径创建名为 MyCustomSite “” 的网站

  • 该网站在端口 8080 上具有 HTTP 绑定,并具有特定的主机名

  • 使用iisWebSite参数将 ASP.NET 核心应用程序部署到此自定义网站

使用应用程序请求路由(ARR)

应用程序请求路由(ARR)和 URL 重写模块已预先安装在 Elastic Beanstalk Windows AMI 中并且可供使用。这些模块使用 ebextensions 或应用程序配置,通过 IIS 配置实现高级路由场景和 URL 操作。

以下示例显示了简单的部署清单,该清单使用自定义端口,并结合用于设置基本 ARR 路由的 ebextensions 配置,对网站进行了配置:

例 aws-windows-deployment-manifest.json – 简单 ARR 设置
{ "manifestVersion": 1, "iisConfig": { "websites": [ { "name": "ARRSite", "physicalPath": "C:\\inetpub\\wwwroot\\arrsite", "bindings": [ { "protocol": "http", "port": 8080, "hostName": "localhost" } ] } ] }, "deployments": { "aspNetCoreWeb": [ { "name": "BackendApp", "parameters": { "appBundle": "backend-app.zip", "iisWebSite": "ARRSite", "iisPath": "/backend" } } ] } }

ARR 配置通过 ebextensions 完成。以下配置对基本 ARR 路由规则进行了设置:

例。ebextensions/arr-config.config-基本的 ARR 配置
files: "C:\\temp\\configure-arr.ps1": content: | # Enable ARR proxy at server level Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/proxy' -Name 'enabled' -Value 'True' # Clear any existing global rules to avoid conflicts Clear-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/rewrite/globalRules' # Add global rule to route all requests to backend Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' ` -Filter 'system.webServer/rewrite/globalRules' ` -Name '.' ` -Value @{ name = 'Route_to_Backend' stopProcessing = 'True' match = @{ url = '^(?!backend/)(.*)' } action = @{ type = 'Rewrite' url = 'http://localhost:8080/backend/{R:1}' } } container_commands: 01_configure_arr: command: powershell -ExecutionPolicy Bypass -File "C:\\temp\\configure-arr.ps1" waitAfterCompletion: 0

此配置在端口 8080 上创建网站,并将 ARR 设置为将所有传入请求路由到在该站点上运行的后端应用程序。

配置应用程序池

您可以在 Windows 环境中支持多个应用程序。有两种方法可供选择:

  • 您可以将进程外托管模型与 Kesttrel Web 服务器结合使用。使用此模型,您可以配置多个应用程序以在一个应用程序池中运行。

  • 您可以使用进程内托管模型。在此模型中,您可以使用多个应用程序池来运行多个应用程序,每个池中只有一个应用程序。如果您使用的是 IIS 服务器并且需要运行多个应用程序,则必须使用此方法。

要将 Kesttrel 配置为在一个应用程序池中运行多个应用程序,请在 hostingModel="OutofProcess" 文件中添加 web.config。考虑以下示例。

例 web.config - 用于 Kestrel 进程外托管模型
<configuration> <location path="." inheritInChildApplications="false"> <system.webServer> <handlers> <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" /> </handlers> <aspNetCore processPath="dotnet" arguments=".\CoreWebApp-5-0.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="OutofProcess" /> </system.webServer> </location> </configuration>
例 aws-windows-deployment-manifest.json - 多个应用程序
{ "manifestVersion": 1, "deployments": {"msDeploy": [ {"name": "Web-app1", "parameters": {"archive": "site1.zip", "iisPath": "/" } }, {"name": "Web-app2", "parameters": {"archive": "site2.zip", "iisPath": "/app2" } } ] } }

IIS 不支持一个应用程序池中的多个应用程序,因为它使用进程内托管模型。因此,您需要通过将每个应用程序分配到一个应用程序池来配置多个应用程序。换句话说,只将一个应用程序分配到一个应用程序池。

您可以将 IIS 配置为在 aws-windows-deployment-manifest.json 文件中使用不同的应用程序池。在参考下一个示例文件时进行以下更新:

  • 添加 iisConfig 部分,该部分包含称为 appPools 的子部分。

  • 在 appPools 数据块中,列出应用程序池。

  • 在 deployments 部分中,为每个应用程序定义 parameters 部分。

  • 对于每个应用程序,parameters 部分都将指定一个存档、一个运行该存档的路径以及要在其中运行的 appPool 。

以下部署清单配置了两个应用程序池,它们每 10 分钟重新启动一次应用程序。他们还将应用程序附加到以指定路径运行的 .NET Framework Web 应用程序。

例 aws-windows-deployment-manifest.json - 每个应用程序池一个应用程序
{ "manifestVersion": 1, "iisConfig": {"appPools": [ {"name": "MyFirstPool", "recycling": {"regularTimeInterval": 10} }, {"name": "MySecondPool", "recycling": {"regularTimeInterval": 10} } ] }, "deployments": {"msDeploy": [ {"name": "Web-app1", "parameters": { "archive": "site1.zip", "iisPath": "/", "appPool": "MyFirstPool" } }, {"name": "Web-app2", "parameters": { "archive": "site2.zip", "iisPath": "/app2", "appPool": "MySecondPool" } } ] } }

定义自定义部署

为了实现更多控制,您可以通过定义自定义部署 来完全自定义应用程序部署。在自定义部署中,Elastic Beanstalk 仅运行您提供的 PowerShell 脚本,它不代表您执行 IIS 管理。这msDeploy与aspNetCoreWeb部署不同,在 Elastic Beanstalk 中,Elastic Beanstalk 在安装之前自动停止 IIS,之后启动它,并在需要时重启它。对于自定义部署,您的脚本会放置应用程序内容,配置环境(例如 IIS),然后重新启动应用程序。

重要

在负载平衡的 Web 服务器环境中,Elastic Beanstalk 通过在端口 80 上请求环境的运行状况检查路径(/默认情况下)来检查实例运行状况。某些东西必须满足这一要求,否则即使每个部署脚本都成功了,环境也会变得不健康——这是自定义部署在没有错误的情况下完成但环境处于红色状态的常见原因。对于独立的自定义部署,请从默认网站的根目录为您的应用程序提供服务。当另一个部署已经提供运行状况检查路径(例如,在多应用程序清单中)时,将自定义应用程序放在子路径下是有效的。有关环境运行状况的更多信息,请参阅监控环境。

自定义部署最多定义三个脚本。下表描述了每个脚本、Elastic Beanstalk 何时运行脚本以及您的脚本必须做什么。

Script 当 Elastic Beanstalk 运行它时 责任
uninstall 在安装每个新应用程序版本之前,即在每个应用程序部署之前。 停止服务或删除先前版本的文件。
install 在每次应用程序部署期间。 部署文件,配置 IIS 或您的服务,并在运行状况检查路径上提供应用程序。
restart 在每次应用程序部署之后以及每次配置更改之后。如果您设置skipIISReset为true,Elastic Beanstalk 会在应用程序部署时跳过此脚本,但仍会在配置更改时运行该脚本。选择 “重新启动 App Server” 将在平台级别执行iisreset,不会调用您的自定义重启脚本。 重新启动 IIS (iisreset) 或您的服务,以便新版本上线。

每当 Elastic Beanstalk 将您的应用程序部署到新启动的实例(例如,在初始环境创建期间以及 Auto Scaling 添加实例(向外扩展)时,这些脚本也会运行,因为每个新实例都会在应用程序启动时安装应用程序。

以下部署清单指示 Elastic Beanstalk 在 32 位模式下运行 PowerShell 脚本。它指定install脚本 (install.ps1)、restart脚本 (restart.ps1) 和uninstall脚本 (uninstall.ps1)。uninstall脚本设置ignoreErrors为,true这样第一次部署(没有要删除的内容)不会失败。

例 aws-windows-deployment-manifest.json - 自定义部署
{ "manifestVersion": 1, "deployments": { "custom": [ { "name": "Custom site", "architecture": 32, "scripts": { "install": { "file": "install.ps1" }, "restart": { "file": "restart.ps1" }, "uninstall": { "file": "uninstall.ps1", "ignoreErrors": true } } } ] } }

包括使用清单和脚本运行源包中的应用程序所需的任何项目。Elastic Beanstalk 不会为自定义部署提取这些工件,您的脚本必须提取它们。在以下示例中,应用程序内容打包为MyApp.zip。

例 Custom-site-bundle.zip
. |-- aws-windows-deployment-manifest.json |-- install.ps1 |-- restart.ps1 |-- uninstall.ps1 `-- MyApp.zip

以下脚本显示了应用程序的完整、运行状况检查正确的自定义部署。 IIS-hosted 该install.ps1脚本提取应用程序内容并将默认网站的物理路径指向该内容,以便从运行状况检查所在的根路径 (/) 为应用程序提供服务。

例安装.ps1
$ErrorActionPreference = "Stop" $appPath = "C:\inetpub\MyApp" if (Test-Path $appPath) { Remove-Item $appPath -Recurse -Force } # Elastic Beanstalk doesn't extract your bundle for custom deployments, so extract it yourself. # MyApp.zip ships in the bundle, alongside this script. Resolve it relative to the script's # own location so the path is reliable regardless of the current working directory. $scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path } $bundleZip = Join-Path $scriptDir "MyApp.zip" Add-Type -AssemblyName "System.IO.Compression.FileSystem" [IO.Compression.ZipFile]::ExtractToDirectory($bundleZip, $appPath) # Serve from the Default Web Site root ("/"), where the health check looks. Import-Module WebAdministration Set-ItemProperty "IIS:\Sites\Default Web Site" -Name physicalPath -Value $appPath iisreset.exe /restart if ($LASTEXITCODE -ne 0) { exit 1 }
例重启.ps1
$ErrorActionPreference = "Stop" iisreset.exe /restart if ($LASTEXITCODE -ne 0) { exit 1 }
例卸载.ps1
# Stop IIS first. While the site is live, w3wp holds locks on files under the app directory, # which would make the following removal fail. iisreset.exe /stop | Out-Null Remove-Item "C:\inetpub\MyApp" -Recurse -Force -ErrorAction SilentlyContinue

编写自定义部署脚本时,请记住以下几点:

  • 提供运行状况检查路径。将默认网站的物理路径指向您的应用程序,或以其他方式部署到运行状况检查路径,这样运行状况检查就会成功。

  • 包括默认文档。如果站点根目录没有默认文档,则GET /返回 HTTP 403,并且运行状况检查失败。发布一个设置<defaultDocument>元素的web.config文件,或者命名您的登录页面以匹配 IIS 默认文档名称,例如Default.htm或index.htm。

  • 自己重启 IIS。由于 Elastic Beanstalk 不对自定义部署执行 IIS 管理,iisreset因此您的脚本必须运行才能使更改生效。

  • 找到与脚本相关的捆绑文件。Elastic Beanstalk 将您的脚本和捆绑的构件一起提取到同一个目录中。对照$PSScriptRoot(包含运行脚本的文件夹)解析捆绑文件,而不是假设当前使用特定的工作目录。

  • 让失败导致部署失败。在执行外部命令$LASTEXITCODE后进行设置$ErrorActionPreference = "Stop"和检查。否则,损坏的脚本可以成功退出,即使应用程序无法正常运行,Elastic Beanstalk 也会将部署视为成功部署。

示例:自托管 Windows 服务

使用自定义部署,您可以运行 Windows 服务而不是 IIS-hosted 站点。Elastic Beanstalk Windows 平台不支持工作环境层。在负载平衡环境中,负载均衡器检查端口 80 上的运行状况检查路径,因此 Windows 服务必须响应该请求才能保持环境健康。以下示例是一个自托管服务,它本身监听端口 80,因此它不需要配套的 IIS 站点。

注意

由于 Windows 平台没有工作层,因此仅限后台的服务仍然需要在负载平衡的环境中回答运行状况检查。要么让该服务同时回答运行状况检查路径(如下所示),要么在提供运行状况检查路径的站点旁边运行后台工作。

清单以 64 位模式运行脚本,与用于构建服务的 64 位 C# 编译器相匹配。

例 aws-windows-部署清单.json-Windows 服务
{ "manifestVersion": 1, "deployments": { "custom": [ { "name": "EbCustomService", "architecture": 64, "scripts": { "install": { "file": "serviceInstall.ps1" }, "restart": { "file": "serviceRestart.ps1" }, "uninstall": { "file": "serviceUninstall.ps1", "ignoreErrors": true } } } ] } }

该捆绑包提供清单、三个脚本、服务源代码和一个version.txt文件(例如,其内容是版本字符串v1)。安装脚本在实例上编译服务,因此您的包中不需要构建工具链。

例 Custom-service-bundle.zip
. |-- aws-windows-deployment-manifest.json |-- EbCustomService.cs |-- serviceInstall.ps1 |-- serviceRestart.ps1 |-- serviceUninstall.ps1 `-- version.txt

该serviceInstall.ps1脚本从 IIS 中释放端口 80,使用内置的 C# 编译器 (csc.exe) 从捆绑源代码编译服务,将其注册为LocalSystem服务,然后启动它。以as方式运行LocalSystem可以让服务在不预留 URL ACL http://+:80/ 的情况下进行绑定,从而使示例保持最小化。在生产环境中,首选权限最低的帐户,例如NetworkService并明确授予其绑定。netsh http add urlacl

例服务 Install.ps1
# INSTALL: compile and register a self-hosted HTTP service that owns port 80. $ErrorActionPreference = "Stop" $svcName = "EbCustomService" $appPath = "C:\CustomService" $scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { (Get-Location).Path } # Free port 80 from IIS and keep it from reclaiming the port on reboot. Stop-Service W3SVC -Force -ErrorAction SilentlyContinue Set-Service W3SVC -StartupType Manual -ErrorAction SilentlyContinue # Fresh application directory. if (Test-Path $appPath) { Remove-Item $appPath -Recurse -Force } New-Item -ItemType Directory -Path $appPath | Out-Null # Compile the service with the in-box .NET Framework compiler (no build tooling needed). $csc = Join-Path $env:WINDIR "Microsoft.NET\Framework64\v4.0.30319\csc.exe" $src = Join-Path $scriptDir "EbCustomService.cs" $exe = Join-Path $appPath "EbCustomService.exe" & $csc /nologo /target:exe /out:"$exe" /reference:System.ServiceProcess.dll "$src" if ($LASTEXITCODE -ne 0) { exit 1 } # Write the content served at "/". A real service would serve its own responses; # here the install script writes a simple marker so the health check passes. $version = (Get-Content (Join-Path $scriptDir "version.txt") -Raw).Trim() Set-Content -Path (Join-Path $appPath "marker.txt") ` -Value "Custom service deployment $version succeeded" -NoNewline # Register as a LocalSystem service so it can bind http://+:80/ without a URL ACL. if (Get-Service $svcName -ErrorAction SilentlyContinue) { Stop-Service $svcName -Force -ErrorAction SilentlyContinue sc.exe delete $svcName | Out-Null # sc.exe delete is async; poll until gone so New-Service below doesn't hit # "service marked for deletion". $deadline = (Get-Date).AddSeconds(30) while ((Get-Service $svcName -ErrorAction SilentlyContinue) -and (Get-Date) -lt $deadline) { Start-Sleep -Milliseconds 500 } } New-Service -Name $svcName -BinaryPathName "`"$exe`"" ` -DisplayName "EB Custom Service" -StartupType Automatic | Out-Null Start-Service $svcName

该serviceRestart.ps1脚本会重新启动服务,以便新版本上线。

例服务 Restart.ps1
# RESTART: restart the service so the new version becomes live. $ErrorActionPreference = "Stop" $svcName = "EbCustomService" Restart-Service $svcName -Force

该serviceUninstall.ps1脚本会停止并删除该服务,并删除先前版本的文件。此脚本ignoreErrorstrue的清单设置为,因为第一次部署没有要删除的先前版本。

例服务 Uninstall.ps1
# UNINSTALL: stop and remove the previous version before the new install. # The manifest sets ignoreErrors:true because the first deploy has nothing to remove. $ErrorActionPreference = "Stop" $svcName = "EbCustomService" $appPath = "C:\CustomService" if (Get-Service $svcName -ErrorAction SilentlyContinue) { Stop-Service $svcName -Force -ErrorAction SilentlyContinue sc.exe delete $svcName | Out-Null # sc.exe delete is async; poll until the service is really gone so the next # deploy's New-Service doesn't hit "service marked for deletion". $deadline = (Get-Date).AddSeconds(30) while ((Get-Service $svcName -ErrorAction SilentlyContinue) -and (Get-Date) -lt $deadline) { Start-Sleep -Milliseconds 500 } } if (Test-Path $appPath) { Remove-Item $appPath -Recurse -Force -ErrorAction SilentlyContinue }

该服务本身是一个小型 C# 程序,它托管一个开HttpListener启http://+:80/并返回的文本响应GET /,这正是满足负载均衡器运行状况检查的条件。

例 EbCustomService.cs
// Minimal self-hosted HTTP Windows service. Hosts an HttpListener on http://+:80/ // and answers the load balancer's GET "/" health check with the content that the // install script wrote to marker.txt. using System; using System.IO; using System.Net; using System.ServiceProcess; using System.Text; using System.Threading; namespace EbCustomService { public class MarkerService : ServiceBase { private const string Root = @"C:\CustomService"; private HttpListener _listener; private Thread _worker; private volatile bool _running; public MarkerService() { this.ServiceName = "EbCustomService"; } protected override void OnStart(string[] args) { _running = true; _listener = new HttpListener(); _listener.Prefixes.Add("http://+:80/"); _listener.Start(); _worker = new Thread(Loop) { IsBackground = true }; _worker.Start(); } protected override void OnStop() { _running = false; try { if (_listener != null) _listener.Stop(); } catch { } } private void Loop() { while (_running) { HttpListenerContext ctx; try { ctx = _listener.GetContext(); } catch { break; } try { byte[] buf = Encoding.UTF8.GetBytes(ReadFile(Path.Combine(Root, "marker.txt"))); ctx.Response.StatusCode = 200; ctx.Response.ContentType = "text/plain"; ctx.Response.ContentLength64 = buf.Length; ctx.Response.OutputStream.Write(buf, 0, buf.Length); ctx.Response.OutputStream.Close(); } catch { } } } private static string ReadFile(string p) { try { return File.Exists(p) ? File.ReadAllText(p) : ""; } catch { return ""; } } public static void Main() { ServiceBase.Run(new MarkerService()); } } }
注意

在此示例中,提供的响应/是一个小文本标记 (marker.txt),它代表应用程序的真实内容。相反,生产服务将提供自己的响应。这些脚本中的其他所有内容都是正常运行的 Windows 服务自定义部署所需的最低要求。