应用云平台的可用性——从新浪SAE看云平台设计
云计算平台的可用性,相比传统互联网服务而言,更加复杂和困难,也更具有挑战性。本文借助新浪SAE云平台为读者讲述了云平台可用性的定义、如何打造高可用的平台,以及对云计算的用户提出了建议。
该内容已经被标记书签!
标记书签错误,请重试!

作者 Jonathan Allen 译者 侯伯薇 发布于 2010年2月11日
近来ORM变得越来越普遍,这都归于一种很具说服力的原因;它可以使开发数据库驱动的应用程序变得更快、更省力。但是ORM框架都有点“固执己见”,他们期望开发者遵从特定的规则,当规则被打破的时候就非常难以使用。最通常的规则之一就是,存储过程必须总是返回单独的结果集,其中带有一致的列的列表。不幸的是,有很多这样的存储过程,其中返回的数据的结果根据它自身内部逻辑的不同而不同。例如,一个存储过程可能会接受一个参数,它表示要返回那些列,而另一个参数表示如果它包含了所有行,那么就对其进行合计。或者存储过程的结果可能会根据某些内部的标识而不同,从而应用程序需要检查输出,从而在运行时决定结构。 面对已经确定了的存储过程集合,而这些存储过程并非是针对ORM系统所基于的静态建模的类型所设计的,大多数.NET开发者会转而使用DataTable的方法。但是有了.NET 4.0中新创建的对动态类型的支持,他们会产生另一个主意。如果所有一切——包括存储过程的名称、SQL的参数以及得到的对象——都在运行时处理会怎么样呢? 下面是一些由VB和C#编写的示例代码。你会注意到VB需要使用Option Strict,而C#大量地使用了它的新关键字“dynamic”。 VB Using con As New SqlClient.SqlConnection(connectionString) Dim customer = con.CallSingleProc.CustomerSelect(AccountKey:=12345) Console.WriteLine(customer.FirstName & " " & customer.LastName) Dim orders As IList = con.CallListProc.OrderSearch(AccountKey:=12345, MinCreatedDate:=Now.AddDays(-7), MaxCreatedDate:=Now) Dim totalValue = Aggregate order In orders Into Sum(CDec(order.TotalOrderValue)) Console.WriteLine("This customer ordered a total of $" & totalValue & " last week") For Each order In orders Console.WriteLine(vbTab & "Order Key: " & order.OrderKey & " Value: $" & order.TotalOrderValue) Next End Using C# using (var con = new SqlConnection(connectionString)) { var customer = con.CallSingleProc().CustomerSelect(AccountKey: 12345); Console.WriteLine(customer.FirstName + " " + customer.LastName); IList<dynamic> orders = con.CallListProc().OrderSearch(AccountKey: 12345, MinCreatedDate: DateTime.Now.AddDays(-7), MaxCreatedDate: DateTime.Now); var totalValue = orders.Sum(order => (decimal)order.TotalOrderValue); Console.WriteLine("This customer ordered a total of $" + totalValue + " last week"); foreach (var order in orders) { Console.WriteLine("\tOrder Key: " + order.OrderKey + " Value: $" + order.TotalOrderValue); } } 这看起来和一般的.NET代码很类似,但是那些方法和属性实际上很多都不存在。下面是相同的代码,其中突出显示了不存在的成员。 VB Using con As New SqlClient.SqlConnection(connectionString) Dim customer = con.CallSingleProc.CustomerSelect(AccountKey:=12345) Console.WriteLine(customer.FirstName & " " & customer.LastName) Dim orders As IList = con.CallListProc.OrderSearch(AccountKey:=12345, MinCreatedDate:=Now.AddDays(-7), MaxCreatedDate:=Now) Dim totalValue = Aggregate order In orders Into Sum(CDec(order.TotalOrderValue)) Console.WriteLine("This customer ordered a total of $" & totalValue & " last week") For Each order In orders Console.WriteLine(vbTab & "Order Key: " & order.OrderKey & " Value: $" & order.TotalOrderValue) Next End Using C# using (var con = new SqlConnection(connectionString)) { var customer = con.CallSingleProc().CustomerSelect(AccountKey: 12345); Console.WriteLine(customer.FirstName + " " + customer.LastName); IList<dynamic> orders = con.CallListProc().OrderSearch(AccountKey: 12345, MinCreatedDate: DateTime.Now.AddDays(-7), MaxCreatedDate: DateTime.Now); var totalValue = orders.Sum(order => (decimal)order.TotalOrderValue); Console.WriteLine("This customer ordered a total of $" + totalValue + " last week"); foreach (var order in orders) { Console.WriteLine("\tOrder Key: " + order.OrderKey + " Value: $" + order.TotalOrderValue); } } 现在一些保守派会开始抱怨延迟绑定可能给他们造成的风险,比方说,程序可能会出错,但直到运行时才会被捕获。这确实是可能的,但实际上情况不会那么坏。当我们将存储过程和列的名称都保存在字符串中的时候,我们也会手误使用到错误的对象,从而在运行时有失败的风险。 为了让它生效,我们需要两样东西。第一样是从静态类型的上下文切换到动态类型上下文的方法。对此,我们选择一组扩展方法,它们会返回“System.Object”。在Visual Basic中,这就足以触发延迟绑定,但在C#中这是不可行的。为了让C#在两种模式之间切换,你还需要使用Dynamic属性来修饰返回值。 Public Module MicroOrm ''' <summary> ''' 调用返回标量值的存储过程 ''' </summary> ''' <returns>Null或者单值</returns> ''' <remarks> 只有第一个结果集的第一行的第一列会被返回。所有其它数据都会被忽略。数据库的null被转换为CLR的null</remarks> <Extension()> Public Function CallScalarProc(ByVal connection As SqlConnection) As <Dynamic()> Object Return New MicroProcCaller(connection, Scalar) End Function ''' <summary> ''' 调用返回单独对象的存储过程 ''' </summary> ''' <returns>Null或者MicroDataObject</returns> ''' <remarks>只会返回第一个结果集的第一行。所有其它数据都会被忽略。数据库的null都被转换为CLR的null</remarks> <Extension()> Public Function CallSingleProc(ByVal connection As SqlConnection) As <Dynamic()> Object Return New MicroProcCaller(connection, [Single]) End Function ''' <summary> ''' 调用返回一系列对象的存储过程 ''' </summary> ''' <returns>每行都有一个MicroDataObject </returns> ''' <remarks>只会返回第一个结果集。所有其它数据都会被忽略。数据库的null会被转换为CLR的null</remarks> <Extension()> Public Function CallListProc(ByVal connection As SqlConnection) As <Dynamic()> Object Return New MicroProcCaller(connection, List) End Function ''' <summary> ''' 调用返回包含一系列对象的列表的存储过程 ''' </summary> ''' <returns>包含MicroDataObject列表的List。每个记录集都会有一个list,并且给定的结果集中的每行都有一个MicroDataObject</returns> ''' <remarks>数据库的null被转换为CLR的null</remarks> <Extension()> Public Function CallMultipleListProc(ByVal connection As SqlConnection) As <Dynamic()> Object Return New MicroProcCaller(connection, MultipleLists) End Function End Module 作为对比,下面是使用C#实现的一个功能。 public static class MicroOrm { public static dynamic CallSingleProc(this SqlConnection connection) { return new MicroProcCaller(connection, CallingOptions.Single); } } 为了设定基本的环境,以下是MicroProcCaller 类的构造函数。注意,这个类被标记为friend(C#的内部标识符)。这样做是因为任何人都不应该声明这个类型的变量;它只是工作在动态的上下文中。并且这个类还是暂时的;调用者不应该持有对它的引用。 Friend Class MicroProcCaller Inherits Dynamic.DynamicObject Private m_Connection As SqlConnection Private m_Options As CallingOptions Public Sub New(ByVal connection As SqlConnection, ByVal options As CallingOptions) m_Connection = connection m_Options = options End Sub End Class Public Enum CallingOptions Scalar = 0 [Single] = 1 List = 2 MultipleLists = 3 End Enum 既然我们已经位于动态上下文中,那么就需要一种方式,用来将延迟绑定的方法调用转换为对存储过程的调用。想要达到这个目的有很多种方法,但其中最简单的就是继承DynamicObject 并重写TryInvokeMember 方法。需要做的步骤如下: Public Overrides Function TryInvokeMember( ByVal binder As System.Dynamic.InvokeMemberBinder, ByVal args() As Object, ByRef result As Object) As Boolean Dim manageConnectionLifespan = (m_Connection.State = ConnectionState.Closed) If manageConnectionLifespan Then m_Connection.Open() Try Using cmd As New SqlClient.SqlCommand(binder.Name, m_Connection) cmd.CommandType = CommandType.StoredProcedure If binder.CallInfo.ArgumentNames.Count <> binder.CallInfo.ArgumentCount Then Throw New ArgumentException("All parameters must be named") End If For i = 0 To binder.CallInfo.ArgumentCount - 1 Dim param As New SqlClient.SqlParameter param.ParameterName = "@" & binder.CallInfo.ArgumentNames(i) param.Value = If(args(i) Is Nothing, DBNull.Value, args(i)) cmd.Parameters.Add(param) Next Select Case m_Options Case CallingOptions.Scalar result = ExecuteScalar(cmd) Case CallingOptions.Single result = ExecuteSingle(cmd) Case CallingOptions.List result = ExecuteList(cmd) Case CallingOptions.MultipleLists result = ExecuteMultpleLists(cmd) Case Else Throw New ArgumentOutOfRangeException("options") End Select End Using Finally If manageConnectionLifespan Then m_Connection.Close() End Try Return True End Function ExecuteScalar方法很简单,它拥有自己方法的唯一原因是要保持一致性。 Private Function ExecuteScalar(ByVal command As SqlCommand) As Object Dim temp = command.ExecuteScalar If temp Is DBNull.Value Then Return Nothing Else Return temp End Function 对于剩下的变量,调用者期望是真正的属性,或者至少看起来像属性。一种选择是基于运行时结果集的内容自动生成代码的类。但是在运行时生成代码会耗费大量的资源,并且我们不会从中得到太多好处,因为没有哪个调用者会通过名字来引用我们的类。因此,在保持动态代码的模式的时候,我们选择使用原型动态对象来替换它。 Friend Class MicroDataObject Inherits Dynamic.DynamicObject Private m_Values As New Dictionary(Of String, Object)(StringComparer.OrdinalIgnoreCase) Public Overrides Function TryGetMember(ByVal binder As System.Dynamic.GetMemberBinder, ByRef result As Object) As Boolean If m_Values.ContainsKey(binder.Name) Then result = m_Values(binder.Name) Else Throw New System.MissingMemberException("The property " & binder.Name & " does not exist") Return True End Function Public Overrides Function TrySetMember(ByVal binder As System.Dynamic.SetMemberBinder, ByVal value As Object) As Boolean SetMember(binder.Name, value) Return True End Function Public Overrides Function GetDynamicMemberNames() As System.Collections.Generic.IEnumerable(Of String) Return m_Values.Keys End Function Friend Sub SetMember(ByVal propertyName As String, ByVal value As Object) If value Is DBNull.Value Then m_Values(propertyName) = Nothing Else m_Values(propertyName) = value End Sub End Class 由于任何类都不会依赖于这个对象,因此我们再次将其标记为Friend(C#的internal修饰符)。这还剩下三个用来管理属性的重写方法:一个用来设置属性,一个用来取得属性,还有一个用来列出属性的名称。另外,还有一个用来使用静态类型代码初始化类的后门方法。 Private Function ExecuteSingle(ByVal command As SqlCommand) As Object Using reader = command.ExecuteReader If reader.Read Then Dim dataObject As New MicroDataObject For i = 0 To reader.FieldCount - 1 dataObject.SetMember(reader.GetName(i), reader.GetValue(i)) Next Return dataObject Else Return Nothing End If End Using End Function Private Function ExecuteList(ByVal command As SqlCommand) As List(Of MicroDataObject) Dim resultList = New List(Of MicroDataObject) Using reader = command.ExecuteReader Do While reader.Read Dim dataObject As New MicroDataObject For i = 0 To reader.FieldCount - 1 dataObject.SetMember(reader.GetName(i), reader.GetValue(i)) Next resultList.Add(dataObject) Loop End Using Return resultList End Function Private Function ExecuteMultpleLists(ByVal command As SqlCommand) As List(Of List(Of MicroDataObject)) Dim resultSet As New List(Of List(Of MicroDataObject)) Using reader = command.ExecuteReader Do Dim resultList = New List(Of MicroDataObject) Do While reader.Read Dim dataObject As New MicroDataObject For i = 0 To reader.FieldCount - 1 dataObject.SetMember(reader.GetName(i), reader.GetValue(i)) Next resultList.Add(dataObject) Loop resultSet.Add(resultList) Loop While reader.NextResult End Using Return resultSet End Function 你刚刚创建的“微型ORM”还有很大的改善空间。可能会增加的特性有:添加对输出参数的支持;选择发送参数化的查询而不是存储过程名称;对其它数据库的支持等等。相关厂商内容
云计算平台的可用性,相比传统互联网服务而言,更加复杂和困难,也更具有挑战性。本文借助新浪SAE云平台为读者讲述了云平台可用性的定义、如何打造高可用的平台,以及对云计算的用户提出了建议。
淘宝高度重视Java平台的健康发展,组建了一个团队专注于Java平台的底层部分的性能、功能与稳定性改进;工作主要基于OpenJDK中的HotSpot VM开展,其中一些通用的功能随后也会逐渐反馈给OpenJDK社区。希望能与使用Java平台开发应用的大家交流经验。
本次演讲视频录制于QCon杭州2011。
2011年4月21日至22日是值得云计算从业者纪念的日子。Amazon的IaaS服务出现故障,导致许多商业网站的服务中断,影响非常严重。作为云计算用户,我们需要思考的是,如何保证即便在云服务不可用的情况,我们的应用架构仍然能够屹立不倒?本文正是站在云计算用户的角度试图探讨这一问题。
12人的技术团队,4组刀片服务器,每月20亿的访问量,每日1次准时部署,99.9%的可用性。这可能吗?当然。想知道如何做的吗?百姓网将与您分享他们在DevOps实践过程中的经验和技巧。
本次演讲视频录制于QCon杭州2011。
篱笆作为一家起源于社区的电子商务公司,反映到技术层面就是同时要面对产品和业务,以及经营战略的变化调整。如何在产品和业务的夹缝之间完成技术架构的抽象与平衡,寻找更有效的价值定位,这当中有些经验教训和个人感悟愿与众人分享。
本次演讲视频录制于QCon杭州2011。
本文将对特性注入以及相关方法做一个扫盲性的介绍。我们会解释这个框架的关键要素,并附上实例来证实它们。为了让文章保持相对较短,我们不会深入到某个工具或方法中,而是会给出一些参考资料,以便大家做进一步的研究。
随着JDK 7的发布,字节码指令集终于迎来了第一位新成员——invokedynamic指令。这条新增加的指令是JDK 7实现“动态类型语言(Dynamically Typed Language)”支持而进行的改进之一,也是为JDK 8可以顺利实现Lambda表达式做技术准备。在这篇文章中,我们将去了解JDK 7这项新特性的出现前因后果和它的意义。
随着互联网应用的发展,Java分布式远程服务技术受到越来越多的关注,本文将对各种相关实现以示例的形式逐一介绍,并总结其中的优缺点,使读者能够在技术选型时有所准备。这是文章的下篇。
5 条回复
关注此讨论 回复