详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署

‍作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
上期文章:详解SpringCloud微服务技术栈:认识微服务、服务拆分与远程调用
订阅专栏:微服务技术全家桶
希望文章对你们有所帮助

Eureka

  • 提供者与消费者
  • Eureka原理分析
  • 搭建Eureka服务
    • 搭建注册中心
    • 服务注册
    • 服务发现
  • Ribbon
    • 负载均衡原理
    • 源码跟踪原理(强推!)
      • 流程总结
    • 负载均衡策略
    • 饥饿加载

提供者与消费者

服务提供者:一次业务中,被其它微服务调用的服务(提供接口给其他微服务)
服务消费者:一次业务中,调用其它微服务的服务(调用其它微服务提供的接口)

若服务A调用服务B,服务B调用服务C,那么服务B是什么角色?

提供者与消费者的概念是相对的,一个服务既可以是提供者也可以是消费者

Eureka原理分析

上一节内容中服务调用存在问题,服务调用使用http请求,网址直接定死了,如果我们有多个服务集群,亦或是网址在后续开发过程中出现变更,就会产生不方便。如下所示:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第1张图片

我们需要解决以下三个问题:

服务消费者该如何获取服务提供者的地址信息?
如果有多个服务提供者,消费者该如何选择?
消费者如何得知服务提供者的健康状态?

而Eureka可以解决这个问题,Eureka的架构:

1、eureka-server:注册中心
2、eureka-client:
(1)服务消费者(集群)
(2)服务提供者(集群)

Eureka的作用及工作流程:

1、每个服务启动的时候,都会将注册服务信息记录在注册中心,例如端口号
2、服务信息都记住了,当服务消费者需要信息的时候,无须自己去记录,而是直接去Eureka-server中拉取,这样就可以得到服务提供者的信息
3、得到的服务提供者信息可能是个集群,包含多台服务的信息,这时候要做负载均衡去选取其中一个服务
4、消费者对提供者发起远程调用

这是核心的工作流程,另外,服务的提供者每隔30s就会向Eureka-server发送心跳续约,如果服务宕机了,那么Eureka-server就会将其剔除,这样能够保证服务消费者做远程调用的时候,能调用服务提供者都是健康的。

搭建Eureka服务

要首先Eureka服务,需要实现3点:

1、搭建注册中心(EurekaServer)
2、实现服务注册(将上一篇文章中的user-service、order-service注册到eureka)
3、实现服务发现(在order-service中完成服务拉取,然后通过负载均衡挑选一个服务,实现远程调用)

搭建注册中心

搭建EurekaServer的步骤如下:
1、创建项目,引入EurekaServer依赖:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第2张图片

	<dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
        dependency>
    dependencies>

2、编写启动类,添加@EnableEurekaServer注解:

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

3、添加application.yml文件,编写下列配置:

server:
  port: 10086 # 服务端口
spring:
  application:
    name: eurekaserver # 服务名称
eureka:
  client:
    service-url: # Eureka的地址信息
      defaultZone: http://localhost:10086/eureka

Eureka自己也是微服务,所以配置Eureka也需要将Eureka本身给注册。

接着就可以启动Eureka服务,访问端口可以看到其界面信息:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第3张图片
可以发现注册到Eureka的实例,现在只有它本身。

服务注册

将user-service注册到EurekaServer步骤如下:
1、将user-service项目引入spring-cloud-starter-netflix-eureka-client的依赖:

	
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>

2、在application.yml文件,编写下面的配置:

spring:
  application:
    name: userservice # 服务名称
eureka:
  client:
    service-url: # Eureka的地址信息
      defaultZone: http://localhost:10086/eureka

同样的方式也可以将order-service注册。
刷新Eureka-server端网址:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第4张图片

如果你出现了java.lang.NoClassDefFoundError的问题,那可能是你的依赖导入错误了,我就不小心把依赖导入成下面这个:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第5张图片
记得要加上starter,才能被SpringBoot当成启动类。

另外,可以将user-service多次启动,模拟多实例部署,单位了避免端口冲突,需要修改端口设置:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第6张图片
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第7张图片
将增加的端口也运行一下,工程看到注册了2个实例的实例列表:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第8张图片
服务注册总体来说分为两步:
1、引入eureka-client依赖
2、在application.yml中配置eureka地址

无论消费者还是提供者,引入eureka-client依赖,知道了eureka地址后,都可以完成服务注册。

服务发现

服务拉取是基于服务名称获取服务列表,然后对服务列表做负载均衡。
1、修改OrderService代码,修改访问的url路径,用服务名来代替ip、端口:

String url = "http://userservice/user/" + order.getUserId();

2、在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:

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

重启OrderApplication服务,按顺序访问:
http://localhost:8080/order/101
http://localhost:8080/order/102

