什么是微服务?
本文导图:SpringCloud 梳理-ProcessOn
分布式架构CAP理论
CAP定理是分布式系统中最基础的原则,所以理解和掌握了CAP对系统架构的设计至关重要。分布式架构下所有系统不可能同时满足以下三点:Consisteny(一致性)、Availability(可用性)、Partition tolerance(分区容错性),CAP指明了任何分布式系统只能同时满足这三项中的两项。即只有下面如下三种情况:
- 满足 一致性(C)、可用性(A);
- 满足 一致性(C)、分区容错性(P);
- 满足 可用性(A)、分区容错性(P);
集群与分布式
集群(Cluster):很多人一起干,干的事情都一样。实际过程就是一个业务模块,部署在多台机器上。
分布式(distribution):很多人一起干,每个人干不一样的事情。这是不一样的事情,合起来是一件大事情。实际过程就是一个大的业务系统,拆分为小的业务模块,分别部署在不同的机器上。
架构的演变
总的来说是:[单一应用架构] ===> [垂直应用架构] ===> [分布式服务架构] ===> [流动计算架构]||[微服务架构] ===> [未知]
- 单一架构(All in One Application):起初当网站流量很小时,将所有功能都写在一个应用里面,对整个应用进行部署,以减少部署节点和成本。
- 垂直架构(Vertical Application):当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。
- 分布式服务架构(Distributed Service):当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
- 微服务架构(又称:流动式计算架构(Elastic Computing)):服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。
微服务细讲
微服务架构将单个大应用分割为细粒度的效应用,每个小应用构成大应用的一个服务。其中:
- 每个小应用都运行在自己单独的机器上(有自己单独的进程);
- 小应用之间的调用通过暴露API来实现。
为什么需要微服务架构
- 单体应用架构中,功能越来越多后,项目会变的臃肿,此时缺点①代码难以维护、②新技术更新很困难。
- 微服务架构中,优点:①将整个项目拆分为单一职责的小服务,单独部署,易于维护;②每个服务可以自由选择使用的技术;③服务之间解耦合,一个服务崩溃不会导致整个项目不可用。
微服务架构的解决方案
目前最流行的两种微服务解决方案是 Spring Cloud 和 Dubbo。
- Dubbo:
- 是由阿里开源的项目,致力于提供高性能、透明化的RPC(远程过程调用)和服务治理。
- 但是后来由于一些原因,Dubbo停止维护,在此时,springcloud不断迭代,所以现在大部分项目都会选择使用springcloud作为微服务架构的解决方案。
微服务架构springcloud 的组件
- 服务网关:可以统一监管访问服务的 身份认证、路由转发、负载均衡等问题。
- 服务注册中心:服务的注册与发现,可以帮助服务消费者自动发现和调用服务提供者。
- 配置中心:集中管理配置文件,当配置文件有改动时,可以通知应用服务拉取最新的配置,达到动态刷新。
- 服务熔断器:通过熔断器,可以避免级联故障,提供了故障监控、降级策略等功能。
- 客户端负载均衡:可以帮助客户端在多个服务提供者之间进行负载均衡。
- 消息总线:帮助应用程序实现分布式时间传递、消息广播。
- 链路追踪与监控:可以跟踪各个应用之间的调用链,提供性能监控、日志聚合。
服务网关
参考:https://apisix.apache.org/zh/blog/2023/03/08/why-do-microservices-need-an-api-gateway/
服务网关,是服务的统一入口,可以实现:路由转发、负载均衡、鉴权认证。目前主流的网关组件有:
网关 | 痛点 | 优势 |
---|---|---|
NGINX | 1. 修改配置需要 Reload 才能生效,跟不上云原生的发展。 | 1. 老牌应用; 2. 稳定可靠,久经考验; 3. 高性能。 |
Apache APISIX | 1. 文档不够丰富和清晰,需要待改进。 | 1. Apache 基金会顶级项目; 2. 技术架构更贴合云原生; 3. 性能表现优秀; 4. 生态丰富; 5. 除了支持 Lua 开发插件外,还支持 Java、Go、Python、Node 等语言插件。 |
Kong | 1. 默认使用 PostgreSQL 或 Cassandra 数据库,使得整个架构非常臃肿,并且会带来高可用的问题; 2. 路由使用的是遍历查找,当网关内有超过上千个路由时,它的性能就会出现比较急剧的下降; 3. 一些重要功能是需要付费的。 | 1. 开源 API 网关的鼻祖,用户数众多; 2. 性能满足大部分用户的需求; 3. 生态丰富; 4. 支持 Lua 和 Go 开发插件。 |
Envoy | 1. 使用 C++,二次开发难度大; 2. 除了 C++ 开发 filter 外,还支持 WASM 和 Lua。 | 1. CNCF 毕业项目 更适合服务网格场景多语言架构部署。 |
Spring Cloud Gateway | 1. 虽然 Spring 社区成熟,但是 Gateway 资源缺乏。 | 1. 内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用; 2. Spring 系列可扩展性强,易配置,可维护性好; 3. Spring 社区成熟; 4. 简单易用; 5. 对于 Java 技术栈来说方便。 |
服务注册中心
概念
在整个微服务架构中,服务注册中心 不是用来完成任何业务功能的,仅仅用来完成对整个为服务系统的 服务注册、发现、服务健康检查。
- 服务注册:可以对所有的微服务的信息进行存储,如微服务的名称、IP、端口等;
- 服务发现:在进行服务调用时,通过服务发现 查询可用的微服务列表及网络地址进行服务调用;
- 服务健康的监控、管理:可以对所有的微服务进行心跳检测,如发现某实例长时间无法访问,就会从服务注册表移除该实例。
常用的组件
- nacos:阿里巴巴出品,Java编写。
- consul:go 语言编写。
- zookeeper:Java语言编写。
- eureka:Netflix 出品。
这些区别是(从CAP理论分析):
- Zookeeper:Zookeeper是实现的CP,也就是将失去A(可用性)。Zookeeper集群下一旦leader节点宕机了,在短时间内服务都不可通讯,因为它们在一定时间内follower进行选举来推出新的leader,因为在这段时间内,所有的服务通信将受到影响,而且leader选取时间比较长,需要花费几十秒甚至上百秒的时间。
- Eureka:Eureka 是 AP,丢弃了C。Eureka集群下每个节点之间都会定时发送心跳,定时同步数据,没有master/slave之分,是一个完全去中心化的架构。因此每个注册到Eureka下的实例都会定时同步ip,服务之间的调用也是根据Eureka拿到的缓存服务数据进行调用。若一台Eureka服务宕机,其他Eureka在一定时间内未感知到这台Eureka服务宕机,各个服务之间还是可以正常调用。
- Nacos:同时支持CP和AP架构。
- 如果注册Nacos的client节点注册时
ephemeral=true
,那么Nacos集群对这个client节点的效果就是AP,采用distro协议实现; - 而注册Nacos的client节点注册时ephemeral=false,那么Nacos集群对这个节点的效果就是CP的,采用raft协议实现。
- 如果注册Nacos的client节点注册时
接下来以nacos为例,介绍服务注册发现的原理。
参考:https://tech.dewu.com/article?id=18、https://www.cnblogs.com/Xianhuii/p/17112392.html
nacos 中的概念
- Namespace(命名空间):日常开发中的不同环境的隔离,比如生产环境是一套配置,开发环境是一套配置,测试环境又是一套配置。
- Group(配置分组):每一个配置都可以属于一个或多个分组。
- 配置集ID(Data ID):每个配置集都有一个对应的 Data ID,通过Data ID 可以准确定位、获取配置信息。
nacos 的服务注册
nacos 的服务注册 工作流程:
-
当服务提供者(微服务实例)启动时,会向Nacos服务注册中心发送注册请求。
- 注册请求中包含了服务提供者的关键信息,如服务名、IP地址、端口号、元数据等
- 服务提供者通常通过发送REST请求或gRPC请求(取决于Nacos的配置和版本)向Nacos服务注册中心注册自己
-
Nacos服务注册中心接收到注册请求后,会验证请求中的信息是否完整且合法。
验证通过后,Nacos会将服务提供者的信息存储在自己的注册表中。
- 注册表是一个存储服务实例信息的内存数据结构,它支持高效的查询和更新操作。nacos 服务注册表结构:
Map<namespace, Map<group::serviceName, Service>>
- 注册表是一个存储服务实例信息的内存数据结构,它支持高效的查询和更新操作。nacos 服务注册表结构:
-
验证过后,会生成服务实例ID。Nacos为每个成功注册的服务实例生成一个唯一的服务实例ID。这个ID用于在后续的服务发现过程中唯一标识服务实例。
-
注册信息同步:在Nacos集群环境中,注册信息会在集群节点之间同步,以确保数据的一致性。这通常通过Nacos自研的Distro协议或其他分布式一致性算法实现。
nacos 的服务发现
-
应用A可以发送查询请求给Nacos,根据服务名获取该服务的所有可用实例信息。
-
Nacos 服务器会返回一个包含所有可用实例信息的响应,服务可以根据这些信息选择合适的实例进行调用。
Nacos支持定时从注册中心查询最新服务实例列表信息,定时频率通常为6秒,发生异常时可能延长至60秒。
nacos 的健康检查
「健康检查类型」:Nacos根据服务实例的类型提供了不同的健康检查机制
-
临时实例:应用端在进行服务注册时,会告诉nacos服务器,我要注册为临时实例。这种方式应用端主动上报心跳的方式进行健康检查。
临时实例会定期(默认是5秒/次)向Nacos服务端发送心跳请求,以告知服务端自己仍然存活。
-
持久实例:应用端在进行服务注册时,会告诉nacos服务器,我要注册为持久实例。这种方式nacos服务器端会主动探测的方式进行健康检查。
Nacos服务端会定时向持久实例发送探测请求(如HTTP请求),并根据持久实例的响应来判断其是否健康。
「健康检查流程」:
-
服务实例启动时,会向Nacos服务注册中心发送注册请求,并在注册时指定健康检查的相关配置,包括健康检查的类型、检查间隔、超时时间等。
-
心跳上报/探测请求:
- 临时实例会按照设定的检查间隔定期向Nacos服务端发送心跳请求。
- 持久实例则等待Nacos服务端的探测请求,并根据请求进行响应。
-
健康状态判断:Nacos服务端根据接收到的心跳请求或探测响应来判断服务实例的健康状态。
- 如果临时实例的心跳请求正常,则认为该实例健康;如果连续几个心跳周期未收到心跳,则认为实例不健康。
- 对于持久实例,如果探测响应正常,则认为实例健康;如果连续几次探测无响应或响应不正确,则认为实例不健康。
-
健康状态更新与通知:Nacos服务端会更新服务实例的健康状态信息,并将状态变化通知给相关的服务消费者。
如果服务实例被标记为不健康,Nacos会将其从服务列表中移除或进行隔离处理,以避免服务消费者将请求路由到不健康的实例上。
配置中心
微服务架构中,每个子服务都会有自己的配置文件,都存储在项目中有一些问题:
- 管理难度大:配置文件散落在各个微服务中,不容易管理。
- 安全性低:这些配置文件存储在代码库中,容易造成配置泄露。
- 时效性差:这些配置文件修改之后,需要重启服务才会生效。
为了解决上面的问题,微服务架构引入了配置中心。常用的配置中心有:
- nacos:是的,nacos可以作为注册中心,它同时也提供了 配置中心的功能。
- spring cloud Config:spring 框架也研发了配置中心。
- QConf:360公司研发的配置中心。
这里主要讲述 spring cloud Config 和 nacos 作为配置中心的工作原理。
spring cloud Config 配置中心 工作原理
- 首先,将配置文件提交到远程git仓库。
- spring cloud Config 服务器端 会从远程仓库中拉取最新的配置文件,并在本地存储一份。
- 应用程序通过 spring cloud Config 服务器端 暴露出来的接口,拉取最新的配置信息。
spring cloud Config 作为配置中心时,有如下缺点:
-
只要有一个配置文件有变动,spring cloud Config 服务器端就会通知所有的 微服务项目 拉取最新的配置。这不合理,因为有很多为服务项目的配置信息是没有变动的,却需要去拉取。
-
手动刷新配置文件:当spring cloud Config 服务器端通知拉取最新配置文件信息之后,需要 微服务项目自己手动去拉取。
所以,如果需要自动刷新配置文件,则需要由 **spring cloud Config + 消息总线(Bus)**组合。
nacos 配置中心的工作原理
nacos 可以实现自动刷新配置文件,相当于 spring cloud Config + 消息总线(Bus)组合。
工作流程:
-
应用系统服务 启动时,会先加载应用系统本身的配置文件(可以获取配置中心的地址信息),然后再去nacos 配置中心服务器端 加载配置文件。
应用系统会实现配置好它需要在 配置中心服务器端 拉取哪些配置文件。这一不是通过应用系统自己配置的如:
group
、DataID
等信息确定。 -
发布/订阅 模式:当配置中心中的配置信息发生变化时,Nacos服务器会主动将更新推送给所有订阅了该配置的客户端。
-
本地缓存刷新:客户端收到配置更新后,会先刷新本地缓存,并触发监听器的回调方法,确保应用程序能够使用最新的配置信息。
-
版本控制:客户端在下次请求配置时,会携带当前的配置版本号,以确保只获取到自上次请求以来有变更的配置信息。
nacos 作为配置中心有几个注意事项:
- 持久化:nacos 的配置信息持久化是存储在数据库中的。nacos 默认自带的数据库是Derby(不好用),在实际使用过程中一般都是使用 MySQL 作为持久化数据库。
- 自动刷新配置文件:nacos 通过 发布/订阅 模式,当配置信息发生变化时,Nacos配置中心会主动通知所有订阅了该配置的应用程序,实现配置的动态更新。
- 版本控制:Nacos为每个配置项分配一个唯一的版本号。当配置发生变化时,版本号也会随之更新。客户端在发起请求时可以携带自己的配置版本号,Nacos服务器会根据版本号判断是否有新的配置变更,从而决定是否需要推送新的配置信息给客户端。
熔断器
在微服务架构中,服务与服务之间存在调用关系。当然会出现服务A调用服务B失败的情况。熔断器就是用来保证某些服务不可用的情况下,保证核心业务能够正常工作。
比如:即使其他所有服务都可用,由于服务 E 的不可用,那么用户请求 1、2、3 都会处于阻塞状态,等待服务 E 的响应。在高并发的场景下,会导致整个服务器的线程资源在短时间内迅速消耗殆尽。
服务不可工作的情况
接下来先看看服务不可用的情况:服务雪崩。服务雪崩是指:当微服务系统的一个服务出现故障时,故障会沿着服务的调用链路在系统中疯狂蔓延,最终导致整个微服务系统的瘫痪,这就是“雪崩效应”。
雪崩效应 描述的是 由于提供方不可用,而导致消费方不可用并将不可用逐渐放大的过程。
服务熔断器 – Hystrix(服务熔断、服务降级)
熔断器Hystrix可以解决 服务雪崩,它通过两种方式:服务熔断、服务降级。
-
服务熔断:“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器(hystrix)的故障监控,某个异常条件被触发,直接熔断整个服务。然后向调用方返回一个
FallBack
的响应,告诉调用方这个服务不可用。当服务恢复可用之后,则即可正常调用。 -
服务降级:当 网站|服务 的流量突然增加时,为了保证系统核心服务政策运行,会有策略的关闭系统中的 边缘服务,从而保证核心服务正常运行。
例如,在电商系统中,当订单量激增时,可以考虑暂时关闭或简化一些非核心的辅助功能,如商品评价、用户积分等,以保证订单处理、支付等核心功能的稳定运行。
客户端负载均衡–Ribbon
客户端负载均衡是指在微服务架构中,将负载(即工作任务或访问请求)在客户端进行分配,以决定由哪个服务实例来处理这些请求。在客户端发送请求之前,通过一定的负载均衡算法选择好要访问的服务实例。这意味着客户端需要持有服务实例的信息(如IP地址和端口号),并能够在发送请求前进行决策。
Ribbon组件工作流程为:
-
服务注册:应用服务 在启动时,会向 服务注册中心 注册自己的信息(包括服务名、IP地址、端口号等)。
-
服务发现:服务的消费者在启动后,会定期的从 服务注册中心中获取 服务提供者的注册信息,这些信息以服务实例列表的形式返回给服务消费者,每个服务实例都包含了足够的信息以便服务消费者进行请求转发。
-
Ribbon 在本地缓存服务实例列表:Ribbon会在本地缓存服务实例列表,以减少对服务注册中心的访问次数,提高性能。
-
后续请求时,Ribbon 拦截器会拦截这些请求。拦截器会根据配置的负载均衡策略,从本地缓存的服务实例列表中选择一个合适的服务实例。
Ribbon内置了多种负载均衡策略,如轮询(RoundRobinRule)、随机(RandomRule)、加权轮询(WeightedResponseTimeRule)等。
-
服务调用:一旦选定了合适的服务实例,Ribbon会将原始请求转发给该实例。服务实例处理请求后,将响应返回给Ribbon,Ribbon再将响应转发给服务消费者。
消息总线 – springcloud Bus
springcloud Bus 使用轻量级的消息代理 将各个微服务之间传递消息,实现了服务间的异步通信和松耦合。
springcloud Bus 中可以使用的**消息代理(消息中间件)**有:Kafka、RabbitMQ。
具体的工作流程:
-
消息发布:当某个微服务需要与其他微服务通信时,它会将消息发布到消息总线上。
这个消息可以包含任何类型的数据,如配置更新、业务事件等。
-
消息传递:消息总线接收到消息后,会根据消息的类型、目标地址等信息,将消息传递给相应的微服务。
这个传递过程可以是同步的,也可以是异步的,具体取决于消息总线的实现和业务需求。
-
消息消费:目标微服务从消息总线上接收到消息后,就可以调用其他服务。
-
消息确认:为了确保消息的可靠传递,消息总线通常会要求接收方在成功处理消息后进行确认。只有在接收到确认信号后,消息总线才会认为该消息已经被成功处理,并从队列中移除。
springcloud bus + springcloud config --> 配置自动刷新
Webhook(网络钩子)是一种在Web应用中用于实现自动化的技术,它允许用户在某个事件发生时自动触发一个HTTP请求到指定的URL。在Spring Cloud Config的上下文中,Webhook被用来在配置中心(Config Server)的远端Git仓库中的配置文件发生变化时,自动通知配置客户端(Config Client)刷新其配置。
工作流程如下:
-
webhooks 监听远程仓库的配置文件。一旦配置文件有改动,就会发送 POST /bus/refresh 通知config server 去拉取最新的配置文件。
webhooks 的好处就是 替代了我们需要手动发送POST /bus/refresh,才能让config server去拉取最新的配置信息。
-
config server收到通知去拉取 远程仓库中的最新版本的配置文件。并给 消息总线(MQ)发送消息。
config server 在消息中间件 中是生产者。
-
消息总线 接受到 config server(生产者)发送过来的消息。会通知 config client(消费者) 。
-
config client(消费者)接收到 消息总线发送的消息之后,就去 config server 中拉取最新的配置信息。
config client 在消息中间件 中就是消费者
链路追踪与监控
微服务架构中的链路追踪与监控是确保分布式系统高效运行和快速故障排查的关键技术。通过记录和分析请求在微服务之间的流转路径,帮助开发人员和运维人员深入理解系统的行为,从而优化性能、定位问题并提升系统的可靠性。
常用的组件有:
- Zipkin:是Twitter开源的分布式追踪系统,用于收集服务的定时数据,解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。
- Spring Cloud Sleuth:是Spring Cloud的一个子项目,用于为Spring Boot应用提供分布式追踪解决方案。它可以与Zipkin、Jaeger等追踪系统无缝集成,提供请求跟踪、服务依赖分析等功能。
服务间通信方式
在springcloud 中服务间调用方式主要是使用 http restful 方式进行服务间调用。主要有三种方式:
- RestTemplate 是直接基于服务地址调用没有在服务注册中心获取服务,也没有办法完成服务的负载均衡如果需要实现服务的负载均衡需要自己书写服务负载均衡策略。
- RestTemplate + Ribbon:解决了负载均衡问题,但是路径写死在代码中不利于维护。
- OpenFeign:可以解决以上的缺陷
基于 RestTemplate 的服务调用
spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
但是这种方式的缺陷是:
- 调用服务的路径主机和服务端口直接写死在url中无法实现服务集群时请求负载均衡。-- 解决办法:基于RestTemplate+Ribbon的服务调用
基于RestTemplate+Ribbon的服务调用
加入Ribbon后,Ribbon会先去注册中心按照服务名称(而不是URL)查找该服务的集群信息,然后RestTemplate再去真正的调用。也就是说添加Ribbon的目的,就是弥补 RestTemplate无法负载均衡的不足。
基于OpenFeign 的服务调用
这里需要说明:
-
Feign是Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。通过 Feign,我们可以像调用本地方法一样来调用远程服务,而完全感觉不到这是在进行远程调用。
Feign
默认集成了 Ribbon,也就是自带客户端负载均衡。但是Feign
本身并不支持 Spring MVC 注解。 -
openFeign
是Spring 官方便推出了一个名为 OpenFeign 的组件作为 Feign 的替代方案。它具有 Feign 的所有功能,并在 Feign 的基础上增加了对 Spring MVC 注解的支持。