您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

Azure Service Fabric Reliable Services 中的 ASP.NET CoreASP.NET Core in Azure Service Fabric Reliable Services

ASP.NET Core 是一个开源跨平台框架。ASP.NET Core is an open-source and cross-platform framework. 此框架用于生成基于云的 Internet 联网应用程序,例如 Web 应用、IoT 应用和移动后端。This framework is designed for building cloud-based, internet-connected applications, such as web apps, IoT apps, and mobile back ends.

本文详细说明如何使用 NuGet 包的 Microsoft.ServiceFabric.AspNetCore 集在 Service Fabric Reliable Services 中This article is an in-depth guide to hosting ASP.NET Core services in Service Fabric Reliable Services by using the Microsoft.ServiceFabric.AspNetCore. 托管 ASP.NET Core 服务。set of NuGet packages.

有关 Service Fabric 中 ASP.NET Core 的入门教程以及如何设置开发环境的说明,请参阅教程:使用 ASP.NET Core Web API 前端服务和有状态后端服务创建和部署应用程序For an introductory tutorial on ASP.NET Core in Service Fabric and instructions on getting your development environment set up, see Tutorial: Create and deploy an application with an ASP.NET Core Web API front-end service and a stateful back-end service.

本文的剩余内容假设你熟悉 ASP.NET Core。The rest of this article assumes you're already familiar with ASP.NET Core. 如果不熟悉,请通读 ASP.NET Core 基础知识If not, please read through the ASP.NET Core fundamentals.

Service Fabric 环境中的 ASP.NET CoreASP.NET Core in the Service Fabric environment

ASP.NET Core 和 Service Fabric 应用都可以在 .NET Core 或完整的 .NET Framework 上运行。Both ASP.NET Core and Service Fabric apps can run on .NET Core or full .NET Framework. 可在 Service Fabric 中以两种的不同方式使用 ASP.NET Core:You can use ASP.NET Core in two different ways in Service Fabric:

  • 作为来宾可执行文件托管Hosted as a guest executable. 此方法主要用于在 Service Fabric 上运行现有 ASP.NET Core 应用程序,无需更改代码。This way is primarily used to run existing ASP.NET Core applications on Service Fabric with no code changes.
  • 在 Reliable Service 内部运行Run inside a reliable service. 此方法可改善与 Service Fabric 运行时的集成,实现有状态的 ASP.NET Core 服务。This way allows better integration with the Service Fabric runtime and allows stateful ASP.NET Core services.

本文的剩余内容说明如何通过 Service Fabric SDK 随附的 ASP.NET Core 集成组件在 Reliable Service 内部使用 ASP.NET Core。The rest of this article explains how to use ASP.NET Core inside a reliable service, through the ASP.NET Core integration components that ship with the Service Fabric SDK.

Service Fabric 服务托管Service Fabric service hosting

在 Service Fabric 中,服务的一个或多个实例和/或副本在服务主机进程(运行服务代码的可执行文件)中运行。In Service Fabric, one or more instances and/or replicas of your service run in a service host process: an executable file that runs your service code. 服务作者拥有服务主机进程,Service Fabric 为服务作者激活并监视此进程。You, as a service author, own the service host process, and Service Fabric activates and monitors it for you.

传统的 ASP.NET(最高为 MVC 5)通过 System.Web.dll 与 IIS 紧密耦合。Traditional ASP.NET (up to MVC 5) is tightly coupled to IIS through System.Web.dll. ASP.NET Core 在 Web 服务器和 Web 应用程序之间提供分隔。ASP.NET Core provides a separation between the web server and your web application. 这种隔离使 Web 应用程序能够在不同的 Web 服务器之间移植。This separation allows web applications to be portable between different web servers. 此外,还允许 Web 服务器自我托管。It also allows web servers to be self-hosted. 这意味着,你可以在自己的进程(而不是由 IIS 等专用 Web 服务器软件拥有的进程)中启动 Web 服务器。This means you can start a web server in your own process, as opposed to a process that's owned by dedicated web server software, such as IIS.

若要合并 Service Fabric 服务和 ASP.NET,无论是作为来宾可执行文件或是在 Reliable Service 中,必须能够在服务主机进程内启动 ASP.NET。To combine a Service Fabric service and ASP.NET, either as a guest executable or in a reliable service, you must be able to start ASP.NET inside your service host process. 可借助 ASP.NET Core 的自托管功能执行此操作。ASP.NET Core self-hosting allows you to do this.

在 Reliable Service 中托管 ASP.NET CoreHosting ASP.NET Core in a reliable service

通常情况下,自托管 ASP.NET Core 应用程序会在应用程序的入口点创建 WebHost,如 Program.cs 中的 static void Main() 方法。Typically, self-hosted ASP.NET Core applications create a WebHost in an application's entry point, such as the static void Main() method in Program.cs. 在这种情况下,WebHost 的生命周期绑定到进程的生命周期中。In this case, the life cycle of the WebHost is bound to the life cycle of the process.

在进程中托管 ASP.NET Core

但是,应用程序入口点并不是在 Reliable Service 中创建 WebHost 的正确位置。But the application entry point isn't the right place to create a WebHost in a reliable service. 这是因为应用程序入口点仅用于向 Service Fabric 运行时注册服务类型,以便它能创建该服务类型的实例。That's because the application entry point is only used to register a service type with the Service Fabric runtime, so that it can create instances of that service type. 应在 Reliable Service 中创建 WebHost。The WebHost should be created in a reliable service itself. 在服务主机进程中,服务实例和/或副本可以完成多个生命周期。Within the service host process, service instances and/or replicas can go through multiple life cycles.

