在云中使用事件驱动的架构, 借助 Windows Azure






4.95/5 (21投票s)
为了演示 Azure 和 EDA,本文将向您展示如何在 Windows Azure 云端为美国所有机场构建一个航班延误管理系统。
引言
本文的目标是通过构建一个云端的 .NET 事件驱动系统,来简化 Windows Azure 和事件驱动架构。让我们从一些基础且枯燥的定义开始。
- 事件驱动架构: “一种促进事件的产生、检测、消费和响应的模式。” - Wikipedia
- Windows Azure:“一个云服务操作系统,它是 Windows Azure 平台的开发、服务托管和服务的管理环境。Windows Azure 通过 Microsoft® 数据中心为开发人员提供按需计算和存储能力,以在互联网上托管、扩展和管理 Web 应用程序。” - Microsoft。
现在您知道上面那个火柴人为什么抓耳挠腮了。尽管这些定义是准确的,但在没有实际案例的情况下,很难抽象出事件驱动架构 (EDA) 的具体实现,也很难理解 Windows Azure 的真正价值。
为了超越教科书式的定义来演示 Azure 和 EDA,本文将向您展示如何在云端为美国所有机场构建一个航班延误管理系统。本文的格式旨在模拟动手实验,引导您完成开发和部署云端应用程序所需的每一步。这个航班延误管理系统将采用多种技术混合构建,包括 SQL 2008 Azure、Visual Studio 2010、Azure 队列服务、Azure 工作进程/Web 角色、Silverlight 3、Twitter SDK 和 Microsoft Bing Maps。虽然本文的实现基于航班延误系统,但这些概念可以应用于跨职能域的云端 IT 系统构建。
航班延误管理器:事件驱动架构 (EDA)
关于事件驱动架构的信息在网络和印刷出版物中都有详尽的记载且广泛可用。有关事件驱动系统和架构模式的背景信息,请参阅 Google 软件架构师 Gregor Hohpe 撰写的这篇 EDA 文章。
本文将侧重于那些尚未被充分记载的内容,即关于如何使用 Visual Studio 2010 和 Windows Azure 等新兴技术来实现 EDA 的示例。为了设计和构建航班延误管理器,我们需要建立 EDA 的基础。
- EDA 在高层级上有四个逻辑层:事件生成、事件传输、事件处理和下游事件活动。
- EDA 由事件的异步流动驱动,这与典型的 SOA 系统中的请求/响应模式直接相反。
- 人类是完美的 EDA 系统的绝佳例子;我们每天都能生成、处理和响应数百万个事件。
对于航班延误管理器,EDA 模型将是:
如您所见,EDA 的四个逻辑层中的每一层都已转换为我们将要构建的 Azure 组件,用于驱动我们的航班延误系统。该设计基于简单的事件处理 (SEP),可以通过接入额外的事件源(如航空公司的延误流和天气预报)来扩展到复杂的事件处理 (CEP)。CEP 可用于通过实时分析传入的事件流来预测航班延误和取消。虽然本文未涵盖 CEP,但未来使用 CEP 的航班延误系统的版本是完全可能的。
航班延误管理器:Windows Azure 设置
要配置和构建本文中的解决方案,您将需要一个 Windows Azure 帐户和 SQL Azure 帐户。Microsoft 目前提供这些服务的免费试用。请访问:http://www.microsoft.com/windowsazure/pricing/ 注册您的帐户。
现在您已经拥有 Azure 帐户,您需要设置上面图表中所示的“事件传输:队列”组件。登录 Azure 开发人员门户 http://windows.azure.com/,然后创建一个新的服务,配置为队列存储。
继续按照 Azure 提示进行操作,例如队列名称和其他基本元数据;完成后,Azure 将显示安全令牌和队列的公共 HTTP 端点。此 Azure 队列将用于在消息生成器和处理器之间传输原始航班延误消息的事件。
航班延误被事件处理器转换为数据点,并由下游的 Silverlight 客户端使用。这些数据点存储在 SQL 2008 Azure 中。要创建数据库,请进入 SQL 门户并单击“创建数据库”,数据库名称 = FlightDelay。
需要注意的是,在使用 SQL Azure 时,需要 Management Studio R2 才能使用对象资源管理器访问 Azure 数据库。并且,不要忘记将您的开发机器 IP 地址添加到防火墙列表(上面的第二个选项卡)并勾选“允许 Microsoft 服务访问此服务器”复选框。就是这样!Windows Azure 后端设置已完成。
航班延误管理器:开发机器设置
在下一节中,我们将继续进行应用程序开发。要在 Windows Azure 中进行开发,您的开发机器上需要安装以下组件:
- IIS 7 + 操作系统:Windows 7、Windows Vista SP1 或 Windows Server 2008
- Visual Studio 2010:http://www.microsoft.com/visualstudio/en-us/products/2010/
- SQL 2008:http://www.microsoft.com/express/Database/
- Windows Azure 工具和 SDK:http://www.microsoft.com/windowsazure/getstarted/
- (可选)Expression Blend – Silverlight 3 开发工具:http://www.microsoft.com/expression/try-it/
在您的开发机器准备就绪并能访问 Azure 后,您需要设置航班延误解决方案。
- 在上面一节中创建的 SQL 2008 Azure 数据库上执行 *FlightDelayManager.sql*。
- 在 Visual Studio 2010 中打开 *FlightUpdates.sln* 并构建解决方案。
- 在项目 *MessageGeneratorService* / *MessageProcessorService* / Web 中,更改(*app/web.config*)中“
FlightUpdates
”连接字符串的配置,指向上面创建的 Azure 数据库。连接字符串可直接从 Azure 开发人员门户复制。
Twitter 消息生成器和消息处理器
消息生成器将负责从 Twitter (http://twitter.com/FltAdvisor) 获取航班延误信息,并将这些消息推送到 Azure 队列。要开始使用消息生成器,请加载 Visual Studio 2010 并打开 *FlightDelayManager.sln*。进入 Twitter Message Generator 解决方案文件夹(也显示了 Azure 项目创建步骤)。
蓝色图标表示一个 Azure 云项目,其中包含两个基本组件:角色和服务配置。角色映射回要在云端执行代码的 .NET 项目。服务配置是云部署包外部的应用程序设置,允许在云端进行动态配置更改。
消息生成器需要访问 Azure 队列来传输 Twitter 航班延误消息。为此,请进入项目“FlightUpdates.MessageGenerator”,打开 *ServiceConfiguration.cscfg*,并将此设置更新为您的 Windows Azure 队列(密钥可在 http://windows.azure.com/ 获取)。
<Setting name="FlightUpdatesQueue"
value="DefaultEndpointsProtocol=http;AccountName=flightupdates;
QueueEndpoint=http://yourqueuename.queue.core.windows.net/;
AccountKey=fakekey...C++
EbamYR7Jv/9Dz9900p9g5FeTlvb9C0DUCeAcXKLch
5PEfQafrIqksuUVaGHAWZCCZqkEiw==" />
接下来,打开 *TwitterMonitorService.cs*。
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using FlightUpdates.Utility.Constants;
using FlightUpdates.TwitterMonitorService.Resource_Files;
namespace FlightUpdates.TwitterMonitorService
{
public class TwitterMonitorService : RoleEntryPoint
{
public override void Run()
{
try
{
Trace.Listeners.Add(new TraceWriterUtil());
Trace.WriteLine(Messages.ServiceStart);
FlightDelayMessageGenerator flightMessageGen =
new FlightDelayMessageGenerator();
while (true)
{
//check for flight delay updates
Trace.WriteLine(Messages.CheckForDelayMessages);
flightMessageGen.RetrieveFlightDelayMessages_PutInQueue();
…}}}
RoleEntryPoint
的实现将此类定义为 Azure 工作进程角色,该角色将轮询 Twitter Feed http://twitter.com/FltAdvisor 以获取航班延误信息。让我们看看如何在 .NET 中获取 Twitter 消息。
using RareEdge.Twitteroo;
core = new TwitterooCore(TwitterSettings.userName,
TwitterSettings.password);
core.PublicTimelineUrl =TwitterSettings.timeline;
users = core.GetTimeline(Timeline.Public);
Trace.WriteLine(Messages.TwitterConnectSuccess);
foreach (User message in users)
{
int postAgeMin = (int)DateTime.Now.Subtract
(message.Status.CreatedAt).TotalMinutes;
if ((postAgeMin < TwitterSettings.runInterMin) || debugMode)
{
string flightDelayMessage = message.Status.Text.ToLower().Trim();
InsertMessageIntoAzureQueue(flightDelayMessage);
}
}
对于这部分,您需要创建一个 Twitter 帐户并将您的凭据添加到类 FlightUpdates.Utility.Constants.TwitterSettings
中。上面的代码非常简单,这要归功于 TwitterooCore,一个免费的 Twitter .NET SDK,可在 http://rareedge.com/twitteroo/ 获取。用大约 10 行代码,我们就可以获取所有当前发布到 FltAdvisor
的推文。现在我们有了消息,让我们将推文插入 Azure 队列。
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
//get Azure queue for flight updates
//(end point set in ServiceConfiguration.cscfg)
var storageAccount = CloudStorageUtil.GetFlightUpdatesQueue();
CloudQueueClient queueStorage =
storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueStorage.GetQueueReference
(CloudQueues_QueueReferenceNames.FlightDelayMessages);
//create queue and add message
queue.Create();
queue.AddMessage(new CloudQueueMessage(flightDelayMessage));
CloudQueueClient
引用您的 Azure 队列实例,CloudQueue
是该实例内的特定队列,CloudQueueMessage
是出站消息。方法 CloudStorageUtil.GetFlightUpdatesQueue()
是标准的样板代码,用于引用我们在 *ServiceConfiguration.cscfg* 中配置的 Azure 队列。
现在我们已经完成了 Twitter 消息生成器的代码编写,将项目“FlightUpdates.MessageGenerator”设置为启动项目并运行应用程序。这将在本地 Azure 开发环境中运行应用程序,该环境将显示每条消息被添加到队列的跟踪信息。
接下来是消息处理器,负责航班延误消息的识别和转换。消息处理器需要访问配置为 Twitter 航班延误消息事件传输的 Azure 队列。为此,请进入项目“FlightUpdates.MessageProcessor”,打开 *ServiceConfiguration.cscfg*,并将此设置更新为您的 Windows Azure 队列。
<Setting name="FlightUpdatesQueue"
value="DefaultEndpointsProtocol=http;AccountName=flightupdates;
QueueEndpoint=http://yourqueuename.queue.core.windows.net/;
AccountKey=fakekey...C++
EbamYR7Jv/9Dz9900p9g5FeTlvb9C0DUCeAcXKLch5PEfQafrIqksuUVaGHAWZCCZqkEiw==" />
配置好队列后,应用程序现在可以通过以下代码块读取队列中的消息:
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
CloudQueueClient queueStorage = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueStorage.GetQueueReference
(CloudQueues_QueueReferenceNames.FlightDelayMessages);
CloudQueueMessage message = queue.GetMessage();
Trace.WriteLine(Messages.ConnectQueueDone);
while (message != null)
//create queue and add message
{
AddFlightDelayToDB(message.AsString);
queue.DeleteMessage(message);
message = queue.GetMessage();
}
检索到队列消息后,应用程序现在可以过滤掉无效消息并将有效消息转换为可用的航班延误数据。
现在,消息转换已完成,航班仪表板可以向下游用户传达延误信息。在下一节中,我们将创建航班延误仪表板,该仪表板通过轮询计时器检测来自消息/事件处理器的这些数据更新。
值得一提的是,这是事件处理器的一个简化实现。通常,此组件会近乎实时地通知下游客户端数据更改。当 Microsoft 重新发布 Azure Workflow(等待 .NET 4.0)时,航班延误管理器 R2 将被重构为使用 .NET Workflow 并向下游客户端发送实时消息。
Silverlight 3 航班仪表板与 Microsoft Bing Maps
这个仪表板实际上相当简单。这得益于 Microsoft 的 Silverlight Bing Map 控件。在做任何事情之前,您应该下载控件和 SDK。您有时间可以仔细研究一下 SDK。它包含一些非常有用的信息,可以帮助您入门。此外,您还需要一个开发人员许可证/密钥。这个密钥是必不可少的,并且访问 Bing Map 控件及其功能将需要它。
现在,在我们继续之前,我想简要提及仪表板的数据访问部分。方法相当简单,所以我不会详细介绍。但是,它确实值得一提。在这种情况下,仪表板有一个服务引用,指向一个相对简单的 Web 托管的 WCF 服务。该服务本身并不起眼,只有两个方法:检索机场和检索延误提要。唯一需要注意的是,为了避免创建跨域策略,该服务托管在与托管 Silverlight 仪表板应用程序相同的 Web 服务器上。
现在,让我们进入正题。在使用地图控件之前,您必须引用 SDK 下载安装的程序集。程序集应该位于您安装 SDK 的位置。一旦您引用了程序集,您就可以像添加其他 Silverlight 控件一样,将控件添加到您的 XAML(或通过代码)中。
<Border Margin="0" Grid.Column="1"
Grid.Row="1" BorderBrush="White"
BorderThickness="2">
<m:Map HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
x:Name="BingMap" Margin="0,0,-4,-4">
</m:Map>
</Border>
现在,您之前生成的密钥需要分配给控件上的 CredentialsProvider
属性。
<Border Margin="0" Grid.Column="1"
Grid.Row="1" BorderBrush="White"
BorderThickness="2">
<m:Map HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CredentialsProvider="Ap0NlORXtMcQuszz1GvM_wiZ8Y3Pq-
YGnHQN9SqV9qhkTnrVFEJ1q9JKbkQcFYXA"
x:Name="BingMap"
Margin="0,0,-4,-4">
</m:Map>
</Border>
在地图加载时,您很可能希望将地图中心设置在特定区域。有几种方法可以做到这一点。您可以同时提供地图的中心点和缩放级别,或者指定一个边界矩形和中心点。所有点都以经度和纬度测量,快速的 Google 搜索通常可以帮助您找到任何区域的经度和纬度。在此项目中,我们使用了包含美国大陆的边界矩形和中心点。
Location center = new Location(39.3683,-95.2734,0.0000);
LocationRect boundingRec = new LocationRect(center,6, 6);
BingMap.SetView(boundingRec);
地图设置好后,我们就想在上面显示一些有意义的信息。由于我们显示的是机场延误,因此显示每个机场的位置会很有意义。在该项目中,向机场位置添加一些推送图钉相当简单。我们只需要机场的经纬度。在这种情况下,这些信息作为列表由 WCF 服务返回给我们。
foreach (AirportInfo item in e.Result)
{
Pushpin pin = new Pushpin();
pin.Location = new Location();
pin.Location.Latitude = item.LatitudeDegree;
pin.Location.Longitude = -item.LongitudeDegree;
…
if (item.DelayTime <=30)
pin.Background = new SolidColorBrush(Colors.Green);
else if (item.DelayTime > 30 && item.DelayTime <= 60)
pin.Background = new SolidColorBrush(Colors.Yellow);
else
pin.Background = new SolidColorBrush(Colors.Red);
BingMap.Children.Add(pin);
}
推送图钉还有一些附加属性。对我们来说,我们希望根据机场延误的大小对图钉进行颜色编码。绿色图钉代表相对较小的延误,黄色代表中等延误,红色代表严重的延误。唯一奇怪的是,内置的推送图钉功能没有滚动文本。最佳做法是创建一个包含推送图钉作为基础的自定义控件。但是,为了快速解决,可以在 MapLayer
中添加一个文本块控件。MapLayer
允许使用地理坐标来定位其子元素。
<m:Map HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
CredentialsProvider="Ap0NlORXtMcQuszz1GvM_wiZ8Y3Pq-
YGnHQN9SqV9qhkTnrVFEJ1q9JKbkQcFYXA"
x:Name="BingMap" Margin="0,0,-4,-4">
<m:MapLayer x:Name="ml_MapText" Margin="0">
<Grid x:Name="grd_Rollover"
HorizontalAlignment="Right"
Margin="0"
VerticalAlignment="Bottom"
Visibility="Collapsed"
Canvas.ZIndex="1000">
<Border Margin="0" Background="Black"
BorderThickness="1"
CornerRadius="5" Opacity="0.785"/>
<Border Margin="0" BorderBrush="White"
BorderThickness="2" CornerRadius="5">
<TextBlock x:Name="tbk_AirportNameLabel"
Text="TextBlock" TextWrapping="Wrap"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White" Padding="10"/>
</Border>
</Grid>
</m:MapLayer>
</m:Map>
为了完成这个滚动文本,我们将为每个推送图钉添加一些事件,这些事件将使文本可见并显示正确的文本。此外,我们将机场项存储为图钉数据上下文的一部分。
foreach (AirportInfo item in e.Result)
{
Pushpin pin = new Pushpin();
pin.Location = new Location();
pin.Location.Latitude = item.LatitudeDegree;
pin.Location.Longitude = -item.LongitudeDegree;
pin.MouseEnter += new MouseEventHandler(pin_MouseEnter);
pin.MouseLeave += new MouseEventHandler(pin_MouseLeave);
pin.MouseLeftButtonUp += new MouseButtonEventHandler(pin_MouseLeftButtonUp);
if (item.DelayTime <=30)
pin.Background = new SolidColorBrush(Colors.Green);
else if (item.DelayTime > 30 && item.DelayTime <= 60)
pin.Background = new SolidColorBrush(Colors.Yellow);
else
pin.Background = new SolidColorBrush(Colors.Red);
pin.DataContext = item;
BingMap.Children.Add(pin);
}
MouseEnter
事件将使 TextBlock
可见,设置 TextBlock
的当前位置,并从数据上下文中提取当前信息并用其填充 TextBlock
。
void pin_MouseEnter(object sender, MouseEventArgs e)
{
Pushpin selectedPin = sender as Pushpin;
AirportInfo selectedPinInfo = selectedPin.DataContext as AirportInfo;
Location loc = new Location(selectedPinInfo.LatitudeDegree,
-selectedPinInfo.LongitudeDegree);
tbk_AirportNameLabel.Text = selectedPinInfo.AirportName +
" (" + selectedPinInfo.AirportCode + ") ";
MapLayer.SetPosition(grd_Rollover, loc);
grd_Rollover.Visibility = System.Windows.Visibility.Visible;
}
上面值得注意的代码是 MapLayer.SetPosition(grd_Rollover, loc);
。这个小小的亮点可以定位大部分(如果不是全部)Silverlight 元素。此属性也可以在 XAML 中设置。MouseLeave
事件只是使 TextBlock
不可见。
void pin_MouseLeave(object sender, MouseEventArgs e)
{
grd_Rollover.Visibility = System.Windows.Visibility.Collapsed;
}
现在,让我们让它做一些更有意义的事情。最后一个事件,MouseLeftButtonUp
(奇怪的是,在撰写本文时没有 Click
事件),将返回服务以检索用户选择的机场的航班延误信息。与滚动文本类似,机场信息是从图钉的数据上下文中检索的,然后将该信息发送回服务。
void pin_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
VisualStateManager.GoToState(this, "FlightDelayHidden", false);
Pushpin selectedPin = sender as Pushpin;
AirportInfo selectedPinInfo = selectedPin.DataContext as AirportInfo;
AirportCodeServiceClient airportService = new AirportCodeServiceClient();
airportService.GetFlightDelayFeedCompleted+=
new EventHandler<GetFlightDelayFeedCompletedEventArgs>
(airportService_GetFlightDelayFeedCompleted);
airportService.GetFlightDelayFeedAsync(selectedPinInfo.AirportCode);
tbk_Airport.Text = selectedPinInfo.AirportName +
" (" + selectedPinInfo.AirportCode + ") ";
}
生成的事件处理程序将填充地图旁边的 DataGrid
。最终结果应该有助于您更好地管理您的行程……。
航班延误管理器:Windows Azure 部署
FlightUpdates 解决方案为每个组件都包含一个 Windows Azure 项目,允许将航班仪表板、消息处理器和消息生成器作为单独的 Azure 服务进行部署。此外,该解决方案还包含一个主 Azure 项目,该项目将所有三个组件(Azure 角色)捆绑到一个 Azure 服务中。根据我最初的 Azure 经验,我建议采用每个组件一个 Azure 项目的方法。这使得部署更容易测试和故障排除,并且 Azure 服务启动速度更快。
让我们采用每个组件一个 Azure 项目的方法,将航班延误管理器仪表板部署到云端。登录 Azure 开发人员门户 http://windows.azure.com/,创建一个名为 FlightManager_code 的新服务。
现在服务已创建,转到 Visual Studio,打开 Silverlight 配置文件“ServiceReferences.ClientConfig”,并将端点更改为上面创建的 URL(http://myappname.cloudapp.net)。
接下来,突出显示 FlightUpdates.AzureWeb
,然后从“生成”菜单中单击“发布”。然后,在 Windows Azure 门户中,上传服务包和服务配置。
就是这样;您现在已经在云端运行了一个应用程序。单击“启动”以启用您的部署,然后单击 Azure 链接以运行航班延误管理器。您应该会看到一个显示所有发布到 Twitter 的美国航班延误的地图,并且可以深入到某个城市以获取航班详细信息。
航班延误管理器 R2
航班延误管理器 R1 是一个简化的事件驱动架构 (EDA) 实现示例。航班延误管理器 R2 将扩展本文建立的代码库和基础 Azure 技能。航班延误管理器 R2 将转变为基于 CEP(复杂事件处理)的实现。目前的想法是(欢迎您提出您希望看到的内容):
- 将事件传输重构为 AppFabric Service Bus,并结合多个实时航班延误事件源。
- 将事件处理器重构为 Azure Workflow,处理多个下游活动,例如移动航班延误警报。
- Silverlight 延误仪表板,用于在新航班延误可用时接收消息通知。
- iPhone Objective-C 应用程序,允许航空公司乘客根据他们的旅行经历输入和共享航班延误信息。该应用程序还将通过 Google Earth 支持与 Silverlight 延误仪表板相同的功能。(……这个项目已经在开发中了)。
- 通过集成 Google App Engine 应用程序作为可能的事件源或下游应用程序,实现云到云集成。
航班延误管理器 R2 完成后将在 CodeProject 上发布并链接到本文。
总结
随着航班延误管理器的完成,您已经使用 SQL 2008 Azure、Visual Studio 2010、Azure 队列服务、Azure 工作进程/Web 角色、Silverlight 3、Twitter SDK 和 Microsoft Bing Maps 在云端开发并部署了一个事件驱动的系统。您已经在云端动态配置了硬件,例如 Web 服务器、数据库服务器和队列服务器。并且,您已实现了一键式生产部署。希望这些技能能为您提供坚实的 Windows Azure 基础,使您能够开始规划和创建未来的云端 IT 系统。
历史
- 2010 年 1 月 29 日:初始版本。
- 2010 年 2 月 5 日:更新文章文本。