天天看点

《低功耗蓝牙开发权威指南》——2.11节范式

本节书摘来自华章社区《低功耗蓝牙开发权威指南》一书中的第2章,第2.11节范式,作者 (英)robin heydon,更多章节内容可以访问云栖社区“华章社区”公众号查看

2.11 范式

最成功的技术常围绕不同的范式而设计,低功耗蓝牙也不例外。低功耗蓝牙技术使用两个主要的架构范式:客户端–服务器架构和面向服务的架构。

2.11.1 客户端–服务器架构

在客户端–服务器架构中,客户端通过网络向服务器发送请求,服务器回复响应。这是互联网背后的主要范式,无疑也是有史以来发布的最成功的网络技术。

举个例子,当你在web浏览器中键入一个url地址时,它首先发送地址到dns服务器。dns服务器将已分配给该名称的对应的ip地址返回。然后,客户端通过超文本传输协议(http)向服务器发送请求,一旦连接确立,请求服务器发送所要求的资源。接着服务器回复适当的资源,通常是一个包含了标记信息(html)的文本文件,告诉浏览器如何显示信息。

该文件还可以包含一些url供客户端获取资源,如图片或其他页面。这些额外的链接被视为html页面彼此链接成一个巨大的web网络的真正原因,也正是因为这样,才有了web页面和web服务器的说法。

关于服务器和客户端的分工问题,二者有明确的区分。服务器通常以结构化形式保存信息,可以说,这些信息正是服务器存在的真正原因。信息可以是任何形式的数据,比如当前夏威夷科纳区的天气,下一班从首尔市区到机场的火车的时刻表,或只是一群朋友间的闲谈。另一方面,客户端没有任何数据,只是将请求发送到服务器。一旦从服务器收到答复,就可以执行指定的任务,例如为用户显示信息或发送通知,告诉他们认识的某人刚在推特上发了些东西。

客户端–服务器架构的主要优点是将客户端和服务器二者划分开来。当系统的不同部分位于不同的设备上时,这种划分必不可少。将其中一部分作为服务器而另一部分作为客户端,系统中二者的关系随之确定下来。

这种架构的主要优点是便于扩展。除了能够访问资源的url,客户端不需要知道任何东西。客户端的数量也可以十分庞大,一些互联网的网站每天可能收到来自数百万客户的请求。服务器并不真正关心这些客户端是谁或来自何处,它只是对每一个请求做出响应。

该服务器架构亦可进行拓展。用一台机器每天响应数百万的请求可能导致过载甚至服务失效。解决的办法是使用许多相同的、有权访问相同信息的服务器。还有个办法是将同一个域名解析成不同的ip地址告诉不同的客户端,使负载均匀地分布在各服务器之上。这就是所谓的负载均衡。

2.11.2 面向服务的架构

客户端–服务器架构之上的进一步抽象是面向服务的范式。这是一个将服务器中的信息组织成服务的模型。该服务可以被发现、进行交互或用做已知的语义。这意味着该服务具有确定的行为,在给定相同的条件时,总会产生同样的结果。

这种范式是最成功的互联网系统的基础,如soap、rest、cobra、rpc、web服务等。

为了更好地说明这一点,一种方法是把它与现实世界的例子相结合。假设有一个包裹需要尽快交付给另一家公司,你可能会做的第一件事是打电话给快递公司安排取件,然后支付服务费。关键在于,你总是知道接下来会发生什么,快递公司始终遵循一套默认的行为:无论哪天给他们一个包裹,他们所做的事情都完全一样—把它及时地快递到目的地。这项服务具有预先确定的行为和已知的语义,并产生可预测的结果。

上述例子里有一个有趣的地方,你和快递公司的两个不同的人打了交道:接电话并接受业务请求的人以及上门取件的快递司机。此外,也许你并未意识到,其实还有一个处理财务账款的人。三人提供的子服务相互结合,形成了快递公司的主营服务。

这些子服务也是通用的,它们可以用于许多不同类型的公司。比如财务事务处理几乎能以同样的方式应用到各家公司。类似地,利用电话的方式在某个地方装货且在另一个地方卸货的服务也可以被应用于出租车公司。

这一切要运转,每个环节都必须遵循一套规则和惯例,下面将对此进行详细介绍。

正式合约

一个服务之所以被视为服务,是因为其在公开的功能以及如何工作两个方面提供了正规的描述。例如,快递公司的司机穿着公司制服,驾驶公司车辆,并愉快地迎接客户。他会在不同地点之间快速、安全地驾驶车辆,将包裹完好无损地送达。任何违反这些规则的行为将被视作违反了客户与快递公司之间的合约。因此,大多数快递公司也要求客户首先同意这种正式合约,然后才去取包裹。

正式合约的一个好处在于,一个服务的实例很容易被另一个服务的实例所代替。只要两个服务的实例具有相同的功能和行为,这种情况就有可能发生。例如一个财务人员离开了公司,公司应该很容易找到一个知晓相同做账规则的替代者。

