Spring Cloud

1. 服务拆分和远程调用

  • 任何分布式架构都离不开服务的拆分,微服务也一样。
  • 服务拆分:一个单体架构按照功能模块进行拆分,变成多个服务。

微服务需要根据业务模块拆分,做到单一职责,不要重复开发相同业务。 

1.1 服务拆分原则 或 服务拆分注意事项:

微服务拆分时粒度要小,模块内高内聚,模块间低耦合: 

  1. 不同的微服务,要做到单一职责(微服务拆分的目的就是单一职责),不要重复开发相同业务
  2. 微服务要做到数据独立,不同微服务都应该有自己独立的数据库,每个微服务都会有自己的数据库,不要访问其它微服务的数据库,做到了数据解耦。
  3. 微服务要对外暴露RESTful的业务接口,供其它微服务调用,并且一定要保证微服务对外接口的稳定性(即:尽量保证接口外观不变)!

Spring Cloud_第1张图片

拆分方式 - 服务拆分时的两种方式:

  • 纵向拆分:就是按照项目的功能模块来拆分,这种拆分模式可以尽可能提高服务的内聚性。
  • 横向拆分:是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务,这样可以提高业务的复用性,避免重复开发,同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。

一般微服务项目会有两种不同的工程结构: 

1. 完全解耦:每一个微服务都创建为一个独立的工程,甚至可以使用不同的开发语言来开发,项                          目完全解耦。

  • 优点:服务之间耦合度低
  • 缺点:每个项目都有自己的独立仓库,管理起来比较麻烦

2. Maven聚合:整个项目为一个Project,然后每个微服务是其中的一个Module

  • 优点:项目代码集中,管理和运维方便(授课也方便)
  • 缺点:服务之间耦合,编译时间较长

注意:在企业开发的生产环境中,每一个微服务都应该有自己的独立数据库服务,而不仅仅是                   database。 

1.2 实现微服务远程调用案例:

  • 需求:根据订单ID查询订单的同时,把订单所属的用户信息一起返回。

Spring Cloud_第2张图片Spring Cloud_第3张图片

  • 要想实现跨服务远程调用,就要把 / 将原来的本地方法调用,改造成跨微服务的远程过程调用RPC,即Remoet Produce Call),微服务之间的远程调用被称为RPC,即远程过程调用!

RPC的实现方式有很多,比如:

  • 基于HTTP协议
  • 基于Dubbo协议

要想实现跨服务远程调用,其实就是发送一次HTTP的请求~! 

1.3 服务间远程调用方式分析

微服务的调用方式: 

  • 基于RestTemplate发起的HTTP请求实现远程调用!
  • HTTP请求做远程调用是与语言无关的调用,这种方式不关心服务提供者的具体技术实现,只要对外暴露HTTP接口即可,只要知道对方的IP、端口、请求路径、请求参数即可,更符合微服务的需要。
转变为,如何在Java代码当中发送HTTP请求?
  • 利用Spring提供的RestTemplate来发送HTTP请求! 
    • RestTemplate其中提供了大量的方法,方便我们发生HTTP的请求,常见的Get、Post、Put、Delete请求都支持,如果请求参数比较复杂,还可以使用exchange方法来构造请求。

调用RestTemplate的API发送HTTP请求,常见的方法有:

  • getForObject:发送Get请求并返回指定类型对象
  • postForObject:发送Post请求并返回指定类型对象
  • put:发生PUT请求
  • delete:发生Delete请求
  • exchange:发送任意类型请求,返回ResponseEntity

因此,我们只能在order-service向user-service发起一个远程调用,发起一个HTTP的请求,调用根据ID查询用户的这个接口,远程调用的大概的步骤是这样的:

  1. 在需要发送远程调用模块的启动类当中注册一个RestTemplate的实例到Spring容器
  2. 修改业务代码(在业务代码当中注入RestTemplate ),使用RestTemplate提供的API来发起HTTP请求:修改order-service服务中的OrderService类中的queryOrderById方法,根据Order对象中的userId查询User
  3. 将查询的User填充到Order对象,一起返回

1. 我们知道,Bean的注入只能放在配置类里面,而启动类就是一个配置类:

Spring Cloud_第4张图片

2.  在业务代码当中注入RestTemplate,使用RestTemplate提供的API来发起HTTP请求

Spring Cloud_第5张图片

1.4 提供者与消费者

在服务调用关系中,会有两个不同的角色:
  • 服务提供者:一次业务中,被其它微服务调用的服务(提供或暴露接口给其它微服务调用)
  • 服务消费者:一次业务中调用其它微服务的服务(调用其它微服务提供的接口) 
  • 服务提供者与服务消费者的角色是相对的,相对于具体的业务,业务不同,角色是会变化的!
Spring Cloud_第6张图片 服务消费者  调用  服务提供者
注意:
  • 服务提供者与服务消费者的角色并不是绝对的,而是相对于业务而言,一个服务既可以是提供者,也可以是消费者! 
  • 技术要跟业务相关联!
思考:服务A调用服务B,服务B调用服务C,那么服务B是什么角色?
  • 对于服务A调用服务B的业务而言,A是服务消费者,B是服务提供者
  • 对于服务B调用服务C的业务而言,B是服务消费者,C是服务提供者

因此,服务B既可以是服务提供者,也可以是服务消费者! 

总结:一个服务既可以是服务提供者,又可以是服务消费者! 

2. Eureka注册中心

2.1  远程调用的问题

回顾:
  • 订单服务(服务消费者需要远程调用我们的用户服务(服务提供者),通过HTTP请求实现了跨微服务的远程调用但是这种手动发送HTTP请求的方式存在一些问题之前定义的的URL路径中的IP、端口、请求路径等都是硬编码,写死的,不够灵活。
假如我们的服务提供者被调用较多,为了应对更高的并发,进行了多实例部署,部署了多个实例,如图:
Spring Cloud_第7张图片思考:
  1. 服务提供者这么多实例,服务消费者在发起远程调用的时候,该如何得知每一个服务提供者实例的IP地址和端口?
  2. HTTP请求要写URL地址,有多个服务提供者实例地址,服务消费者调用时该如何选择,到底该调用哪个实例呢?
  3. 服务消费者如何得知服务提供者的健康状态?服务消费者如何得知某个服务提供者实例是否依然健康,是不是已经宕机? 如果在运行过程中,某一个服务提供者实例突然宕机,服务消费者依然在调用该怎么办?
  4. 如果并发太高,服务提供者临时多部署了N台实例,服务消费者如何知道新实例的IP地址?
这些问题都需要利用Spring Cloud中的注册中心来解决其中最广为人知的注册中心就是Eureka。

目前开源的注册中心框架有很多,国内比较常见的有: 

  • Eureka:Netflix公司出品,目前被集成在Spring Cloud当中,一般用于Java应用
  • Nacos:Aliabab公司出品,目前被集成在Spring Cloud Alibaba中,一般用于Java应用
  • Consul:HashiCorp公司出品,目前集成在Spring Cloud中,不限制微服务语言 

以上几种注册中心都遵循Spring Cloud中的API规范因此在业务开发使用上没有太太差异由于Nacos是国内产品,中文文档比较丰富,而且同时具备配置管理功能,因此在国内使用较多! 

2.2 Eureka注册中心原理 - Eureka的结构和作用

Eureka的结构如下:

Spring Cloud_第8张图片 服务注册与发现模型
在Eureka的结构或架构当中,有两类角色(微服务角色有两类): 
  1. 角色一:Eureka-Server注册中心(Eureka服务端):记录和管理这些微服务(记录服务信息、心跳监控)!
  2. 角色二:服务提供者和服务消费者,不管是服务提供者还是服务消费者,都是微服务,所以统称为Eureka的客户端 - Eureka Client端。

在Spring Cloud的生态中,采用服务注册与发现模型,来实现微服务之间的互相发现发现与调用: 

Spring Cloud_第9张图片 服务注册与发现模型

如上图所示,通过在微服务系统中引入一个叫注册中心的组件,来作为协调者。

其最简化的过程是,所有的微服务应用在启动过程中会将自己的服务信息,包含服务名称、主机IP地址和端口号等信息发送到注册中心中,这个叫服务注册,然后上游的微服务(服务消费者)在处理请求过程中,根据服务名称到注册中心中查找对应服务的所有实例IP地址和端口号,因为一个服务可能多实例部署(服务发现或服务拉取),然后服务消费者,也就是调用者对实例列表做负载均衡,挑选一个实例冰箱该实例发起远程调用,从而让分散的微服务系统之间能像一个整体一样对外提供请求处理能力。 

回答之前的三个问题:

问题一:服务消费者在发起远程调用的时候,如何得知每一个服务提供者实例的IP地址? 

获取地址信息的流程如下:

  • 所有的微服务应用的实例在启动过程中会将自身包含服务名称、主机IP地址和端口号等信息发送或注册到Eureka-Server注册中心(Eureka服务端)中,这个叫服务注册
  • Eureka-Server注册中心(Eureka服务端)保存服务名称到服务实例地址列表的映射关系;
  • 服务消费者根据服务名称,拉取服务列表,拉取对应服务的所有实例IP地址和端口号列表,这个叫服务发现或服务拉取。
问题二:服务消费者如何从多个服务提供者实例中选择具体的实例?
  • 服务消费者从实例列表中利用负载均衡算法选中一个实例地址(选中一个微服务),接着向该实例地址(微服务)发起远程调用! 
问题三:服务消费者如何得知某个服务提供者实例是否依然健康,是不是已经宕机?

心跳监控

  • 服务提供者(所有的微服务应用)会每隔一段时间(默认30秒)向Eureka-Server注册中心(Eureka服务端)发起或发送一次心跳请求,确认自己的健康状态,称为心跳请求;
  • Eureka会更新服务记录列表信息:当服务提供者超过一定时间没有发送心跳时,Eureka-Server注册中心(Eureka服务端)会认为该微服务实例故障或宕机,会将该实例从服务的实例列表中剔除(心跳不正常的会被剔除);
  • 这样,服务消费者在拉取服务时,就能将故障实例排除了。

问题四:如果并发太高,服务提供者临时多部署了N台实例,服务消费者如何知道新实例的IP地址? 

  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表!
  • 当注册中心的服务实例列表变更时,会主动通知微服务,更新本地服务列表! 
注意:
  • 一个微服务,既可以是服务提供者,又可以是服务发现者,因此Eureka将服务注册、服务发现等功能统一封装到了Eureka-Client(Eureka客户端)!

因此,接下来我们动手实践的步骤包括: 

Spring Cloud_第10张图片

2.3 搭建EurekaServer

Eureka-Server - 注册中心服务端的搭建,必须创建一个独立的微服务! 

在cloud-demo父工程下,创建一个子模块:

Spring Cloud_第11张图片填写模块信息:  

Spring Cloud_第12张图片然后填写服务信息:

Spring Cloud_第13张图片

搭建Eureka - Server服务的步骤如下:

1. 创建项目,引入eureka-server依赖,引入Spring Cloud为eureka提供的starter依赖:spring-cloud-starter-netflix-eureka-server


    org.springframework.cloud
    spring-cloud-starter-netflix-eureka-server

Spring Cloud_第14张图片

2. 编写启动类,添加@EnableEurekaServer注解(EurekaServer自动装配的开关)

package cn.itcast.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * 手动编写启动类
 * 给Eureka-Server服务编写一个启动类
 * 注意:一定要添加@EnableEurekaServer注解,开启Eureka的注册中心功能
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class,args);
    }
}

3. 添加application.yml文件,在application.yml中配置eureka地址 或 编写下面的配置:

  • Eureka自己也是一个微服务,所以Eureka在启动的时候会将自己也注册到Eureka注册中心中,这是为了将来Eureka集群之间通信去用的。
  • 是为了做服务的注册采取配置的Eureka的服务名称以及Eureka的地址信息!
Spring Cloud_第15张图片 注意等级关系,否则配置会失效!!!
server:
  port: 10086  #服务的端口,随便给定一个端口号
spring:
  application:
    name: eureka-server #服务的名称(Eureka的服务名称)
eureka:
  client:
    service-url:  #Eureka的地址信息(如果是集群,则地址之间用逗号隔开,现在是单机)
      defaultZone: http://127.0.0.1:10086/eureka
启动服务:
  • 启动微服务,然后在浏览器访问:http://127.0.0.1:10086 

看到下面Eureka的管理页面就算是成功了:  

Spring Cloud_第16张图片

  • 实例:一个服务每部署一份儿就是一个实例! 
  • Eureka会记录一个服务的所有实例!

2.4 服务注册

下面,我们将服务提供者注册到eureka-server注册中心中去,服务注册需要经过两个步骤:

1. 在服务提供者的项目中引入eureka-client依赖,引入spring-cloud-starter-netfix-eureka-            client的依赖


        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

2. 在application.yml文件中,添加服务名称以及配置eureka地址,编写下面的配置:

spring
  application:
    name: user-service #服务的名称
  eureka:
    client:
      service-url: #Eureka的地址信息(如果是集群,则地址之间用逗号隔开,现在是单机)
        defaultZone: http://127.0.0.1:10086/eureka

Spring Cloud_第17张图片将服务消费者的信息也注册到EurekaServer中去,服务列表验证: 

Spring Cloud_第18张图片

一个服务启动多个实例的操作

  • 我们可以将一个服务多次启动,模拟多实例部署,但为了避免端口冲突,需要修改端口设置 

首先,复制原来的服务启动配置 - 复制配置:

Spring Cloud_第19张图片

然后,在弹出的窗口中,填写信息,配置启动项,注意重命名配置新的端口号,避免端口冲突(重新配置一个端口以及修改服务名称):

  • -D代表就是参数了! 

Spring Cloud_第20张图片Spring Cloud_第21张图片

添加成功:

Spring Cloud_第22张图片

Spring Cloud_第23张图片

查看Erueka-Server的管理页面:

Spring Cloud_第24张图片

总结:
  • 凡是引Eureka客户端依赖 + 配置Eureka地址就是在做服务的注册! 
  • 无论是服务消费者还是服务提供者,只要引入eureka-client依赖、配置eureka地址后,都可以完成服务注册!

2.5 服务发现 /  服务拉取

  • 服务消费者要去注册中心订阅服务,这个过程就是服务发现或服务拉取。
  • 服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡。 

下面,我们将服务消费者的逻辑修改:向eureka-server注册中心拉取服务提供者的信息,实现服务发现或服务拉取。 

服务消费者完成服务拉取的步骤: 

1. 在服务消费者的pom文件中,引入eureka-client依赖

  • 之前说过,服务发现或服务拉取、服务注册统一都封装在eureka-client依赖,因此这一步与服务注册时一致。 

        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

2. 配置文件

  • 服务发现或服务拉取也需要知道Eureka地址,因此第二步与服务注册一样,都是配置Eureka信息,在服务消费者的application.yml文件中,添加服务名称以及eureka地址:
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: order-service #服务的名称
eureka:
  client:
    service-url:  #Eureka的地址信息(如果是集群,则地址之间用逗号隔开,现在是单机)
      defaultZone: http://127.0.0.1:10086/eureka

3. 服务的拉取和负载均衡

服务调用者去eureka-server注册中心中拉取服务提供者的实例列表,并且实现负载均衡,虽然服务提供者有多个实例,但真正发起调用时只需要知道一个实例的地址,因此服务调用者必须利用负载均衡算法,从多个实例中挑选一个去访问。

  • 负载均衡可以将流量分散到多个服务器上,避免了单一服务器的过载问题,同时可以提高系统的整体吞吐量和可用性。

常见的负载均衡算法有:

  • 随机负载均衡(Round):随机选择一台服务器来处理每个请求。
  • 轮询负载均衡算法(Round Robin):依次将请求分发给每台服务器,循环往复,适用于负载相对均衡的环境。
  • 权重轮询(Weighted Round Robin):为每个服务器分配不同的权重值,按照权重比例分发请求,高权重的服务器将接收到更多的请求,适用于服务器性能不均衡的情况。
  • IP的Hash散列算法(IP Hash):基于客户端的IP地址进行哈希运算,然后将请求发送给对应的服务器。
  • 最少连接算法(Least Connection):将请求分发给当前连接数最少的服务器。
  • 最短响应时间算法:将请求分发给响应时间最短的服务器。
  • 自适应负载均衡(Adaptive Load Balancing):基于实时监测服务器的负载和性能指标,动态调整请求的分发策略。

3.1 修改服务消费者的业务代码,修改访问的URL路径,用服务提供者的服务名称代替IP以及端口        从而实现远程调用: 

Spring Cloud_第25张图片

3.2 在服务消费者项目的启动类中,给RestTemplate这个Bean添加负载均衡注解 -                              @LoadBalanced注解: 

  • LoadBalanced:负载均衡 

Spring Cloud_第26张图片

重启服务消费者,清空服务提供者实例的控制台日志,看看服务消费者到底访问的是哪一个服务提供者的实例? 

  • 发现发起两次请求,有时候会远程调用同一个服务实例,有时候会两个服务实例都被远程调用! 

Spring Cloud_第27张图片

Spring会自动帮助我们从eureka-server端,根据服务名称来获取服务实例列表,而后完成负载均衡! 

  • 负载均衡的目的:避免请求堆积在同一个服务器上!

3. Ribbon负载均衡

在刚才,我们在RestTemplate的Bean上面添加了@LoadBalanced注解,即可实现负载均衡功能,这是什么原理呢?

3.1 负载均衡原理

Spring Cloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。

负载均衡流程

  • 我们发出的请求明明是http://userservice/user/1  ,它不是一个真实可用的地址,怎么变成了http://localhost:8081 的呢?

Spring Cloud_第28张图片Ribbon拦截我们的RestTemplate请求并获取到请求所指定的对应的服务名称(serviceId)之后,它要根据服务名称去找到Eureka-Server注册中心去进行服务拉取或服务发现,然后在对拉取到的服务列表(ServiceList)做负载均衡。

源码跟踪

为什么我们在代码当中定义的URL地址输入的是service服务名称,而不是真实的IP和端口,但却能远程调用,访问成功呢?

  • @LoadBalanced注解相当于是一个标记,标记RestTemplate发起的请求将来要被Ribbon去拦截和处理了,只不过拦截的动作是由LoadBalancerInterceptor去完成的。
  • 显然有人帮我们根据service服务名称,获取到了服务实例真实的IP和端口号它就是LoadBalancerInterceptor:这个类会对在RestTemplate的请求进行拦截,然后从Eureka-Server注册中心根据服务名称获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,从而替换服务名称。
我们进行源码跟踪:

ClientHttpRequestInterceptor:客户端HTTP请求的拦截器  

Spring Cloud_第29张图片

(1)LoadBalancerIntercepor - 负载均衡拦截器

Spring Cloud_第30张图片可以看到这里的interceprt()方法,拦截了用户或客户端的HttpRequest请求,然后做了几件事:

  • request.getURI():获取请求URI,本例中就是 http://user-service/user/8
  • originalUri.getHost():获取URI路径的主机名,说白了就是获取服务名称serviceName - ServiceId
  • this.loadBalancer.execute():根据服务名称去进行服务拉取,然后在对拉取到的服务列表 - ServiceList做负载均衡,负载均衡的规则:IRule rule

(2)LoadBalancerClient:客户端负载平衡器 

  • 这里的execute()是重写的LoadBalancerClient接口的方法:

Spring Cloud_第31张图片

RibbonLoadBalancerClient

  • RibbonLoadBalancerClient实现了LoadBalancerClient接口,重写了execute()方法! 

Spring Cloud_第32张图片

(3)负载均衡策略 - IRule 

继续跟入RibbonLoadBalancerClient对execute()方法的具体实现:

Spring Cloud_第33张图片

在刚才的代码中,可以看到获取服务是通过一个getServer()方法来做的负载均衡,因此继续跟入getServer()方法的详情,还是在RibbonLoadBalancerClient当中:

Spring Cloud_第34张图片

继续跟踪源码chooseServer()方法:

  • chooseServer()方法是定义在ILoadBalancer接口当中的一个抽象方法。

Spring Cloud_第35张图片我们来看它的重写或看它的实现方法,选择BaseLoadBalancer: 

Spring Cloud_第36张图片我们来看看这个rule是谁,继续跟踪源码:

Spring Cloud_第37张图片

点击看看IRule的源码,I应该指的就是Interface - 接口:

Spring Cloud_第38张图片 IRule接口决定了负载均衡的策略
按Ctrl + H,看看IRule接口的实现类:

Spring Cloud_第39张图片

3.2 负载均衡策略

Ribbon的负载均衡策略/规则是由一个叫做IRule的接口来定义的,IRule接口就定义了负载均衡的策略(IRule接口决定了负载均衡的策略),并且IRule接口有很多不同的实现类,它的每一个实现类都是一种规则:

Spring Cloud_第40张图片 IRule接口的继承关系图

IRule接口默认的实现或默认的负载均衡规则是ZoneAvoidancerRule,是一种轮询方案 - Round Robin!

不同规则的含义如下:
内置负载均衡规则类 规则描述
RoundRobinRule 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则
AvailabilityFilteringRule 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。
WeightedResponseTimeRule 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。
ZoneAvoidanceRule 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。
BestAvailableRule 忽略那些短路的服务器,并选择并发数较低的服务器。
RandomRule 随机选择一个可用的服务器。
RetryRule 重试机制的选择逻辑

注意:一般情况下使用默认的负载均衡策略或方案就可以了,不需要做修改。 

修改负载均衡策略或自定义负载均衡策略

  • 通过定义IRule实现可以修改或自定义负载均衡策略,自定义负载均衡有两种方式:

1. 代码方式:在服务(消费者)的启动类当中,定义一个新的IRule的Bean注入到Spring容器:

Spring Cloud_第41张图片 

2. 配置方式:在application.yml配置文件当中,来添加新的配置也可以修改负载均衡的规则:

# 给某个微服务配置负载均衡规则
serviceName:  #服务提供者的服务名称
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.IRule接口的具体实现类的全类名 # 负载均衡规则

 Eg:

Spring Cloud_第42张图片

两种负载均衡的配置方式的区别 - 作用范围不同:
  • 第一种代码方式是针对于全局或全体,可以做到全局配置,针对于服务调用者远程调用任意一个服务提供者都是这个策略;缺点:修改时需要重新打包发布;
  • 而第二种配置方式:缺点:配置文件修改只是针对于某一个具体的服务提供者而言的,无法做全局配置;优点是直观、方便,无需重新打包发布。 
注意:
  • 在Spring框架中,Bean的优先级会高于application.yml配置文件的优先级:因为当Spring容器初始化时,会首先创建Bean并进行依赖注入,然后再加载配置文件中的属性与值,因此,如果Bean中定义了某个属性,并且在配置文件当中也存在相同名称的属性,那么Bean重点额属性值会覆盖配置文件中额度值。 

3.3 懒加载 & 饥饿加载

Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient - 客户端负载平衡器,请求时间会很长。

  • 验证:第一次远程调用访问耗时550ms,而第二次远程调用访问耗时只有27ms! 

而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面的配置开启饥饿加载: 

ribbon:
  eager-load:
    enabled: true  #开启饥饿加载
    clients: serviceName #指定饥饿加载的服务名称,指定对该服务开启饥饿加载
ribbon:
  eager-load:
    enabled: true  #开启饥饿加载
    clients:  #如果有多个服务,则需要换行指定饥饿加载的服务名称
      - serviceName1 
      - serviceName2
      - serviceName3

懒加载 & 饥饿加载 

  • 懒加载和饥饿加载是两个相对的概念,它们在计算机领域中有着不同的含义!
懒加载(Lazy Loading)
  • 懒加载指的是在需要的时候才进行加载或初始化操作
  • 懒加载的优点是可以节省资源,在实际需要时再进行加载,避免不必要的性能开销;缺点是第一次访问时,请求时间会很长。

饥饿加载(Eager Loading)

  • 饥饿加载指的是系统在应用程序启动或对象创建时即进行加载或初始化所有需要的资源或对象。
  • 饥饿加载的优点是可以减少后续操作的等待时间;缺点是可能会占用较多的初始资源。

因此,懒加载和饥饿加载时两种加载策略!

补充:SpringMVC的容器  - dispactherServlet的初始化也是懒加载!

4.  Nacos注册中心

4.1 认识和安装Nacos

  • 国内一般都推崇阿里巴巴的技术,比如注册中心,Spring Cloud Alibaba也推出了一个名为Nacos的注册中心。
  • Nacos是阿里巴巴的产品,现在是Spring Cloud中的一个组件,相比Eureka功能更加丰富,在国内受欢迎程序较高。
  • Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • 注意:Eureka有两个版本,其中2.x版本是已经停止维护了,1.4.x版本并没有停止维护!

Nacos是基于Java语言去实现的,Nacos的默认端口是8848!

Spring Cloud_第43张图片

Nacos服务搭建:

  1. 下载安装包
  2. 解压
  3. 在bin目录下运行cmd窗口执行指令:startup.cmd -m standalone 

4.2 Nacos快速入门

服务注册到Nacos

Nacos是Spring Cloud Aliaba的组件,而Spring Cloud Alibaba也遵循Spring Cloud中定义的服务注册、服务发现规范,因此使用Nacos和使用Eureka对于微服务来说,并没有太大区别,主要差异在于:

  • 依赖不同
  • 服务地址不同

服务注册到Nacos的步骤

  • 引入依赖
  • 配置Nacos地址
  • 重启微服务

1. 引入依赖

  • 在父工程的pom文件中的中引入Spring Cloud Alibaba的管理依赖

    com.alibaba.cloud
    spring-cloud-alibaba-dependencies
    2.2.5.RELEASE
    pom
    import

Spring Cloud_第44张图片

  • 在服务的pom文件中添加或引入Nacos的客户端依赖包:nacos-discovery依赖 


    com.alibaba.cloud
    spring-cloud-starter-alibaba-nacos-discovery

Spring Cloud_第45张图片

  • 注意:不要忘了注释掉服务中原有的erueka的客户端依赖! 

2. 配置Nacos地址:spring.cloud.nacos.server-addr

在服务的application.yml配置文件当中添加Nacos地址:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848    # nacos服务端地址
  • 注意:不要忘了注释掉erueka的地址! 

Spring Cloud_第46张图片

3. 重启微服务,登录Nacos管理页面,可以看到微服务信息: 

Spring Cloud_第47张图片

点击详情,还可以查看到服务的详细信息! 

记得启动数据库,打开浏览器刷新: 

Spring Cloud_第48张图片

4.3 Nacos服务分级存储模型

一个服务可以包含多个实例,也就是一个服务可以多实例部署,但是如果将多个实例部署在同一个机房,则会存在单点故障问题,就好比把多个鸡蛋放在一个篮子里,为了解决单点故障问题,我们会将一个服务的多个实例部署到多个不同地域机房或服务器  => 也就是搞集群,这样就可以解决单点故障问题(容灾),而Nacos就是将同一机房或服务器内的多个实例划分为一个集群,也就是说,同一个服务可以包含多个集群(集群按地域划分),每个集群下可以有多个实例,形成服务分级存储模型,所以在Nacos的服务分级存储模型中,一级是服务 => 二级是集群 => 三级是实例:将同一个服务的多个实例分成多个集群,每个集群下又包含多个实例,集群就是一个地域的划分,如图:

Spring Cloud_第49张图片

服务跨集群调用问题:

微服务相互访问时的两种方式: 
  1. 本地局域网内访问(推荐)     
  2. 访问其它集群内的实例

由于本地局域网内的访问,它的距离比较短,所以速度比较快,延迟比较低,而跨集群的访问调用,延迟是非常高的!

因此微服务互相访问调用时,应该尽可能选择本地集群的服务,尽可能访问同一个集群的实例,当跟本地集群不可用或不可访问时,再去访问其它集群,例如:

Spring Cloud_第50张图片

为什么Nacos要引入服务分级存储模型呢,为什么要加地域划分?
  • 为了防止跨集群调用!

给服务配置集群属性:如何设置实例的集群属性

修改application.yml,添加集群配置:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos服务端地址
      discovery:
        cluster-name: HZ # 配置集群名称,也就是机房位置,例如:HZ代指杭州

Spring Cloud_第51张图片

添加完集群配置后,重启该服务即可! 

Spring Cloud_第52张图片

我们在Nacos控制台看到下面结果:

Spring Cloud_第53张图片也可以再次复制一个服务启动配置(即一个服务启动多个实例),添加属性:

Spring Cloud_第54张图片

添加属性(配置端口号以及实例的集群属性): 

-Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=SH 

Spring Cloud_第55张图片

然后启动该项目即可! 

同集群优先的负载均衡配置:NacosRule负载均衡策略

  • 刚才我们学习了如何配置集群属性,但是我们最终想要实现的是服务在进行远程调用时优先选择本地集群,由于IRule默认的ZoneAvoidanceRule采用的是轮询方案,并不能实现根据同集群优先来实现负载均衡,因此我们也需要给服务调用者也配置一个集群属性。
  • 在Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例。

1. 给服务调用者配置集群信息

  • 修改服务调用者的application.yml文件,添加集群配置:
spring:
  cloud:
    nacos:
      server-addr: localhost:8848  # Nacos服务端地址
      discovery:
        cluster-name: SH # 配置集群名称,也就是机房位置,例如:SH代指上海
2. 修改服务调用者的负载均衡规则
  • 修改服务调用者的application.yml文件,修改负载均衡规则,设置负载均衡的IRule为NacosRule,这个规则会寻找与自己同集群的服务实例列表,会优先选择本地集群,在本地集群内的多个服务当中,它再采用随机方式进行负载均衡来挑选实例;如果本地没有,它才会跨集群访问,而跨集群访问时它会报警告,来去提醒我们的运维人员:
user-service: # 给某个微服务配置负载均衡规则,这里是user-service服务
  ribbon:               # NacosRule的负载均衡规则优先会寻找与自己同集群的服务
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule 

接着重启服务调用者,去Nacos控制台看看: 

Spring Cloud_第56张图片发起远程调用,看一看会不会调用与自己同集群的服务:  

服务实例的权重配置:根据权重负载均衡

实际部署中会出现这样的场景:

  • 服务器设备之间的性能有差异,部分实例所在机器性能较好,而部分较差,我们当然希望性能好的机器来承担更多的用户请求,但默认情况下NacosRule的负载均衡规则是同集群内随机挑选,不会考虑机器的性能问题,因此,Nacos提供了权重配置来控制访问频率,通过修改服务实例的权重,可以控制访问频率,权重越大则访问频率越高!

在Nacos控制台,找到服务的实例列表,点击编辑,即可设置或修改服务实例的权重:

Spring Cloud_第57张图片在弹出的编辑窗口,即可修改服务实例的权重: 

Spring Cloud_第58张图片

注意:如果权重修改为0,则该实例永远不会被访问! 

4.4 Nacos环境隔离

   

你可能感兴趣的:(spring,cloud,spring,后端)