微服务架构现在越来越流行,很多公司都在从单体架构向微服务架构转型。
我所在的公司,去年也开始了微服务化的改造,把一个庞大的单体应用,拆分成了十几个微服务。在这个过程中,我们遇到了很多问题,也踩了很多坑,特别是在服务治理方面,积累了很多经验和教训。
今天就来聊聊微服务治理架构设计,如何实现高可用高并发。本文是我在实际项目中积累的经验和思考,希望能给正在做微服务架构的朋友一些参考和启发。
一、什么是微服务
在开始聊微服务治理之前,先简单说说什么是微服务。
微服务是一种架构风格,它把一个大型的单体应用,拆分成一组小型的、独立的服务,每个服务运行在自己的进程中,服务之间通过轻量级的机制(通常是HTTP REST API或者消息队列)进行通信。每个服务围绕着具体的业务能力构建,并且可以独立部署、独立扩展、独立维护。
微服务的核心思想是"分而治之",把复杂的大系统,拆分成简单的小系统,每个小系统只负责一个业务功能,这样更容易开发、测试、部署、维护,也更容易扩展。
二、微服务的优缺点
微服务不是银弹,它有优点,也有缺点,在决定要不要用微服务之前,一定要先了解清楚。
优点:
- 技术栈灵活: 每个微服务可以选择不同的技术栈,比如有的服务用Java,有的用Python,有的用Go,根据业务需求和团队特点选择最合适的技术栈。
- 独立部署: 每个微服务可以独立部署,修改一个服务,只需要重新部署这个服务,不需要重新部署整个应用,部署更快,风险更小。
- 独立扩展: 每个微服务可以独立扩展,哪个服务压力大,就扩展哪个服务,不需要扩展整个应用,资源利用率更高。
- 故障隔离: 一个服务出问题,不会影响整个应用,其他服务还能正常运行,系统的可用性更高。
- 团队自治: 每个微服务可以由一个小团队负责,从开发、测试、部署到运维,团队全权负责,团队的积极性和效率更高。
- 易于理解和维护: 每个微服务的代码量比较小,业务逻辑比较简单,更容易理解和维护,新人上手也更快。
缺点:
- 分布式系统的复杂性: 微服务是分布式系统,分布式系统比单体系统复杂得多,需要处理网络延迟、分布式事务、数据一致性、服务发现、负载均衡等问题,开发和运维的难度都很大。
- 服务间通信的开销: 服务之间通过网络通信,比单体应用内部的方法调用慢得多,也更不可靠,需要处理超时、重试、熔断等问题。
- 数据一致性的挑战: 每个微服务有自己的数据库,数据分散在不同的数据库中,要保证数据的一致性很困难,需要用分布式事务或者最终一致性的方案。
- 运维复杂度高: 微服务的数量很多,每个服务都需要部署、监控、日志、告警,运维的工作量很大,需要完善的自动化运维工具和平台。
- 测试难度大: 微服务之间有依赖关系,集成测试和端到端测试很困难,需要搭建完整的测试环境,模拟各种服务的依赖和异常。
- 团队协作的挑战: 微服务拆分之后,团队之间的协作变得更复杂,需要定义清晰的服务接口和契约,需要协调服务的版本和发布,需要处理服务之间的依赖和冲突。
所以,微服务不是适合所有的项目,如果项目比较小,业务比较简单,团队比较小,用单体架构可能更合适;如果项目比较大,业务比较复杂,团队比较大,需要独立部署和扩展,那么微服务架构可能更合适。
三、微服务治理的核心问题
微服务架构带来了很多好处,但是也带来了很多新的挑战,特别是服务治理方面的问题。如果这些问题处理不好,微服务架构反而会比单体架构更脆弱,更难维护。
微服务治理的核心问题,主要有以下几个方面:
1. 服务注册与发现
微服务的数量很多,每个服务可能有多个实例,服务的地址和端口可能会动态变化(比如服务扩容、缩容、重启、迁移)。服务之间怎么找到对方?怎么知道对方有哪些实例?怎么知道每个实例的地址和端口?这就需要服务注册与发现。
服务注册与发现的核心是一个注册中心,所有的服务启动的时候,把自己的地址和端口注册到注册中心,服务消费方从注册中心获取服务提供方的地址列表,然后根据负载均衡策略选择一个实例进行调用。当服务实例下线或者故障的时候,注册中心会把它从地址列表中移除,这样服务消费方就不会调用到故障的实例。
常见的注册中心有:Eureka、Consul、Zookeeper、Nacos等。
2. 负载均衡
服务消费方从注册中心获取到服务提供方的地址列表之后,怎么选择一个实例进行调用?这就需要负载均衡。
负载均衡的策略有很多,比如轮询、随机、加权轮询、加权随机、最少连接、一致性哈希等。不同的策略适用于不同的场景,需要根据业务需求选择合适的策略。
负载均衡可以在服务端做(比如用Nginx),也可以在客户端做(比如用Ribbon、Feign)。微服务架构中,一般用客户端负载均衡,因为服务消费方已经从注册中心获取了地址列表,可以在客户端做负载均衡,不需要额外的负载均衡设备,性能更好,也更灵活。
3. 熔断与降级
在微服务架构中,服务之间有依赖关系,一个服务可能依赖多个其他服务。如果某个服务出现故障,响应很慢,或者直接不可用,那么调用它的服务就会被阻塞,线程池被占满,最终导致这个服务也不可用,然后这个服务的调用方也会被影响,最终导致整个系统雪崩,这就是"雪崩效应"。
为了防止雪崩效应,需要熔断和降级机制。
熔断就是当某个服务的故障率达到一定阈值的时候,自动断开对这个服务的调用,直接返回错误或者默认值,不再调用这个服务,避免资源被耗尽。熔断之后,每隔一段时间,尝试调用一次,如果调用成功,就关闭熔断,恢复正常调用;如果还是失败,就继续熔断。这就是"半开状态"。
降级就是当系统压力很大的时候,主动关闭一些非核心的服务,或者返回一些默认值,释放资源,保证核心服务的正常运行。比如电商系统,在大促的时候,可以降级商品评论、商品推荐这些非核心服务,保证下单、支付这些核心服务的正常运行。
常见的熔断降级组件有:Hystrix、Resilience4j、Sentinel等。
4. 限流
在高并发的场景下,如果请求量超过了系统的处理能力,系统就会被压垮,导致所有的请求都无法处理。为了保护系统,需要限流机制,控制请求的速率,当请求量超过阈值的时候,拒绝多余的请求,或者排队等待,保证系统不会被压垮。
限流的算法有很多,比如计数器、滑动窗口、漏桶、令牌桶等。不同的算法有不同的特点,需要根据业务需求选择合适的算法。
限流可以在网关层做,也可以在服务层做。一般在网关层做全局限流,在服务层做服务级别的限流,多层限流,更好地保护系统。
5. 链路追踪
微服务架构中,一个请求可能会经过多个服务,调用链路很长。如果请求出现问题,比如响应慢、报错,怎么快速定位是哪个服务出了问题?怎么知道每个服务的耗时?怎么知道调用的顺序和关系?这就需要链路追踪。
链路追踪就是给每个请求分配一个唯一的traceId,在请求经过的每个服务中,都记录这个traceId,以及调用的服务、方法、耗时、结果等信息。这样,当请求出现问题的时候,就可以通过traceId,查到这个请求经过的所有服务,以及每个服务的耗时和结果,快速定位问题。
常见的链路追踪组件有:Zipkin、Jaeger、SkyWalking、Pinpoint等。
6. 配置管理
微服务的数量很多,每个服务都有自己的配置,配置的管理很麻烦。特别是配置需要动态更新的时候,如果每个服务都要重新部署,效率很低,也很容易出错。这就需要统一的配置管理中心,把所有服务的配置集中管理,支持动态更新,服务不需要重新部署,就能获取到最新的配置。
配置中心还需要支持配置的版本管理、环境隔离、权限控制等功能,保证配置的安全和可靠。
常见的配置中心有:Spring Cloud Config、Apollo、Nacos等。
7. API网关
微服务架构中,服务的数量很多,客户端(比如Web端、移动端)如果直接调用各个服务,会很麻烦,需要知道每个服务的地址,需要处理认证、鉴权、限流、日志等通用逻辑,每个服务都要重复实现这些逻辑,很冗余。这就需要API网关,作为所有请求的入口,统一处理认证、鉴权、限流、日志、路由、协议转换等通用逻辑,客户端只需要和网关交互,不需要知道后端服务的细节。
API网关还可以做请求的聚合,把多个服务的调用聚合成一个请求,减少客户端的请求次数,提高性能。
常见的API网关有:Zuul、Spring Cloud Gateway、Kong、Nginx+Lua等。
8. 日志收集与分析
微服务架构中,服务的数量很多,每个服务都有自己的日志,日志分散在不同的服务器上,查看和分析日志很麻烦。特别是排查问题的时候,需要查看多个服务的日志,一个个登录服务器去看,效率很低。这就需要统一的日志收集与分析系统,把所有服务的日志集中收集起来,存储到一个地方,支持搜索、分析、可视化,方便排查问题和监控系统运行状态。
常见的日志收集与分析系统有:ELK(Elasticsearch+Logstash+Kibana)、EFK(Elasticsearch+Fluentd+Kibana)、Loki等。
9. 监控与告警
微服务架构中,服务的数量很多,系统的复杂度很高,需要完善的监控与告警系统,实时监控系统的运行状态,包括服务器的CPU、内存、磁盘、网络,服务的QPS、响应时间、错误率,业务的指标等。当指标出现异常的时候,及时告警,通知运维人员处理,避免问题扩大。
监控与告警系统,需要支持指标的采集、存储、查询、可视化、告警等功能。
常见的监控与告警系统有:Prometheus+Grafana、Zabbix、Datadog等。
四、高可用架构设计
高可用是微服务架构的核心目标之一,高可用意味着系统能够持续提供服务,即使出现一些故障,也不会影响系统的正常运行。
高可用架构设计,主要从以下几个方面入手:
1. 服务无状态化
服务要尽量无状态,也就是不把状态保存在服务的内存中,而是把状态保存在外部的存储中,比如Redis、数据库等。这样,服务的任何一个实例,都可以处理任何请求,请求可以在不同的实例之间切换,服务扩容、缩容、重启、迁移都不会影响请求的处理,系统的可用性更高。
如果服务有状态,比如把用户的登录状态保存在服务的内存中,那么用户的请求必须路由到保存了状态的那个实例,否则就会丢失状态,服务的扩容、缩容、重启都会受到影响,系统的可用性也会降低。
所以,微服务架构中,服务要尽量无状态化,把状态保存在外部的存储中。
2. 多实例部署
每个服务都要部署多个实例,避免单点故障。一个实例出问题,还有其他实例可以提供服务,系统不会因为一个实例的故障而不可用。
多实例部署,还要注意把实例部署在不同的服务器、不同的机架、不同的可用区,甚至不同的地域,避免因为服务器、机架、可用区、地域的故障,导致所有实例都不可用。
3. 负载均衡
多实例部署之后,需要负载均衡,把请求均匀地分发到各个实例,避免某个实例压力过大,而其他实例空闲。负载均衡还可以自动剔除故障的实例,把请求分发到健康的实例,保证系统的可用性。
4. 熔断降级
前面提到过,熔断降级是防止雪崩效应的重要机制。当某个服务出现故障的时候,熔断机制可以自动断开对这个服务的调用,避免资源被耗尽;降级机制可以在系统压力大的时候,主动关闭非核心服务,保证核心服务的正常运行。
熔断降级是高可用架构中必不可少的组件,一定要做好。
5. 超时与重试
服务之间的调用,一定要设置超时时间,避免因为某个服务响应慢,导致调用方的线程被长时间阻塞,资源被耗尽。超时时间要根据业务场景合理设置,不能太长,也不能太短。
超时之后,可以进行重试,但是重试要注意以下几点:
- 重试的次数不能太多,一般1-3次,避免因为重试导致压力更大。
- 重试要有间隔,比如指数退避,第一次重试间隔100ms,第二次200ms,第三次400ms,避免集中重试导致服务被压垮。
- 只有幂等的接口才能重试,非幂等的接口(比如下单、支付)不能重试,否则会导致数据重复。
- 重试要配合熔断机制,当服务故障的时候,不要重试,直接熔断。
6. 异步化
对于一些非核心的、不需要立即返回结果的操作,可以异步化处理,比如用消息队列,把请求放到消息队列中,立即返回,然后由消费者异步处理。这样可以提高系统的响应速度,也可以削峰填谷,应对突发的高并发,提高系统的可用性。
比如,用户注册之后,发送欢迎邮件、发送短信通知、初始化用户数据这些操作,都可以异步化处理,不需要同步等待,用户注册的响应速度会更快,系统的可用性也更高。
7. 数据高可用
服务高可用了,数据也要高可用,否则数据存储出问题,系统还是不可用。
数据高可用,主要从以下几个方面入手:
- 数据库主从复制,读写分离,避免单点故障。
- 数据库分库分表,提高数据库的并发处理能力和存储容量。
- 数据备份,定期备份数据,防止数据丢失。
- 多地域部署,数据同步,避免因为地域故障导致数据不可用。
8. 灰度发布与回滚
服务发布的时候,不要一次性全量发布,而是灰度发布,先发布一小部分实例,观察一段时间,没有问题再逐步扩大发布范围,最后全量发布。如果发布过程中出现问题,可以立即回滚,把影响降到最低。
灰度发布可以用蓝绿部署、滚动发布、金丝雀发布等方式,根据业务需求和基础设施选择合适的方式。
五、高并发架构设计
高并发是微服务架构的另一个核心目标,高并发意味着系统能够处理大量的请求,在请求量很大的时候,依然能够保持良好的性能和可用性。
高并发架构设计,主要从以下几个方面入手:
1. 水平扩展
高并发最直接的方式就是水平扩展,也就是增加服务的实例数量,通过更多的实例来处理更多的请求。微服务架构的一个重要优势就是支持独立的水平扩展,哪个服务压力大,就扩展哪个服务,不需要扩展整个应用,资源利用率更高。
水平扩展要注意服务的无状态化,只有无状态的服务才能方便地水平扩展。
2. 缓存
缓存是提高并发处理能力的重要手段,把热点数据缓存起来,请求来了直接从缓存中获取,不需要访问数据库,这样可以大大提高响应速度,也可以减轻数据库的压力。
缓存的层级有很多,比如浏览器缓存、CDN缓存、网关缓存、应用本地缓存、分布式缓存(Redis)等。根据数据的特点和业务需求,选择合适的缓存层级和策略。
使用缓存要注意以下几个问题:
- 缓存穿透:查询一个不存在的数据,缓存中没有,每次都要查询数据库。解决方法:缓存空值,或者用布隆过滤器。
- 缓存雪崩:大量缓存同时失效,或者缓存服务宕机,导致所有请求都打到数据库,数据库被压垮。解决方法:缓存过期时间加随机值,避免同时失效;缓存服务集群部署,高可用;服务熔断降级。
- 缓存一致性:缓存和数据库的数据不一致。解决方法:更新数据库之后,删除缓存,而不是更新缓存;用延迟双删;用Canal监听数据库binlog,异步更新缓存。
3. 数据库优化
数据库往往是高并发系统的瓶颈,数据库的优化很重要。
数据库优化,主要从以下几个方面入手:
- 索引优化:给常用的查询字段加索引,避免全表扫描,提高查询速度。但是索引也不是越多越好,索引会降低写入的速度,占用更多的存储空间,要合理设计索引。
- SQL优化:优化SQL语句,避免慢查询,比如避免SELECT *,避免大表JOIN,避免子查询,用LIMIT分页,用EXPLAIN分析SQL的执行计划。
- 读写分离:主库负责写,从库负责读,读请求分发到从库,减轻主库的压力,提高读的并发能力。
- 分库分表:当单库单表的数据量太大,并发太高的时候,可以分库分表,把数据分散到多个库多个表中,提高并发处理能力和存储容量。分库分表可以用ShardingSphere、MyCat等中间件。
- NoSQL:对于一些特定的场景,可以用NoSQL数据库,比如Redis做缓存和会话存储,MongoDB存文档数据,Elasticsearch做全文检索,HBase存海量数据,这些NoSQL数据库在特定场景下比关系型数据库性能更好,并发能力更强。
4. 异步化与消息队列
前面提到过,异步化可以提高系统的响应速度,也可以削峰填谷,应对突发的高并发。用消息队列,把请求放到消息队列中,立即返回,然后由消费者异步处理,这样可以把同步的高并发请求,变成异步的平缓处理,系统不会被突发的高并发压垮。
消息队列还可以解耦服务,服务之间不需要直接调用,通过消息队列通信,降低服务之间的耦合度,提高系统的可维护性和可扩展性。
常见的消息队列有:RabbitMQ、Kafka、RocketMQ、ActiveMQ等。
5. CDN与静态资源优化
对于静态资源(比如图片、CSS、JS、视频等),可以用CDN(内容分发网络),把静态资源缓存到离用户最近的CDN节点,用户访问的时候,直接从CDN节点获取,不需要回源,这样可以大大提高访问速度,也可以减轻源站的压力。
静态资源还可以做压缩(Gzip、Brotli)、合并、雪碧图、懒加载等优化,减少请求的大小和数量,提高页面的加载速度。
6. 连接池与线程池优化
服务的连接池(数据库连接池、HTTP连接池等)和线程池的参数,要根据业务场景和服务器配置合理设置,不能太大,也不能太小。太大了会占用太多资源,太小了会导致请求排队,响应慢。
连接池和线程池的参数,需要通过压测来调优,找到最优的配置。
六、服务治理组件选型
微服务治理的组件很多,怎么选择适合自己的组件?下面是一些常见的选型建议:
1. 注册中心:
- Eureka:Spring Cloud原生,AP模型,简单易用,但是已经停止维护了,不建议新项目用。
- Consul:支持多数据中心,支持健康检查,支持KV存储,功能比较丰富,但是性能一般。
- Zookeeper:CP模型,一致性好,但是可用性一般,不适合做注册中心(注册中心需要AP),更适合做分布式协调。
- Nacos:阿里开源,支持注册中心和配置中心,支持AP和CP切换,功能丰富,性能好,国内用的很多,文档和社区都比较完善,推荐使用。
2. 配置中心:
- Spring Cloud Config:Spring Cloud原生,支持Git、SVN、本地文件等存储,简单易用,但是功能比较基础,不支持动态推送(需要配合Spring Cloud Bus)。
- Apollo:携程开源,功能丰富,支持配置的版本管理、环境隔离、权限控制、灰度发布、动态推送,国内用的很多,文档和社区都比较完善,推荐使用。
- Nacos:同时支持注册中心和配置中心,功能也比较丰富,如果已经用了Nacos做注册中心,可以直接用它的配置中心,减少组件的数量。
3. 熔断降级:
- Hystrix:Netflix开源,功能丰富,但是已经停止维护了,不建议新项目用。
- Resilience4j:轻量级,函数式编程,支持熔断、限流、重试、隔离等,但是功能比Hystrix少一些。
- Sentinel:阿里开源,功能丰富,支持熔断、限流、系统保护、热点参数限流等,性能好,国内用的很多,文档和社区都比较完善,推荐使用。
4. 链路追踪:
- Zipkin:Twitter开源,轻量级,简单易用,但是功能比较基础。
- Jaeger:Uber开源,支持OpenTracing标准,功能比较丰富,UI比较好看。
- SkyWalking:华为开源,支持多语言,功能丰富,性能好,不需要侵入代码(Java Agent),国内用的很多,推荐使用。
- Pinpoint:Naver开源,功能丰富,但是侵入性比较强,需要修改字节码。
5. API网关:
- Zuul:Netflix开源,Spring Cloud原生,但是是阻塞IO,性能一般,已经停止维护了(Zuul 1.x),不建议新项目用。
- Spring Cloud Gateway:Spring Cloud官方,基于WebFlux,非阻塞IO,性能好,功能丰富,推荐使用。
- Kong:基于Nginx+OpenResty,性能好,插件丰富,但是需要Lua开发,有一定的学习成本。
- Nginx+Lua:灵活,性能好,但是需要自己开发很多功能,运维成本高。
6. 消息队列:
- RabbitMQ:功能丰富,支持多种消息模式,可靠性高,但是吞吐量一般,适合中小规模的系统。
- Kafka:高吞吐量,高可用,适合大数据场景和日志收集,但是功能比较基础,不支持复杂的消息模式。
- RocketMQ:阿里开源,功能丰富,支持事务消息、顺序消息、延迟消息等,可靠性高,吞吐量高,国内用的很多,推荐使用。
7. 监控告警:
- Prometheus+Grafana:云原生标准,功能丰富,性能好,社区活跃,推荐使用。
- Zabbix:传统的监控系统,功能丰富,但是配置比较复杂,更适合监控服务器和网络设备。
8. 日志收集:
- ELK(Elasticsearch+Logstash+Kibana):功能丰富,搜索能力强,但是资源消耗大,运维成本高。
- EFK(Elasticsearch+Fluentd+Kibana):比ELK轻量,Fluentd比Logstash性能更好,资源消耗更少。
- Loki:轻量级,资源消耗少,和Grafana集成好,但是搜索能力不如Elasticsearch。
选型的时候,不要盲目追求功能多、技术新,要根据自己的业务需求、团队技术栈、运维能力来选择,适合自己的就是最好的。组件的数量也不要太多,能合并的就合并,减少运维成本和复杂度。
七、实际案例分享
下面分享一下我们公司微服务改造的实际案例,以及遇到的问题和解决方案。
我们公司原来有一个单体应用,用的是SSM(Spring+SpringMVC+MyBatis)框架,代码量很大,有几十万行,部署在几台Tomcat上。随着业务的发展,这个单体应用的问题越来越明显:代码耦合严重,修改一个功能要改很多地方,容易出bug;部署慢,每次发布都要重新部署整个应用,发布一次要半个小时;扩展难,某个功能压力大,要扩展整个应用,资源利用率低;团队协作困难,多个团队同时开发同一个应用,代码冲突频繁,发布经常互相影响。
所以,我们决定进行微服务化改造,把单体应用拆分成微服务。
我们的微服务技术栈,选择的是Spring Cloud Alibaba,具体组件如下:
- 注册中心和配置中心:Nacos
- 熔断降级和限流:Sentinel
- 网关:Spring Cloud Gateway
- 服务间调用:OpenFeign
- 消息队列:RocketMQ
- 链路追踪:SkyWalking
- 监控告警:Prometheus+Grafana
- 日志收集:ELK
- 数据库:MySQL+Redis
- 容器化:Docker+Kubernetes
我们把单体应用拆分成了十几个微服务,比如用户服务、商品服务、订单服务、支付服务、搜索服务、推荐服务、消息服务等。每个服务独立部署,独立扩展,有自己的数据库。
在改造的过程中,我们遇到了很多问题,也踩了很多坑,下面分享几个典型的问题:
问题1:分布式事务
微服务拆分之后,原来的一个事务,现在跨了多个服务,比如下单,要同时操作订单服务、库存服务、用户服务,怎么保证数据的一致性?
我们一开始用的是2PC(两阶段提交),用的是Seata的AT模式,但是发现性能很差,因为2PC要锁定资源,事务的时间很长,并发高的时候,经常出现锁等待和死锁,吞吐量上不去。
后来,我们改用了最终一致性的方案,用消息队列(RocketMQ的事务消息),下单的时候,先在订单服务创建订单(状态是"待处理"),然后发送事务消息,库存服务和用户服务消费消息,分别扣减库存和扣减余额,处理成功之后,回调订单服务,把订单状态改成"已完成";如果处理失败,就回滚,把订单状态改成"已取消",恢复库存和余额。这样,虽然不是强一致性,但是最终是一致的,性能也比2PC好很多,满足了业务的需求。
问题2:服务雪崩
有一次,我们的推荐服务因为代码bug,响应变得很慢,导致调用推荐服务的首页服务的线程池被占满,首页服务也不可用了,然后调用首页服务的网关也被影响了,最终导致整个系统不可用,这就是典型的雪崩效应。
那次故障,影响了半个多小时,损失很大。故障之后,我们做了深刻的复盘,然后引入了Sentinel做熔断降级和限流,给每个服务的调用都设置了超时时间、熔断规则、限流规则,当某个服务响应慢或者故障率高的时候,自动熔断,快速失败,避免资源被耗尽;当系统压力大的时候,自动限流,保护系统不被压垮。
引入Sentinel之后,类似的故障就再也没有发生过了,即使某个服务出问题,也只会影响这个服务本身,不会导致整个系统雪崩。
问题3:数据库压力大
微服务拆分之后,服务的并发能力提高了,但是数据库的压力也变大了,特别是订单服务和商品服务的数据库,经常出现慢查询,CPU使用率很高,响应很慢。
我们做了以下优化:
- 索引优化:给常用的查询字段加了索引,删除了一些没用的索引,慢查询减少了很多。
- SQL优化:优化了一些慢SQL,比如大表JOIN改成了分两次查询,子查询改成了JOIN,分页查询优化了(用延迟关联)。
- 读写分离:主库负责写,从库负责读,读请求分发到从库,主库的压力减轻了很多。
- 缓存:把热点数据(比如商品信息、用户信息)缓存到Redis,请求来了直接从缓存中获取,不需要访问数据库,数据库的压力大大减轻。
- 分库分表:订单表的数据量很大,我们做了分库分表,按用户ID分库,按时间分表,把数据分散到多个库多个表中,并发处理能力和查询速度都提高了很多。
经过这些优化,数据库的压力大大减轻,响应速度也提高了很多。
问题4:运维复杂度高
微服务的数量很多,十几个服务,每个服务有多个实例,部署、监控、日志、告警都很麻烦,运维的工作量很大。
我们做了以下优化:
- 容器化:用Docker打包服务,用Kubernetes编排和管理容器,服务的部署、扩容、缩容、重启都自动化了,运维效率大大提高。
- CI/CD:用Jenkins做持续集成和持续部署,代码提交之后,自动构建、自动测试、自动部署,发布效率大大提高,也减少了人为出错的可能。
- 统一监控:用Prometheus+Grafana做统一的监控,所有服务的指标都集中采集和展示,有异常自动告警,运维人员可以快速发现和定位问题。
- 统一日志:用ELK做统一的日志收集,所有服务的日志都集中收集和存储,支持搜索和分析,排查问题的时候,不需要登录各个服务器去看日志,效率大大提高。
- 链路追踪:用SkyWalking做链路追踪,请求经过的所有服务都能追踪到,每个服务的耗时和结果都能看到,排查问题的时候,可以快速定位是哪个服务出了问题。
经过这些优化,运维的复杂度大大降低,运维效率大大提高。
八、踩坑总结
最后,总结一下微服务治理中常见的坑,希望大家能避免:
1. 不要为了微服务而微服务
微服务不是银弹,不是所有的项目都适合微服务。如果项目比较小,业务比较简单,团队比较小,用单体架构可能更合适,微服务反而会增加复杂度,降低开发效率。不要盲目跟风,为了微服务而微服务,要根据项目的实际情况来选择架构。
2. 服务拆分的粒度要合适
服务拆分的粒度很重要,不能太粗,也不能太细。太粗了,就和单体架构差不多,没有发挥微服务的优势;太细了,服务的数量太多,运维复杂度太高,服务之间的调用太多,性能也会受影响。
服务拆分的原则是"高内聚,低耦合",把相关的业务功能放在一个服务里,不同的业务功能拆分成不同的服务。一般来说,一个服务由一个小团队(2-5人)负责比较合适,服务的代码量不要太大,业务逻辑不要太复杂。
3. 不要忽略服务治理
很多团队做微服务,只关注服务的拆分和开发,忽略了服务治理,结果服务拆完了,但是服务注册发现、负载均衡、熔断降级、限流、链路追踪、监控告警这些都没做好,系统很脆弱,经常出问题,出了问题也很难排查。
微服务架构,服务治理是关键,一定要在微服务化的同时,把服务治理做好,否则微服务反而会比单体架构更难维护。
4. 不要过度设计
微服务治理的组件很多,功能也很丰富,但是不要过度设计,不要把所有的组件、所有的功能都用上,这样会增加系统的复杂度和运维成本。要根据业务需求,选择需要的组件和功能,够用就好,简单就好。
比如,如果你的系统并发不高,可能不需要限流;如果你的服务之间没有强一致性的要求,可能不需要分布式事务;如果你的团队很小,可能不需要太复杂的CI/CD。不要为了用而用,要根据实际需求来选择。
5. 做好服务的版本管理和兼容
微服务架构中,服务之间有依赖关系,服务的接口要做好版本管理和向后兼容,避免因为一个服务的接口变更,导致依赖它的服务出问题。
接口变更的时候,要遵循"兼容优先"的原则,尽量不要修改已有接口的参数和返回值,如果要修改,要新增接口,或者新增参数,保留旧的接口和参数,给依赖方足够的时间升级。服务的发布,要灰度发布,先发布一小部分,观察没有问题再全量发布,避免因为服务发布导致整个系统出问题。
6. 做好数据的备份和容灾
微服务架构,数据分散在不同的服务、不同的数据库中,数据的备份和容灾很重要。一定要定期备份数据,做好容灾方案,避免因为数据丢失或者数据库故障,导致系统不可用。
重要的数据,要多副本存储,跨地域备份,确保即使一个地域出问题,数据也不会丢失,系统也能快速恢复。
7. 团队的组织架构要匹配
微服务架构,不仅仅是技术架构的变化,也需要团队的组织架构匹配。康威定律说,系统的架构,反映了团队的沟通结构。如果团队的组织架构还是大团队、集中式的,那么即使技术上拆成了微服务,团队之间的沟通和协作还是会很困难,微服务的优势也发挥不出来。
所以,做微服务化的同时,也要调整团队的组织架构,把大团队拆分成小团队,每个小团队负责一个或几个微服务,从开发、测试、部署到运维,团队全权负责,这样才能真正发挥微服务的优势。
写在最后
微服务治理架构设计:高可用高并发。
微服务架构,是一把双刃剑,它解决了单体架构的很多问题,但是也带来了很多新的挑战。服务治理,是微服务架构的关键,只有把服务治理做好,才能真正发挥微服务的优势,实现高可用高并发。
本文从微服务的概念和优缺点、到微服务治理的核心问题、到高可用架构设计、到高并发架构设计、到服务治理组件选型、到实际案例分享、再到踩坑总结,聊了聊微服务治理架构设计,希望能给正在做微服务架构的朋友一些参考和启发。
微服务治理,是一个持续优化的过程,不是一蹴而就的。在实际的项目中,要根据业务需求和团队情况,逐步完善服务治理,不要急于求成,不要过度设计,适合自己的就是最好的。
最后,用一句话结尾:
"微服务不是目的,而是手段。高可用高并发,也不是目的,而是为了给用户提供更好的服务。不要为了技术而技术,要始终以业务和用户为中心,技术服务于业务。"
愿大家都能设计出稳定、高效、可维护的微服务架构,给用户提供更好的服务。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录