前言
一直很想写一篇能覆盖分布式方面的文章,但当写的时候发现分布式技术发展到现在阶段,有太多的最佳实践和方法论,网上的文章也特别多,根本无从下手。思来想去,发现很多最佳实践都是有一定的理论支撑,我尝试“通过现象看本质”的写法,写写试试。
在写这边博客之前,需要先拉起一些相关概念,此篇博客不会特别深入阐述某些理论和详细设计方案,希望通过几个案例,让大家了解在分布式时需要具备的思想或思考方向,起到抛砖引玉的作用。
文章我准备分三部分写,此博客会写第一部分:
-
第一篇主要以应用分布式视角,讲述从有状态单体服务-->无状态服务-->解耦的进化过程;
-
第二篇主要说一说分布式系统容器化,聊聊容器云给系统带来不可变基础设施、自动扩散容等变化;
-
第三篇主要以Paas平台支撑分布式应用视角,聊聊数据一致性所需要的CAP理论、Raft算法、一致性哈希算法等理论,及其在底层支撑发挥的作用。
无状态服务,开启分布式第一步
以一个简单的商城系统购买商品场景为例,用户登录商城,系统一般会在本机以Session ID的形式保持会话状态,这样该用户每次预览、购买商品的时候,都是保持登录后的状态。用户在购买商品时在系统逻辑中会完成库存数量查询,如果有库存,可以继续购买,如果没有,返回告知用户物品卖完,后面是扣减库存和支付,每一步出现问题都可以通过事务进行自动回退,不会出现多扣库存或超卖等现象。
随着用户越来越多,访问流量愈来愈大,单个节点服务已经无法支撑。为了快速支撑目前的并发量一般会做如下优化:
-
将Session ID等有状态的参数,放到Redis进行统一存储,如登录状态;
-
将频繁查询的数据放入到Redis中,减轻数据库压力,如查询库存信息。
在目前场景下,使用Redis的时候,需要注意两点:
-
添加信息时,主要设置key的过期时间;
-
需要解决Redis与数据库数据的一致性,简单点的解决方式是“先更新数据库,再更新缓存”。
通过这波操作,一般能解决大部分小公司的流量问题,如果觉得还是不够,也可以使用开发语言的线程池队列,对瞬时的流量进行削峰。
解耦,“天下大势,分久必合,合久必分”
随着公司业务越来越复杂,单体应用也会有一些问题,如
-
单体应用模块间代码互相影响,简单的代码修改需要整体打包,整体测试,效率低下;
-
无法做到单一模块的扩展,每次扩展都需要整体应用扩展,造成资源浪费;
-
单体应用决定了应用内部必须采用同样的技术栈进行开发,内部应用无法自己决定采用最合适的技术。
微服务特点
有一个叫Martin Fowler的老外提出了微服务的概念。
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,每个服务运行在其独立的进程中,服务间采用轻量级的通信机制互相沟通(通常是基于HTTP协议的RESTful API). 每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。
从而实现了:
-
开发人员通过编译并重新部署单个子服务的方式来验证自己的更改不再需要重新编译整个应用;
-
由于每个子服务是独立的,因此各个服务内部可以自行决定最为合适的实现技术,使得这些子服务的开发变得更为容易;
-
系统的容量不够,只需要找到成为系统瓶颈的子服务,并扩展该子服务的容量即可。
微服务优缺点
微服务平台落地实践
可以参考我之前写的博客《正式线上环境下微服务平台落地实践》,主要是运行时支撑组件和可观测性组件的建设。运行时支撑组件实现服务注册发现、负载均衡、配置、认证等功能,可观测性组件则是完成日志、指标和链路等监控的建设,实现事前留痕、事中告警、事后问题快速定位的全生命周期可观测能力。
微服务拆分原则
微服务架构的核心思想是将一个大型的应用系统拆分成多个小型的、自治的服务单元,以便于开发、部署和维护。微服务拆分的原则可以帮助你合理划分服务边界,下面是一些常见的微服务拆分原则:
-
单一职责原则 (Single Responsibility Principle, SRP):每个微服务应该专注于一个明确的业务领域或功能。避免将多个不相关的功能混合在同一个微服务中。
-
界限上下文 (Bounded Context):根据领域驱动设计(Domain-Driven Design)原则,将微服务划分为不同的界限上下文。每个界限上下文表示一个业务领域的边界,确保在一个上下文内的模型和语言保持一致。
-
松耦合 (Loose Coupling):微服务之间应该尽量减少相互依赖,以便于独立开发、部署和扩展。使用清晰的接口和协议定义,避免直接数据库访问和紧密耦合的集成。
-
高内聚 (High Cohesion):每个微服务内部的组件应该紧密相关,具有高内聚性。这有助于减少微服务内部的复杂度,并使其更易于维护。
-
自治性 (Autonomy):每个微服务应该是自治的,能够独立开发、部署和运行。避免过度共享代码和数据,以减少对其他微服务的依赖。
-
可替代性 (Replaceability):设计微服务时考虑到未来可能需要替换某个微服务。确保微服务之间的依赖关系不会过于复杂,以便于在需要时进行更换。
-
按业务功能拆分 (Business Capability):将微服务按照业务功能进行拆分,每个微服务负责一个独立的业务功能。这有助于更好地理解系统的业务流程。
-
按照团队组织拆分 (Team Organization):根据团队的结构和能力,将微服务进行划分。每个团队负责一个或多个微服务,以促进快速开发和迭代。
-
可扩展性 (Scalability):将可能需要独立扩展的功能划分为独立的微服务,以便按需扩展系统的各个部分。
-
性能优化 (Performance Optimization):将潜在的性能瓶颈或高负载功能划分为微服务,以便进行独立的性能优化。
-
最小可行性 (Minimum Viable):初始阶段,将系统划分为最小可行的微服务集,以便快速推出原型并根据反馈进行调整。
-
数据管理 (Data Management):根据数据访问模式和数据所有权,将微服务划分为适当的数据管理单元,以确保数据一致性和合理的数据隔离。
-
界面定义 (Interface Definition):定义清晰的接口和协议,确保微服务之间的通信和集成是稳定和可靠的。
微服务拆分方式
对于拆分,业绩一般有三种拆分形式:一次性拆分、迭代拆分和外围扩展
-
一次性拆分:顾名思义就是将一个大系统直接彻底的拆分,比如将一个商场系统,拆成商品、订单、支付和库存等系统;
-
迭代拆分:根据需要和相应的业务场景,迭代慢慢进行拆分;
-
外围扩展:不拆分现有系统,当需要添加新功能的时候,不会在原有系统中添加功能代码,可以通过RPC调用的方式开发新的系统。
“一次性拆分”会拆的比较彻底,大多是在重大转型或重大架构变化时出现,一般需要最高层介入,多部门协作,设计范围广、人员多、工期长,声势搞的特别大,容易出政绩。但是由于工程太大,沟通成本和资源成本消耗巨大,很容易出现“拔出萝卜带出泥”,各种细节考虑不周等问题,会有很长的震荡周期。
大部分公司会选择“迭代拆分”+“外围扩展”来解耦系统,新增的系统如果跟当前的系统耦合不大,可以在外围通过RPC框架进行交互,内部解耦可以现将一些耦合性比较低的代码进行拆分,拆一两个看看效果,拆分力度可以由大到小进行解耦,然后通过实际调用、线上运行和对业务需求支撑等情况,综合考虑迭代拆分,可以边走边看,边总结经验培养团队,慢慢达到适合自身组织结构的解耦。
总结
微服务的拆分原则是灵活的,取决于具体的业务需求、组织结构和技术栈。合理的微服务拆分可以使系统更易于管理、扩展和维护,但需要平衡各种因素以达到最佳效果。
参考
-
Redis数据一致性
-
How to Design Microservices for Agile Architecture