ServiceStack是一个开源的、支持.NET与Mono平台的REST Web Services框架。InfoQ有幸与Demis Bellot深入地讨论了这个项目。在这篇两部分报道的第1部分中,我们主要谈论了ServiceStack项目建立的原动力,以及项目中的各种设计方案选择。

InfoQ: 你是否认为微软对服务的实现方式有什么问题?ServiceStack又是怎样解决这些问题的呢?

Demis: 有一些问题是由于微软一直以来自认为良好的框架设计的方式所造成的常见问题,另一些问题的根源是由于微软的设计总是倾向于满足设计器优先的工具,并且试图为使用设计器作为向导的开发者提供一套熟悉的API,而这种设计倾向造成了一些副作用:

 
  • 在服务API设计时提倡用C#进行远程过程调用(RPC)的方法调用,这导致了非常细粒度(chatty)的设计,并且产生了许多特定于客户端的API。
  • 企图将脆弱的、臃肿的、低效并且过于复杂的SOAP以及WS-*序列化格式进行标准化。
  • 试图通过繁重的人为抽象、UI设计器以及各种大型工具达到简化终端用户开发的目的。
  • 为了尝试给开发者展现一个人为定制的服务端对象模型,创建了繁重的抽象API。
  • 试图通过一个共享的抽象对象模型将所有网络终结点(endpoint)统一起来。
  • 过度使用XML配置与代码生成。
  • 没有预先考虑可测试性与性能。

RPC方法签名

由于微软过于追求为开发者提供一套熟悉的RPC API,并在VS.NET中提供丰富的UI工具(例如“添加服务引用”对话框)以保证上手的简易性,其代价就是推广了远程服务中的各种反模式,从长期来看将导致不必要的不协调与脆弱性。不幸的是,微软长期以来在其发布的每个web service框架中都持续使用这种设计方式,这种方式提倡创建RPC服务API的设计方式,造成了开发者在意识中将远程服务也当作本地方法一样调用。这种结果的害处是多方面的,远程服务意味着它包装了一个外部依赖,对它的调用要比本地方法慢上几百万倍,并且更容易受外界的影响而发生错误。将隐式的服务契约与服务端RPC方法签名绑定在一起,就意味着你的API服务契约和服务端的实现紧密耦合在一起了。由于没有规定一个定义良好的边界,导致了糟糕的关注分离,并且出现了将繁重的ORM数据模型通过网络返回给客户端的糟糕实践。从定义上来说,这种数据模型的关系型结构与循环式关系对于数据迁移对象(DTO)来说并非一种好的选择,它使调用可能偶尔会失败。这种方式还将隐式的服务契约与你的底层关系型数据库(RDBMS)数据模型耦合在一起,这样在进行修改时就会产生额外的冲突。而且远程API与调用它们的服务端网站是无连接的,而这些调用的方法在客户端代理部署后也会不断地演化。理想的框架应该能够提倡可演化的、灵活的API设计,以避免在服务端改变时产生运行时错误。

这些有一张关于WCF提倡的细粒度的RPC API方式与ServiceStack所鼓励的基于消息的API方式的不同之处的对比图。此外,这个用ServiceStack重写Web API的入门教程的示例则展示了基于消息的API如何减少细粒度调用,增加了服务的可重用性。

ServiceStack采用了Martin Fowler所推荐的远程服务最佳实践,因此避免了之前困扰着.NET web service开发者的很多潜在问题:

 

远程门面

远程门面提倡使用基于消息的、粗粒度的批量调用接口。它能够将通信次数减至最低,并促进了创建更少但是可用性更好、并且支持版本化的服务接口。ServiceStack采取了基于消息的设计确保开发者始终创建粗粒度的API设计。

数据迁移对象(MSDN)

数据迁移对象要求使用特定用途的POCO以创建web service响应的数据格式。ServiceStack一直鼓励使用DTO,它本身与它的实现完全解耦,并且存在于一个没有外部依赖也不需要具体实现的程序集中。这种策略允许将定义服务端的服务类型共享给所有.NET客户端,因而提供了一个端到端的类型化API,并且无需使用代码生成。

