加载中…
个人资料
AlibabaInfrastructure
AlibabaInf
rastructure 新浪机构认证
  • 博客等级:
  • 博客积分:0
  • 博客访问:125,528
  • 关注人气:250
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

远程接口设计经验分享

(2014-12-10 17:47:37)
标签:

java核心技术

架构

分布式系统与计算

写在前边

 

分布式架构是互联网应用的基础架构,很多新人入职以来就开始负责编写和调用阿里的各种远程接口。但如同结婚一般,用对一个正确的接口就如同嫁一个正确的人一样,往往难以那么顺利的实现,或多或少大家都会在这个上边吃亏。

每年双十一系统调用复盘的时候,我都会听到以下声音

  • 你们调我的接口报错了竟然不会自己重试?
  • 我的返回值应该从这里取
  • 我返回isSuccess() == true,不代表业务成功,你还需要判断ERROR_CODE
  • 这个ERROR_CODE没说全部都要重试啊!
  • 这个ERROR_CODE必须要重试!

还有很多了,本文的目标就是帮助大家思考,如何设计自己的远程接口,让接口做到健壮易用,节省大家在这块泥潭中所挣扎的时间。

 

一个日志服务LogService

 

PS:本例子的代码可以见 Excavatore-DEMO

远程接口设计经验分享

系统架构

 

一个集中性的日志服务器,要求应用通过日志系统提供的日志服务,将所有日志集中统一的输出到固定的文件中。

 

系统架构图

远程接口设计经验分享

远程接口设计经验分享

接口v0.1版

 

远程接口设计经验分享

远程接口设计经验分享

RPC调用


 

什么是RPC调用

RPC(Remote Procedure Call)远程过程调用,一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的技术实现。

RPC采用C/S模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息的到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。

以上信息摘录自百度百科

一次完整的RPC调用过程

 远程接口设计经验分享

请求过程

  1. 客户端函数将参数传递到客户端句柄
  2. 客户端句柄将请求序号、远程方法、参数等信息封装到请求对象中,并完成请求对象序列化形成请求报文,通过网络客户端发送请求报文。
  3. 请求报文通过网络客户端网络服务端所约定的协议(HTTP、RMI或自定义)进行通讯。
  4. 网络服务端收到请求报文之后,通过反序列化,从请求对象中解析出远程方法、参数等信息,并根据这些信息找到服务器句柄
  5. 通过服务器句柄完成服务器函数的本地调用过程

    自此,整个请求流程完成。

  • 应答过程
  1. 服务器函数执行的过程将结果返回服务器句柄,返回的结果可能是正常返回,也可能是以抛异常的形式返回。
  2. 服务器句柄根据返回的值与请求序号封装到应答对象中,并完成应答对象的序列化,形成应答报文,通过网络服务端发送应答报文。
  3. 应答报文通过网络服户端网络客务端所约定的协议(HTTP、RMI或自定义)进行通讯。
  4. 网络客户端收到应答报文之后,通过反序列化,从应答对象中解析出请求序号所挂钩的客户端句柄
  5. 客户端句柄将返回数据返回到客户端函数,以返回值或抛异常的形式将信息返回

    自此,整个应答流程完成。

远程接口设计经验分享

远程接口设计经验分享

一次远程调用出错的可能

 

通讯框架错误

 

通讯框架错误根据发生环节分可以细分为

  1. Marshell & UnMarshell

    C/S双方采用了不一致的序列化/反序列化算法,导致在通讯之前或之后无法正常取得通讯的对象。从而导致双方在编码、解码的过程中发生错误。

    如果你的通讯框架使用了Hessian那基本上你都有机会遇到过。至于序列化和反序列化的梗,都可以开个专题了。这里就不在啰嗦。

  2. 网络通讯错误

    系统错误会导致无法预测的异常产生,具体取决于RPC的实现方式。对于这种错误,唯一的处理方式只有:另外找时间/机会重试。

业务系统错误