Reliable Service 实例由派生自 StatelessServiceStatefulService 的服务类表示。A Reliable Service instance is represented by your service class deriving from StatelessService or StatefulService. 服务的通信堆栈包含在服务类中的 ICommunicationListener 实现内。The communication stack for a service is contained in an ICommunicationListener implementation in your service class. Microsoft.ServiceFabric.AspNetCore.* NuGet 包内附 ICommunicationListener 的实现,这些实现可启动和管理 Reliable Service 中 Kestrel 或 HTTP.sys 的 ASP.NET Core WebHost。The Microsoft.ServiceFabric.AspNetCore.* NuGet packages contain implementations of ICommunicationListener that start and manage the ASP.NET Core WebHost for either Kestrel or HTTP.sys in a reliable service.

在 Reliable Service 中托管 ASP.NET Core 的示意图

ASP.NET Core ICommunicationListenersASP.NET Core ICommunicationListeners

Microsoft.ServiceFabric.AspNetCore.* NuGet 包中 Kestrel 和 HTTP.sys 的 ICommunicationListener 实现具有类似的使用模式。The ICommunicationListener implementations for Kestrel and HTTP.sys in the Microsoft.ServiceFabric.AspNetCore.* NuGet packages have similar use patterns. 但它们针对每个 Web 服务器所执行的操作略有不同。But they perform slightly different actions specific to each web server.

这两种通信侦听器都能提供采用以下参数的构造函数:Both communication listeners provide a constructor that takes the following arguments:

  • ServiceContext serviceContext :这是包含有关运行中服务的信息的 ServiceContext 对象。ServiceContext serviceContext: This is the ServiceContext object that contains information about the running service.
  • string endpointName :这是 ServiceManifest.xml 中 Endpoint 配置的名称。string endpointName: This is the name of an Endpoint configuration in ServiceManifest.xml. 它是两个通信侦听器的主要不同之处。It's primarily where the two communication listeners differ. HTTP.sys 需要 Endpoint 配置,而 Kestrel 不需要。HTTP.sys requires an Endpoint configuration, while Kestrel doesn't.
  • Func<string, AspNetCoreCommunicationListener, IWebHost> build :这是实现的 lambda,在其中创建和返回 IWebHostFunc<string, AspNetCoreCommunicationListener, IWebHost> build: This is a lambda that you implement, in which you create and return an IWebHost. 它允许按平时一样在 ASP.NET Core 应用程序中使用的方法配置 IWebHostIt allows you to configure IWebHost the way you normally would in an ASP.NET Core application. lambda 提供生成的 URL,具体取决于使用的 Service Fabric 集成选项和你提供的 Endpoint 配置。The lambda provides a URL that's generated for you, depending on the Service Fabric integration options you use and the Endpoint configuration you provide. 然后,可以修改 URL 或使用它来启动 Web 服务器。You can then modify or use that URL to start the web server.

Service Fabric 集成中间件Service Fabric integration middleware

NuGet 包包含添加 Service Fabric 感知中间件IWebHostBuilder的扩展方法。UseServiceFabricIntegration Microsoft.ServiceFabric.AspNetCoreThe Microsoft.ServiceFabric.AspNetCore NuGet package includes the UseServiceFabricIntegration extension method on IWebHostBuilder that adds Service Fabric–aware middleware. 此中间件将 Kestrel 或 HTTP.sys ICommunicationListener 配置为向 Service Fabric 命名服务注册唯一的服务 URL。This middleware configures the Kestrel or HTTP.sys ICommunicationListener to register a unique service URL with the Service Fabric Naming Service. 然后,它验证客户端请求,以确保客户端连接到适当的服务。It then validates client requests to ensure clients are connecting to the right service.

为了防止客户端错误地连接到错误的服务,必须执行此步骤。This step is necessary to prevent clients from mistakenly connecting to the wrong service. 这是因为,在 Service Fabric 等共享主机环境中,多个 Web 应用程序可在同一物理机或虚拟机上运行,但不使用唯一的主机名。That's because, in a shared-host environment such as Service Fabric, multiple web applications can run on the same physical or virtual machine but don't use unique host names. 后续部分将对此方案进行详细说明。This scenario is described in more detail in the next section.

错误标识示例A case of mistaken identity

服务副本(无论哪种协议)侦听唯一的 IP:port 组合。Service replicas, regardless of protocol, listen on a unique IP:port combination. 服务副本开始侦听 IP:port 终结点后,它向 Service Fabric 命名服务报告该终结点地址。Once a service replica has started listening on an IP:port endpoint, it reports that endpoint address to the Service Fabric Naming Service. 该命名服务中的客户端或其他服务可以发现该地址。There, clients or other services can discover it. 如果服务使用动态分配的应用程序端口,服务副本可能恰巧使用同一物理计算机或虚拟机上的以前其他服务所使用的相同 IP:port 终结点。If services use dynamically assigned application ports, a service replica might coincidentally use the same IP:port endpoint of another service previously on the same physical or virtual machine. 这可能会导致客户端错误地连接到错误的服务。This can cause a client to mistakenly connect to the wrong service. 如果发生以下事件序列,可能会出现此情况:This scenario can result if the following sequence of events occurs:

  1. 服务 A 通过 HTTP 侦听 10.0.0.1:30000。Service A listens on 10.0.0.1:30000 over HTTP.
  2. 客户端解析服务 A 并获取地址 10.0.0.1:30000。Client resolves Service A and gets address 10.0.0.1:30000.
  3. 服务 A 移动到其他节点。Service A moves to a different node.
  4. 服务 B 放置在 10.0.0.1 并恰巧使用了同一端口 30000。Service B is placed on 10.0.0.1 and coincidentally uses the same port 30000.
  5. 客户端尝试使用缓存地址 10.0.0.1:30000 连接到服务 A。Client attempts to connect to service A with cached address 10.0.0.1:30000.
  6. 客户端现已成功连接到服务 B,但未意识到已连接到错误的服务。Client is now successfully connected to service B, not realizing it's connected to the wrong service.