网关(Gateway)(MSDN)

网关的作用是将所有服务端通信包装起来,隐藏在一个显式定义并且可重用的客户端网关之后。这种最佳实践和代码生成的代理(例如WCF)有所不同,后者将代码生成的类型与底层的服务客户端揉合在一起,使得生成结果难以模拟(mock)、测试以及替换为不同的实现,也经常导致对现有服务的改变会造成客户端代码的编译错误。这一点对ServiceStack来说很少会成为问题,因为它能够重用通用的服务客户端,因此在接口方面唯一需要改变的东西仅限于类型本身而已。并且由于基于消息的设计的优势,无论新增或移除任何功能都不会影响到现有的客户端。

ServiceStack在它的通用并且可重用的类型化.NET服务客户端采用了网关模式。我们同时支持SilverlightJavaScriptDart甚至是MQ(消息队列)的客户端。让所有.NET服务客户端都共享相同的接口,这使测试(通过注入或者写日志方式)变得简单,而且能够方便地在JSON、XML、JSV、MessagePack以及ProtoBuf等现有的客户端之间进行切换,而无需改动应用程序的代码。这就允许你在开发时使用类似于JSON这样便于调试的文本格式,随后在部署构建时切换为使用.NET上速度最快的二进制格式,例如Message PackProtoBuf,以降低数据传输量并实现最高的性能。

SOAP Web Services

以当前的情况来看,SOAP这种技术是不应该继续存在的,或者说应该仅仅限制在那些它还能够带来一些好处的地方使用。它的存在本身就是基于一个错误的假设,即为了实现数据的互交换性,必须表现出复杂性,要尽可能的严格与明确,这使得基于它的所有现实都必须实现一个复杂的schema以进行通信。而事实上,之与相对设计方式反而被证明是正确的,如果使用了类似于JSON、CVS、Protocol Buffers、MessagePack、及BSON等最小化并且灵活的格式,就会大大减少实现的障碍与复杂性,结果的生成会更快,互操作性与版本能力也会更强。

我总是觉得这件事很难想象,尽管SOAP是建立在HTTP基础之上的,但它的设计者们在开发出WSDL时似乎完全没有吸取那些宝贵的经验。WSDL所引入的类型、消息、操作、端口、绑定以及服务这些概念真的能够取代HTTP中简单的URL标识符,以及Accept/Content-Type这些头信息吗?我觉得这一点也不难判断啊。

虽然它名为SOAP(简单对象访问协议),但它既不简单,也不是什么对象访问协议,而且对于开发服务来说,它在许多方面都显示出它是个糟糕的选择,例如:

  • 臃肿而缓慢——通常各种尺寸与性能指标评比中都会作为反例出现。
  • 脆弱——对命名空间、字段、枚举或类型的改变都会造成运行时异常。
  • 在编程时难以使用——它本身不能够自然地映射为任何对象类型系统,在与应用程序代码进行转换时经常造成各种冲突。
  • 违反了HTTP——HTTP对于分布式访问提供了简单的谓词(verb),例如GET方法应该是无副作用的,因此可以被缓存在客户端以及中间服务器(例如代理)上。而由于每个SOAP请求都是一个HTTP POST方法,它就无法利用HTTP中最主要的一项好处。
  • 可访问性糟糕——一个SOAP消息在本质上就是为了传送定制的XML内容体与SOAP头的一个抽象容器。这种方式很糟糕,因为抽象消息格式需要花费你更多的精力去实现访问与序列化,同时为你的数据提供了弱类型的访问。而且它限制了只有能够收发SOAP消息的客户端才能够访问你的服务。由于它实在无法提供任何有意义的价值,许多Web API已经不再把响应封装在消息容器内,而是简单地将序列化的JSON与XML按原样输出,这种实践被称为POX/J(Plain Old XML)。
  • 复杂 – SOAP、WSDLs、UDDI、WS-*带来了不必要的复杂性。最终恐怕只有这些规格说明的参与者(或者SOAP框架开发者)能够良好地理解整个WS-*系列技术,以及使用那些它们曾经打算正式采用的一些大型复杂框架去以最好的方式去实现它了。
  • 鼓励代码生成——由于它的复杂性,如果不使用SOAP框架及代码生成的代理,一般来说是难以手工实现SOAP的。