业务系统错误分两种情况

  1. 业务错误

    Client传递了违背业务规则的参数,导致业务逻辑处理失败。这种错误无论重复多少次都会得到一样结局。

  2. 系统错误

    Server处理内部逻辑时出现了无法控制的错误,常见的有:

  • 数据库访问失败
  • 文件写入失败
  • 网络通讯失败

    一般遇到这种错误,可以通过重试解决。

各种出错场景&解决方案梳理

远程接口设计经验分享

远程接口设计经验分享

远程接口设计经验分享

代码组织

 

如果你有机会重新搭建一个应用,推荐大家采用分包的策略来考虑自己的模块组织。

远程接口设计经验分享

common:定义core和client所共用的内容

  • 业务接口声明
  • LogService
  • Domain对象(这里为了简单,所有的DO、TO、DTO都统一命名为DO)
  • ResultDO
  • 业务异常
  • LogException

client:富客户端,在这一层可以组织cache、业务无关的通用校验,这一次层并非必须。

  • 服务客户端实现
  • LogServiceClient
  • AsyncLogServiceClient

core:业务服务的实现,这一层的代码运行在服务端。

  • 服务业务逻辑实现,同时内部按照习惯可以再次分层为(ServiceManagerDao)
  • LogServiceImpl

远程接口设计经验分享

正确处理返回值

这套RPC接口声明的理念在于:如何通过约定区分出系统异常与业务异常。区分的关键就在于ResultDOLogException

ResultDO

info方法不需要返值,但服务端需要在业务出错的时候,将错误码返回给客户端,以便友好的错误提示。所以在Result对象中有两个方法:

  • public boolean isSuccess();

isSuccesstrue时表明业务处理成功:当客户端获取到这个值时,表明服务端已正确经接请求到并且成功的处理了这个请求,业务完成。这是最好的情况。

isSuccessfalse时表明业务处理失败:当客户端获取到时,表明服务端已经正确接到请求,但业务处理失败,失败原因在错误码errorCode中体现。

  • public String getErrorCode();

当服务端正确接到请求,但业务处理失败时,失败的原因以错误码形式返回。

LogException

这个异常主要用于收缩和屏蔽服务层的具体错误信息,当服务端遇到无法处理的错误情况时,需要继续向客户端外抛,让客户端来择机进行重试。客户端亦可通过LogException快速判断当前业务中断的原因来自于LogService的失败。

 

客户端对返回值的处理总结

  • 客户端处理逻辑表

远程接口设计经验分享

为什么要有Client

老实说,这一层不是必须的,很多情况下客户端直接使用服务端声明的Service接口足矣。但若遇到在客户端容灾、增强的场景,则ServiceClient的优势就体现出来。

远程接口设计经验分享

接口v0.2版

远程接口设计经验分享

远程接口设计经验分享

接口的Wrapper

几乎可以肯定的,在公司中你肯定不是第一个声明接口的人。所以当你定出了远程接口设计规范之后,如何面对老接口则成了一个头疼的问题。

 

先人的智慧是无穷的,现在我们讨论的问题,我们的前辈都已经面临并解决了(运气不好你可能还会遇到新手练手写的接口),只是解决的方法各种各样,没有形成约定。何解?

此时可以考虑使用装饰模式将不规范的接口重新包装成符合设计规范的接口,这样做有两个好处:

  • 解决老接口不规范问题
  • 减小老接口暴露到业务代码中的概率

    这里需要解释下。外部接口的定义不受控制,如果此时一个Service需要升级,则改动、回归、代码REVIEW范围仅限于Wrapper类即可,若将所有业务代码直接引用外部的Service/ServiceClient类,则升级的回归面将被放大。

所以无论对方声明的接口是否符合约定,我都会建议客户端不要直接使用Service/ServiceClient,而是Wrapper一层。

 

远程接口设计经验分享


作者:李夏驰(杜琨)   阿里菜鸟事业部 - 基础平台技术专家    新浪微博 李夏驰

 

远程接口设计经验分享

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有