SpringCloud-注册中心、负载均衡

398 阅读11分钟

一、微服务介绍

1. 系统架构的演变

1.1 单体架构

将业务的所有功能集中在一个项目中开发,打成一个包部署。当网站流量很小时,单体架构非常合适。 image-20210713202807818.png

单体架构的优缺点如下:

优点:

  • 架构简单
  • 开发简单、运维简单

缺点:

  • 耦合度高(维护困难、升级困难)
  • 任何一个功能要升级,整个项目都需要重新编译、打包、部署
  • 任何一个功能出bug,都有可能会影响到整个服务的运行

1.2 分布式服务

分布式架构:根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务。 image-20210713203124797.png

分布式架构的优缺点:

优点:

  • 降低服务耦合
  • 有利于服务升级和拓展

缺点:

  • 服务有重复代码
  • 服务调用关系错综复杂
  • 服务容错性较差

1.3 微服务

微服务其实是在给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。

因此,可以认为微服务是一种经过良好架构设计的分布式架构方案,微服务以如下特征:

  • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责。一个服务只做一件事
  • 服务自治:团队独立、技术独立、数据独立,独立部署和交付
  • 面向服务:服务对外暴露统一标准的接口,与语言和技术无关。SpringCloud体系里使用HTTP协议的接口
  • 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题

但是使用了微服务,也会带来一些新的问题:

  • 框架设计复杂

  • 不同服务之间需要相互调用 –远程调用技术

  • 远程调用时可能会出现问题 –服务保护,防止雪崩

  • 远程调用时需要获取地址 – 服务治理问题

  • 众多的服务需要一个统一入口 – 服务网关

  • 每个服务都有配置文件,修改变更非常麻烦 –配置中心

image-20210713203753373.png

2. SpringCloud介绍

SpringCloud微服务解决方案:不是一个框架,是一系列框架集合,目前包含二十多个框架,还在不断增加中……

SpringCloud是Pivotal团队提供的、基于SpringBoot开箱即用的、一站式微服务解决方案的框架体系。目前已成为国内外使用最广泛的微服务框架。

SpringCloud集成了各种微服务功能组件。

微服务需要解决的问题(微服务的5个核心组件):

  • 注册中心:解决的是服务治理问题,即 服务地址的管理。Nacos
  • 远程调用:解决服务之间的请求调用问题。RestTemplate(今天临时用),Feign
  • 服务保护:防止服务之间出现级联问题导致雪崩。Hystrix,Sentinel
  • 配置中心:解决的是每个服务都有配置文件,配置文件散乱不方便管理的问题。Nacos
  • 服务网关:解决的是 要给客户端提供一个统一的访问入口。SpringCloudGateway

二、注册中心

1. 服务治理问题

目前已经可以实现微服务之间的调用,但是我们把服务提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:

  • 一旦服务提供者地址变化,就需要手工修改代码
  • 一旦服务变得越来越多,人工维护调用关系困难

这时候就需要通过注册中心动态的实现服务治理

服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现

  • 服务注册: 在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳的方式去监测清单中的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
  • 服务拉取: 服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实例的访问。

image-20240426115154712.png

2. Nacos 使用入门

Nacos是SpringCloudAlibaba的组件,而SpringCloudAlibaba也遵循SpringCloud中定义的服务注册、服务发现规范。因此使用Nacos和使用Eureka对于微服务来说,并没有太大区别。

主要差异在于:

  • 依赖坐标不同
  • 配置参数不同

微服务 开启服务注册与发现,步骤:

  1. 依赖:在添加SpringCloudAibaba的版本锁定,再添加nacos-discovery的依赖坐标
  2. 配置:注册中心的地址 和 微服务的名称
  3. 引导类:添加@EnableDiscoveryClient,开启服务注册与发现

示例

1. 依赖

父工程锁定SpringCloudAlibaba的依赖版本

在父工程pom.xml的dependencyManagement中添加SpringCloudAlibaba的版本锁定

<!--SpringCloudAlibaba-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

再微服务添加nacos的服务发现依赖坐标


\<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2. 配置

修改微服务的application.yaml,配置nacos

spring:
  application:
    name: order-service #微服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #注册中心的地址

3. 开启服务

再所有微服务的引导类,在引导类上添加注解@EnableDiscoveryClient

@EnableDiscoveryClient
@SpringBootApplication
@MapperScan("com.itheima.order.mapper")
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

4. 功能测试

验证服务是否注册到Nacos

  1. 先启动Nacos,再启动用户服务和订单服务

  2. 打开Nacos控制台,访问http://localhost:8848/nacos,查看注册的服务信息

image-20220904113027774.png

验证远程调用是否成功

  1. 修改Order服务的引导类,给RestTemplate对象添加 @LoadBalanced

        @Bean
    	@LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
  2. 修改OrderService类,根据服务名进行远程调用

    @Service
    public class OrderService {
        @Autowired
        private OrderMapper orderMapper;
        @Autowired
        private RestTemplate restTemplate;
        public Order findById(Long id){
            Order order = orderMapper.selectById(id);
            //使用RestTemplate,向名称为user-service的服务发起远程调用,得到结果
            User user = restTemplate.getForObject("http://user-service/user/" + order.getUserId(), User.class);
            order.setUser(user);
            return order;
        }
    }
    
  3. 打开浏览器,访问http://localhost:7070/order/101 image-20220904112844346.png