这可能导致在随机时间出现 bug,并且很难诊断。This can cause bugs at random times that can be difficult to diagnose.

使用唯一的服务 URLUsing unique service URLs

若要防止这些 bug,服务可向具有唯一标识符的命名服务发布终结点,并在客户端请求期间验证该唯一标识符。To prevent these bugs, services can post an endpoint to the Naming Service with a unique identifier, and then validate that unique identifier during client requests. 这是非恶意租户受信任环境中的服务之间的协作操作。This is a cooperative action between services in a non-hostile-tenant trusted environment. 这不会在恶意租户环境中提供安全的服务身份验证。It doesn't provide secure service authentication in a hostile-tenant environment.

在受信任的环境中,由 UseServiceFabricIntegration 方法自动添加的中间件可对已发布到命名服务的地址追加唯一标识符。In a trusted environment, the middleware that's added by the UseServiceFabricIntegration method automatically appends a unique identifier to the address posted to the Naming Service. 它会在每次请求时验证该标识符。It validates that identifier on each request. 如果标识符不匹配,该中间件会立即返回 HTTP 410 Gone 响应。If the identifier doesn't match, the middleware immediately returns an HTTP 410 Gone response.

使用动态分配的端口的服务应使用此中间件。Services that use a dynamically assigned port should make use of this middleware.

使用固定唯一端口的服务在协作环境中不存在此问题。Services that use a fixed unique port don't have this problem in a cooperative environment. 固定唯一端口通常用于面向外部的服务,此类服务需要可供客户端应用程序连接到的已知端口。A fixed unique port is typically used for externally facing services that need a well-known port for client applications to connect to. 例如,大多数面向 Internet 的 Web 应用程序将使用端口 80 或 443 进行 Web 浏览器连接。For example, most internet-facing web applications will use port 80 or 443 for web browser connections. 在此情况下,不应启用唯一标识符。In this case, the unique identifier shouldn't be enabled.

下图显示了启用中间件时的请求流:The following diagram shows the request flow with the middleware enabled:

Service Fabric ASP.NET Core 集成

Kestrel 和 HTTP.sys ICommunicationListener 实现以完全相同的方式使用此机制。Both Kestrel and HTTP.sys ICommunicationListener implementations use this mechanism in exactly the same way. 尽管 HTTP.sys 可使用底层 HTTP.sys 端口共享功能基于唯一 URL 路径内部区分请求,但 HTTP.sys ICommunicationListener 实现不使用此功能。Although HTTP.sys can internally differentiate requests based on unique URL paths by using the underlying HTTP.sys port sharing feature, that functionality is not used by the HTTP.sys ICommunicationListener implementation. 这是因为,它会导致上述方案中出现 HTTP 503 和 HTTP 404 错误状态代码。That's because it results in HTTP 503 and HTTP 404 error status codes in the scenario described earlier. 这进而使客户端难以确定错误原因,因为 HTTP 503 和 HTTP 404 通常用于指示其他错误。That in turn makes it difficult for clients to determine the intent of the error, as HTTP 503 and HTTP 404 are commonly used to indicate other errors.

因此,Kestrel 和 HTTP.sys ICommunicationListener 实现会在 UseServiceFabricIntegration 扩展方法提供的中间件上执行标准化。Thus, both Kestrel and HTTP.sys ICommunicationListener implementations standardize on middleware provided by the UseServiceFabricIntegration extension method. 因而客户端只需对 HTTP 410 响应执行服务终结点重新解析操作。Therefore, clients only need to perform a service endpoint re-resolve action on HTTP 410 responses.

Reliable Services 中的 HTTP.sysHTTP.sys in Reliable Services

可以通过导入 Microsoft.ServiceFabric.AspNetCore.HttpSys NuGet 包来使用 Reliable Services 中的 HTTP.sys。You can use HTTP.sys in Reliable Services by importing the Microsoft.ServiceFabric.AspNetCore.HttpSys NuGet package. 此包包含 HttpSysCommunicationListenerICommunicationListener 的实现)。This package contains HttpSysCommunicationListener, an implementation of ICommunicationListener. HttpSysCommunicationListener 允许使用 HTTP.sys 作为 Web 服务器在 Reliable Service 内部创建 ASP.NET Core WebHost。HttpSysCommunicationListener allows you to create an ASP.NET Core WebHost inside a reliable service by using HTTP.sys as the web server.

Windows HTTP Server API 上生成 HTTP.sys。HTTP.sys is built on the Windows HTTP Server API. 此 API 使用 HTTP.sys 内核驱动程序处理 HTTP 请求,并将其路由到运行 Web 应用程序的进程。This API uses the HTTP.sys kernel driver to process HTTP requests and route them to processes that run web applications. 这可允许同一物理计算机或虚拟机上的多个进程在同一端口上托管 Web 应用程序,通过唯一 URL 路径或主机名来消除歧义。This allows multiple processes on the same physical or virtual machine to host web applications on the same port, disambiguated by either a unique URL path or host name. Service Fabric 在同一群集中托管多个网站时,这些功能非常有用。These features are useful in Service Fabric for hosting multiple websites in the same cluster.

备注

HTTP.sys 实现仅适用于 Windows 平台。HTTP.sys implementation works only on the Windows platform.

