微服务这几年很火,从大厂到小公司都在谈微服务。很多团队觉得微服务很高级,不管自己团队多大业务多复杂,上来就想拆微服务。但微服务不是银弹,复杂的微服务架构需要很多基础设施和运维能力,小团队根本玩不转。
我见过很多小团队盲目上微服务,结果服务拆得稀碎,十几个服务只有三五个开发,运维成本飙升,部署一次要改一堆配置,出了问题排查半天找不到是哪个服务的问题,开发效率反而比单体还低。最后又把服务合并回去,折腾一圈回到原点。
但这不是说微服务不好,而是说小团队不应该照搬大厂那套重量级微服务架构。小团队应该设计轻量级的微服务架构,既享受微服务的好处,比如独立部署、技术选型灵活、团队自治,又不被微服务的复杂度拖垮。
我做过几个小团队的微服务架构设计和落地,从三个人的创业团队到十几人的中型团队都有。踩了很多坑也总结了很多经验。今天分享轻量级微服务架构设计的思路和实践,希望能给小团队一些参考。
一、什么时候该上微服务
在说架构设计之前,先说说什么时候该上微服务,什么时候不该上。很多团队的问题不是微服务架构设计得不好,而是根本不该上微服务。
不该上微服务的情况:
第一,团队太小。如果只有两三个开发,维护一个单体都费劲,就不要搞微服务了。微服务增加了很多运维和沟通成本,人少根本顾不过来。一般建议至少五到八个开发再考虑微服务。
第二,业务太简单。如果就是一个简单的CRUD系统,业务逻辑不复杂,没有高并发高可用的需求,单体就够了。微服务解决的是复杂系统的问题,简单系统用微服务是杀鸡用牛刀。
第三,没有运维能力。微服务需要部署很多服务,需要监控、日志、链路追踪、CI/CD等基础设施。如果团队没有运维能力,开发也不懂运维,上了微服务就是灾难。
该上微服务的情况:
第一,团队足够大,单体已经影响开发效率。多个团队在同一个代码库上开发,经常冲突,部署要排队,一个功能上线要等所有人都测试完。这时候拆微服务让每个团队独立开发独立部署,能提升效率。
第二,业务足够复杂,不同模块差异大。比如电商系统,订单、库存、支付、用户、商品,每个模块业务逻辑都很复杂,技术选型和性能要求也不同。拆成微服务可以独立优化独立扩展。
第三,有高可用和弹性扩展需求。某些模块流量大需要单独扩容,某些模块要求高可用不能影响其他模块。微服务可以独立扩容独立部署,故障隔离。
如果你的团队符合这些情况,可以考虑微服务。但即使要上,也建议从轻量级开始,不要一上来就搞全套重量级架构。
二、轻量级微服务的核心原则
轻量级微服务的核心原则是:够用就好,能简单就不复杂,能复用就不重复造轮子,能托管就不自建。
具体来说有几个原则:
第一,服务粒度不要太细。 很多团队拆微服务喜欢拆得很细,一个功能一个服务,结果搞出几十个服务。小团队根本维护不了。轻量级微服务建议按业务领域拆,一个领域一个服务,比如用户服务、订单服务、商品服务。一般五到十个服务比较合适,不要超过十五个。
第二,技术栈统一。 微服务的好处之一是技术选型灵活,每个服务可以用不同技术栈。但小团队不要这么玩,技术栈太多种类维护成本高,出了问题没人能搞定。建议统一技术栈,比如都用Java Spring Boot或者都用Go,最多两到三种。
第三,基础设施尽量用托管服务。 小团队没有精力自建各种基础设施。数据库用云厂商的RDS,缓存用云Redis,消息队列用云MQ,对象存储用云OSS,监控日志用云服务或者SaaS。不要自己搭MySQL主从、自己搭Kafka集群、自己搭ELK,运维成本太高。把精力放在业务上。
第四,能简化的组件就简化。 服务发现不用搞Eureka或者Consul集群,用K8s的Service或者Nginx就够了。配置中心不用搞Nacos集群,用K8s的ConfigMap或者环境变量。网关不用搞Spring Cloud Gateway集群,用Nginx或者云负载均衡。链路追踪可以先不上,等需要了再加。
第五,DevOps自动化。 小团队人少,更需要自动化。CI/CD一定要搞,代码提交自动构建自动部署,不要手动打包手动上传。自动化测试也要有,至少核心流程要有自动化测试,不然十几个服务手动测试测不过来。
三、架构设计
下面说说具体的架构设计。这是我们在几个小团队项目中验证过的轻量级微服务架构,简单实用。
整体架构:
客户端通过HTTPS访问,请求先到云负载均衡(SLB/ALB),负载均衡转发到API网关。API网关负责路由、鉴权、限流、日志。网关后面是各个业务微服务,服务之间通过HTTP或者gRPC调用。数据层用云数据库RDS,每个服务有自己的数据库。缓存用云Redis,消息队列用云MQ。静态资源放对象存储OSS加CDN。监控日志用云服务或者SaaS。
API网关:
网关是所有请求的入口。轻量级方案推荐用Nginx或者OpenResty做网关,配置简单性能好,能满足路由、反向代理、限流、鉴权等基本需求。如果需要更复杂的功能比如动态路由、服务发现集成,可以用Kong或者APISIX,都是基于Nginx的,性能好插件丰富。
不推荐小团队用Spring Cloud Gateway或者Zuul,Java写的网关性能一般,还要自己部署维护集群,成本高。除非团队Java技术栈很深且需要深度定制。
网关的主要功能:路由转发,把请求转发到对应服务;统一鉴权,验证token或者session;限流熔断,保护后端服务;请求日志,记录所有请求;跨域处理;静态资源缓存。
服务间通信:
服务间通信推荐用HTTP REST,简单通用调试方便,所有语言都支持。如果对性能要求高或者内部调用频繁,可以用gRPC,性能好有强类型定义,但调试和兼容性比HTTP差一些。
不推荐小团队用消息队列做服务间同步调用,MQ适合异步解耦,不适合同步请求响应。同步调用用HTTP或gRPC,异步通知用MQ。
服务间调用要做好超时和重试。超时设短一点,比如3秒,不要等太久。重试最多两次,且只对幂等接口重试,非幂等接口不要重试,避免重复执行。
服务发现:
如果用K8s部署,直接用K8s的Service和DNS做服务发现,不需要额外的服务发现组件。每个服务部署成Deployment加Service,其他服务通过Service名称访问,K8s自动做负载均衡。
如果不用K8s,用Docker Compose或者直接部署,可以用Nginx做反向代理和负载均衡,配置upstream指向各个服务实例。或者用Consul做服务发现,但Consul也要维护集群,小团队能不用就不用。
配置管理:
轻量级方案推荐用环境变量或者K8s ConfigMap管理配置。不同环境(开发、测试、生产)用不同的环境变量,部署时注入。配置变更不需要改代码,重新部署就行。
如果配置需要动态刷新、版本管理、灰度发布,可以用Nacos或者Apollo,但这些都需要维护集群,小团队如果不是特别需要就先不上。大部分场景环境变量加ConfigMap就够了。
数据层:
每个微服务有自己独立的数据库,不要共享数据库。共享数据库会导致服务耦合,一个服务改了表结构影响其他服务,也没法独立优化。数据库用云RDS,不用自己维护。
如果数据量不大,可以一个RDS实例里建多个数据库,每个服务一个库,节省成本。数据量大了再拆到不同实例。
缓存用云Redis,共享一个Redis实例但用不同的key前缀或者不同的db区分。缓存要设置过期时间,避免缓存永不过期。
消息队列用云MQ,比如阿里云RocketMQ或者腾讯云CMQ。用来做异步解耦、流量削峰、最终一致性。不要用MQ做同步调用。
四、部署和运维
轻量级微服务推荐用K8s部署,现在云厂商都有托管K8s(ACK/EKS/GKE),不用自己搭集群。K8s能解决服务部署、弹性伸缩、负载均衡、滚动更新、健康检查等很多问题,是微服务的最佳部署平台。
如果团队对K8s不熟悉,也可以用Docker Compose或者云容器服务(比如阿里云SAE、腾讯云TKE)。SAE是Serverless应用引擎,不用管集群,直接上传镜像就能部署,自动扩缩容,很适合小团队。
CI/CD方面,用GitLab CI或者GitHub Actions或者云效,代码提交自动构建镜像自动部署到K8s。构建流程:代码检查、单元测试、构建镜像、推送镜像仓库、部署到测试环境、自动化测试、手动确认部署到生产。
监控方面,用云厂商的监控服务或者Prometheus加Grafana。监控每个服务的CPU、内存、QPS、响应时间、错误率。设置告警,错误率高或者响应慢了及时通知。
日志方面,用云日志服务(SLS/CLS)或者ELK。每个服务输出结构化日志(JSON格式),包含trace_id、服务名、请求路径、耗时、状态码等。集中收集到日志服务,方便查询和分析。
链路追踪可以先不上,等服务多了排查问题困难了再加。用Jaeger或者SkyWalking,接入成本不高。
五、数据一致性
微服务最难的问题之一是分布式事务和数据一致性。轻量级方案推荐用最终一致性,不要用强一致性的分布式事务(比如2PC、TCC),复杂度高性能差。
最终一致性的实现方式:
第一,本地消息表。在本地数据库建消息表,业务操作和写消息在同一个本地事务里。然后有个定时任务扫描消息表,发送到MQ,消费端处理。处理成功后标记消息已处理。这种方式简单可靠,适合大多数场景。
第二,MQ事务消息。RocketMQ支持事务消息,先发半消息,本地事务执行成功后提交,失败回滚。消费端消费消息做后续操作。比本地消息表更优雅,但需要用支持事务消息的MQ。
第三,Saga模式。长流程的业务用Saga,每个步骤有补偿操作,失败了按顺序回滚。适合订单流程这种多步骤的业务。
不管用哪种方式,都要做好幂等性。消费端可能重复消费,所以每个操作都要幂等,用唯一ID去重。
六、服务拆分的经验
最后说说服务拆分的经验,这是很多团队最容易踩坑的地方。
第一,从单体开始,逐步拆分。不要一开始就拆成十几个微服务。先做一个单体,把业务跑起来,然后随着业务发展和团队扩大,逐步把独立的模块拆成服务。这样风险小,也能避免拆错。
第二,按业务领域拆分,不是按技术层拆分。不要拆成用户Controller服务、用户Service服务、用户DAO服务,这是错误的。应该按业务领域拆,比如用户服务、订单服务、商品服务,每个服务包含自己的Controller、Service、DAO。
第三,先拆变化快的、独立的模块。比如支付模块和其他模块耦合少,变化快,可以先拆。订单模块复杂,和其他模块耦合多,可以后拆。
第四,拆分后要做好接口定义。服务间通过API通信,API要定义清晰稳定,有版本管理。不要频繁改API影响其他服务。
第五,不要为了拆而拆。如果一个模块和其他模块耦合很紧,拆成服务后调用很多,反而增加复杂度。那就先不拆,等业务清晰了再拆。
七、写在最后
以上就是轻量级微服务架构设计的经验总结。核心思路是:小团队不要盲目追求大厂那套重量级微服务架构,要根据自己的团队规模和业务复杂度,设计够用的轻量级架构。
微服务不是目的,提升开发效率和系统稳定性才是目的。如果微服务反而降低了效率增加了复杂度,那还不如用单体。
架构是演进的,不是一步到位的。从单体开始,随着业务和团队发展逐步演进到微服务。每一步都要评估收益和成本,不要为了技术而技术。
2021年云原生发展很快,K8s、Serverless、Service Mesh这些技术越来越成熟,微服务的门槛也在降低。小团队可以利用云服务和托管服务,用很小的成本享受微服务的好处。
希望这篇文章能给小团队的架构设计一些参考。微服务没有标准答案,适合自己团队的就是最好的。不要盲目跟风,不要过度设计,够用就好,持续演进。
祝所有小团队都能设计出适合自己的架构,高效开发稳定运行,业务越做越大。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录