为了减少流经服务总线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类。
例如,团队修改了控制器在读模型中使用计时器的轮询更新方式。
为服务总线采用预读取