1 接口评价标准
一个好的接口设计通常需要满足以下几点:
-
简单:满足需求的同时,越简单越好。
-
易懂:优秀的API可读性好,尽量做到不需要文档就能读懂接口名称、参数的大概含义,提供给第三方的的接口要进行详细地描述。包括参数的取值范围、错误码、异常返回规则、SLA相关指标等。
-
一致:对于同一公司、站点提供的API,最好有同一的规则,让开发者只要看过几个API后,基本能猜到剩余API的含义。
-
稳定:最好在开始的时候就考虑好,不要轻易修改API,否则会给使用方造成麻烦。
-
安全:设计时要考虑超出预期的情况如何处理,给出被限流的情况。如果提供给第三方,则要考虑如何认证。
2 接口设计原则
- 单一职责
单一性是指接口要做的事情应该是一个比较单一的事情,比如登陆接口,登陆完成应该只是返回登陆成功以后一些用户信息即可。 - 开闭原则
对扩展开放,对修改关闭。需对变化封装:
(1)将相同的变化封装到一个接口或抽象类中 ;
(2)将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。 封装变化,也就是受保护的变化,找出预计有变化或不稳定的点,为这些变化点创建稳定的接口。
-
接口隔离原则
使用多个专门的接口,而不使用单一的总接口。 其实接口隔离原则说白了就是单一职责的扩展。
对于一个比较细化的接口,更有利于书写单元测试,也更有利于调试。 -
高内聚低耦合
一个接口要包含完整的业务功能,而不同接口之间的业务关联要尽可能的小。 -
安全性原则
安全性问题是一个接口必须要保证的规范,可使用token(常用参数appid,appkey,timestamp,nonce,sign)、白名单、防刷、限流、隔离等手段保证接口调用的合法合规合理调用。 -
兼容性原则
接口设计要尽量考虑接口不同版本的兼容性,尽量避免升级的过程中不出现兼容性问题。
如果修改API不可避免,那么最好是通过增加接口而不是修改已有接口来兼容。如果一定要改变已有接口,也要通过明确的版本号加以区分。
3 接口设计方法论
接口设计时,需要重点考虑一下问题:
- 接口的命名。
- 请求参数。
- 支持的协议。
- TPS、并发数、响应时长。
- 数据存储。DB选型、缓存选型。
- 是否需要依赖于第三方。
- 接口是否拆分。
- 接口是否需要幂等。
- 防刷。
- 接口限流、降级。
- 负载均衡器支持。
- 如何部署。
- 是否需要服务治理。
- 是否存在单点。
- 接口是否资源包、预加载还是内置。
- 是否需要本地缓存。
- 是否需要分布式缓存、缓存穿透怎么办。
- 是否需要白名单。
- 架构设计时,可以根据场景的应用情况来做checklist。
4 Dubbo接口规范
4.1 Dubbo协议适用范围
Dubbo缺省协议采用单一长连接和NIO异步通信,适合于小数据量(传入传出参数数据包建议小于100k)大并发的服务调用。
反之,Dubbo缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
4.2 命名规范
com.xxx.xxdomain.子系统.自定义接口名
4.3 粒度
- 对外服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将地面临分布式事务问题,Dubbo暂未原生提供分布式事务支持,同时也可以减少网络交互。
- 服务接口建议以业务场景为单位划分,并对相近的业务做抽象,防止接口数量爆炸。
- 不建议使用过于抽象的通用接口,如Map query(Map),这样的接口没有明确语义,会给后期维护带来不便。
4.4 版本及兼容性
- 每个接口都应定义版本号,为后续不兼容升级提供可能。
- 建议使用两位版本号,第一位主版本号,第二位次版本号,只有不兼容时才需要变更服务主版本。
- 当不兼容时,先升级一半提供者为新版本(通常在验证或低压力时间段),再将消费者全部升级为新版本,然后将剩下的一半提供者升为新版本。
- 服务接口增加方法,或服务模型增加字段,可向后兼容。
- 删除方法或删除字段,将不再兼容,枚举类型新增字段也不兼容,需要通过变更版本号升级。
4.5 异常
- 建议使用异常报告错误,而不是返回错误码,异常能携带更多信息,并且语义更又好。
- 如果担心性能问题,在必要时,可以override掉异常类的fillInStackTrace()方法为空方法,使其不拷贝栈信息。
- 查询方法不建议抛出checked异常,否则调用方在查询时将过多的try…catch,并且不能进行有效处理。
- 服务提供方不应将DAO或SQL等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常。
4.6 其它Tips
- 服务参数及返回值建议使用POJO对象。
- 建议Provider端对输入参数进行校验。
- Provider端尽量多配置Consumer端的属性,让provider的实现者一开始就思考provider端的SLA。
5 RESTful接口规范
5.1 协议
考虑到服务的安全性,建议使用https作为API的通信协议,当然http也是可以的。
5.2 URL约定
URL格式:http(s)😕/server.com/{domain}/{version}/api-name。
- 其中domain是各个子系统领域,可以根据需要分层,但最好不要超过2层。
- 不用大写。
- 建议用中杠-,不用下划线_。
- 路径中不能有动词,只能有名词。名词表示资源集合,要使用复数形式。在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。
- 词根规范
动作 | 前缀 | 备注 |
---|---|---|
获取 | get | get{XXX} |
新增 | add | add{XXX} |
修改 | update | update{XXX} |
保存 | save | save{XXX} |
删除 | delete | delete{XXX} |
上传 | upload | upload{XXX} |
5.3 数据格式
使用JSON作为微服务提交和返回数据的通用语言。
使用HTTP状态码来传达服务调用的状态。
5.4 请求参数
- 动词释义
请求方式 | 描述 |
---|---|
GET | 获取数据 |
POST | 新增数据 |
PUT | 更新数据 |
DELETE | 删除数据 |
- 请求参数
1)Query:是指请求的参数,一般是指URL中?后面的参数。
2)Header:请求头,存放公共参数、requestId、token、加密字段等。
3)Body:请求体,存放请求接口的参数数据。
-
版本信息
考虑到服务接口的平滑升级,可以将API的版本号放入URL,也可以将版本号放在HTTP头信息中,但不如放入URL方便和直观。
-
过滤信息
请求信息应该为集合提供过滤、排序、选择和分页等功能。
1)Filtering过滤
使用唯一的查询参数进行过滤:如GET /cars?color=red 返回红色的cars。
2)Sorting排序
允许针对多个字段排序:如GET /cars?sort=-manufactorer,+model #这是返回根据生产者降序和模型升序排列的car集合。
3)Field selection选择
给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。如GET /cars?fields=manufacturer,model,id,color。
4)Paging分页
使用 limit 和offset.实现分页。如GET /cars?offset=10&limit=5。有时候为了将总数发给客户端,可以使用订制的HTTP头:如X-Total-Count。
5.5 返回参数
- 返回包体格式
参数 | 类型 | 说明 | 备注 |
---|---|---|---|
code | Number | 结果码 | |
showMsg | String | 显示信息 | |
errorMsg | String | 错误信息 | |
data | Object | 数据 | JSON 格式 |
- 返回码
http协议标准返回码及含义如下:
200 OK [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)
201 CREATED [POST/PUT/PATCH]:用户新建或修改数据成功
202 Accepted [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT [DELETE]:用户删除数据成功
400 INVALID REQUEST [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的
401 Unauthorized [*]:表示用户没有权限(令牌、用户名、密码错误)
403 Forbidden [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的
404 NOT FOUND [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的
406 Not Acceptable [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)
410 Gone [GET]:用户请求的资源被永久删除,且不会再得到的
422 Unprocesable entity [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误
500 INTERNAL SERVER ERROR [*]:服务器发生错误,用户将无法判断发出的请求是否成功
- 幂等说明
幂等:请求一次和请求多次的效果是一样的。
GET:由于GET请求仅仅是获取资源 并不修改资源,所以能保证幂等。
PUT:同样的请求,修改一次和修改多次是一样的,能保证幂等。
DEL:同理,能保证幂等。但是多次请求,只有第一次能返回200,其他都应该是404。
POST:不幂等,多次请求会在数据库表中生成多条记录。
因此,对POST的接口幂等性需要在业务代码上去实现。