CQRS之旅——旅程7(增加弹性和优化性能) (15)

为了减少流经服务总线Topic的消息数量,我们创建了两个附加主题来传输订单(Order)和可用座位(SeatAvailability)聚合发布的事件,从而对服务总线进行了分区。这有助于我们避免在应用程序承受非常高的负载时被服务总线节流。Settings.xml文件中的以下片段展示了这些新Topic的定义:

Topic Path="conference/orderevents" IsEventBus="true"> <Subscription RequiresSession="false"/> <Subscription RequiresSession="false" SqlFilter="TypeName IN ('OrderPlaced')"/> <Subscription RequiresSession="false" SqlFilter="TypeName IN ('OrderUpdated','SeatsReserved','PaymentCompleted','OrderConfirmed')"/> <Subscription RequiresSession="true" SqlFilter="TypeName IN ('OrderPlaced','OrderUpdated','OrderPartiallyReserved','OrderReservationCompleted', 'OrderRegistrantAssigned','OrderConfirmed','OrderPaymentConfirmed')"/> <Subscription RequiresSession="true" SqlFilter="TypeName IN ('OrderPlaced','OrderTotalsCalculated','OrderConfirmed', 'OrderExpired','SeatAssignmentsCreated','SeatCreated','SeatUpdated')"/> <Subscription RequiresSession="true" SqlFilter="TypeName IN ('SeatAssignmentsCreated','SeatAssigned','SeatUnassigned','SeatAssignmentUpdated')"/> <Subscription RequiresSession="true" SqlFilter="TypeName IN ('OrderConfirmed','OrderPaymentConfirmed')"/> <Subscription RequiresSession="true" SqlFilter="TypeName IN ('OrderPlaced','OrderRegistrantAssigned','OrderTotalsCalculated', 'OrderConfirmed','OrderExpired','SeatAssignmentsCreated','SeatAssigned','SeatAssignmentUpdated','SeatUnassigned')"/> </Topic> <Topic Path="conference/availabilityevents" IsEventBus="true"> <Subscription RequiresSession="false"/> <Subscription RequiresSession="false" SqlFilter="TypeName IN ('OrderUpdated','SeatsReserved','PaymentCompleted','OrderConfirmed')"/> <Subscription RequiresSession="true" SqlFilter="TypeName IN ('OrderPlaced','OrderTotalsCalculated','OrderConfirmed', 'OrderExpired','SeatAssignmentsCreated','SeatCreated','SeatUpdated')"/> <Subscription RequiresSession="true" SqlFilter="TypeName IN ('ConferenceCreated','ConferenceUpdated','ConferencePublished', 'ConferenceUnpublished','SeatCreated','SeatUpdated','AvailableSeatsChanged', 'SeatsReserved','SeatsReservationCancelled')"/> </Topic> 其他的优化和坚固性更改

本节概述了团队优化应用程序性能和提高其弹性的一些额外方法:

使用顺序GUIDs。

使用异步ASP.NET MVC controllers。

使用预取从服务总线获取多个消息。

并行接受多个Azure服务总线会话。

使座位预订命令过期。

顺序GUIDs

在此之前,系统生成Guid,用于聚合的Id,例如订单和注册聚合。使用Guid.NewGuid方法,它生成随机Guid。如果在SQL数据库实例中使用这些Guid作为主键值,这将导致索引中频繁的页面分割,从而对数据库的性能产生负面影响。在V3版本中,团队添加了一个实用程序类来生成连续的Guid。这确保SQL数据库表中的新条目总是追加在后面,这提高了数据库的整体性能。下面的代码示例显示了新的GuidUtil类:

public static class GuidUtil { private static readonly long EpochMilliseconds = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks / 10000L; /// <summary> /// Creates a sequential GUID according to SQL Server's ordering rules. /// </summary> public static Guid NewSequentialId() { // This code was not reviewed to guarantee uniqueness under most conditions, nor completely optimize for avoiding // page splits in SQL Server when doing inserts from multiple hosts, so do not re-use in production systems. var guidBytes = Guid.NewGuid().ToByteArray(); // Get the milliseconds since Jan 1 1970. byte[] sequential = BitConverter.GetBytes((DateTime.Now.Ticks / 10000L) - EpochMilliseconds); // Discard the 2 most significant bytes, as we only care about the milliseconds increasing, but the highest ones should be 0 for several thousand years to come. if (BitConverter.IsLittleEndian) { guidBytes[10] = sequential[5]; guidBytes[11] = sequential[4]; guidBytes[12] = sequential[3]; guidBytes[13] = sequential[2]; guidBytes[14] = sequential[1]; guidBytes[15] = sequential[0]; } else { Buffer.BlockCopy(sequential, 2, guidBytes, 10, 6); } return new Guid(guidBytes); } }

有关进一步信息,请参见The Cost of GUIDs as Primary Keys和Good Page Splits and Sequential GUID Key Generation

异步ASP.NET MVC controllers

团队将公共会议web应用程序中的一些MVC控制器转换为异步控制器。这避免了阻塞一些ASP.NET线程并使我们能够在里面使用Task类。

例如,团队修改了控制器在读模型中使用计时器的轮询更新方式。

为服务总线采用预读取

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpspxg.html