由于SOAP明显背离了服务的核心优势,所以它能够流行起来实在是一个令人吃惊的事实。我曾有许多年为政府项目与大型企业开发服务的经验,很不幸的是其中的许多项目都有一个必需实现的要求,即把他们的服务暴露为SOAP终结点。不过许多互联网公司与专注于创造价值的创业公司都不太会使用这门技术了,他们更多地选择创建简单的HTTP API,返回单纯的XML或者JSON格式的响应。

虽然在多数情况下SOAP是一个糟糕的选择,但ServiceStack还是为你创建的服务提供了对SOAP终结点的支持,因为如今仍然有许多现存的企业系统只能够与SOAP终结点进行通信。这一点也和ServiceStack的核心目标相一致,即为你的服务提供最大化的功能性、可访问性以及连接方式。

SOAP在设计上是一种非常脆弱的格式,如果某个服务稍稍偏离了WSDL,而客户端又是由这个WSDL生成的,那你通常就会看到运行时错误的发生。虽然在静态类型系统中,快速失败被视为一种优势,但对于远程服务来说就不是一种理想的方式了,因为你通常希望能够实现向前兼容与向后兼容,因此服务端的API改变不会破坏现有的客户端。这一目标显然被WCF忽略了,它提倡RPC方法签名、SOAP格式以及代码生成,这可以说是当今的所有web service实现技术中最为脆弱的组合了。

SOAP与Protocol Buffers

将SOAP与Google的Protocol Buffers进行一下对比是很有趣的,它是Google推出的一种简单的接口描述语言(IDL),Google在其内部的所有RPC协议与文件格式中几乎都使用了该技术:

  • 它比SOAP小了好几倍,而速度要快上几个数量级
  • 它使用了一种简单的领域特定语言(DSL)来定义基于消息的API
  • 使用.proto文件来定义类型,对于编程语言来说在编程角度上非常适合
  • 包括了对多版本管理的实现
  • 为Python、Java与C++提供了原生的客户端与服务端绑定

MessagePack RPC以及用于开发Facebook的Apache Thrift是另外两个流行的开源选择,它们都提供了快速的,简单的IDL,并且为多数主流平台提供了绑定。

虽然是由不同的公司开发,但以上每个IDL都比SOAP更简单、快速并且易于使用。从SOAP的规格说明书的厚度来看,它应该是由委员会所开发的,但显然完全没有考虑过性能,大小以及易于实现。

JSON

虽然在大小与性能方面有优势,但Protocol Buffers与MessagePack都有一个问题,即它们使用了二进制格式。由于基于文本的格式与协议更易于创建、维护及调试,它们经常成为整个开放web的更流行的选择。近年来,在基于文本的数据互交换格式上的赢家显而易见是JSON,这是一个简单的、紧凑的自描述文本格式,它的主要优势是能够在所有支持JavaScript的浏览器上进行直接应用,只需使用浏览器内置的“eval()”语句就能够立即将其转换为JavaScript对象了。由于它的流行性,主流浏览器如今都加入了对JSON的原生支持,它们为eval()方法提供了一个更安全的替代方案,可以在老浏览器上提供一个基于JavaScript的备选实现方式。如果想更多的了解JSON以及它的好处,我推荐你去看看Douglas Crockford这个有趣的演讲——“Heresy & Heretical Open Source”

由于它的普遍性、功能多样并且易于使用,JSON在多数平台上都得到了原生的支持,并且很快地成为了开发Web API的首选数据格式,尤其是使用在Ajax客户端的时候。

从我自身的经验来看,我认为JSON对于实现服务是一种优秀的格式。它是一种更精简、灵活、容忍度高并且适应性更强的格式,并且它易于创建、调用以及调试,比起SOAP减少了冲突并提高了生产力。当我们开始ServiceStack项目时,JSON的唯一问题就是.NET Framework自带的序列化器比XML序列化器还要慢。出于对性能上的严谨态度,我们并不愿意接受这点不足,因此我们发布了我们自己实现的序列化器,它比任何其它.NET JSON序列化器都要快上3倍以上,并且比.NET Framework中的任何序列化器(包括二进制序列化器)都要快上2.5倍以上。

