在本文中,将探讨如何将现有的 CarClient 和 CarAPI 项目迁移到 Service Fabric 集群,并在本地集群环境中运行。这个过程涉及到对原有代码的修改,以及对 Service Fabric 环境的配置。
本文基于之前的一篇文章,其中包含了原始的 CarClient 和 CarAPI 项目。在本文中,将这些项目迁移到了 Service Fabric 集群中。
在本地集群中部署和运行此应用程序之前,请确保计算机上没有使用 http://localhost:80 和 http://localhost:83。如果 http://localhost:80 已经被占用,可以在 CarClient 的 ServiceManifest.xml 文件中更改端口。例如,要使用端口 8080,请写入:
<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="8080" />
然后,可以通过 http://localhost:8080 在浏览器中启动程序。请注意,此代码仅在 Google Chrome 上进行了测试。CarClient 中的 JavaScript 在 Google Chrome 上运行良好,但在 Internet Explorer 和 FireFox 上可能会遇到问题。
要运行此示例,需要一个 Azure 帐户和自己的 Azure SQL Server 应用程序。必须更新连接字符串,以指向自己的 Azure SQL Server。还需要安装 Service Fabric SDK 时获得的 Service Fabric Explorer 和 Service Fabric Cluster Manager。
SQLite 已被AzureSQL Server 替换,CarClient 和 CarAPI 项目现在成为了无状态的 Service Fabric 服务。
通过 Visual Studio 创建此解决方案时,选择了Service Fabric应用程序作为解决方案模板。在解决方案中,添加了两个无状态的 ASP.NET Core 项目:一个 WebAPI 和一个 Web 应用程序,如下所示:
将旧的 CarAPI 和 CarClient 的代码添加到了默认项目中,尽可能少地进行修改。该解决方案可以在 Azure 集群和本地集群中运行。Azure 集群是通过 Visual Studio 创建的。当从 Visual Studio 创建 SF 集群时,将获得一个使用证书安全性的集群。这迫使应用程序在 Azure 中使用 https 进行反向代理,而不是在本地运行时使用 http。
在Azure中,CarAPI 使用的 URL 是 http://carfabric.northeurope.cloudapp.azure.com:83/,而在本地集群中,使用的 URL 是 http://localhost:83/。这个 URL 被 CarClient 中的 JavaScript 用于从数据库中检索和更新数据。
可以在本地集群中通过 http://localhost:83/swagger 检查 CarAPI。对于 CarClient,使用相同的 URL,只是端口号更改为 80。URL 通过环境变量 ApiAddressConnectionString 传递,该变量在 CarClient 的 ServiceManifest.xml 中定义。在本地和Azure中运行应用程序时如何定义该值,可以在 ApplicationParameters 文件夹中的 Cloud.xml 和 Local1.xml 中看到。获取反向代理地址的代码如下:
public static Uri GetProxyAddress(Uri serviceName)
{
string ApiAddress = Environment.GetEnvironmentVariable("ApiAddressConnectionString");
if (ApiAddress.IndexOf("localhost") > 0)
{
return new Uri($"http://localhost:19081{serviceName.AbsolutePath}/");
}
else
{
return new Uri($"https://localhost:19081{serviceName.AbsolutePath}/");
}
}
这段代码位于 CarClient 项目中的 Utils.cs 文件中。
比较旧的传统解决方案和服务织物解决方案中的 HTTP 客户端代码,可以清楚地看到差异。
新的 Service Fabric 代码:
namespace CarClient
{
public static class Utils
{
public static async Task<T> Get<T>(string url)
{
Uri serviceName = CarClientApp.GetCarAPIServiceName();
Uri proxyAddress = GetProxyAddress(serviceName);
HttpClientHandler handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
using (var client = new HttpClient(handler) { BaseAddress = proxyAddress })
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
return await Task.Run(() => JsonConvert.DeserializeObject<T>(content));
}
}
}
}
旧代码如下:
namespace CarClient
{
public static class Utils
{
private static readonly Uri Endpoint = new Uri("http://localhost:54411/");
public static async Task<T> Get<T>(string url)
{
using (var client = new HttpClient() { BaseAddress = Endpoint })
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
return await Task.Run(() => JsonConvert.DeserializeObject<T>(content));
}
}
}
}
要了解实施细节,请下载 CarFabric.zip 并阅读代码。