下图演示了 HTTP.sys 如何在 Windows 上使用 HTTP.sys 内核驱动程序进行端口共享:The following diagram illustrates how HTTP.sys uses the HTTP.sys kernel driver on Windows for port sharing:

HTTP.sys 示意图

无状态服务中的 HTTP.sysHTTP.sys in a stateless service

若要在无状态服务中使用 HttpSys,需替代 CreateServiceInstanceListeners 方法并返回 HttpSysCommunicationListener 实例:To use HttpSys in a stateless service, override the CreateServiceInstanceListeners method and return a HttpSysCommunicationListener instance:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                new WebHostBuilder()
                    .UseHttpSys()
                    .ConfigureServices(
                        services => services
                            .AddSingleton<StatelessServiceContext>(serviceContext))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build()))
    };
}

有状态服务中的 HTTP.sysHTTP.sys in a stateful service

由于底层 HTTP.sys 端口共享功能所具有的复杂性,当前不能在有状态服务中使用 HttpSysCommunicationListenerHttpSysCommunicationListener isn't currently designed for use in stateful services due to complications with the underlying HTTP.sys port sharing feature. 有关详细信息,请参阅以下关于 HTTP.sys 动态端口分配的部分。For more information, see the following section on dynamic port allocation with HTTP.sys. 对于有状态服务,Kestrel 是建议的 Web 服务器。For stateful services, Kestrel is the suggested web server.

终结点配置Endpoint configuration

对于使用 Windows HTTP Server API 的 Web 服务器(包括 HTTP.sys),需要配置 EndpointAn Endpoint configuration is required for web servers that use the Windows HTTP Server API, including HTTP.sys. 使用 Windows HTTP Server API 的 Web 服务器首先必须保留带有 HTTP.sys 的 URL(通常可使用 netsh 工具实现)。Web servers that use the Windows HTTP Server API must first reserve their URL with HTTP.sys (this is normally accomplished with the netsh tool).

此操作需要提升的权限,默认情况下服务不具备此权限。This action requires elevated privileges that your services don't have by default. 用于 ServiceManifest.xml 中 Endpoint 配置的 Protocol 属性的“http”或“https”选项,可专门用于指示 Service Fabric 运行时代表你注册带有 HTTP.sys 的 URL。The "http" or "https" options for the Protocol property of the Endpoint configuration in ServiceManifest.xml are used specifically to instruct the Service Fabric runtime to register a URL with HTTP.sys on your behalf. 它使用强通配符 URL 前缀来提供此指示。It does this by using the strong wildcard URL prefix.

例如,若要保留服务的 http://+:80,请在 ServiceManifest.xml 中使用以下配置:For example, to reserve http://+:80 for a service, use the following configuration in ServiceManifest.xml:

<ServiceManifest ... >
    ...
    <Resources>
        <Endpoints>
            <Endpoint Name="ServiceEndpoint" Protocol="http" Port="80" />
        </Endpoints>
    </Resources>

</ServiceManifest>

并且必须将终结点名称传递到 HttpSysCommunicationListener 构造函数:And the endpoint name must be passed to the HttpSysCommunicationListener constructor:

 new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
 {
     return new WebHostBuilder()
         .UseHttpSys()
         .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
         .UseUrls(url)
         .Build();
 })

将 HTTP.sys 和静态端口配合使用Use HTTP.sys with a static port

要将 HTTP.sys 与静态端口配合使用,请在 Endpoint 配置中提供端口号:To use a static port with HTTP.sys, provide the port number in the Endpoint configuration:

  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

将 HTTP.sys 和动态端口配合使用Use HTTP.sys with a dynamic port

要将 HTTP.sys 与动态分配端口配合使用,请在 Endpoint 配置中省略 Port 属性:To use a dynamically assigned port with HTTP.sys, omit the Port property in the Endpoint configuration:

  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" />
    </Endpoints>
  </Resources>

Endpoint 配置分配的动态端口仅为每个主机进程提供一个端口。A dynamic port allocated by an Endpoint configuration provides only one port per host process. 当前的 Service Fabric 托管模型允许在同一进程中托管多个服务实例和/或副本。The current Service Fabric hosting model allows multiple service instances and/or replicas to be hosted in the same process. 这意味着,通过 Endpoint 配置分配时,每个实例/副本共享相同的端口。This means each one will share the same port when allocated through the Endpoint configuration. 多个 HTTP.sys 实例可以使用底层 HTTP.sys 端口共享功能共享一个端口。Multiple HTTP.sys instances can share a port by using the underlying HTTP.sys port sharing feature. HttpSysCommunicationListener 不支持此做法,因为这会增大客户端请求的复杂性。But it's not supported by HttpSysCommunicationListener due to the complications it introduces for client requests. 对于使用动态端口,Kestrel 是建议的 Web 服务器。For dynamic port usage, Kestrel is the suggested web server.

Reliable Services 中的 KestrelKestrel in Reliable Services

可以通过导入 Microsoft.ServiceFabric.AspNetCore.Kestrel NuGet 包来使用 Reliable Services 中的 Kestrel。You can use Kestrel in Reliable Services by importing the Microsoft.ServiceFabric.AspNetCore.Kestrel NuGet package. 此包包含 KestrelCommunicationListenerICommunicationListener 的实现)。This package contains KestrelCommunicationListener, an implementation of ICommunicationListener. KestrelCommunicationListener 允许使用 Kestrel 作为 Web 服务器在 Reliable Service 内部创建 ASP.NET Core WebHost。KestrelCommunicationListener allows you to create an ASP.NET Core WebHost inside a reliable service by using Kestrel as the web server.

