服务化最佳实践
一、分包分发
建议将服务接口、服务模型、服务异常等均放在 API 包中,因为服务模型和异常也是 API 的一部分,这样做也符合分包原则:重用发布等价原则(REP),共同重用原则(CRP)。
异常声明处理是服务调用中不可缺少的部分。通常服务提供方定义明细业务错误异常码,调用方进行捕获处理。进一步的异常规约我们稍后会具体说明。
二、接口粒度
接口粒度:服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题。也就是说同一个操作功能场景尽可能囊括到一个服务里,减少调用关联方。
抽象:服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。不建议使用过于抽象的通用接口,如:Map query(Map)
,这样的接口没有明确语义,会给后期维护带来不便。
三、服务版本
如果可能,有必要细粒度版本化到服务接口级别,以更好的控制服务迭代升级。
建议使用两位版本号,因为第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。
当不兼容时,先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本。
四、升级兼容性
除非是新的功能服务接口,旧的服务升级必须处理接口的向后兼容性。
接口增加参数或者返回值增加字段一般可以兼容,涉及修改或者删除则及枚举类型使用时,则需要通过版本号升级处理。
枚举值:
如果是完备集,可以用 Enum
,比如:ENABLE
, DISABLE
。
如果是业务种类,以后明显会有类型增加,不建议用 Enum
,可以用 String
代替。
如果是在返回值中用了 Enum
,并新增了 Enum
值,建议先升级服务消费方,这样服务提供方不会返回新值。
如果是在传入参数中用了 Enum
,并新增了 Enum
值,建议先升级服务提供方,这样服务消费方不会传入新值。
五、序列化
服务参数及返回值建议使用 POJO 对象,即通过 setter
, getter
方法表示属性的对象。
服务参数及返回值都必须是传值调用,而不能是传引用调用,消费方和提供方的参数或返回值引用并不是同一个,只是值相同。
六、关于调用异常
建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,并且语义更友好。
如果担心性能问题,在必要时,可以通过 override 掉异常类的 fillInStackTrace()
方法为空方法,使其不拷贝栈信息。
查询方法不建议抛出受检异常,否则调用方在查询时将过多的 try...catch
,并且不能进行有效处理。
服务提供方不应将 DAO 或 SQL 等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常。
七、参数检查过滤
调用方及服务方都应该对输入参数进行校验。可以通过统一的拦截方式处理。
如通过检查空值、参数长度等进行过滤,减少不必要的调用资源消耗及异常隐患,
八、附加订阅