企业集成 - Azure 服务总线全面解析
1. Azure 服务总线实体
Azure 服务总线支持三种不同类型的实体,它们在处理通信时提供了不同的选择:
-
队列(Queues)
:是服务中最简单的实体。包含以下概念:
-
生产者(Producer)
:向队列推送消息的应用程序或服务。
-
队列(Queue)
:消息的容器。
-
消费者(Consumer)
:使用拉取模型从队列读取消息的应用程序或服务。拉取模型意味着生产者需要主动请求队列接收消息。并且可以有多个生产者和多个消费者,锁定期限功能可确保同一时间只有一个消费者读取消息。
-
主题(Topics)
:与队列略有不同,允许实现发布/订阅通信模型。队列是点对点通信,而主题可以将不同消息分发到不同队列,便于过滤和隔离消息,使消费者只读取感兴趣的消息。需注意,基本层不提供主题功能,至少要使用标准层。
-
中继(Relays)
:队列和主题仅设计用于单向通信,若要实现双向通信,则需使用中继。Azure 中继是一个独立的服务,它能安全地暴露企业网络内托管的服务,支持单向、发布/订阅和双向通信等多种通信模型,且不像 VPN 那样改变网络,更稳定且作用于单个应用程序端点。
2. Azure 服务总线设计模式
Azure 服务总线常是众多云服务集成的核心,可用于数据集成、信息广播甚至双向通信等多种场景。凭借丰富的功能,可以实现各种职责。
3. 使用 Azure 服务总线 SDK 开发解决方案
GitHub 上有大量使用 Azure 服务总线的示例代码,这里仅介绍基本操作。
-
发送消息到队列
:
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus;
namespace HandsOnAzure.ServiceBus
{
internal class Program
{
private static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
private static async Task MainAsync()
{
var client = new QueueClient("<connection-string>", "<queue-name>");
var message = "This is my message!";
await client.SendAsync(new Message(Encoding.UTF8.GetBytes(message)));
}
}
}
若要使用主题,可使用
TopicClient
替代
QueueClient
:
var client = new TopicClient("<connection-string>", "<topic-name>");
只需安装
Microsoft.Azure.ServiceBus
NuGet 包即可。
-
拉取消息
:
-
PeekAsync 示例
:
using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus.Core;
namespace HandsOnAzure.ServiceBus.Reader
{
internal class Program
{
private static void Main()
{
MainAsync().GetAwaiter().GetResult();
Console.ReadLine();
}
private static async Task MainAsync()
{
var receiver = new MessageReceiver("<connection-string>", "<queue-name>");
while (true)
{
var message = await receiver.PeekAsync();
if (message == null) continue;
Console.WriteLine($"New message: [{message.ScheduledEnqueueTimeUtc}] {Encoding.UTF8.GetString(message.Body)}");
await Task.Delay(100);
}
}
}
}
- **ReceiveAsync**:仅使用 `PeekAsync` 不会创建消息存储,若要实现此功能,需使用 `ReceiveAsync`。`PeekAsync` 不会改变消息状态,而 `ReceiveAsync` 会根据 `ReceiveMode` 选项操作,可能作为原子 `CompleteAsync` 操作。使用 `PeekAsync` 后,可使用 `CompleteAsync` 标记消息为已读。
4. Azure 服务总线安全
作为企业级云服务,Azure 服务总线在安全功能方面有较高要求。除共享访问令牌外,还有一些预览中的新功能,可实现更灵活的访问管理。
-
托管服务标识(Managed Service Identity,MSI)
:Azure 云中的一项功能,可简化服务间的身份验证,无需在代码中存储凭据。使用时,只需在访问控制(IAM)刀片中找到标识,示例代码如下:
var tokenProvider = TokenProvider.CreateManagedServiceIdentityTokenProvider();
var sendClient = new QueueClient($"sb://{namespace}.servicebus.windows.net/", {queue-name}, tokenProvider);
await sendClient.SendAsync(new Message(Encoding.UTF8.GetBytes(messageInfo.MessageToSend)));
await sendClient.CloseAsync();
- 基于角色的访问控制(RBAC) :可利用 Azure AD 中定义的角色授予对服务的访问权限。首先要将用户添加到服务,使其获得访问权限并能开始推送和接收消息。此功能可更好地控制消息的发布和接收,相比 Azure 存储队列有很大改进。在 MSI 不可用时,也可使用 RBAC 身份验证授予服务对另一服务的访问权限,且无需交互式登录。
5. Azure 服务总线高级功能
-
死信处理(Dead lettering)
:队列中无人接收的消息被视为死信,有两种处理方式:永久删除或推送到死信队列。在 Azure 服务总线中,可通过设置消息的最大生命周期或使用
MessageReceiver的DeadLetterAsync方法将消息推送到死信队列。示例代码如下:
while (true)
{
var message = await receiver.ReceiveAsync();
if (message == null) continue;
Console.WriteLine($"New message: [{message.ScheduledEnqueueTimeUtc}] {Encoding.UTF8.GetString(message.Body)}");
await receiver.DeadLetterAsync(message.SystemProperties.LockToken, "HandsOnAzure - test");
await Task.Delay(100);
}
可使用
EntityNameHelper.FormatDeadLetterPath("<entity-path>")
获取死信队列名称。
-
会话(Sessions)
:用于实现先进先出(FIFO)保证。服务通常不控制消息间的关系,为将消息放入会话,需使用
SessionId
属性:
await client.SendAsync(new Message(Encoding.UTF8.GetBytes(message)) { SessionId = Guid.Empty.ToString()});
在接收端,需使用
QueueClient
实例的
RegisterSessionHandler
方法处理会话,并实现
IMessageSession
。
-
事务(Transactions)
:涉及多个实体,包括客户端(
QueueClient
、
TopicClient
)、消息(通过
Complete
、
Defer
、
Abandon
等操作)和会话(
GetState/SetState
)。拉取消息时需使用
ReceiveMode.PeekLock
模式,并在循环或
OnMessage
回调中打开事务范围。示例代码如下:
var message = receiver.Receive();
using (scope = new TransactionScope())
{
var newMessage = // transfer
sender.Send(newMessage);
message.Complete();
scope.Complete();
}
当事务实现后,只有整个事务成功时才会提交到队列日志,否则不会有处理消息的痕迹。
6. 处理中断和灾难
若将 Azure 服务总线作为架构核心,需确保其可复制且能抵御灾难,主要涉及灾难恢复和处理中断两个方面。
-
灾难恢复(Disaster recovery)
:当灾难发生时,可能会丢失部分或全部数据。灾难通常指整个服务的临时或永久性丢失,且无法保证其再次可用。一般需要两个不同的数据中心来实现灾难恢复,且两个数据中心应尽量远离。实现步骤如下:
1. 创建主区域
2. 创建次区域
3. 进行配对
4. 定义故障转移触发条件
示例代码如下:
var client = new ServiceBusManagementClient(creds) { SubscriptionId = subscriptionId };
var namespace2 = await client.Namespaces.CreateOrUpdateAsync("<resource-group-name>", "<secondary-namespace>", new SBNamespace { ... params ... });
ArmDisasterRecovery drStatus = await client.DisasterRecoveryConfigs.CreateOrUpdateAsync("<resource-group-name>", "<primary-namespace>", "<alias>", new ArmDisasterRecovery { PartnerNamespace = namespace2.Id })
配对完成后,可使用以下代码触发故障转移:
client.DisasterRecoveryConfigs.FailOver("<resource-group-name>", "<secondary-namespace>", "<alias>");
故障转移完成后,可使用次区域处理消息。为应对再次中断,需设置另一个次命名空间并进行配对。
-
处理中断(Handling outages)
:中断指服务暂时不可用,解决后可能需要同步两个服务总线命名空间。此过程自动但可能耗时,文档表明每分钟仅能传输 50 - 100 个实体。可考虑使用主动/被动复制概念:
-
主动复制(Active)
:使用两个主动命名空间,接收器会接收两个命名空间的消息,需为消息添加相同唯一标识符以检测重复项。
-
被动复制(Passive)
:仅在主命名空间无法传递消息时使用第二个队列或主题,可能会导致消息传递延迟、丢失或重复。示例代码如下:
private async Task SendMessage(BrokeredMessage message1, int maxSendRetries = 10)
{
do
{
var message2 = message1.Clone();
try
{
await _activeQueueClient.SendAsync(message1);
return;
}
catch
{
if (--maxSendRetries <= 0)
{
throw;
}
lock (_swapMutex)
{
var client = _activeQueueClient;
_activeQueueClient = _backupQueueClient;
_backupQueueClient = client;
}
message1 = message2.Clone();
}
}
while (true);
}
主动复制示例代码如下:
var task1 = primaryQueueClient.SendAsync(m1);
var task2 = secondaryQueueClient.SendAsync(m2);
try
{
await task1;
}
catch (Exception e)
{
exceptionCount++;
}
try
{
await task2;
}
catch (Exception e)
{
exceptionCount++;
}
if (exceptionCount > 1)
{
throw new Exception("Send Failure");
}
此外,处理中断时还可考虑使用分区发送器(高级层不可用),以确保在单个消息存储中断时仍能正常发送和接收数据。示例代码如下:
var ns = NamespaceManager.CreateFromConnectionString(myConnectionString);
var td = new TopicDescription(TopicName);
td.EnablePartitioning = true;
ns.CreateTopic(td);
综上所述,Azure 服务总线是一个功能强大的服务,适用于简单和关键场景,具有较高的灵活性和易用性。通过合理使用其各种功能和特性,可以构建出可靠、高效的企业级集成解决方案。同时,在面对灾难和中断时,也有相应的应对策略和方法,保障服务的稳定性和数据的安全性。
企业集成 - Azure 服务总线全面解析
7. 总结与常见问题解答
Azure 服务总线功能丰富,涵盖了从基础的队列、主题通信,到高级的死信处理、会话管理和事务操作等多个方面。它不仅适用于简单的应用场景,在复杂的企业级集成中也能发挥重要作用。下面是一些常见问题的解答:
|问题|解答|
| ---- | ---- |
|队列和主题有什么区别?|队列是点对点通信,生产者将消息推送到队列,消费者从队列拉取消息;而主题是发布/订阅模型,可以将不同消息分发到不同队列,便于消息的过滤和隔离。|
|能否在基本层使用主题?|不能,基本层不提供主题功能,至少要使用标准层。|
|使用死信队列的原因是什么?|当队列中的消息无人接收时,可将这些消息推送到死信队列,以便后续分析或处理,避免消息永久丢失。|
|Azure 服务总线中会话的作用是什么?|会话用于实现先进先出(FIFO)保证,确保消息按顺序处理。|
|启用分区后,单个队列最大为 1GB 时,队列的最大大小是多少?|文档未提及该问题的具体答案。|
|主动复制和被动复制有什么区别?|主动复制使用两个主动命名空间,接收器接收两个命名空间的消息,并需标记消息以检测重复项;被动复制仅在主命名空间无法传递消息时使用第二个队列或主题,可能会导致消息传递延迟、丢失或重复。|
|Azure 服务总线如何实现灾难恢复?|需要创建主区域和次区域,进行配对并定义故障转移触发条件。配对完成后,在需要时触发故障转移,使用次区域处理消息。为应对再次中断,需设置另一个次命名空间并进行配对。|
8. 未来展望
Azure 服务总线作为企业集成的重要工具,未来可能会在以下几个方面继续发展和完善:
-
性能优化
:随着企业数据量的不断增长和业务复杂度的提高,对服务总线的性能要求也越来越高。未来可能会进一步优化消息处理速度、吞吐量和响应时间,以满足大规模数据传输和实时处理的需求。
-
安全增强
:安全始终是企业关注的重点。Azure 服务总线可能会引入更多先进的安全技术和功能,如更细粒度的访问控制、加密算法升级、多因素身份验证等,以保障数据的安全性和隐私性。
-
与其他服务的集成
:为了提供更全面的企业集成解决方案,Azure 服务总线可能会加强与其他 Azure 服务以及第三方服务的集成能力。例如,与 Azure Functions、Azure Logic Apps 等服务更紧密地结合,实现自动化的工作流和业务流程。
-
简化开发体验
:降低开发人员的学习成本和开发难度,提供更简洁、易用的 SDK 和工具,使开发人员能够更快速地构建和部署基于 Azure 服务总线的应用程序。
9. 流程图展示
以下是 Azure 服务总线灾难恢复的流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B(创建主区域):::process
B --> C(创建次区域):::process
C --> D(进行配对):::process
D --> E(定义故障转移触发条件):::process
E --> F{是否发生灾难?}:::decision
F -- 是 --> G(触发故障转移):::process
G --> H(使用次区域处理消息):::process
H --> I(设置新的次命名空间并配对):::process
F -- 否 --> J([继续正常运行]):::startend
I --> J
这个流程图清晰地展示了 Azure 服务总线灾难恢复的整个过程,从区域创建到配对,再到故障转移和后续的处理。
总之,Azure 服务总线在企业集成领域具有广阔的应用前景和发展潜力。通过不断学习和掌握其各种功能和特性,开发人员和企业可以更好地利用这一强大的工具,构建出高效、可靠、安全的企业级应用系统,应对各种复杂的业务需求和挑战。
超级会员免费看
20

被折叠的 条评论
为什么被折叠?