Kestrel 是基于 libuv 的 ASP.NET Core 的跨平台 Web 服务器,libuv 是跨平台异步 I/O 库。Kestrel is a cross-platform web server for ASP.NET Core based on libuv, a cross-platform asynchronous I/O library. 与 HTTP.sys 不同,Kestrel 不使用集中式终结点管理器。Unlike HTTP.sys, Kestrel doesn't use a centralized endpoint manager. 与 HTTP.sys 的另一个区别在于,Kestrel 不支持在多个进程之间共享端口。Also unlike HTTP.sys, Kestrel doesn't support port sharing between multiple processes. Kestrel 的每个实例必须使用唯一端口。Each instance of Kestrel must use a unique port.

Kestrel 示意图

无状态服务中的 KestrelKestrel in a stateless service

若要在无状态服务中使用 Kestrel,需替代 CreateServiceInstanceListeners 方法并返回 KestrelCommunicationListener 实例:To use Kestrel in a stateless service, override the CreateServiceInstanceListeners method and return a KestrelCommunicationListener instance:

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
                new WebHostBuilder()
                    .UseKestrel()
                    .ConfigureServices(
                        services => services
                            .AddSingleton<StatelessServiceContext>(serviceContext))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build();
            ))
    };
}

有状态服务中的 KestrelKestrel in a stateful service

若要在有状态服务中使用 Kestrel,需替代 CreateServiceReplicaListeners 方法并返回 KestrelCommunicationListener 实例:To use Kestrel in a stateful service, override the CreateServiceReplicaListeners method and return a KestrelCommunicationListener instance:

protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
    return new ServiceReplicaListener[]
    {
        new ServiceReplicaListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, (url, listener) =>
                new WebHostBuilder()
                    .UseKestrel()
                    .ConfigureServices(
                         services => services
                             .AddSingleton<StatefulServiceContext>(serviceContext)
                             .AddSingleton<IReliableStateManager>(this.StateManager))
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.UseUniqueServiceUrl)
                    .UseStartup<Startup>()
                    .UseUrls(url)
                    .Build();
            ))
    };
}

此示例中为 WebHost 依赖关系注入容器提供 IReliableStateManager 的单一实例。In this example, a singleton instance of IReliableStateManager is provided to the WebHost dependency injection container. 这不是必需的,但通过此操作,可在 MVC 控制器操作方法中使用 IReliableStateManager 和 Reliable Collections。This isn't strictly necessary, but it allows you to use IReliableStateManager and Reliable Collections in your MVC controller action methods.

有状态服务中不会为 KestrelCommunicationListener 提供Endpoint 配置名称。An Endpoint configuration name is not provided to KestrelCommunicationListener in a stateful service. 后续部分会对此进行详细说明。This is explained in more detail in the following section.

将 Kestrel 配置为使用 HTTPSConfigure Kestrel to use HTTPS

如要在服务中为 Kestrel 启用 HTTPS,需要设置多个侦听选项。When enabling HTTPS with Kestrel in your service, you'll need to set several listening options. ServiceInstanceListener 更新为使用 EndpointHttps 终结点和侦听特定端口(例如端口 443)。Update the ServiceInstanceListener to use an EndpointHttps endpoint and listen on a specific port (such as port 443). 配置使用 Kestrel Web 服务器的 Web 主机时,须将 Kestrel 配置为针对所有网络接口上的 IPv6 地址进行侦听:When configuring the web host to use the Kestrel web server, you must configure Kestrel to listen for IPv6 addresses on all network interfaces:

new ServiceInstanceListener(
serviceContext =>
    new KestrelCommunicationListener(
        serviceContext,
        "EndpointHttps",
        (url, listener) =>
        {
            ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

            return new WebHostBuilder()
                .UseKestrel(opt =>
                {
                    int port = serviceContext.CodePackageActivationContext.GetEndpoint("EndpointHttps").Port;
                    opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
                    {
                        listenOptions.UseHttps(GetCertificateFromStore());
                        listenOptions.NoDelay = true;
                    });
                })
                .ConfigureAppConfiguration((builderContext, config) =>
                {
                    config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                })

                .ConfigureServices(
                    services => services
                        .AddSingleton<HttpClient>(new HttpClient())
                        .AddSingleton<FabricClient>(new FabricClient())
                        .AddSingleton<StatelessServiceContext>(serviceContext))
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                .UseUrls(url)
                .Build();
        }))

如需查看教程中的完整示例,请参阅配置 Kestrel 以使用 HTTPSFor a full example in a tutorial, see Configure Kestrel to use HTTPS.

终结点配置Endpoint configuration

使用 Kestrel 时不需要 Endpoint 配置。An Endpoint configuration isn't required to use Kestrel.

Kestrel 是简单的独立 Web 服务器。Kestrel is a simple standalone web server. 与 HTTP.sys(或 HttpListener)不同,Kestrel 不需要 ServiceManifest.xml 中的 Endpoint 配置,因为它在启动之前无需 URL 注册。Unlike HTTP.sys (or HttpListener), it doesn't need an Endpoint configuration in ServiceManifest.xml because it doesn't require URL registration before starting.

将 Kestrel 和静态端口配合使用Use Kestrel with a static port

可在 ServiceManifest.xml 的 Endpoint 配置中配置静态端口,以将其与 Kestrel 配合使用。You can configure a static port in the Endpoint configuration of ServiceManifest.xml for use with Kestrel. 虽然这不是必需的,但这样做有两个潜在好处:Although this isn't strictly necessary, it offers two potential benefits:

  • 如果此端口不在应用程序端口范围内,则会由 Service Fabric 通过 OS 防火墙将其打开。If the port doesn't fall in the application port range, it's opened through the OS firewall by Service Fabric.
  • 通过 KestrelCommunicationListener 提供的 URL 将使用此端口。The URL provided to you through KestrelCommunicationListener will use this port.
  <Resources>
    <Endpoints>
      <Endpoint Protocol="http" Name="ServiceEndpoint" Port="80" />
    </Endpoints>
  </Resources>