继承了JSON保持体积最小的精神,我们也开发了JSV格式,它类似于JSON,但使用了CVS风格的转义功能,使得它比JSON略微加快,更精简并且更便于人类阅读,它不仅能够用于.NET与.NET服务的交互,而且能够用以解析查询字符串,在ServiceStack的GET请求中是能够允许传递复杂的对象图的

ServiceStack与WCF方式

虽然WCF与ServiceStack在实现方式上是完全不同的服务框架,但它们却有着非常类似的目标。ServiceStack在创建服务上采取了一种非常面向服务的方式,它的设计经过了做好,能够以最大程度重用的方式达到服务的实现。通过代码优先、类型化设计的方式,我们能够为你的服务提供更强大的智能推断,以允许你自动地生成XSD、WSDL文件,自动生成元数据页,并且自动暴露预定义的路径。每个新加入的功能、特性、终结点与Content-Type都是围绕着你的现有模型创建的,并且不需要任何额外的操作以及对应用程序代码的任何改动就可以立即获得新功能。我们将这种方式理解为从一个代码优先的模型开始,作为最权威的部分,随后再将其功能暴露出来。

WCF的目标也是提供一个服务框架,以支持在多种终结点上运行服务,但他们站在一个不同的视角,并且为所有网络终结点提供了一个统一的抽象,你必须对服务进行配置以绑定到终结点。作为WCF的主要目标之一,他们是少数几个在最终实现中对WS-*系列技术提供了深入支持的框架。

我们最终的目标都是提供一个易于使用的服务框架,我们只是在如何取得简便性上有着完全不同的想法。

简便性与处理复杂性

WCF看起来比较喜欢繁重的抽象,用复杂的运行时XML配置进行管理,以及用大型工具为开发者提供端到端的连接能力。这个抽象层包含了许多复杂的技术:经过抽象的人工服务端对象模型很复杂、配置很复杂、WSDL很复杂、SOAP/WS-*也很复杂。最终结果是,为了对这些主题有一个良好的理解,对每个主题都需要看好几本书才行。

抽象

有一种陈旧的观念认为处理复杂性的解决之道就是添加更高层的抽象。这种方式的问题在于,只有这种抽象的设计很完美,它才能够显出效果。(例如编程语言比机器代码更合适)否则当你遇到了一些预料之外的行为,或者是配置、整合与互操作性上的问题是,你需要去理解在这些抽象层之下到底发生了什么。在这种情况中添加更多的抽象实际上只会增加开发者必须去熟悉的概念,以及当他们针对高级别抽象层进行开发时额外的认知。这也使得理解你的代码库更加困难,因为你必须理解每一个之下都发生了些什么。WCF的服务端对象模型是全新的、并且不够自然,这进一步放大了问题,新的开发者们对此一无所知,因为这种模型是独一无二的,并且它完全没有对服务或HTTP领域中的概念知识作出任何指导。因此当开发者们之后转而使用最好的服务框架时,他们从WCF中学到的东西完全没有用武之地。与其投入时间去阅读那些如何使用WCF的书籍,还不如把时间花在学习HTTP与TCP/IP上,因为即使你转而使用其它web service框架,这些知识依然能起到作用,它们是完全独立于编程语言或平台的。

从技术实现的层面来说,过多的抽象会导致不必要的性能负担,并且这种性能问题很难优化。因为当你尝试直接与底层API打交道时,这些抽象会产生阻碍以及各种强制性的阻力。而我们的方式是只在绝对必要时才会添加抽象,例如我们的IHttpRequestIHttpResponse封装对于为ASP.NET与自托管的HttpListener提供一个通用的API就是必需的。相比起添加抽象层的方式,我们更倾向于添加一些非强制性的特性,它们只是将一些底层接口与一些常见功能包装起来,以实现DRY。这种方式带来的好处就是允许终端用户自由地调用各种方式,并在他们自己开发的类库中加入自己定义的功能增强,以促进他们实现精益的、DRY以及可读性良好的代码库。暴露底层API能够确保我们保持框架的灵活性,用户可以通过多种方式进行访问而得已完整地控制系统的输出,也因为用户可以定制返回的响应,因此他们不会受到任何限制,而得已控制返回的每一个字节。