可以看到UserApplication与UserApplication2各做了一次数据库查询操作:
在这里插入图片描述
在这里插入图片描述
所以,服务的拉取以及负载均衡已经完成。

服务发现的实现流程总结:
1、引入eureka-client依赖
2、在application.yml中配置eureka地址
3、给RestTemplate添加@LoadBalanced注解
4、给用户提供者的服务名称远程调用

Ribbon

在上面已经实现了服务的拉取,并且验证了负载均衡。那么其原理是怎样的,什么时候做的服务拉取,又是什么时候做的负载均衡,咱们都还不知道。
而其中,负载均衡是由Ribbon组件实现的。

负载均衡原理

首先思考,url已经做了修改:

String url = "http://userservice/user/" + order.getUserId();

然而访问一下http://userservice/user/1,并不能访问到,说明它并不是真实可用的地址。因此,中间肯定有组件拦截了这个请求并且做了处理,这个组件就叫做Ribbon。
整个负载均衡的流程如下:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第9张图片
而具体到底是什么时候接受这个请求的,又是怎么去做后序工作的,需要跟踪源码来做分析。

源码跟踪原理(强推!)

1、找到LoadBalancerInterceptor类,发现它实现了ClientHttpRequestInterceptor接口:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第10张图片
2、进入这个接口,查看注释:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第11张图片

其说明的大致意思是,当客户端有http请求发出的时候,立马就会被这个接口给拦截,那大概就可以知道,当实现类调用这个接口的实现方法的时候,应该要能够实现拦截

3、再回到其实现类,在实现类中打上断点,并debug模式重启OrderApplication:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第12张图片

4、在浏览器中发起请求:http://localhost:8080/order/101,可以发现请求确实成功被拦截了
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第13张图片
5、跟踪下去,可以看到它解析出了我们要请求的提供者服务端是userservice:
在这里插入图片描述
在这里插入图片描述
6、解析完了就要开始进行拉取了,在这里我们可以看到Ribbon的词眼:
在这里插入图片描述
这是一个RibbonLoadBalancerClient对象,意为Ribbon负载均衡的客户端。
7、跟入execute方法,查看其执行的底层:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第14张图片
在得到动态服务列表均衡器(DynamicServerListLoadBalancer)后,可以看到这里已经成功拉取到了服务。而getServer方法就肯定是在做负载均衡了。
8、点击进入getServer方法,可以发现这里就是在做服务器的选择了:
在这里插入图片描述
9、跟进,可以看到它在实现父类的方法,可以猜想到,服务器的选择是基于某种规则的:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第15张图片
10、找到rule的对象,这是一个接口,按住Ctrl+H找到其实现类:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第16张图片
RoundRobinRule指代轮询负载均衡,RandomRule指代随机负载均衡,这些其实在nginx里面都是接触过的,到这里为止,其实现的机理就不在这里继续跟踪了,想知道的话直接学nginx就可以了。

流程总结

1、order-service(消费者)发起请求,负载均衡拦截器LoadBalancerInterceptorRibbonLoadBalancerClient会接收请求的url,获取url中的服务id
2、RibbonLoadBalancerClient把服务id交给DynamicServerListLoadBalancer
3、DynamicServerListLoadBalancer会去eureka-server中拉取userservice,并返回服务列表
4、DynamicServerListLoadBalancer需要在服务列表中选一个,这个选择即为负载均衡,交给了一个交IRule的接口对象。
5、IRule会选择一个方法在列表中选择服务,把值返回给RibbonLoadBalancerClient
6、RibbonLoadBalancerClient将会修改url,使得其实正确的真实地址,即可在前端成功访问了。

负载均衡策略

Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每个子接口都是一种规则:
详解SpringCloud微服务技术栈:强推!源码跟踪分析Ribbon负载均衡原理、Eureka服务部署_第17张图片
其中,ZoneAvoidanceRule是默认方式,其父类的父类是基于轮询的,因此也可以推测ZoneAvoidanceRule是基于轮询的。
ZoneAvoidanceRule:以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。(Zone可以配置,没配置就默认所有服务都在一个Zone内)。

通过定义IRule可以修改负载均衡的规则,有2种方式:
1、代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:

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

2、配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:

userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

测试的话大家自己多访问几次网址,看控制台就可以知道是不是随机负载均衡了。

饥饿加载

这部分看不懂就去回顾一下设计模式吧。
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients: # 指定饥饿加载的服务名称
      - userservice

总结:

1、Ribbon负载均衡规则
(1)规则接口是IRule
(2)默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
2、负载均衡自定义方式
(1)代码方式:配置灵活,但修改时需要重新打包发布
(2)配置方式:直观,方便,无需重新打包发布,但是无法做全局配置
3、饥饿加载
(1)开启饥饿加载
(2)指定饥饿加载的微服务名称

你可能感兴趣的:(微服务技术全家桶,spring,cloud,微服务,ribbon,负载均衡,eureka,java,spring,boot)