如果已配置 Endpoint,则其名称必须传递到 KestrelCommunicationListener 构造函数:If an Endpoint is configured, its name must be passed into the KestrelCommunicationListener constructor:

new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) => ...

如果 ServiceManifest.xml 不使用 Endpoint 配置,请在 KestrelCommunicationListener 构造函数中省略此名称。If ServiceManifest.xml doesn't use an Endpoint configuration, omit the name in the KestrelCommunicationListener constructor. 在此情况下,将使用动态端口。In this case, it will use a dynamic port. 有关详细信息,请参阅下一部分。See the next section for more information about this.

将 Kestrel 和动态端口配合使用Use Kestrel with a dynamic port

Kestrel 无法使用 ServiceManifest.xml 的 Endpoint 配置中的自动端口分配。Kestrel can't use the automatic port assignment from the Endpoint configuration in ServiceManifest.xml. 这是因为,Endpoint 配置中的自动端口分配将为每个主机进程分配唯一端口,并且单个主机进程可以包含多个 Kestrel 实例。That's because automatic port assignment from an Endpoint configuration assigns a unique port per host process, and a single host process can contain multiple Kestrel instances. 此方案不可行,因为 Kestrel 不支持端口共享。This doesn't work with Kestrel because it doesn't support port sharing. 因此,必须在唯一端口上打开每个 Kestrel 实例。Therefore, each Kestrel instance must be opened on a unique port.

要将 Kestrel 和动态端口分配配合使用,请全省略 ServiceManifest.xml 中的 Endpoint 配置,并且不要将终结点名称传递到 KestrelCommunicationListener 构造函数,如下所示:To use dynamic port assignment with Kestrel, omit the Endpoint configuration in ServiceManifest.xml entirely, and don't pass an endpoint name to the KestrelCommunicationListener constructor, as follows:

new KestrelCommunicationListener(serviceContext, (url, listener) => ...

在此配置中,KestrelCommunicationListener 会自动从应用程序端口范围中选择未使用的端口。In this configuration, KestrelCommunicationListener will automatically select an unused port from the application port range.

Service Fabric 配置提供程序Service Fabric configuration provider

ASP.NET Core 中的应用程序配置基于配置提供程序建议的键值对。App configuration in ASP.NET Core is based on key-value pairs established by the configuration provider. 请阅读 ASP.NET Core 中的配置,详细了解一般的 ASP.NET Core 配置支持。Read Configuration in ASP.NET Core to understand more on general ASP.NET Core configuration support.

本部分介绍如何通过导入 Microsoft.ServiceFabric.AspNetCore.Configuration NuGet 包,将 Service Fabric 配置提供程序与 ASP.NET Core 配置相集成。This section describes how the Service Fabric configuration provider integrates with ASP.NET Core configuration by importing the Microsoft.ServiceFabric.AspNetCore.Configuration NuGet package.

AddServiceFabricConfiguration 启动扩展AddServiceFabricConfiguration startup extensions

导入 Microsoft.ServiceFabric.AspNetCore.Configuration NuGet 包后,需要将 Service Fabric 配置源注册到 ASP.NET Core 配置 API。After you import the Microsoft.ServiceFabric.AspNetCore.Configuration NuGet package, you need to register the Service Fabric Configuration source with ASP.NET Core configuration API. 为此,可以针对 IConfigurationBuilder 检查 Microsoft.ServiceFabric.AspNetCore.Configuration 命名空间中的 AddServiceFabricConfiguration 扩展。You do this by checking AddServiceFabricConfiguration extensions in the Microsoft.ServiceFabric.AspNetCore.Configuration namespace against IConfigurationBuilder.

using Microsoft.ServiceFabric.AspNetCore.Configuration;

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddServiceFabricConfiguration() // Add Service Fabric configuration settings.
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

现在,ASP.NET Core 服务可以访问 Service Fabric 配置设置,如同访问任何其他应用程序设置一样。Now the ASP.NET Core service can access the Service Fabric configuration settings, just like any other application settings. 例如,可以使用选项模式将设置加载到强类型化对象中。For example, you can use the options pattern to load settings into strongly typed objects.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyOptions>(Configuration);  // Strongly typed configuration object.
    services.AddMvc();
}

默认键映射Default key mapping

默认情况下,Service Fabric 配置提供程序包含包名称、节名称和属性名称。By default, the Service Fabric configuration provider includes the package name, section name, and property name. 它们共同构成了 ASP.NET Core 配置键,按如下所示:Together, these form the ASP.NET Core configuration key, as follows:

$"{this.PackageName}{ConfigurationPath.KeyDelimiter}{section.Name}{ConfigurationPath.KeyDelimiter}{property.Name}"

例如,如果名为 MyConfigPackage 的配置包包含以下内容,则可以通过 MyConfigPackage:MyConfigSection:MyParameter 在 ASP.NET Core IConfiguration 上使用配置值。For example, if you have a configuration package named MyConfigPackage with the following content, then the configuration value will be available on ASP.NET Core IConfiguration through MyConfigPackage:MyConfigSection:MyParameter.

<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">  
  <Section Name="MyConfigSection">
    <Parameter Name="MyParameter" Value="Value1" />
  </Section>  
</Settings>

Service Fabric 配置选项Service Fabric configuration options

