快速开发是否可能 - (门户)Service Fabric 项目中的 Web 应用程序
Is rapid development possible - (portal) Webapplication inside Service Fabric project
我添加了一个 WebApplication 来将我们的 Web 门户构建到现有的 ServiceFabric 集群 VS sln 设置中。但是,我很快意识到这不适用于快速 development/debug 循环。为了更改 CSHTML 文件然后在浏览器中快速试用它们,我需要能够在 VSTS 中按 F5 并快速获得 portal.exe 运行 并提供页面;所以我切换到 "Portal" 项目作为启动项目(粗体)并使用 IISExpress 或 Portal 配置文件(来自 launchSettings.json)。
但是,失败并出现异常:
System.Fabric.FabricException: 'An error occurred during this operation. Please check the trace logs for more details.'
来自这一行:
ServiceRuntime.RegisterServiceAsync("PortalApp",
context => new APIGateway(context)).GetAwaiter().GetResult();
这是因为我不在 SF 集群内 运行ning。有道理。
那么如何让这个场景发挥作用?是否有一种方法可以在无需每次都将解决方案部署到集群的情况下进行开发?即使在单节点本地集群中,这也非常慢(最多 30-45 秒)并且严重阻碍了生产力。
有没有我可以添加的 "RunLocal" 代码路径来规避和创建一个不依赖于 SF 运行time,但仍然使用我的项目的其余部分和 运行s 的 WebHost在集群外?
好的,我为我的场景解决了这个问题(大部分)。
这是我所做的。基本上,我没有使用 Service FabricRuntime 在 program.cs 中创建 WebHost(基本上只在 SF 集群内部工作),而是检查我们是否在 运行 集群内部(从这里借来的)和然后使用相同的 Startup class 实例化我自己的 WebHost 实例,以便使用相同的设置配置 webHost(在我的例子中是 Kestrel)。从那时起,接管所有原始代码。
这适用于托管 WebHost(http 侦听器)的 ServiceFabric 项目。但是,我有另一个 SF 项目没有,其他人通过服务远程处理调用它。不幸的是,我怀疑可以像上面那样容易地工作。
完成这些更改后,您可以更改解决方案的启动项目以包含 1 个或所有虚拟主机 EXE。然后按 F5,这将在集群外部本地启动 (1..N) EXE,您可以在那里调试它们。这样我的工作流程就是离开虚拟主机 EXE 运行,更改我的 CSHTML 文件,保存,然后只刷新网页。可以立即看到和测试更改!对其他编译代码的更改也可以非常快速地进行测试。
(从这个帖子中获得了上述一些想法:How to see if running under service fabric)
代码如下:
// program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.ServiceFabric.Services.Runtime;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Web.Portal
{
internal static class Program
{
/// <summary>
/// This is the entry point of the service host process.
/// </summary>
private static void Main(string[] args)
{
if (Environment.GetEnvironmentVariable("Fabric_ApplicationName") == null)
{
ServiceEventSource.Current.Message("Detected as running locally OUTSIDE the Fabric Cluster");
BuildWebHost(args).Run();
}
else
{
ServiceEventSource.Current.Message("Detected as running INSIDE the Fabric Cluster");
try
{
ServiceRuntime.RegisterServiceAsync("Web.PortalType",
context => new Portal(context)).GetAwaiter().GetResult();
// The ServiceManifest.XML file defines one or more service type names.
// Registering a service maps a service type name to a .NET type.
// When Service Fabric creates an instance of this service type,
// an instance of the class is created in this host process.
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Portal).Name);
// Prevents this host process from terminating so services keeps running.
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}
public static IWebHost BuildWebHost(string[] args)
{
// for some reason, the appSetting.json values arent getting loaded by default config builder
// even though, per MSDN, they should be in ASPNET Core 2.0. I am adding them in manually for now.
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appSettings.Development.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
return new WebHostBuilder()
.UseConfiguration(config)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
}
}
}
在 Service Fabric SDK 上,您有名为“Refresh Application”的应用程序调试模式,部署此功能将创建一个符号 link 从 SF 部署到您的开发路径,您所做的每一次更改到您的文件,将反映在 SF 运行 应用程序
上的更改
要使用此部署模式,您可以通过以下方式更改此设置:右键单击您的 SF 项目 > 属性 > 属性 window 将显示设置(如下)> 从 "remove application" 切换到"Refresh Application"
此模式有一些限制:
- It only works on a one node development cluster.
- It only supports changes to the contents of code, config, or data packages and the project directory contents in the case of ASP.NET
Core projects. Application structural changes - like changes to
ServiceManifest.xml or ApplicationManifest.xml - aren't supported
because the structure of the deployed application may change with
unwanted results. You can still make structural changes while in
Refresh Application debug mode, but if you do, the application will be
redeployed.
- Stateful services don't maintain their state if you recompile. In order to get your new binaries running, the service has to be removed
to stop the process so new binaries can be written to disk, which of
course means all your state is wiped out. This is usually fine for
rapid development, but to really test your stateful services you'll
still need to run them in a 5-node cluster.
我添加了一个 WebApplication 来将我们的 Web 门户构建到现有的 ServiceFabric 集群 VS sln 设置中。但是,我很快意识到这不适用于快速 development/debug 循环。为了更改 CSHTML 文件然后在浏览器中快速试用它们,我需要能够在 VSTS 中按 F5 并快速获得 portal.exe 运行 并提供页面;所以我切换到 "Portal" 项目作为启动项目(粗体)并使用 IISExpress 或 Portal 配置文件(来自 launchSettings.json)。
但是,失败并出现异常:
System.Fabric.FabricException: 'An error occurred during this operation. Please check the trace logs for more details.'
来自这一行:
ServiceRuntime.RegisterServiceAsync("PortalApp",
context => new APIGateway(context)).GetAwaiter().GetResult();
这是因为我不在 SF 集群内 运行ning。有道理。
那么如何让这个场景发挥作用?是否有一种方法可以在无需每次都将解决方案部署到集群的情况下进行开发?即使在单节点本地集群中,这也非常慢(最多 30-45 秒)并且严重阻碍了生产力。
有没有我可以添加的 "RunLocal" 代码路径来规避和创建一个不依赖于 SF 运行time,但仍然使用我的项目的其余部分和 运行s 的 WebHost在集群外?
好的,我为我的场景解决了这个问题(大部分)。
这是我所做的。基本上,我没有使用 Service FabricRuntime 在 program.cs 中创建 WebHost(基本上只在 SF 集群内部工作),而是检查我们是否在 运行 集群内部(从这里借来的)和然后使用相同的 Startup class 实例化我自己的 WebHost 实例,以便使用相同的设置配置 webHost(在我的例子中是 Kestrel)。从那时起,接管所有原始代码。
这适用于托管 WebHost(http 侦听器)的 ServiceFabric 项目。但是,我有另一个 SF 项目没有,其他人通过服务远程处理调用它。不幸的是,我怀疑可以像上面那样容易地工作。
完成这些更改后,您可以更改解决方案的启动项目以包含 1 个或所有虚拟主机 EXE。然后按 F5,这将在集群外部本地启动 (1..N) EXE,您可以在那里调试它们。这样我的工作流程就是离开虚拟主机 EXE 运行,更改我的 CSHTML 文件,保存,然后只刷新网页。可以立即看到和测试更改!对其他编译代码的更改也可以非常快速地进行测试。
(从这个帖子中获得了上述一些想法:How to see if running under service fabric)
代码如下:
// program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.ServiceFabric.Services.Runtime;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Web.Portal
{
internal static class Program
{
/// <summary>
/// This is the entry point of the service host process.
/// </summary>
private static void Main(string[] args)
{
if (Environment.GetEnvironmentVariable("Fabric_ApplicationName") == null)
{
ServiceEventSource.Current.Message("Detected as running locally OUTSIDE the Fabric Cluster");
BuildWebHost(args).Run();
}
else
{
ServiceEventSource.Current.Message("Detected as running INSIDE the Fabric Cluster");
try
{
ServiceRuntime.RegisterServiceAsync("Web.PortalType",
context => new Portal(context)).GetAwaiter().GetResult();
// The ServiceManifest.XML file defines one or more service type names.
// Registering a service maps a service type name to a .NET type.
// When Service Fabric creates an instance of this service type,
// an instance of the class is created in this host process.
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Portal).Name);
// Prevents this host process from terminating so services keeps running.
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
}
public static IWebHost BuildWebHost(string[] args)
{
// for some reason, the appSetting.json values arent getting loaded by default config builder
// even though, per MSDN, they should be in ASPNET Core 2.0. I am adding them in manually for now.
IConfiguration config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appSettings.Development.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
return new WebHostBuilder()
.UseConfiguration(config)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
}
}
}
在 Service Fabric SDK 上,您有名为“Refresh Application”的应用程序调试模式,部署此功能将创建一个符号 link 从 SF 部署到您的开发路径,您所做的每一次更改到您的文件,将反映在 SF 运行 应用程序
上的更改要使用此部署模式,您可以通过以下方式更改此设置:右键单击您的 SF 项目 > 属性 > 属性 window 将显示设置(如下)> 从 "remove application" 切换到"Refresh Application"
此模式有一些限制:
- It only works on a one node development cluster.
- It only supports changes to the contents of code, config, or data packages and the project directory contents in the case of ASP.NET Core projects. Application structural changes - like changes to ServiceManifest.xml or ApplicationManifest.xml - aren't supported because the structure of the deployed application may change with unwanted results. You can still make structural changes while in Refresh Application debug mode, but if you do, the application will be redeployed.
- Stateful services don't maintain their state if you recompile. In order to get your new binaries running, the service has to be removed to stop the process so new binaries can be written to disk, which of course means all your state is wiped out. This is usually fine for rapid development, but to really test your stateful services you'll still need to run them in a 5-node cluster.