虽然系统越来越复杂,以及新分布式架构设计的思想普及,越来越多的系统采用了分布式的架构,特别是HTTP为交互方式的接口调用,移动端和PC端的并行对分布式架构带来了很大的推动。各式各样的服务接口,在处理业务流程之外有一些共性的问题,正视设计和解决这些问题,会大大提高程序的可用性,扩展性和可维护性。
1、日期格式时间在生活中是一个容易忽视但是影响很大的一个因素,在古代,是否有自己的历法是评定一个统治者是否建立通知的一个重要的标志。应用程序中大量存在日期数据,比如数据的创建时间,更新时间,业务处理时间等。接口交互中关于日期传输需要考虑两个问题:格式和精度。 日期的格式主要有两种主要的方式:可读方式和不可读(不容易直接读)的方式。比如对于当前时间(笔者写此文章的这一刻),可以使用2017-03-01 20:30:30或者2017/3/1 20:30:30等其他的明确年月日时分秒各个元素可读格式表示;另外一种方式为以”1970-1-1 08:00:00″为基准的long型计数法。可读型的格式在调试,排查问题时方便易懂,但是在开发过程中可能需要做数据解析,转换为开发语言识别的类型用于日期的操作,增加了部分开发工作量。long型格式在执行效率上要高效,但是在开发调试过程中需要转换表示方式。虽然本人更加赞同long型的格式,但是如果按照可读型的方式返回,对于大多数应用不会有很大的区别。但是需要统一方式,系统中多个服务提供方对于日期格式的不统一会给客户端调用带来大量无效代码的开发和错误引入的可能性。 常用的计算机或者开发语言关于时间的最小单位为微秒,但大部分的业务使用秒级已经满足需要了,所以可以定义通用单位为秒,如果需要接口可以自定义可精确到微秒甚至更低。如果没有通用单位的定义,在以long型传输时会导致差之毫厘谬以千里的错误。 2、小数的传输我们先来看一个简单的浮点数计算小程序,以java语言为例:
结果为:0.010000000000000009
大部分的编程语言都会有更高精度方式处理浮点数的运算,保证计算的精确性,比如Java语言的BigDecimal,我们使用BigDecimal再次测试1-0.99:
结果为:
结果为:0.01 因此我们在数据交互中,浮点数最好以字符串的方式进行传输,保证调用方在进行浮点数计算时可以采用不损失精度的方式进行初始化并运算。 3、结果值的格式如果你服务提供方是否经常会听到这样的抱怨:结果值返回格式不统一,结果值的属性名不一样,属性的个数不一样,值表示的含义不一样,等等。如果具有通用意义的调用状态,错误码,错误信息提示,业务结果返回方式风格不统一,调用方在进行封装接口调用,错误重试,结果值解析等通用功能时,将不得不花更多的精力,以及牺牲程序可读性,扩展性。 推荐使用以下格式作为接口的返回值:
分析一些知名公司的开发平台接口,存在把status和errorCode合成一个属性表示接口,但笔者认为分开表示更友好,因为调用结果状态是有限的,比如成功,参数错误,失败等,是一个业务无关,但是失败的原因(errorCode)是业务相关的,并且可以无限多,并且多个服务端的错误码存在共用的可能性,因此建议使用两个属性表示结果状态和错误编码。 对于返回的业务数据,建议把data作为Map格式,把结果值作为map的键值对存入,方便返回多个业务数据的情况。 4、幂等性幂等性:指一次和多次请求某一个资源应该具有同样的副作用。幂等性是分布式系统设计中十分重要的概念,在分布式系统中网络抖动(不可预知的短期不可达到),业务超时等都可能导致调用方没有收到服务器端的返回值或者不是预期的返回值(比如:应该接收一个JSON字符串,但是返回了一个网络异常),为了保证系统的高可用性,重试是一个经常会采用的方法,因此服务器端可能会收到多次调用,保证多次调用具有相同的副作用是接口的重要属性。 如上图所示以上5个步骤中任何一个流程出现问题都会导致客户端不能接收到预期的结果,可能执行重试,从步骤3开始服务器端的业务可能已经执行完毕,如果重试表示服务方会接收两次相同的调用。服务方识别出重复调用,并且当已经执行过业务逻辑,不再次执行重复并返回正确的结果就是幂等性。 如何实现幂等性呢?
3、存在一些调用没有关键字段的情况,比如用户购买商品下单,无论是商品ID,商品数量,价格,会员ID都无法作为关键字段用于检查是否已执行,有人说可以使用入参是否完全一致作为判断条件,实际中这样是可以作为判断依据,但是理论上我们无法完全断定是用户的主动行为还是系统的异常重试。对于这种场景,引入一个业务无关的关键字段可以更好的解决问题,无论命名这个字段为requestId,ticketId或者其他,有如下要求:a、这个字段是由客户端生成的,b、全局唯一的(至少要一个可能会发生重试的时间范围或者空间范围内是唯一的),服务器端可以依据该字段作为是否重复调用的依据。 如何判断是否存在关键字段,也存在随着业务发展以前作为关键字段的数据不能作为判断依据了,因此在设计开发时所有的接口都添加requestId(假设我们采用了requestId这个命名),使用requestId作为幂等性的依据可以提高接口的可用性和重试的校验功能的重用性。 5、接口安全作为接口提供方有时候会遇见一些场景:错误的入参,导致业务异常、错误提示,但是不能确定数据是哪里产生的;接口升级不知道需要通知哪些调用方,因为调用方都可以通过文档自行接入;部分调用方需要特殊业务处理,但是没有关键字段把它与其他调用方区分;数据统计缺少关键字段判断来源等。 因此入参中含有准确的表示调用方字段,并能够识别是否为调用方身份,可以提高系统的安全性,扩展性。
对比以上常用的方式的优劣,建议采用在性能不敏感(非对称和对称加密在对入参进行Hash后签名的实现方式时效率差别不大)的情况下尽量采用非对称加密,因为在多对多的交互场景中,非对称加密比对称加密的签名方式更高的安全性。
上图所示:公用密码会导致密码扩散,非公用密码导致调用方保存多套密码。 采用非对称加密多对多交互方式: 上图所示:CS秘钥对分别在调用方和服务方保存,保证了安全性和准确性。 6、数据字典分布式架构使系统中不同的模块可以在不同的团队开发维护,因此属性含义和命名的一致性的要求急剧上升,无数次的参数命名转换就是命名不一致的墓碑。 命名不一致主要包含2个方面:
如果没有很好沟通和协商,就会导致调用方各种转换,增加大量无用属性转换代码,并且容易引入错误。因此一个符合团队条件的数据字典管理(wiki,文档,git,应用程序等)可以提供开发的效率和可用性。 以上几点是笔者在项目开发时的心得体会,若能在你设计开发分布式系统时带来些许帮助,就深感欣慰。当然分布式设计开发不仅仅以上几个问题可以探讨,比如事务,交互过程的超时处理,重试机制等,限于笔者水平以及关于通用问题的判断,本文没有涉及,见谅。
| ||||||
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|