Service Fabric 配置提供程序还支持使用 ServiceFabricConfigurationOptions 来更改键映射的默认行为。The Service Fabric configuration provider also supports ServiceFabricConfigurationOptions to change the default behavior of key mapping.

加密的设置Encrypted settings

与 Service Fabric 配置提供程序一样,Service Fabric 也支持加密的设置。Service Fabric supports encrypted settings, as does the Service Fabric configuration provider. 默认情况下,加密的设置不会解密到 ASP.NET Core IConfigurationThe encrypted settings aren't decrypted to ASP.NET Core IConfiguration by default. 而是存储在其中。The encrypted values are stored there instead. 但是,若要解密该值并将其存储在 ASP.NET Core IConfiguration 中,可以在 AddServiceFabricConfiguration 扩展中将 DecryptValue 标志设置为 false,如下所示:But if you want to decrypt the value to store in ASP.NET Core IConfiguration, you can set the DecryptValue flag to false in the AddServiceFabricConfiguration extension, as follows:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    var builder = new ConfigurationBuilder()        
        .AddServiceFabricConfiguration(activationContext, (options) => options.DecryptValue = false); // set flag to decrypt the value
    Configuration = builder.Build();
}

多个配置包Multiple configuration packages

Service Fabric 支持多个配置包。Service Fabric supports multiple configuration packages. 默认情况下,包名称将包含在配置键中。By default, the package name is included in the configuration key. 但是,可将 IncludePackageName 标志设置为 false,如下所示:But you can set the IncludePackageName flag to false, as follows:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    var builder = new ConfigurationBuilder()        
        // exclude package name from key.
        .AddServiceFabricConfiguration(activationContext, (options) => options.IncludePackageName = false); 
    Configuration = builder.Build();
}

自定义键映射、值提取和数据填充Custom key mapping, value extraction, and data population

Service Fabric 配置提供程序还支持使用更高级的方案来通过 ExtractKeyFunc 自定义键映射,以及通过 ExtractValueFunc 按自定义方式提取值。The Service Fabric configuration provider also supports more advanced scenarios to customize the key mapping with ExtractKeyFunc and custom-extract the values with ExtractValueFunc. 甚至可以使用 ConfigAction,来更改将 Service Fabric 配置中的数据填充到 ASP.NET Core 配置的整个过程。You can even change the whole process of populating data from Service Fabric configuration to ASP.NET Core configuration by using ConfigAction.

以下示例演示如何使用 ConfigAction 来自定义数据填充:The following examples illustrate how to use ConfigAction to customize data population:

public Startup()
{
    ICodePackageActivationContext activationContext = FabricRuntime.GetActivationContext();
    
    this.valueCount = 0;
    this.sectionCount = 0;
    var builder = new ConfigurationBuilder();
    builder.AddServiceFabricConfiguration(activationContext, (options) =>
        {
            options.ConfigAction = (package, configData) =>
            {
                ILogger logger = new ConsoleLogger("Test", null, false);
                logger.LogInformation($"Config Update for package {package.Path} started");

                foreach (var section in package.Settings.Sections)
                {
                    this.sectionCount++;

                    foreach (var param in section.Parameters)
                    {
                        configData[options.ExtractKeyFunc(section, param)] = options.ExtractValueFunc(section, param);
                        this.valueCount++;
                    }
                }

                logger.LogInformation($"Config Update for package {package.Path} finished");
            };
        });
  Configuration = builder.Build();
}

配置更新Configuration updates

Service Fabric 配置提供程序还支持配置更新。The Service Fabric configuration provider also supports configuration updates. 可以使用 ASP.NET Core IOptionsMonitor 接收更改通知,然后使用 IOptionsSnapshot 重新加载配置数据。You can use ASP.NET Core IOptionsMonitor to receive change notifications, and then use IOptionsSnapshot to reload configuration data. 有关详细信息,请参阅 ASP.NET Core 选项For more information, see ASP.NET Core options.

默认支持这些选项。These options are supported by default. 无需进一步编写代码即可启用配置更新。No further coding is needed to enable configuration updates.

方案和配置Scenarios and configurations

本部分提供 Web 服务器、端口配置、Service Fabric 集成选项的组合,以及建议用来排查以下问题的其他设置:This section provides the combination of web server, port configuration, Service Fabric integration options, and miscellaneous settings we recommend to troubleshoot the following scenarios:

  • 外部公开的 ASP.NET Core 无状态服务Externally exposed ASP.NET Core stateless services
  • 仅限内部的 ASP.NET Core 无状态服务Internal-only ASP.NET Core stateless services
  • 仅限内部的 ASP.NET Core 有状态服务Internal-only ASP.NET Core stateful services

外部公开的服务公开可从群集外部调用的终结点(通常通过负载均衡器)。An externally exposed service is one that exposes an endpoint that's called from outside the cluster, usually through a load balancer.

仅限内部的服务的终结点只能从群集内部调用。An internal-only service is one whose endpoint is only called from within the cluster.

备注

通常不应将有状态服务终结点公开到 Internet。Stateful service endpoints generally shouldn't be exposed to the internet. 位于无法识别 Service Fabric 服务解析的负载均衡器(如 Azure 负载均衡器)后的群集将无法公开有状态服务。Clusters behind load balancers that are unaware of Service Fabric service resolution, such as Azure Load Balancer, will be unable to expose stateful services. 这是因为,负载均衡器无法找到流量并将其路由到相应的有状态服务副本。That's because the load balancer won't be able to locate and route traffic to the appropriate stateful service replica.

外部公开的 ASP.NET Core 无状态服务Externally exposed ASP.NET Core stateless services