WCF的抽象

WCF为服务采取了一种统一的方式,它促进所支持的所有终结点都标准化为一个单一的人为抽象模型。这种方式也遇到了抽象的通用原则所产生的副作用,即一个统一的抽象模型要为每一种试图抽象化的实现承担所有的复杂性,因而它们只能够处理各种终结点的特性的交集部分,而且为每个结终点实现最低程度的通用功能。结果就是生成了一个不完整的外部API,并且由于抽象的存在阻碍了对底层终结点的访问,使得这种API的可访问性与可配置能力都大大削弱了。

要彻底了解WCF的功能需要强大的技术能力,需要你投入巨大的精力研究各种技术资源。但本质上来看这种精力投入是一种浪费行为,因为WCF与其它平台上的一些采用了标准方式开发的框架相比,生产力与功能都有所欠缺。对于终端用户来说,哪怕是为了稍稍掌握一些WCF的使用经验,也需要投入大得多的精力。出于以上原因,我不希望看到有人去使用类似于WCF之类的框架,或者是它那种统一的终结点的抽象方式。由于它的复杂性与庞大的技术实现细节,我也很怀疑它是否能够保持演化,以适应新的开发范式或是服务开发模式。我怀疑WCF会遭受到与WebForms相同的命运,退化为一个过时的技术,最终被微软下一代框架取而代之。

除了现有的REST、SOAP、MQ以及RCON之外,我们也计划为ServiceStack加入更多的终结点。但由于我们采取了一种基于约定的方式,我们会避免使用繁重的配置。我们所采用的基于消息的设计方式会避免使用大型工具,并简化必须实现的外部接口部分。首先从C#开始,逐渐移植其它语言,这使得终端用户可以立即使用新功能,而无需任何额外的投入。我们的松耦合架构不存在统一的抽象模型,这可以允许我们加入互不冲突的新终结点与特性,而不会为现有的组件带来强制的复杂性。

总而言之,我们提供了一个特性完整、性能更好并且更易于理解的服务框架,它的代码库也更加精益和灵活,并且使用它进行开发和维护所需的技术投入要小的多。

繁重的配置

WCF所推崇的另一个概念是XML配置,而这也是我们无法苟同的,它妨碍了测试,并且需要投入更多精力进行维护。只有你的应用程序中真正可配置的部分才应该放在你的应用配置文件中,定义及组织你的服务依赖应该交给代码去做,这种方式的额外优点在于它便于调试,而且可以在编译时静态地进行验证。WCF需要大量的配置,在ServiceStack中创建一个完整的应用程序所需的代码量比起一个的WCF Service所需的XML WCF 配置还要小。如果把配置信息放在IOC(控制反转)这一怪,那代码会更干净与整洁,因为它直接就可以引用你所需的特性的.NET API了。

大型工具

大型工具的问题在于,当你打算在其中尝试些新功能,或者是某些不在其设计初衷里的功能时,它就无能为力了。也就是说你成为了这个工具的奴隶,只能提供一些它所支持的功能。而且框架一旦进行了重写,大型工具就无法继续使用,因为它们所提倡的繁重的、复杂的代码库是很难演化或者是重构的。我推测这就是微软为什么总是重写新的服务框架,而不是改进现有的框架的原因。也是为什么Web API不能够重用MVC现有的抽象方式,并且不支持SOAP的缘故,尽管它们都采用了相同的RPC API设计方式,并且它的自托管功能选项也是基于WCF创建的。

由于ServiceStack是基于ASP.NET的最底层创建的,因此它提供了对ASP.NET MVC良好的集成能力,也能够方便地重用MVC中许多组件现有的功能实现,例如Authentication filter attributeCachingSession provider等等。你也可以在MVC中方便地调用ServiceStack服务,比起C#方法直接调用只是稍稍麻烦一点。

ServiceStack的复杂性

ServiceStack与WCF相反,它看起来要简单许多。例如ServiceStack的架构只要一页纸就能画下启用它只需在web.config中加入一行,告诉ASP.NET将所有请求转发给ServiceStack即可。

约定与人为的复杂性

ServiceStack与微软开发类库与框架所采用的方式,其观念上的最大不同或许就是在如何最好地处理复杂性这一点上了。微软倾向于明确性,XML方式的可配置能力,引入繁重的抽象层(为了前瞻性而支持所有潜在的用例),依赖于抽象层将简单的面向用户的门面经过改头换面后暴露出去,为了方便使用设计器工具及新手开发者进行优化。微软的主要动力是为开发者提供一个熟悉的编程模型,并且通过使用大型工具来简化模型的上手难度。从WebForms中就可以看出这一点,它为WinForm开发者提供了一个基于事件的编程模型来开发网站,WCF则让开发者使用传统的C#方法与接口方式去创建远程服务。它们采取的方式通常会导致完成功能需要投入大量的技术精力去研究巨大的代码库。

与之相反,我们将大型的代码库当作代码的最大敌人,并且避免引入人为的复杂性,这是我们的主要目标之一。比方说我们强烈反对引入新概念、抽象或编程模型。我们崇尚小巧的低级别接口,它与底层领域1对1进行映射,以实现灵活性的最大化,减少冲突,并且在需要进一步定制化时将转换的复杂性降至最低。通过可重用的工具类与扩展方法保证了DRY与高级别的功能。我们采取一种代码优先的开发模型,通过代码去捕捉用户意愿的本质,这促进了一种更优雅、不妥协的设计方式,避免了使用设计器工具时所带来的转换的复杂性。我们的所有类库都使用POCO,以获得最大的可重用性,并且我们内置的转换器可以简化在领域特定模型中进行转换所需的精力。

我们为用户展现了一种基于消息设计的最佳实践,为从头开始开发的服务提供了一种最优的方式。服务就是普通的C#类,无需关注任何终结点的问题,因为这种依赖是可以自动解析的,这种方式会促使用户采用良好的代码实践。

约定以及减少对外部知识的了解是我们处理复杂性最好的武器。相比明确的指定实现方式,更好的办法是提供一个基于约定的默认行为,使它按预计的方式运行。这就让用户不必一定要学习你的框架API,因为我们可以假定预计的标准行为的结果,并允许我们随时提供更多的能力,比方说,它能够自动支持外部所调用的所有内置终结点与格式。你也可以让你的服务返回任意的响应,它会自动序列化为所请求的Content-Type。

达成简单性

我们相信,达成简单性的最好方式就是在第一时间避免各种复杂性,我们采取了以下方式:

  • 去除了各种不必要的部分与堆积的抽象——而是直接基于ASP.NET基本的IHttpHandler接口所创建。
  • 约定胜于配置——所有实现了IService的服务都会被自动加载,并且随时使用。
  • 合理的默认表现——ServiceStack可直接被外部调用,无需进一步的配置。
  • 支持全部特性——所有内置的格式与终结点都默认可用,预定义的约定路由也自动可用,但也可以通过配置关掉它。
  • 自动探索——ServiceStack包含了一个元数据页面,列举了全部的服务、所处的路由、XSD及WSDL等等。
  • 未引入新的构造函数——ASP.NET开发者能够立即了解如何定制化HTTP请求与响应。
  • 以C#语言开发——以C#语言开发,并且以无冲突的方式添加新特性,因此在普通的开发过程中就能够立即应用这些新特性了。
  • 追求生产力——在ServiceStack中的每个请求DTO都必须是唯一的,它允许你在调用服务时只提供请求DTO的名称与内容就可以了。
  • 围绕POCO开发功能——你可以在ServiceStack中使用同一个POCO作为DTO、OrmLite、Data Model、Cache、Session以及Config等等。
  • 使用基于消息的设计——比起RPC方法签名,绑定至一个单一的模型更易于实现和推断。
  • 灵活——提供一系列Custom Filter以及可以插入实现的事件钩子,以此定制Request与Response管道中每一阶段的行为。

