MySpace作为.NET架构在互联网平台最为成功的案例之一,其中非常重要的系统datarelay分布式数据缓存也开源了,DataRelay提供了高性能的缓存系统和消息处理机制,并支持自定义计算Component组件,支持Cluster,有完整的Replication和负载均衡机制,组件都是以windows服务的形式,可以非常灵活的进行部署,客户端与服务端使用Socket进行通信通讯,另外还可以很方便的扩展各种自定义组件,譬如缓存部分可以使用Memcached,还有最近比较流行Redis。
MySpace虽然开源出来datarelay,但是没有很好的文档帮助大家学习,下面将对整套代码分析,让大家从全局认识DataRelay这套在.net平台上少有的精品。
CodePlex代码下载地址:http://datarelay.codeplex.com
MIX 10上的演讲:Robots at MySpace: Massive Scaling a .NET Website with the Microsoft Robotic Studio http://ecn.channel9.msdn.com/o9/mix/10/pptx/EX04.pptx
DataRelay架构分析
主要分析DataRelay的架构,分别从DataRelay的特点,系统的物理部署架构,以及系统的内部结构和基于组件规范接口分析,详细地介绍了DataRelay系统的构架思想以及实现方案。
1.DataRelay的基本特点
DataRelay在参考各种数据缓存功能和设计理念基础上,在.NET平台体系下设计并实现的一套分布式缓存体系,具有以下特点:
<!--[if !supportLists]-->1) <!--[endif]-->利用现有的Cache解决方案来完成本地Cache功能。现有的Berkeley DB、Memcached、 本地Cache模块都可以作为插件,接入该系统中,作为本地Cache机制。
<!--[if !supportLists]-->2) <!--[endif]-->自定义序列化和反序列接口,减少存储空间,提供网络传输效率。
<!--[if !supportLists]-->3) <!--[endif]-->服务部署简单,对服务节点支持热插拔。
<!--[if !supportLists]-->4) <!--[endif]-->对符合DataRelay组件接口定义的模块,通过统一的组件接口管理模块,对服务端组件支持动态更新。
<!--[if !supportLists]-->5) <!--[endif]-->规范的组件开发接口,大大简化了组件开发,提高扩展性。
<!--[if !supportLists]-->6) <!--[endif]-->网络消息分发与同步结合了Replicated Cache和Distributed Cache模式,保证了系统的可靠行。
<!--[if !supportLists]-->7) <!--[endif]-->利用微软CCR组件(并发与协调运行时(Concurrency and Coordination Runtime))很好的管理消息的异步、并发、协调和失败处理,保证了系统的高效,稳定。
2 DataRelay的物理架构
DataRelay的物理架构图如图1所示,标明了DataRelay在整个网站系统中所处的地位。DataRelay处于整个网站体系中的中间层,不同于一般中间层设计,Web服务器即连接数据库服务器,也同时连接中间层,这样设计可以防止单点,如果Web服务器只连接中间层,一旦中间层服务器荡机,整个网站将不能工作,而采用图中设计方案,一旦中间层服务器当机,Web服务器同样可以直接访问数据库服务器,不至于不工作。当Web服务器请求被Cache的业务对象时,首先请求DataRelay系统,如果该数据在DataRelay系统中存在,将直接返回给Web服务器,当不存在DataRelay系统中,系统将请求转向数据库请求,请求到数据首先将数据保存到DataRelay系统中,过后返回给Web服务器。
图1 网站物理架构图
整个DataRelay集群部署如图2所示,对于DataRelay的服务器的组织结构,主要有以下三点定义:
<!--[if !supportLists]-->1) <!--[endif]-->Groups
<!--[if !supportLists]-->l <!--[endif]-->不同的组存储不同的数据,在DataRelay系统中,可以定义多个组,可以针对组进行访问模式设置。
<!--[if !supportLists]-->2) <!--[endif]-->Clusters
<!--[if !supportLists]-->l <!--[endif]-->多个集群存在一个组中,缓存业务的数据对象根据Distributed Cache模式分配选择需要保存的集群地址。
<!--[if !supportLists]-->3) <!--[endif]-->Servers
<!--[if !supportLists]-->l <!--[endif]-->DataRelay集群中的服务器,每个集群中服务器之间采用Replicated Cache模式同步保存数据。
图2 DataRelay集群部署图
在此结构中,每一组Cluster中的服务器之间会同步数据,保存同样的数据备份,当Web服务器请求数据,获取数据服务器节点的算法:
Cluster Index = ObjectID %(# Cluster)
Server Node = Random (Cluster Index)
注:ObjectID 表示存储数据的类型ID,# Cluster 表示一个Group中有多少个Cluster
当确定了Cluster Index过后,就随机从该Cluster中取出一台可用的节点服务器处理数据请求。
3 DataRelay内部模块组成
DataRelay各个模块之间协调工作,保障了系统的正常运行,各个模块的设计有各自的责任,DataRelay的内部模块组成如图3所示,主要职责如下:
<!--[if !supportLists]-->1) <!--[endif]-->DataRelay.Client:是整个体系提供给客户端使用的接口,客户端通过该接口完成数据操作。
<!--[if !supportLists]-->2) <!--[endif]-->DataRelay.Server:服务端的管理组件,控制服务的生命周期,以及扩展组件的热插拔。
图3 DataRelay的内部模块结构图
<!--[if !supportLists]-->3) <!--[endif]-->DataRelay.Transports.Socket:管理客户端和服务端的TCP连接池管理。
<!--[if !supportLists]-->4) <!--[endif]-->DataRelay.Common:主要封装了DataRelay体系中通用操作以及接口定义,主要包括:
<!--[if !supportLists]-->a) <!--[endif]-->RelayComponent.Interface 定义DataRelay的组件接口规范,扩展组件必须实现该接口。
<!--[if !supportLists]-->b) <!--[endif]-->RelayMessage 定义了服务端和客户端交互的消息类型,是整个体系通信的基础。
<!--[if !supportLists]-->c) <!--[endif]-->RelayConfiguration Schemas 对体系中的配置文件进行格式验证,保证配置的准确性。
<!--[if !supportLists]-->5) <!--[endif]-->DataRelay.Components:组件模块,包含了基本模块,以及扩展模块
<!--[if !supportLists]-->a) <!--[endif]-->Storage是真正存放Cache的地方,对于存放的介质有多种,采用Berkeley DB用来持久化存储数据,也可以为了高性能,采用内存保存Cache。这部分采用DataRelay组件设计规范,可以根据缓存的数据类型以及数据操作方式,扩展合适的存储组件模块。
<!--[if !supportLists]-->b) <!--[endif]-->Forwarding :网络消息分发组件,该组件模块是整个DataRelay的核心组件,它负责RelayMessage的传递,以及消息的处理,它的组成包含以下几个核心模块:
<!--[if !supportLists]-->l <!--[endif]-->CCR 是微软提供的异步编程组件,在Forwarding中它负责管理消息的异步、并发、协调和失败处理。
<!--[if !supportLists]-->l <!--[endif]-->NodeManager 对DataRelay服务器节点的管理,Forwarding通过它很好的对节点进行分配以及调用,完成网络消息的分发与同步。
<!--[if !supportLists]-->l <!--[endif]-->PerfCounter 性能计数器[10]主要职责是监控服务器各个节点的服务状态。
<!--[if !supportLists]-->6) <!--[endif]-->DataRelay.Logging:负责记录DataRelay的日志。
4序列化和反序列化
DataRelay为了提升序列化效率,对业务缓存对象进行了自定义序列化和反序列化的实现,自定义的序列化数据结构非常紧凑,如图4自定义序列化数据结构图所示,32位整型(int32)只占用4个字节,布尔型(bool)占用1个字节,一个长度为2的16位的整型数组(int16[2])占用总共8个字节,数组长度占4个字节,每位16位数占用2个字节。可以看出,DataRelay自行编码的序列化数据结构相当的紧凑。
图4 自定义序列化数据结构
通过对序列化和反序列化的实现,做了对比测试,在包含一系列System.Int32类型的数据对象中,使用.NET序列化体系,做序列化生成的字节流是190 KB,如果使用自定义序列化实现,将生成仅仅14 KB,字节流减少超过85%,并且序列化的时间减少14.4s,减少了字节流就减少了网络的传输量以及序列化时间缩短,网络传输性能显著提高。
5 DataRelay消息(RelayMessage)
RelayMessage是DataRelay框架的通信数据基础,它负责承载需要缓存的数据在服务端和客户端之间交互。RelayMessage设计具有以下特点:
<!--[if !supportLists]-->1) <!--[endif]-->规范消息的类型定义,包括get,update,save,delete 等等,随着框架的扩展,添加扩展的类型。
<!--[if !supportLists]-->2) <!--[endif]-->为了提供传输性能,减少网络传输量,消息被序列化成Byte数组存放到服务端,客户端获取到数据过后需要反序列。
<!--[if !supportLists]-->3) <!--[endif]-->每个消息具有唯一的ID,如果ID不能确定唯一性,还有ExtendedID 组合使用。
<!--[if !supportLists]-->4) <!--[endif]-->消息TypeID, 针对每种类型的消息都将分配一个TypeID,用来定位缓存的数据位置。
6 DataRelay组件
DataRelay是一套基于组件的体系架构,网络消息分发是个组件,持久化存储是个组件,内存存储是个组件,在DataRelay中任何功能的开发都是一个组件,这样能很好的提供了系统的扩展性。
当然对与设计组件本身,还具有很强的自主性,每个组件可以定义自己的配置文件,在配置文件中通过反射生成处理自身配置信息的实例,如DataRelay设计的Berkeley DB存储组件,由于Berkeley DB本身配置就相当复杂,所以DataRelay在设计该组件时,单独对Berkeley DB配置进行管理,
在DataRelay组件接口定义中,主要是定义了组件要处理消息的接口以及组件自身运行时的信息。特点如下:
<!--[if !supportLists]-->1) <!--[endif]-->服务框架依赖组件的接口操作RelayMessage。
<!--[if !supportLists]-->2) <!--[endif]-->组件可以自定义的配置文件,服务框架通过反射,获取组件配置信息。
<!--[if !supportLists]-->3) <!--[endif]-->当组件配置文件变动,服务框架会自动读取重新读取配置信息。
7 DataRelay 组件容器
DataRelay体系是基于组件模块的,对于组件的运行需要一个环境,DataRelay提供组件容器,组件容器的主要职责就是维护组件的生命周期,以及调度消息在组件中传输。该类实现两个接口,分别是IRelayNode和IDataHandler。
<!--[if !supportLists]-->1) <!--[endif]-->IRelayNode:该接口定义容器中组件节点的生命周期,以及配置信息,通过该接口,我们可以获取到容器中各个组件当前的运行状况,以及相关配置信息。
<!--[if !supportLists]-->2) <!--[endif]-->IDataHandler:该接口是传输消息的接口定义,同样在组件接口定义中也需要集成该接口,该接口定义在整个体系中消息的传输。
服务端消息将接收到消息全部传输到组件容器中,有组件容器进行消息分发,所以在RelayNode类的设计上,对大量高并发的消息,也采用CCR组件管理。
8 DataRelay 网络消息分发机制
网络消息分发在DataRelay中是由Forwarding组件模块完成的,Forwarding是DataRelay的一个核心模块,在服务端和客户端都要使用。它完成DataRelay分布式缓存系统的网络消息分发与同步。它对消息分发与同步机制分成两种方式,一种是要实时操作消息,一种是异步操作消息。对于获取Cache数据,需要实时操作,对于更新、保存、删除Cache数据可以根据业务场景选择异步操作。
DataRelay在系统中实现的分布式Cache是Replicated Cache和Distributed Cache相结合的做法:
<!--[if !supportLists]-->1) <!--[endif]-->在对于同一个组下缓存对象的选择放在某个集群中存储是采用Distributed Cache方式,根据Mod运算定位存放的集群位置。
<!--[if !supportLists]-->2) <!--[endif]-->对于在同一个组下同一个集群中节点机器上的Cache数据分布采用的是Replicated Cache,即是指在同一个组中的同一个集群下的每个节点所包含的Cache数据是一致的。
Forwarding组件模块处理Cache数据主要分为2个方面,一方面是获取数据,另一个方面是更新数据。如图10示意了在保存和获取的逻辑过程。假设当前DataRelay系统4台服务器节点,分成2个集群,在同一App组中,需要处理的缓存业务对象数据有2个,2个数据对象的ID分别是120和121。下面分别说明数据在获取和保存的逻辑过程。
<!--[if !supportLists]-->1) <!--[endif]-->获取对象Id为121的缓存数据:
<!--[if !supportLists]-->a) <!--[endif]-->获取对象Id为121配置描述中设置的组名称:App。
<!--[if !supportLists]-->b) <!--[endif]-->选择Cluster Index:首先根据Cluster Index 算法,计算得出 Cluster Index = 1 ( 121 % 2 = 1 ) mod ( 业务对象ID,集群数量 )。
<!--[if !supportLists]-->c) <!--[endif]-->从该Cluster中随机选择一台服务节点,获取数据。
<!--[if !supportLists]-->2) <!--[endif]-->保存对象Id为120的缓存数据:
<!--[if !supportLists]-->a) <!--[endif]-->获取对象Id为120配置描述中设置的组名称:App。
<!--[if !supportLists]-->b) <!--[endif]-->选择Cluster Index: 首先根据Cluster Index算法,计算得出 Cluster Index = 0 ( 120 % 2 )。
<!--[if !supportLists]-->c) <!--[endif]-->从该Cluster中随机选择一台服务器节点,保存数据
<!--[if !supportLists]-->d) <!--[endif]-->该服务器节点会异步发送网络消息,同步该缓存数据到该Cluster的其他节点中。
图10 网络消息分发模型
DataRelay在Forwarding的设计上具有以下几个特点:
<!--[if !supportLists]-->1) <!--[endif]-->结合了Replicated Cache 和Distributed Cache各自特点,很好的处理了Cache数据在集群上的数据分布与同步。
<!--[if !supportLists]-->2) <!--[endif]-->CCR组件模块的集成,使得网络消息处理具有高效行,可靠性。
<!--[if !supportLists]-->3) <!--[endif]-->通过配置可以对消息批量打包,一次性提交,减少网络通信。
9 DataRelay 服务部署
在服务部署这块,datarelay也有独特之处,利用了.net appdomain这个特性,做到可热插拔,在设计这部分功能时,采用DataRelay系统框架和组件模块使用不同的AppDomain来加载,将在一个独立的 AppDomain将所有组件模块程序集加载到组件容器中,这样当添加或者更新组件dll和配置文件时,DataRelay将可以动态卸载 AppDomain,过后在从新创建新的 AppDomain,然后将当前组件加载到其中。
这样就可以不用从新启动DataRelay服务管理组件更新,在线上运维还是非常方便的,设想一下如果有几十台Relay服务器需要更新组件,这样部署很便利很高效。
《程序员2012.11期》 作者:张庆化
原文:http://www.tita.com/blog/tech/myspace-datarelay-分布式数据缓存源码分析