在低功耗蓝牙中,这些正式合约位于服务规范之中,并为蓝牙sig所采用。这些规范也有相应的测试规范,以确保实施行为的有效性。

松耦合

在面向对象的软件中,单独的系统组件是指被设计成无边界效应的独立对象。那些发生在组件之间的相互作用可以被明确地定义和测试。

将依赖关系减少到最低限度,使修改服务的实现时不会带来意想不到的边界效应,从而降低风险。从这一点出发可以得到一个合理的推论,即应当将正式合约及其实现这二者分离开来。这样,只要正式合约不被破坏或修改,实现就可以根据需要随意变换。

打个比方,快递公司可以增加更多的司机,把一个司机完成所有工作的局面变成很多司机在城市的一小块区域内取件、运回分拣中心、再发送到目的地,而这些工作可能由不同的司机完成。从客户的角度来看,该服务依然如故,包裹仍然按照预期被收走而后送达,但实现的过程却是完全不同的。注意,这里并未改变财务服务或订单服务。

抽象化

服务抽象是十分重要的设计原则,如果违背该原则可能造成严重的后果。假如不利用抽象化,而让客户端掌握服务的所有实现细节,那么客户端使用该服务的方式将会严重制约服务的演化。

知识总是多多益善,这虽然是个常识,但在面向服务的体系结构下,客户端对服务的实现知识了解得越少越好。太多的知识会让客户端与某种具体的实现发生关联,从而妨碍服务的重用或是重新设计。一旦服务的实现发生变化,客户端有可能崩溃。

为了确保遵循该原则,服务公开的状态应当尽可能少。此外,只应规定服务行为的外在表现。

可重用性

可重用性的概念多年来一直是面向对象方法所期望的设计目标。但是,真正意义上的可重用性是令服务适用于多种不同应用的一种能力。如果未经认真思考,设计的服务往往仅能完成某一种工作。而在良好的设计方案中,服务可以与具体的实现过程相互独立。这意味着该服务能够在其他应用程序中快速、轻松地获得重用。

蓝牙技术联盟为应对这一挑战设立了一个工作组,该工作组的唯一工作就是找寻通用的功能,对其需求进行抽象以实现有效的重用。

无状态

为了让众多客户端支持服务扩展,服务器不能保存任何客户端的状态数据。服务器或许可以定义一个服务用来记住客户端已经告诉它们的所有信息,使客户端在后续请求时不必重复这些信息。但该方法的问题在于,这些信息占用了大量的内存,且依赖于客户端和服务器二者之间同步的共享状态信息。这将导致服务器完全依赖客户端的正确操作,而这样的假设无疑站不住脚。

因此,无状态的设计目标是删除客户端和服务器之间所有的交互状态。虽然仍然有一些状态信息存储在服务器上,但是它们均为服务器状态,而非客户端状态。这意味着,无论任何客户端在任何时间发送任何请求,服务器都将以完全相同的方式响应相同的请求,而不管请求来自哪个客户端。

可组合性

上面提及的各种目标意味着,服务应该被设计得小而简单,然而现实世界从来都不是那么简单。现实世界中的服务是复杂的。要解决这个明显的矛盾冲突,面向服务的体系结构鼓励聚合较小的服务,以实现更高级的服务接口。

这一设计目标鼓励服务间相互组合。例如,快递公司的服务是由三个独立的服务组成的,这些单独的服务只要围绕既定目标,就可以合并成一个快递公司。同样,一些服务可以结合在一起组成出租车公司或商务汽车租赁服务。尽管实现不尽相同—送货车、家用车、行政豪华轿车,但本质上是相同的服务。

自治

为了重用和组合服务,服务本身必须是可靠的。基于系统内其他组件构造的服务在执行请求处理的可靠性上无疑要比对所有事情实施完全控制的服务更差。

自治的服务可以独立执行任务,而不管在其周边发生的情况。这些服务要在其他应用程序中实现重用将会非常容易。非自治服务可能会带来许多额外的支撑服务,并且可能与其他服务相冲突。

例如,快递公司的司机们按照规定自主活动,完成包裹收集和递送。即使办公室那边出了岔子,他们仍能继续独立地工作。

可发现性

最后,要想使用服务就必须能够发现服务。乍看上去似乎显而易见,但其实服务发现能力在移动自组织网络中是必不可少的。没有服务发现,所有服务必须采用静态编程,复杂、繁琐而且容易出错。

可发现性通常是通过一个单独的、与服务交互的协议来实现的。比方说,要找到快递公司,人们应当使用电话簿或在互联网上搜索,绝不会是随机拨打电话号码并希望接通的是个快递公司。

低功耗蓝牙采用不同的方法:使用同一个协议实现服务发现以及服务交互。该协议称为“属性协议”,服务可发现性在其规范中被称为“通用属性规范”。这二者都将在第10章中进行介绍。