如何做好服务拆分?

服务拆分的前提

说到微服务,服务拆分是绕不过去的话题,但是微服务不是说拆就能拆的,有很多的前提条件,需要完成前面几节所论述的部分。

 

首先要有一个持续集成的平台,使得服务在拆分的过程中,功能的一致性,这种一致性不能通过人的经验来,而需要经过大量的回归测试集,并且持续的拆分,持续的演进,持续的集成,从而保证系统时刻处于可以验证交付的状态,而非闭门拆分一段时间,最终谁也不知道功能最终究竟有没有bug,因而需要另外一个月的时间专门修改bug。

 

其次在接入层,API和UI要动静分离,API由API网关统一的管理,这样后端无论如何拆分,可以保证对于前端来讲,统一的入口,而且可以实现拆分过程中的灰度发布,路由分发,流量切分,从而保证拆分的平滑进行。而且拆分后的微服务之间,为了高性能,是不建议每次调用都进行认证鉴权的,而是在API网关上做统一的认证鉴权,一旦进入网关,服务之间的调用就是可信的。

 

其三对于数据库,需要进行良好的设计,不应该有大量的联合查询,而是将数据库当成一个简单的key-value查询,复杂的联合查询通过应用层,或者通过Elasticsearch进行。如果数据库表之间耦合的非常严重,其实服务拆分是拆不出来的。

 

其四要做应用的无状态化,只有无状态的应用,才能横向扩展,这样拆分才有意义。

 

服务拆分的时机

满足了服务拆分的前提之后,那先拆哪个模块,后拆哪个模块呢?什么情况下一个模块应该拆分出来呢?

 

微服务拆分绝非一个大跃进运动,由高层发起,把一个应用拆分的七零八落的,最终大大增加运维成本,但是并不会带来收益。

 

微服务拆分的过程,应该是一个由痛点驱动的,是业务真正遇到了快速迭代和高并发的问题,如果不拆分,将对于业务的发展带来影响,只有这个时候,微服务的拆分是有确定收益的,增加的运维成本才是值得的。

 

微服务解决的问题之一,就是快速迭代。

 

互联网产品的特点就是迭代速度快,一般一年半就能决出胜负,第一一统天下,第二被第一收购,其他死翘翘。所以快速上线,快速迭代,就是生命线,而且一旦成功就是百亿身家,所以无论付出多大运维成本,使用微服务架构都是值得的。

 

这也就是为什么大部分使用微服务架构的都是互联网企业,因为对于这些企业来讲收益明显。而对于很多传统的应用,半年更新一次,企业运营相对平稳,IT系统的好坏对于业务没有关键性影响,在他们眼中,微服务化改造带来的效果,还不如开发多加几次班。

 

微服务拆分时机一:提交代码频繁出现大量冲突

 

微服务对于快速迭代的效果,首先是开发独立,如果是一单体应用,几百人开发一个模块,如果使用Git做代码管理,则经常会遇到的事情就是代码提交冲突。

 

同样一个模块,你也改,他也改,几百人根本没办法沟通。所以当你想提交一个代码的时候,发现和别人提交的冲突了,于是因为你是后提交的人,你有责任去merge代码,好不容易merge成功了,等再次提交的时候,发现又冲突了,你是不是很恼火。随着团队规模越大,冲突概率越大。

 

所以应该拆分成不同的模块,每十个人左右维护一个模块,也即一个工程,首先代码冲突的概率小多了,而且有了冲突,一个小组一吼,基本上问题就解决了。

 

每个模块对外提供接口,其他依赖模块可以不用关注具体的实现细节,只需要保证接口正确就可以。

 

微服务拆分时机二:小功能要积累到大版本才能上线,上线开总监级别大会

 

微服务对于快速迭代的效果,首先是上线独立。如果没有拆分微服务,每次上线都是一件很痛苦的事情。当你修改了一个边角的小功能,但是你不敢马上上线,因为你依赖的其他模块才开发了一半,你要等他,等他好了,也不敢马上上线,因为另一个被依赖的模块也开发了一半,当所有的模块都耦合在一起,互相依赖,谁也没办法独立上线,而是需要总监协调各个团队,大家开大会,约定一个时间点,无论大小功能,死活都要这天上线。

 

这种模式导致上线的时候,单次上线的需求列表非常长,这样风险比较大,可能小功能的错误会导致大功能的上线不正常,将如此长的功能,需要一点点check,非常小心,这样上线时间长,影响范围大。因而这种的迭代速度快不了,顶多一个月一次就不错了。

 

服务拆分后,在接口稳定的情况下,不同的模块可以独立上线。这样上线的次数增多,单次上线的需求列表变小,可以随时回滚,风险变小,时间变短,影响面小,从而迭代速度加快。

 

对于接口要升级部分,保证灰度,先做接口新增,而非原接口变更,当注册中心中监控到的调用情况,发现接口已经不用了,再删除。

 

微服务解决的问题之二,就是高并发。

 

互联网一个产品的特点就是在短期内要积累大量的用户,这甚至比营收和利润还重要,如果没有大量的用户基数,融资都会有问题。

 

因而对于并发量不大的系统,进行微服务化的驱动力差一些,如果只有不多的用户在线,多线程就能解决问题,最多做好无状态化,前面部署个负载均衡,单体应用部署多份。

 

微服务拆分时机三:横向扩展流程复杂,主要业务和次要业务耦合

 

单体应用无状态化之后,虽然通过部署多份,可以承载一定的并发量,但是资源非常浪费。因为有的业务是需要扩容的,例如下单和支付,有的业务是不需要扩容的,例如注册。如果一起扩容,消耗的资源可能是拆分后的几倍,成本可能多出几个亿。而且由于配置复杂,在同一个工程里面,往往在配置文件中是这样组织的,这一块是这个模块的,下一块是另一个模块的,这样扩容的时候,一些边角的业务,也是需要对配置进行详细审核,否则不敢贸然扩容。

 

