写点什么

采访 ServiceStack 的项目领导 Demis Bellot——第 1 部分

  • 2013-11-19
  • 本文字数:10587 字

    阅读完需:约 35 分钟

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

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 打交道时,这些抽象会产生阻碍以及各种强制性的阻力。而我们的方式是只在绝对必要时才会添加抽象,例如我们的 IHttpRequest IHttpResponse 封装对于为 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 attribute Caching Session 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 Pack Protocol 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 客户端

关于受访者

Demis Bellot是来自 Stack Exchange 的一位开发者,他维护着 StackOverflow Careers 2.0 的后台网站以及基于 ServiceStack 创建的 MQ 服务。他同时也是 ServiceStack 的创始人以及项目领导。

查看英文原文: Interview With Demis Bellot, Project Lead of ServiceStack - Part 1

2013-11-19 08:476783
用户头像

发布了 428 篇内容, 共 178.4 次阅读, 收获喜欢 38 次。

关注

评论

发布
暂无评论
发现更多内容

谈 C++17 里的 Observer 模式

hedzr

c++ 设计模式 Design Patterns c++17 observer pattern

【LeetCode】从根到叶的二进制数之和Java题解

Albert

算法 LeetCode 9月日更

数据中台建设的9大误区,你中了几条?

博文视点Broadview

腾讯看点CTO徐羽: QQ浏览器背后的推荐AI中台 | AICon

博文视点Broadview

新基建+新科技,智慧港口船舶抢抓数字化转型先机

一只数据鲸鱼

数据可视化 智慧港口 智慧船舶 智慧码头

设定Docker容器日志的大小和轮询规则

耳东@Erdong

Docker log 9月日更

架构实战营 外包学生管理系统的架构文档

💤 ZZzz💤

架构实战营

固定QPS压测初试

FunTester

性能测试 测试框架 压力测试 QPS FunTester

眼界大开 声临其境丨胡宜峰:视频深度伪造检测技术在内容安全领域的探索与实践

网易云信

人工智能 深度学习 音视频

选择低代码应用程序开发框架的5个关键标准

低代码小观

程序员 低代码 企业开发 低代码开发 开发框架

Go 编码习惯

baiyutang

Go 语言 9月日更

在线JSON转typescript工具

入门小站

工具

架构训练营模块三作业

TIEDPAG

架构训练营 模块三

【Flutter 专题】45 图解矩阵变换 Transform 类 (二)

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 9月日更

共助数据自主创新生态|DataPipeline实时数据融合平台与华为云GaussDB数据库完成兼容互认证

DataPipeline数见科技

企业级数据融合平台上线,DataPipeline助力中国最大保险公司海外业务再创佳绩!

DataPipeline数见科技

10行代码集2000张美女图,Python爬虫120例,再上征途

梦想橡皮擦

9月日更

IP地理定位之数据驱动广告矩阵

郑州埃文科技

密码学系列之:bcrypt加密算法详解

程序那些事

算法 加密解密 密码学 程序那些事

OpenMetric与时序数据库模型之主流TSDB分析

华为云开发者联盟

Prometheus Influxdb 时序数据库 tsdb OpenMetric

双赞的一体机主板能应用到哪些行业?

双赞工控

18.理智分析--人类离威胁还相当遥远

Databri_AI

人工智能

面试官让手写队列,差点挂了

bigsai

Vue进阶(幺幺叁):element ui 表单验证 this.$refs[formName].validate()问题解决

No Silver Bullet

Vue 9月日更

玩转TypeScript工具类型(中)

有道技术团队

typescript 大前端 网易有道

国足历届世界杯对战记录整理

6979阿强

图算法 GraphScope 2022年卡塔尔世界杯 中国国足

图数据库在社交方向上的应用

6979阿强

社交网络 GraphScope 图数据 图关系

学生管理系统架构设计

一叶知秋

#架构实战营

深入了解现代web浏览器(第四部分)

GKNick

企业数字化转型选用“低代码平台”的8条建议!

优秀

低代码

盘点 | 主流云原生数据库技术方案

RadonDB

数据库 云原生

采访ServiceStack的项目领导Demis Bellot——第1部分_SOA_Roopesh Shenoy_InfoQ精选文章