避免大型工具与代码生成

我们并不喜欢代码生成,因为我们相信它为项目带来了不必要的麻烦,我们也反对在开发工作的核心部分依赖于任何大型工具。

由于你的ServiceStack服务的服务契约在设计上是由你的请求与响应DTO维护的,我们就能够提供一个类型化的端到端的API,只需要你用来创建服务的那些类型以及那些通用的、可重用的.NET客户端就可以了。这允许我们为任何.NET上的服务框架提供一个最简洁的、类型化的以及端到端的API。

可测试性

基于WCF的设计方式以及它对配置的严重依赖,看起来WCF在设计时完全没有考虑到可测试性。而这一点是ServiceStack的核心目标之一,我们默认提供了一个内置的(并且可重写的)IOC容器,鼓励在一开始就遵循良好的开发实践。开发一个服务只需实现一个不包括任何方法的IService接口,或者从其它现有的Service类、或ServiceBase类继承即可,这些类都是可独立测试的,并且完全可以mock。这一系列努力的成果就是,你所实现的单元测试可以用作XML、JSON、JSV以及SOAP的集成测试。我们所提供的自托管HttpListener类型使得进行内存中的集成测试非常方便。

性能

性能是ServiceStack的首要目标之一,我们将性能视为最重要的特性。基于消息的设计提倡更少的网络调用,我们也很小心地保证只暴露那些运行快速的面向用户的API。一般情况下我们不接受来自外部的贡献代码,因为它们会包含速度较慢的代码。我们对自己的实现是全力以赴的,我们没有使用任何运行时的反射或者正规表达式匹配,而是采用其它更快的解决方案。

最快的序列化格式

我们开发并维护着.NET上最快的JSON、JSV以及CSV文本序列化器,同时允许以插件的方式使用Message PackProtocol Buffers这两个.NET上最快的二进制序列化器。

丰富的Caching Provider

由于缓存是创建高性能服务不可或缺的技术,我们提供了一个丰富的Caching Provider模型,包含了对各种内存缓存、Redis、Memcached、Azure以及Amazon后台的实现。缓存API会保证使用最优化的格式,比方说,如果客户端支持,我们会将压缩后的JSON输出缓存起来,并在之后发生调用时直接将缓存中的内容输出到响应流中,这就保证了托管代码能产生最快的响应速度。

修复了.NET中的性能问题

每次我们找出微软的类库中的瓶颈时,我们都会使用更快速的实现去替代它。我们已经实现了自己的JSON序列化器,作为插件的Gzip和Deflate压缩类库,并且提供了自己的Session实现,它能够与以上任意一种Caching provider相配合使用,它的出现也避免了困扰ASP.NET开发者已久的严重性能问题

为最快的分布式NoSQL数据库提供最好的客户端

出于我们对开发高性能服务的技术追求,我们为最快的分布式NoSQL数据库Redis开发并维护着一个.NET上最好的C# Redis客户端

采访ServiceStack的项目领导Demis Bellot——第1部分(网摘)的更多相关文章

  1. 采访ServiceStack的项目领导Demis Bellot——第2部分(转)

    ServiceStack是一个开源的.支持.NET与Mono平台的REST Web Services框架.InfoQ有幸与Demis Bellot深入地讨论了这个项目.在这篇两部分报道的第2部分中,我 ...

  2. 采访ServiceStack的项目领导Demis Bellot——第1部分(转)

    ServiceStack是一个开源的.支持.NET与Mono平台的REST Web Services框架.InfoQ有幸与Demis Bellot深入地讨论了这个项目.在这篇两部分报道的第1部分中,我 ...

  3. spring原理案例-基本项目搭建 01 spring framework 下载 官网下载spring jar包

    下载spring http://spring.io/ 最重要是在特征下面的这段话,需要注意: All avaible features and modules are described in the ...

  4. 项目- Vue全家桶实战去哪网App

    最近在学习Vue,花了几天时间跟着做了这个项目,算是对学习Vue入门的一个总结,欢迎同学们star 去哪网APP

  5. struts项目部署在Tomca上在断网情况下启动报错

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6015693.html 前段时间,项目部署到现场后,反馈Tomcat能正常启动,但是项目有时访问不了也不报错. ...

  6. APP手工项目02-用例编写-测试报告-fiddler弱网测试

    回顾 APP专项测试(兼容性,安装,卸载,升级,交叉事件,PUSH消息,性能,其他类型) 项目环境(开发环境,测试环境,准生产环境,生产环境) APP内测发布平台(蒲公英,fir.im)支持apk,i ...

  7. frp 配置多个 web 项目,无需购买域名 (访问内网可视化界面,jupyter noterbook, visdom, tensorboard)

    frp 配置多个 web 项目,无需购买域名 简单配置,参考 前言: 网上也有很多教程包括官方文档,都需要购买域名,并且把 frpc.ini 中 [web]节配置的  custom_domains 的 ...

  8. 开源项目 PM 浅谈如何设计官网

      有用户反馈进入官网首页光秃秃的一片,大家忙着做产品,忽略了官网的建设,惭愧惭愧.   确实,极简风格和极其简单还是有很大区别的.     旧的 Web 端   除了极其简单之外,它还有一个小问题, ...

  9. Arduino IDE for ESP8266 项目云盒子(3)外网访问

    互联网访问esp8266 https://item.taobao.com/item.htm?spm=a230r.1.14.20.eYblO3&id=521945102409&ns=1& ...

随机推荐

  1. Sql Server之旅——第四站 你必须知道的非聚集索引扫描

    非聚集索引,这个是大家都非常熟悉的一个东西,有时候我们由于业务原因,sql写的非常复杂,需要join很多张表,然后就泪流满面了...这时候就 有DBA或者资深的开发给你看这个猥琐的sql,通过执行计划 ...

  2. 《java JDK7学习笔记》之跨平台与路径设置

    Platforms表示Solaris.Linux.Windows各种操作系统平台,在这些平台上架构了java Virtaul Machine,简称JVM,Java虚拟机. Java虚拟机可以让java ...

  3. 按Enter键后Form表单自动提交的问题

    怪事年年有,今年特别多. 话说,最近项目中遇到一件怪事,当我鼠标focus在文本框中,轻轻敲了下回车键,尼玛页面突然刷新了,当时把宝宝给吓得. 接下来就是一番苦逼的烧脑和蛋疼~ 一.被表象所迷惑 突然 ...

  4. Linux nm命令

    一.简介 显示关于对象文件.可执行文件以及对象文件库里的符号信息. 二.选项 http://www.cnblogs.com/wangkangluo1/archive/2012/07/02/257243 ...

  5. linux chmod命令和chown命令

    一.chmod及文件权限 1.了解文件权限 root账户新建一个目录permission,在该目录新建一个文件file,通过ll就可以查看其权限. root@development:~# cd per ...

  6. python curses使用

    python 中curses封装了c语言的curses,把c中复杂部分简单化,比如addstr(),mvaddstr(),mvwaddstr()合并成了一个addstr()方法. 一.语法入门 1.打 ...

  7. 自罚一杯-PHP基础(一)

    hi 懒癌是最可怕的癌了吧,在即将迎来本命年时,给自己定下每天必干的几件成习惯的事情,望不再鸽!! php是我一直在学习的东西,但总以为php简单,难点在需要掌握大量的相关知识,其实并不完全对,php ...

  8. 挤点时间写博客-php&MySQL实践

    hi 晚上要吃火锅的嘛,挤点时间写点东西吧,别被老板发现哦 1.PHP与MySQL 五.文章发布系统之后台 5.2 创建配置文件和初始化文件 为了统一配置以及管理方便,还有就是减少代码的冗余. 分别为 ...

  9. 解决chrome在docky上的图标模糊或不能锁定的问题

    1,终端执行以下命令:sudo nautilus2,打开usr/share/application,找到chrome的图标,右键找到属性,把“命令”里的内容换成/usr/bin/google-chro ...

  10. 如何通过JQuery将DIV的滚动条滚动到指定的位置

    这里有一个方法可以将DIV的滚动条滚动到其子元素所在的位置,方便自动定位. var container = $('div'), inner = $('#inner'); container.scrol ...