3. Nacos的原理

3.1 临时实例与非临时实例

从Nacos1.0开始就提供了一个配置参数:spring.cloud.nacos.discovery.ephemeral,用于设置微服务实例是临时实例还是非临时实例

  • 值为true:微服务是临时实例。当微服务长时间不能向Nacos续约时,Nacos会剔除微服务实例的信息
  • 值为false:微服务是非临时实例。即使微服务长时间不能向Nacos续约,Nacos也仅仅是将服务实例标记为不健康状态,而不会剔除服务实例的信息

两者的作用是:

  • 临时实例:适用于应对流量突增的情况,当流量洪峰时,增加服务实例数量;当流量高峰过去以后,服务实例停止,就自动从Nacos里剔除了
  • 非临时实例:适用于服务的保护。当Nacos发现短时间有大量微服务不健康时,实际可能是Nacos的网络波动等情况,而微服务实例其实是正常的。 这时Nacos仍然保留服务信息,供消费者进行拉取

3.2 Nacos的原理

image-20210714001728017.png

三、负载均衡Ribbon

1. 什么是负载均衡

通俗的讲,负载均衡就是将负载(工作任务,访问请求)分摊到多个操作单元上进行执行。

根据负载均衡发生位置的不同,一般分为服务端负载均衡客户端负载均衡

  • 服务端(提供者)负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡。

  • 客户端(消费者)负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求。

我们在微服务调用关系中一般会选择客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供者执行。

image-20240426145034482.png

2. Ribbon负载均衡

Ribbon是NetFlix提供的客户端负载均衡工具,它有助于控制HTTP和TCP客户端的行为。

Ribbon提供了多种负载均衡算法,包括轮询、随机等等,同时也支持自定义负载均衡算法。

它不需要独立部署,而是几乎存在于SpringCloud的每个组件中,包括Nacos也已经引入了Ribbon。

  • 当使用RestTemplate发起远程调用时,需要给RestTemplate对象添加@LoadBalanced注解
  • 当使用OpenFeign发起远程调用时,不需要做任何额外配置,就具备负载均衡效果

3. Ribbon实现原理分析

@LoadBalanced注解

注解的文档中说明了:如果使用@LoadBalanced标记一个RestTemplate或者WebClient对象,表示将会配置使用一个LoadBanalcerClient对象

image-20211105122306144.png

LoadBalancerClient源码

LoadBalancerClient有一个子类RibbonLoadBalancerClient,它使用Ribbon实现了负载均衡调用

image-20230509211112892.png

IRule源码

在上一步的getServer方法代码如下:

image-20230509211249785.png

继续跟进去,发现是由rule对象挑选了一个目标服务地址

image-20230509211354446.png

这个IRule是什么呢? image-20230509211509091.png

4. Ribbon负载均衡策略

4.1 负载均衡策略介绍

IRule是负载均衡策略的顶级接口,它的每个实现类就是一个我们可以选择使用的负载均衡策略 image-20211105123242605.png

Ribbon内置了多种负载均衡策略,这些策略的顶级接口是com.netflix.loadbalancer.IRule,它的常用实现有:(可参考上一章节里的IRule类图)

策略名描述详细说明
RandomRule随机策略随机选择一个Server
RoundRobinRule轮询策略按顺序依次选择Server(默认的策略)
RetryRule重试策略在一个配置时间段内,如果选择的Server不成功,则一直尝试选择一个可用的Server
BestAvailableRule最低并发策略逐个考察Server,如果Server断路器打开则忽略掉;再选择其余Server中并发链接最低的
AvailabilityFilteringRule可用过滤策略过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server
WeightedResponseTimeRule响应时间加权重策略根据server的响应时间分配权重,响应时间越长权重越低,被选择到的机率越小。响应时间越短被选中的机率越高。刚开始运行时使用robin策略选择Server
ZoneAvoidanceRule区域权重策略综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个aws zone的运行性能是否可用,剔除不可用的zon中的所有server

4.2 修改负载均衡策略

配置文件方式

我们可以通过修改配置文件来调用Ribbon的负载均衡策略,只要修改**调用者的配置文件**即可:

服务名:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

例如,在订单服务的application.yaml里添加如下配置:

#调用用户服务时,使用指定的负载均衡策略
user-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

代码@Bean方式

我们修改订单服务的配置类(或引导类),添加:

@Bean
public IRule randomRule(){
    return new RandomRule();
}

注意事项

  1. 使用配置文件方式,只能设置 调用某一服务时指定负载均衡策略,而不是全局的策略;

    使用@Bean方式,配置的是全局的负载均衡策略

  2. 实际开发中,通常使用默认的负载均衡策略,不需要修改

5. 饥饿加载

在我们的示例代码里,订单服务里必须要先拉取到用户服务的地址列表,创建LoadBanalcerClient对象,然后才可以发起远程调用。而Ribbon有两种策略:

  • 懒加载:即第一次访问目标服务时,才会去创建LoadBalanceClient对象,会导致第一次请求时间比较长
  • 饥饿加载:当服务启动时,就立即拉取服务列表,并创建好LoadBalancerClient对象;这样的话,即使第一次远程调用时也不会花费很长时间

Ribbon默认采用懒加载方式,如果我们需要修改成饥饿加载的话,以订单服务为例,可以在订单服务的配置文件里增加如下配置:

ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients: #要饥饿加载的服务列表
      - user-service