微服务拆分时机四:熔断降级全靠if-else

 

在高并发场景下,我们希望一个请求如果不成功,不要占用资源,应该尽快失败,尽快返回,而且希望当一些边角的业务不正常的情况下,主要业务流程不受影响。这就需要熔断策略,也即当A调用B,而B总是不正常的时候,为了让B不要波及到A,可以对B的调用进行熔断,也即A不调用B,而是返回暂时的fallback数据,当B正常的时候,再放开熔断,进行正常的调用。

 

有时候为了保证核心业务流程,边角的业务流程,如评论,库存数目等,人工设置为降级的状态,也即默认不调用,将所有的资源用于大促的下单和支付流程。

 

如果核心业务流程和边角业务流程在同一个进程中,就需要使用大量的if-else语句,根据下发的配置来判断是否熔断或者降级,这会使得配置异常复杂,难以维护。

 

如果核心业务和边角业务分成两个进程,就可以使用标准的熔断降级策略,配置在某种情况下,放弃对另一个进程的调用,可以进行统一的维护。

服务拆分的方法

好了,当你觉得要将一个程序的某个部分拆分出来的时候,有什么方法可以保障平滑吗?

 

首先要做的,就是原有工程代码的标准化,我们常称为“任何人接手任何一个模块都能看到熟悉的面孔”

 

例如打开一个Java工程,应该有以下的package:

 

  • API接口包:所有的接口定义都在这里,对于内部的调用,也要实现接口,这样一旦要拆分出去,对于本地的接口调用,就可以变为远程的接口调用。

  • 访问外部服务包:如果这个进程要访问其他进程,对于外部访问的封装都在这里,对于单元测试来讲,对于这部分的Mock,可以使得不用依赖第三方,就能进行功能测试。对于服务拆分,调用其他的服务,也是在这里。

  • 数据库DTO:如果要访问数据库,在这里定义原子的数据结构。

    访问数据库包:访问数据库的逻辑全部在这个包里面。

    服务与商务逻辑:这里实现主要的商业逻辑,拆分也是从这里拆分出来。

  • 外部服务:对外提供服务的逻辑在这里,对于接口的提供方,要实现在这里。

 

另外是测试文件夹,每个类都应该有单元测试,要审核单元测试覆盖率,模块内部应该通过Mock的方法实现集成测试。

 

接下来是配置文件夹,配置profile,配置分为几类:

 

  • 内部配置项(启动后不变,改变需要重启)

  • 集中配置项(配置中心,可动态下发)

  • 外部配置项(外部依赖,和环境相关)

 

当一个工程的结构非常标准化之后,接下来在原有服务中,先独立功能模块 ,规范输入输出,形成服务内部的分离。在分离出新的进程之前,先分离出新的jar,只要能够分离出新的jar,基本也就实现了松耦合。

 

接下来,应该新建工程,新启动一个进程,尽早的注册到注册中心,开始提供服务,这个时候,新的工程中的代码逻辑可以先没有,只是转调用原来的进程接口。

 

为什么要越早独立越好呢?哪怕还没实现逻辑先独立呢?因为服务拆分的过程是渐进的,伴随着新功能的开发,新需求的引入,这个时候,对于原来的接口,也会有新的需求进行修改,如果你想把业务逻辑独立出来,独立了一半,新需求来了,改旧的,改新的都不合适,新的还没独立提供服务,旧的如果改了,会造成从旧工程迁移到新工程,边迁移边改变,合并更加困难。如果尽早独立,所有的新需求都进入新的工程,所有调用方更新的时候,都改为调用新的进程,对于老进程的调用会越来越少,最终新进程将老进程全部代理。

 

接下来就可以将老工程中的逻辑逐渐迁移到新工程,由于代码迁移不能保证逻辑的完全正确,因而需要持续集成,灰度发布,微服务框架能够在新老接口之间切换。

 

最终当新工程稳定运行,并且在调用监控中,已经没有对于老工程的调用的时候,就可以将老工程下线了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解了您的要求。接下来我将按照要求进行大表的拆分相关问题的回答。 相关问题: 1. 为什么要进行大表拆分? 2. 大表拆分会带来哪些影响? 3. 如何确定何时适合进行大表拆分? 对于MySQL大表拆分,以下是一些常见的策略和考虑因素: 1. **垂直拆分**:将一个大表按照列的类型进行拆分,例如将一个包含大量数据的表拆分为多个包含特定列的表。这种方法适用于某些特定列的数据量过大,导致整个表的数据量过大。 2. **水平拆分**:将一个大表按照某个字段的值进行拆分,通常使用主键或唯一索引作为拆分的依据。这种方法适用于表中的数据量差异较大,需要将数据分布到不同的服务器或数据库中。 3. **使用分区**:MySQL提供了分区功能,可以将一个大表按照特定的规则分成多个分区,每个分区可以独立管理。这种方法适用于数据量非常大,需要提高查询性能和扩展性。 在进行大表拆分时,需要考虑以下因素: 1. **数据一致性**:确保拆分后的表之间数据的一致性,避免出现数据不一致的情况。 2. **性能影响**:拆分后的表可能会影响查询性能,需要评估拆分后的性能是否满足需求。 3. **备份和恢复**:在拆分之前,需要做好数据备份和恢复工作,确保数据的安全性。 4. **系统资源**:需要考虑系统资源(如CPU、内存、磁盘空间等)的限制,确保拆分后的表能够正常运行。 如果您有任何其他问题或需要进一步的解释,欢迎随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值