对于公开面向 Internet 的外部 HTTP 终结点的前端服务,Kestrel 是建议的 Web 服务器。Kestrel is the suggested web server for front-end services that expose external, internet-facing HTTP endpoints. 在 Windows 上,HTTP.sys 可提供端口共享功能,允许使用同一端口在同一组节点上托管多个 Web 服务。On Windows, HTTP.sys can provide port sharing capability, which allows you to host multiple web services on the same set of nodes using the same port. 在此情况下,Web 服务按主机名或路径进行区分,而不依赖前端代理或网关来提供 HTTP 路由。In this scenario, the web services are differentiated by host name or path, without relying on a front-end proxy or gateway to provide HTTP routing.

向 Internet 公开时,无状态服务应使用可通过负载均衡器到达的已知稳定终结点。When exposed to the internet, a stateless service should use a well-known and stable endpoint that's reachable through a load balancer. 需将此 URL 提供给应用程序的用户。You'll provide this URL to your application's users. 建议使用以下配置:We recommend the following configuration:

说明Notes
Web 服务器Web server KestrelKestrel Windows 和 Linux 都支持 Kestrel,因此它是首选的 Web 服务器。Kestrel is the preferred web server, as it's supported across Windows and Linux.
端口配置Port configuration 静态static 应在 ServiceManifest.xml 的 Endpoints 配置中配置已知静态端口,例如为 HTTP 配置 80 或为 HTTPS 配置 443。A well-known static port should be configured in the Endpoints configuration of ServiceManifest.xml, such as 80 for HTTP or 443 for HTTPS.
ServiceFabricIntegrationOptionsServiceFabricIntegrationOptions NoneNone 配置 Service Fabric 集成中间件时应使用 ServiceFabricIntegrationOptions.None 选项,以使服务不会验证传入请求是否具有唯一标识符。Use the ServiceFabricIntegrationOptions.None option when configuring Service Fabric integration middleware, so that the service doesn't attempt to validate incoming requests for a unique identifier. 应用程序的外部用户不会知道中间件使用的唯一标识信息。External users of your application won't know the unique identifying information that the middleware uses.
实例计数Instance Count -1-1 在典型用例中,应将实例计数设置设置为 -1In typical use cases, the instance count setting should be set to -1. 这样,便可以在从负载均衡器接收流量的所有节点上使用某个实例。This is done so that an instance is available on all nodes that receive traffic from a load balancer.

如果多个外部公开的服务共享同一组节点,可通过唯一且稳定的 URL 路径使用 HTTP.sys。If multiple externally exposed services share the same set of nodes, you can use HTTP.sys with a unique but stable URL path. 可以通过修改配置 IWebHost 时提供的 URL 来实现此目的。You can accomplish this by modifying the URL provided when configuring IWebHost. 请注意,这仅适用于 HTTP.sys。Note that this applies to HTTP.sys only.

new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
    url += "/MyUniqueServicePath";

    return new WebHostBuilder()
        .UseHttpSys()
        ...
        .UseUrls(url)
        .Build();
})

仅限内部的无状态 ASP.NET Core 服务Internal-only stateless ASP.NET Core service

仅从群集内部调用的无状态服务应使用唯一的 URL 和动态分配的端口,以确保多个服务之间的协作正常进行。Stateless services that are only called from within the cluster should use unique URLs and dynamically assigned ports to ensure cooperation between multiple services. 建议使用以下配置:We recommend the following configuration:

说明Notes
Web 服务器Web server KestrelKestrel 尽管 HTTP.sys 可用于内部无状态服务,但 Kestrel 是最佳的服务器,它允许多个服务实例共享一台主机。Although you can use HTTP.sys for internal stateless services, Kestrel is the best server to allow multiple service instances to share a host.
端口配置Port configuration 动态分配dynamically assigned 有状态服务的多个副本可能会共享主机进程或主机操作系统,因此需要唯一端口。Multiple replicas of a stateful service might share a host process or host operating system and thus will need unique ports.
ServiceFabricIntegrationOptionsServiceFabricIntegrationOptions UseUniqueServiceUrlUseUniqueServiceUrl 通过动态端口分配,此设置可以防止前面所述的错误标识问题。With dynamic port assignment, this setting prevents the mistaken identity issue described earlier.
InstanceCountInstanceCount 任意any 可根据操作服务的需要将实例计数设置设置为任何值。The instance count setting can be set to any value necessary to operate the service.

仅限内部的有状态 ASP.NET Core 服务Internal-only stateful ASP.NET Core service

仅从群集内部调用的有状态服务应使用动态分配的端口,以确保多个服务之间的协作正常进行。Stateful services that are only called from within the cluster should use dynamically assigned ports to ensure cooperation between multiple services. 建议使用以下配置:We recommend the following configuration:

说明Notes
Web 服务器Web server KestrelKestrel HttpSysCommunicationListener 不能用于副本在其中共享主机进程的有状态服务。The HttpSysCommunicationListener isn't designed for use by stateful services in which replicas share a host process.
端口配置Port configuration 动态分配dynamically assigned 有状态服务的多个副本可能会共享主机进程或主机操作系统,因此需要唯一端口。Multiple replicas of a stateful service might share a host process or host operating system and thus will need unique ports.
ServiceFabricIntegrationOptionsServiceFabricIntegrationOptions UseUniqueServiceUrlUseUniqueServiceUrl 通过动态端口分配,此设置可以防止前面所述的错误标识问题。With dynamic port assignment, this setting prevents the mistaken identity issue described earlier.

后续步骤Next steps

使用 Visual Studio 调试 Service Fabric 应用程序Debug your Service Fabric application by using Visual Studio