随着业务发展,很多系统需要经历服务拆分的过程。微服务化过程踩坑也是很正常的事。如果在服务拆分之前做好充分准备,能帮我们少走很多弯路。本文主要从服务依赖,接口版本,隔离,数据一致等方面说说微服务化过程应该注意的点。
循环依赖问题
微服务化之后服务之间会存在各种依赖关系,不过依赖需要遵循一定的规则,不能太随意。否则,就会出现循环依赖的问题,而且会让调用关系变得错综复杂难于维护。下面是服务依赖的几条规则:
1,上层服务可以调用下层服务。
2,同级服务之间不能产生依赖关系,及不能产生调用关系。
3,下层服务不能调用上层服务。
4,服务之间的调用关系只能是单向的。
例如,在电商系统里包括支付服务(Pay),库存服务(Inventory),订单服务(Order)。支付服务和库存服务属于基础服务,订单服务属于上层服务。支付服务和库存服务是同级的服务,他们之间不能存在调用关系。订单服务属于上层服务,订单服务可以调用支付服务和库存服务,但是支付服务和库存服务不能调用上层的订单服务。
假设我们不管这些规则,让Order和Pay可以互相调用。这样就会产生循环依赖,Order调用Pay,Pay也调用Order,这样彼此都会依赖对方。
循环依赖导致哪些问题?
1,无限递归调用
假如,Order调用Pay的A方法,Pay调用Order的B方法。然后,A方法里又调用了Order的B方法,B方法里又调用了Pay的A方法。这样就会产生无限的递归调用,后果自然不言而喻了。
- Order {
- void B(){
- Pay.A();
- }
- }
- Pay{
- void A(){
- Order.B();
- }
- }
2,部署依赖问题
假设Order,Pay,Inventory彼此之间都可以通过API互相调用。当API接口发生变更时,为了让其他服务能够正常调用,API需要重新编译。如果Order和Pay的API都有变化,上线发布时就需要特别小心。为了保证发布成功,就需要根据服务间API的依赖关系,详细考虑先打包部署哪个服务,后打包部署哪个服务,才不至于发布失败。如果有更多的服务呢?比如10几个,梳理依赖关系都会把人搞疯的。
3,另外,循环依赖会让服务间的调用关系变得错综复杂,系统难于维护。
接口版本兼容
一些初中级程序员往往会忽略接口变更的问题,经常会因为接口变更导致线上问题。比如某个小型电商平台的订单服务调用支付服务的某个接口,产品突然提了一个需求,这个需求需要在这个支付接口上加一个参数。开发这个需求的是个新手,他直接在原来的接口方法上实现了需求并加上了参数,联调测试通过后就发布上线了。结果刚上线订单服务就开始报错,因为方法变了,加了参数,订单服务找不到老的方法了。所以就会一直报错,直到订单服务上线为止。
所以我们一定要注意接口版本问题。我们可以新加一个方法去重载老的方法,在新方法里实现新的功能,新方法的定义除了多一个参数外,其他的和老方法一样。也就是给老方法加了一个新版本。
这样在支付服务上线后,订单服务上线之前就不会报错了,因为老方法仍然可用。订单服务上线后就直接切到了新版本的方法。
如果我们服务框架选用的是Dubbo,当一个接口的实现,出现不兼容升级时,可以用Dubbo的版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
1. 在低压力时间段,先升级一半提供者为新版本
2. 再将所有消费者升级为新版本
3. 然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
- <dubbo:service interface="com.foo.BarService" version="1.0.0" />
新版本服务提供者配置:
- <dubbo:service interface="com.foo.BarService" version="2.0.0"
老版本服务消费者配置:
- <dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
新版本服务消费者配置:
- <dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />