Spring Cloud微服务架构学习-3

前言

客户端负载均衡和服务端负载均衡的区别:

客户端负载均衡:客户端➡服务端
(自己选择服务器实现)
服务端负载均衡:客户端➡负载均衡服务器➡服务端
(有一个管理分配服务器)

什么是负载均衡?

使用负载均衡的web架构

使用负载均衡的web架构

  负载均衡器会维护一个可用的服务清单,通过心跳检测来剔除清单中故障的服务端节点。当客户端借助网络发送请求到负载均衡器时,负载均衡器从维护的服务清单里面选择一个服务器,并将客户端请求转发到此服务器,从而提高系统的可用性和稳定性。

一、Ribbeon实现客户端负载均衡

1、什么是Ribbon

  Ribbon是Netflix开源的一款用于客户端负载均衡的软件工具,它在集群中为各个客户端的通信提供了支持,有助于控制HTTP和TCP客户端的行为,提供了很多负载均衡的算法,例如轮询,随机等, 同时也可以实现自定义的算法。
  在Spring Cloud 构建的微服务中,Ribbon作为服务消费者的负载均衡器,有两种使用方式,一种是与RestTemplate相结合,另一种是与Feign相结合。Feign已经默认集成了Ribbon。

用于生产的Ribbon的子模块
Ribbon包含很多子模块,但很多子模块没有用于生产环境,目前用于生产的Ribbon的子模块具体如下:
• ribbon-core:定义负载均衡接口、客户端接口、内置的负载均衡实现等API。
• ribbon-eureka :提供eureka客户端实现负载均衡的API。
• ribbon-httpclient:对Apache的HttpClient进行封装,该模块提供了含有负载均衡功能的REST客户端。

Ribbon整合Eureka
在Spring Cloud 中,当Ribbon和Eureka配合使用时,Ribbon可从Eureka Server中获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。 Ribbon整合Eureka的结构示例如下图所示。

在这里插入图片描述

  在上图中,搭建了一个Eureka服务器,三个服务提供者以及一个含有Ribbon的服务消费者。三个服务提供者向Eureka服务器注册服务,当多个URL向服务调用者发起请求时,基于Ribbon的负载均衡器能够有效地将请求分摊到不同的机器上。
  微服务学习-2中已经实现了Eureka 高可用,理论上使得微服务已经很完美了。但是,考虑到机器自身硬件条件的限制,面对流量高峰,系统同样还会存在宕机等情况。此时,如果使用Ribbon整合Eureka实现负载均衡,将用户请求分摊到多个服务上,能够大幅减轻访问服务压力,使系统达到更好的负载能力。下面我们在之前搭建的Eureka集群基础上进行改造。

2、第一个Ribbon实例

第一个Ribbon实例的架构图:
在这里插入图片描述

2.1、改造服务者

改造微服务学习2中服务提供者Eureka-provider和eureka-provider-another
(1)在两个服务者下各自创建一个Controller包
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)在各自的Controller包下创建一个PortController类
在这里插入图片描述
在这里插入图片描述
(3)在两个PortController下都填写如下代码
(该类能够返回当前项目的端口号。)
在这里插入图片描述

在这里插入图片描述

2.2、搭建含有Ribbon的服务消费者
2.2.1. 创建项目,引入依赖

(1)在父级目录下创建Ribbon服务消费者模块
在这里插入图片描述
在这里插入图片描述

(2)在pom中添加ribbon依赖如下:

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
   </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
</dependencies>
2.2.2、在application.yml文件进行相关配置

(1)在resources目录下添加application.yml文件
在这里插入图片描述

(2)application.yml下填写如下配置:
在这里插入图片描述

2.2.3、添加@EnableEurekaClient注解

(1)新建一个RibbonClient包
在这里插入图片描述

(2)添加RibbonClientApplication启动类
在这里插入图片描述
在 项 目 启 动 类 RibbonClientApplication 上 添 加@EnableEurekaClient注解开启Eureka Client功能。
在这里插入图片描述

2.2.4、创建配置类

(1)创建一个Config包
在这里插入图片描述
在这里插入图片描述

(2)在config包下创建RibbonConfig类
在这里插入图片描述
在这里插入图片描述

(3)在RibbonConfig类注入restTemplate的Bean,并在这个Bean中加上@LoadBalanced注解
在这里插入图片描述
@Bean注解的使用和详解
RestTemplate简洁使用指南

2.2.5、创建Service类

(1)新建Service包
在这里插入图片描述

(2)在Service下创建一个RibbonService类
在这里插入图片描述
在这里插入图片描述

(3)在RibbonService类中添加代码
(在该类的hi()方法中使用restTemplate调用eureka-provider的API接口)
在这里插入图片描述

2.2.6、创建Controller类

(1)新建Controller包
在这里插入图片描述

(2)在Controller包下创建一个RibbonController类
在这里插入图片描述
在这里插入图片描述

(3)在RibbonController类中添加如下代码
(在该类上添加@RsetController注解,将RibbonController 标注为一个Controller类。在类中写一个hi ()方法,调用RibbonService的hi()方法。)
在这里插入图片描述

2.2.7、测试运行

依次启动 eureka-server, eureka-server-another, eureka-provider, eureka-provider-another,ribbon-client,所有服务启动成功后,在浏览器输入地址栏输入127.0.0.1:8764/hi一直刷新,每次刷新显示的端口号一直在变说明负载均衡成功。
在这里插入图片描述
在这里插入图片描述

3、Ribbon的工作原理

  前面我们使用Ribbon实现负载均衡时,基本用法是注入一个RestTemplate, 并使用@LoadBalanced注解标注RestTemplate,从而使RestTemplate具备负载均衡的能力。 当Spring容器启动时,使用@LoadBalanced注解修饰的RestTemplate会被添加拦截器,拦截器中使用了LoadBalancerClient处理请求,从而达到负载均衡的目的。那么LoadBalancerClient内部是如何做到的呢?接下来我们通过源码分析的方式来剖析Ribbon负载均衡的工作原理。

LoadBalancerClient是Spring Cloud提供的一个非常重要的接口,它继承自ServiceInstanceChooser接口,该接口的实现类是RibbonLoadBalanceClient,它们之间的关系如下图所示。
在这里插入图片描述

3.1、ServiceInstanceChooser源码解析

为了大家更好地理解LoadBalancerClient接口及其实现类的实现细节,我们先查看LoadBalancerClient的部分源码,具体如下:

public interface LoadBalancerClient extends ServiceInstanceChooser {
<T> T execute(String serviceId, LoadBalancerRequest<T> request) 
throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, 
LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}

  上述源码中,LoadBalancerClient提供的两个execute()方法用于执行请求,reconstructURI()方法用于重构URL。
  继续查看LoadBalancerClient继承的ServiceInstanceChooser接口源码,具
体如下:

public interface ServiceInstanceChooser {
ServiceInstance choose(String serviceId);
}

上述源码中,ServiceInstanceChooser接口定义一个choose()方法,该方法用于根据serviceId选择一个服务实例,即通过服务名选择服务实例。

3.2、介绍ILoadBalancer接口

RibbonLoadBalanceClient是LoadBalancerClient的实现类,它用来执行最终的负载均衡请求。其中,RibbonLoadBalanceClient的一个choose()方法用于选择具体的服务实例,其内部是通过getServer()方法交给ILoadBalancer完成的。
ILoadBalancer是一个接口,该接口定义了一系列实现负载均衡的方法。ILoadBalancer接口的实现类结果如下图所示。
在这里插入图片描述

3.3、BaseLoadBalancer和DynamicServerListLoadBalancer实现的配置

  查看BaseLoadBalancer和DynamicServerListLoadBalancer源码,默认情况下实现了以下配置:
(1)IClientConfig clientConfig:用于配置负载均衡客户端,默认实现类是DefaultClientConfigImpl。
(2)IRule rule:用于配置负载均衡的策略,默认使用的是RoundRobinRule策略,也就是轮询策略。
(3)IPing ping:用于检查当前服务是否有响应,从而判断当前服务是否可用,默实现类是DummyPing,该实现类的isAlive()方法返回值是true,默认所有服务实例都是可用的。
(4)ServerList serverList: 用于获取所有Server注册列表信息。通过跟踪源码会发现,ServerList的实现类是DiscoveryEnabledNIWSServerList,该类定义的obtainServersViaDiscovery()方法是根据eurekaClientProvider.get()方法获取EurekaClient,再根据EurekaClient获取服务注册列表信息。EurekaClient的实现类是DiscoveryClient,DiscoveryClient具有服务注册、获取服务注册列表等功能。
(5)ServerListFilter filter:定义了根据配置过滤或者动态获取符合条件的服务列表,默认实现类是ZonePreferenceServerListFilter,该策略能够优先过滤出与请求调用方处于同区域的服务实例。
  综上所述,使用RibbonLoadBalanceClient实现负载均衡时,会从EurekaClient获取服务列表信息,然后根据IPing判断服务是否可用。如果服务可用,则会根据IRule选择负载均衡策略,否则会重新获取服务清单。

4、 Ribbon负载均衡策略

4.1、介绍IRule接口

  默认情况下,Ribbon使用的负载均衡策略是轮询,实际上,Ribbon提供了很多负载均衡算法,其中IRule接口就是所有负载均衡算法的父接口,它的实现类结构如下图所示。

在这里插入图片描述

4.2、介绍AbstractLoadBalancerRule抽象类

AbstractLoadBalancerRule是负载均衡策略的抽象类,该抽象类中定义了负载均衡器ILoaderBalancer对象,该对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息作为分配依据,并以此设计一些算法来实现针对特定场景的高效策略。

在这里插入图片描述

4.3、介绍实现负载均衡算法的实现类

接下来,对实现负载均衡算法的实现类进行介绍,具体如下:
RoundRobinRule:实现了按照线性轮询的方式依次选择服务的功能。
WeightedResponseTimeRule: 它是对RoundRobinRule的扩展,会根据平均响应时间计算所有服务的权重,响应时间越快,服务权重越大,被选中的概率越高。
ZoneAvoidanceRule:它是PredicateBasedRule的具体实现类,其内部通过使用ZoneAvoidancePredicate和AvailabilityPredicate判断是否选择某一个服务,前者用于判断服务所在区域的性能是否可用,后者用于过滤掉连接数过多的服务。
AvailabilityFilteringRule: 使用AvailabilityPredicate过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数超过阀值的服务,然后对剩余的服务列表进行轮询。
  BestAvailableRule:用于先过滤掉多次访问故障而处于断路跳闸状态的服务,然后选择一个并发量最小的服务。
  RandomRule:该策略实现了从服务清单中随机选择一个服务的功能。
在这里插入图片描述
ClientConfigEnableRoundRobinRule:该类是一个抽象类,该类本身没有实现什么特殊的处理逻辑,我们也不会直接使用该策略,但是通过BestAvailableRule和继承该策略默认实现了线性轮询,它的内部定义了一个RoundRobinRule策略,
PredicateBasedRule:继承了ClientConfigEnableRoundRobinRule,其内部会先通过chooseRoundRobinAfterFiltering()方法筛选服务清单,然后以线性轮询的方式从过滤后的服务清单中选择一个服务。
在这里插入图片描述

二、Feign实现客户端负载均衡

1、Feign简述

  Feign是Netflix开发的声明式、模板化的HTTP客户端。当Feign与Eureka和Ribbon组合使用时,Feign就具有了负载均衡的功能。在Feign的实现下,我们只需要定义一个接口并使用注解方式配置,即可完成服务接口的绑定,从而简化了Ribbon自动封装服务调用客户端的开发工作量。如此看来,我们可以把Feign理解为一个Spring Cloud远程服务的框架或者工具,它能够帮助开发者用更少的代码,更好的兼容方式对远程服务进行调用。

2、第一个Feign程序

2.1、创建Feign客户端

1.在父级目录下创建Feign服务消费者模块
在这里插入图片描述

2.在pom中添加ribbon依赖如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3.在resources目录下添加application.yml文件
在这里插入图片描述

4.application.yml下填写如下配置:
在这里插入图片描述

5.新建一个FeignClient包
在这里插入图片描述

6.添加FeignClientApplication启动类
在这里插入图片描述

7.在启动类里添加如下代码:
在启动类EurekaFeignClientApplication中添加@EnableEurekaClient注解开启Eureka Client功能,再添加@EnableFeignClients注解开启Feign Client功能。
在这里插入图片描述

8.创建一个Service包
在这里插入图片描述

9.在Service包下创建FeignService接口类
在这里插入图片描述

10.在接口类中添加代码如下
(通过添加@FeignClient注解指定要调用的服务。)
在这里插入图片描述

11.新建Controller包
在这里插入图片描述

12在Controller包下创建一个FeignController类
在这里插入图片描述

13.在FeignController类中添加代码
(该类定义的hi()方法用于调用FeignService的sayHello()方法。)
在这里插入图片描述

2.2、测试运行

依次启动eureka-server,eureka-server-another,eureka-provider,eureka-provider-another,feign-client,所有服务启动成功后,在浏览器输入地址栏输入127.0.0.1:8765/hi,一直刷新,每次刷新显示的端口号一直在变说明feign启动并负载均衡成功。

在这里插入图片描述
在这里插入图片描述

3、Feign配置

  Feign从全局配置和指定服务配置两个方面对服务调用进行相关设置。

3.1、全局配置

  全局配置其实非常简单,直接在application.yml配置文件中,示例代码如下:

feign:
client:
config:
default:
connectTimeout: 1000 #设置连接超时,单位毫秒
readTimeout: 6500 #设置调用超时,单位毫秒

  为了测试设置的ConnectTimeout参数是否生效,在服务提供者eurekaprovider的getPort ()方法中添加下列代码:

try{
Thread.sleep(6000);
}catch(Exception e){
}

  由于ConnectTimeout参数设置的超时时间是5秒,上述代码设置的程序等待时间是6秒,这样必然会导致出现超时。此时使用浏览器访问http://localhost:8765/hi,发现消费者lesson04-feign-client的控制台会报java.net.SocketTimeoutException: Read timed out。

3.2、指定服务配置

  大多数的情况下,我们对于服务调用超时时间可能会根据实际服务的特性做一些调整,所以仅仅依靠默认的全局配置是不行的。使用Spring Cloud Feign时,如果要对各个服务消费者进行不同配置,可以指定服务进行设置,示例代码如下:

feign:
client:
config:
eureka-provider:
connectTimeout: 1000 #设置连接超时,单位毫秒
readTimeout: 5000 #设置调用超时,单位毫秒
3.3、其他配置

  Spring Cloud Feign支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗。只需要通过以下参数设置,就可以开启请求与响应的压缩功能,代码如下:

feign:
compression:
request:
enabled: true
response:
enabled: true

  还能对请求压缩做一些细致的设置,例如,下面的配置内容指定了压缩请求支持的MIME TYPE类型,并设置了请求压缩的大小下限,只有超过这个大小的请求才会对其进行压缩。代码如下

feign:
compression:
request:
mime-types:
text/xml,application/xml,application/json
min-request-size: 2048

  上述配置的参数中,feign.compression.request.mime-types和feign.compressionfeign.compression.request.min-request-size设置的均为默认值.

4、Feign的工作原理

Feign服务调用的工作原理可以分为以下几个步骤:
(1)首先通过@EnableFeignClients注解开启FeignClient功能。程序启动时,会通过该注解开启对@FeignClient注解的包扫描。
(2)根据Feign规则实现接口,并在接口上面添加@FeignClient注解。
(3)程序启动后,会进行包扫描,扫描所有的@FeignClient注解类并将这些信息注入IoC容器。
(4)当接口方法被调用时,通过JDK的代理生成具体的RequestTemplate模板对象。根据RequestTemplate再生成HTTP请求的Request对象,Request对象交给Client处理。
在这里插入图片描述
总结:
Feign调用工作原理:
@EanbleFeignClient开启FeignClient;
Feign接口用@FeignClient注入IoC容器;
通过JDK代理生成具体的HTTP服务调用对象。
扩展:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值