• SpringCloud微服务 【实用篇】| Eureka注册中心、Ribbon负载均衡


    目录

    一:Eureka注册中心

    1. Eureka原理

    2. 动手实践

    ①搭建EurekaServer

    ②服务注册

    ③服务发现 

    二:Ribbon负载均衡

    1. 负载均衡原理

    2. 负载均衡策略

    3. 懒加载


    一:Eureka注册中心

    前面已经分析了,无论是SpringCloud还是SpringCloudAlibaba,两者的注册中心都有Eureka,所以现在就来学习一下Eureka。

    Dubbo

    SpringCloud

    SpringCloudAlibaba

    注册中心

    zookeeper、Redis

    Eureka、Consul

    Nacos、Eureka

    服务远程调用

    Dubbo协议

    Feign(http协议)

    Dubbo、Feign

    配置中心

    SpringCloudConfig

    SpringCloudConfig、Nacos

    服务网关

    SpringCloudGateway、Zuul

    SpringCloudGateway、Zuul

    服务监控和保护

    dubbo-admin,功能弱

    Hystix

    Sentine

    1. Eureka原理

    服务调用出现的问题

    ①服务消费者该如何获取服务提供者的地址信息?--------》注册中心

    ②如果有多个服务提供者,消费者该如何选择?--------》负载均衡

    ③消费者如何得知服务提供者的健康状态?--------》心跳反应

    Eureka的原理

    在Eureka的结构当中,分为两个角色:

    第一个角色:eureka-server(注册中心)服务端,记录和管理微服务。

    第二个角色:user-service(服务提供者)和 order-service(服务消费者);不管是服务者还是消费者都是微服务,所以提供者和消费者统称为Eureka的客户端client

    ①user-service在启动的那一刻,会把自己的信息注册给eureka;注册中心会记录服务器名称、IP端口等信息。此时如果有人想消费,就不需要自己去记录信息,直接找eureka,拉取服务器信息;这实际上就解决了上述的第一个问题

    ②例如:此时拿到了3个记录信息,如何选呢?通过负载均衡的知识选出来一个,然后就可以远程调用发请求;这就解决了上述的第二个问题

    ③那选出来的这个会不会是挂的呢?不会,因为提供者服务每隔30秒都会向eureka发一次心跳,来确认自己的状态;如果检测到不跳了、挂了,eureka就会把它从注册中心的列表中剔除掉,消费者就可以拉取到最新的信息;这就解决了上述的第三个问题

    总结:在Eureka架构中,微服务角色有两类

    (1)EurekaServer:服务端,注册中心

    作用:记录服务信息,进行心跳监控。

    (2)EurekaClient:客户端

    Provider:服务提供者,例如上述的 user-service

    ①注册自己的信息到EurekaServer。

    ②每隔30秒向EurekaServer发送心跳。

    Consumer:服务消费者,例如上述的 order-service

    ①根据服务名称从EurekaServer拉取服务列表。

    ②基于服务列表做负载均衡,选中一个微服务后发起远程调用。

    2. 动手实践

    接下来就动手实践:总共分为三个部分:搭建注册中心服务注册服务发现

    ①搭建EurekaServer

    以下搭建信息是基于这篇博客的内容SpringCloud 【实用篇】| 服务拆分及远程调用http://t.csdnimg.cn/pj77E

    第一步:创建Maven的独立项目eureka-server引入spring-cloud-starter-netflix-eureka-server的依赖

    注:从这里也可以看出实际上Spring已经集成了Erueka,直接引入依赖使用即可!与我们后面学习的Nacos注册中心不同!

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <parent>
    5. <artifactId>cloud-demoartifactId>
    6. <groupId>cn.itcast.demogroupId>
    7. <version>1.0version>
    8. parent>
    9. <modelVersion>4.0.0modelVersion>
    10. <artifactId>eureka-serverartifactId>
    11. <properties>
    12. <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    13. <maven.compiler.source>1.8maven.compiler.source>
    14. <maven.compiler.target>1.8maven.compiler.target>
    15. properties>
    16. <dependencies>
    17. <dependency>
    18. <groupId>org.springframework.cloudgroupId>
    19. <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
    20. dependency>
    21. dependencies>
    22. project>

    第二步:编写启动类,添加@EnableEurekaServer注解,表示自动装配的开关

    1. package cn.itcast.eureka;
    2. import org.springframework.boot.SpringApplication;
    3. import org.springframework.boot.autoconfigure.SpringBootApplication;
    4. import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    5. @SpringBootApplication
    6. // 假如@EnableEurekaServer注解
    7. @EnableEurekaServer
    8. public class EurekaApplication {
    9. public static void main(String[] args) {
    10. SpringApplication.run(EurekaApplication.class,args);
    11. }
    12. }

    第三步:添加application.yml文件,配置信息

    注:为什么还要配置eureka的地址信息,自己配置自己?

    答:eureka自己也是一个微服务,在启动时,会将自己也注入到eureka上,为了以后集群之间通信去用,相互做注册进行通信。

    1. #配置端口号
    2. server:
    3. port: 10086
    4. #eureka的服务器名称
    5. spring:
    6. application:
    7. name: eureka-server
    8. #eureka的地址
    9. eureka:
    10. client:
    11. service-url:
    12. defaultZone: http://127.0.0.1:10086/eureka

    启动eureka,进行访问

    此时的注册列表就自己,把自己注册进去了!

    ②服务注册

    user-service服务order-service服务注册到EurekaServer(已user-service为例),步骤如下:

    第一步:在项目user-service引入spring-cloud-starter-netflix-eureka-client的依赖

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

    第二步:application.yml文件,编写下面的配置:

    1. spring:
    2. application:
    3. name: user-service #新加入的eureka服务器的名称
    4. eureka: #新加入的eureka地址
    5. client:
    6. service-url:
    7. defaultZone: http://127.0.0.1:10086/eureka

    启动order-service和user-service,此时的注册列表

    此时的注册列表就把order-service和user-service项目注册进去了!

    补充:我们可以将user-service多次启动, 模拟多实例部署,但为了避免端口冲突,需要修改端口设置:

    此时的eureka页面

    此时对于注册进去的user-service启动了两个实列!

    ③服务发现 

    在order-service完成服务拉取:服务拉取是基于服务名称获取服务列表,然后在对服务列表做负载均衡。

    第一步:修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口

    1. // 原来是硬编码的方式,写死了
    2. String url = "http://localhost:8081/user/"+order.getUserId();
    3. // 现在使用加入eureka注册中心的服务器名称代替IP和端口号
    4. String url = "http://user-service/user/"+order.getUserId();

    第二步:在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解@LoadBalanced

    1. package cn.itcast.order;
    2. import org.mybatis.spring.annotation.MapperScan;
    3. import org.springframework.boot.SpringApplication;
    4. import org.springframework.boot.autoconfigure.SpringBootApplication;
    5. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.web.client.RestTemplate;
    8. @MapperScan("cn.itcast.order.mapper")
    9. @SpringBootApplication
    10. public class OrderApplication {
    11. public static void main(String[] args) {
    12. SpringApplication.run(OrderApplication.class, args);
    13. }
    14. // 注册RestTemplate
    15. @Bean
    16. @LoadBalanced // 加入负载均衡的注解
    17. public RestTemplate restTemplate(){
    18. return new RestTemplate();
    19. }
    20. }

    此时重启order-service,连续进行两次访问,此时应该访问UserApplication--->8081还是UserApplication2--->8082呢?

    UserApplication日志信息显示:

    UserApplication2日志信息显示:

    发现两个是轮循方式访问,成功实现了负载均衡,至于负载均衡的原理下面会讲!

    总结:

    ①搭建EurekaServer

    引入eureka-server依赖;

    启动类上添加@EnableEurekaServer注解;

    application.yml中配置eureka端口号、地址、服务器名称

    ②服务注册

    引入eureka-client依赖;

    application.yml配置eureka地址、服务器名称

    ③服务发现

    用服务提供者的服务名称(代替端口号和IP)远程调用;

    给注入的RestTemplate添加@LoadBalanced注解

    二:Ribbon负载均衡

    前言:对于高版本的SpringCloud的Ribbon组件已经弃用了被Spring Cloud Loadbalancer替代!

    前面我们用EurekaClient(注册中心)实现了服务的拉取和负载均衡,我们只是指定了一个服务器的名称、加了@LoadBalanced注解,一切就自动完成了!那么是什么时候进行的服务拉取?什么时候进行的负载均衡?负载均衡的原理是什么?策略是什么?接下来就学习SpringCloud的第二个组件Ribbon

    1. 负载均衡原理

    order-service发起请求http://userservice/user/1,实际上这个请求并不是真实可用的地址,在浏览器是无法进行访问,是无法到达后面某个服务的;中间会被Ribbon把请求拦下来进行处理,去找到真实的地址;通过服务器名称userservice找eureka确定真实的地址(IP和Port),拉取服务,然后做负载均衡。

    源码分析 

    @LoadBalanced注解就相当于一个标记,标记RestTemplate发起的请要被Ribbon拦截

    1. // 注册RestTemplate
    2. @Bean
    3. @LoadBalanced // 假如负载均衡的注解
    4. public RestTemplate restTemplate(){
    5. return new RestTemplate();
    6. }

     这个拦截的动作是通过LoadBalancerInterceptor类去完成的

    这个类实现了ClientHttpRequestInterceptor接口(客户端Http请求的拦截器);会去拦截由客户端发起的Http请求;而RestTemplate就是发Http请求的客户端。

    1. package org.springframework.cloud.client.loadbalancer;
    2. import java.io.IOException;
    3. import java.net.URI;
    4. import org.springframework.http.HttpRequest;
    5. import org.springframework.http.client.ClientHttpRequestExecution;
    6. import org.springframework.http.client.ClientHttpRequestInterceptor;
    7. import org.springframework.http.client.ClientHttpResponse;
    8. import org.springframework.util.Assert;
    9. public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    10. private LoadBalancerClient loadBalancer;
    11. private LoadBalancerRequestFactory requestFactory;
    12. public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
    13. this.loadBalancer = loadBalancer;
    14. this.requestFactory = requestFactory;
    15. }
    16. public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
    17. this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    18. }
    19. // 重写的Intercept方法
    20. public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
    21. URI originalUri = request.getURI();
    22. String serviceName = originalUri.getHost();
    23. Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
    24. return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    25. }
    26. }

    在重写的intercept方法上打断点

    浏览器发送localhost:8080/order/101请求回到断点

    拿到地址后,调用getHost方法拿到主机名

    拿到服务器名称去找eureka完成服务的拉取;把服务器名称交给loadBalancer去执行,实际上loadBalancer对象就是一个RibbonLoadBalanceClient(负载均衡客户端)

    execute方法就是执行的意思,所以要跟进这个方法,会进入RibbonLoadBalanceClient对象的excute方法(一些版本的Jar包会进入接口LoadBalanceClient的excute方法,在其实现类多打一个断点即可)

    这个ILoadBalancer对象(实际上是DynamicServerListLoadBalance-动态服务列表负载均衡器)就可以拿到对应的服务(成功被拉取到服务列表)

    拉取到服务列表,接下来就是进行负载均衡! 

    此时getServer方法又调用了chooseServer方法,又去转到ZoneAwareLoadBalancer类调用父类的chooseServer方法

    最终调用到BaseLoadBalancer类的rule类的choose方法

    rule类是一个IRule接口,默认的负载均衡规则ZoneAvoidanceRule

    这个接口还有其它负载均衡规则的实现类

    负载均衡流程

    2. 负载均衡策略

    上面我们学写了负载均衡的原理,知道IRule接口决定了负载均衡的策略;接下来就分析IRule接口有哪些实现,以及将来如何修改每个实现!

    Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则:

     常见的负载均衡策略

    内置负载均衡规则类

    规则描述

    RoundRobinRule

    简单轮询服务列表来选择服务器。

    AvailabilityFilteringRule

    对以下两种服务器进行忽略:

     (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。

    (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。

    WeightedResponseTimeRule

    为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。

    ZoneAvoidanceRule

    以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。(先分区域,在区域内在轮循

    BestAvailableRule

    忽略那些短路的服务器,并选择并发数较低的服务器。

    RandomRule

    随机选择一个可用的服务器。

    RetryRule

    重试机制的选择逻辑

    通过定义IRule实现可以修改负载均衡规则,有两种方式:

    第一种方式:在OrderApplication启动类中注册新的Rule-------这是全局的配置

    1. package cn.itcast.order;
    2. import com.netflix.loadbalancer.IRule;
    3. import com.netflix.loadbalancer.RandomRule;
    4. import org.mybatis.spring.annotation.MapperScan;
    5. import org.springframework.boot.SpringApplication;
    6. import org.springframework.boot.autoconfigure.SpringBootApplication;
    7. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    8. import org.springframework.context.annotation.Bean;
    9. import org.springframework.web.client.RestTemplate;
    10. @MapperScan("cn.itcast.order.mapper")
    11. @SpringBootApplication
    12. public class OrderApplication {
    13. public static void main(String[] args) {
    14. SpringApplication.run(OrderApplication.class, args);
    15. }
    16. // 注册RestTemplate
    17. @Bean
    18. @LoadBalanced // 加入负载均衡的注解
    19. public RestTemplate restTemplate(){
    20. return new RestTemplate();
    21. }
    22. // 修改负载均衡的规则---随机
    23. @Bean
    24. public IRule randomRule(){
    25. return new RandomRule();
    26. }
    27. }

    第二种方式:在application.yml文件中修改配置-------针对某个服务进行配置

    1. user-service: # 给某个微服务配置负载均衡规则,微服务的名称
    2. ribbon:
    3. NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负载均衡规则

    3. 懒加载

    Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。

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

    1. ribbon:
    2. eager-load:
    3. enabled: true # 开启饥饿加载,clients是一个集合,后面可以配多个
    4. clients:
    5. - user-service # 指定对user-service这个服务饥饿加载

    Ribbon负载均衡总结

    ①Ribbon负载均衡规则:规则接口是IRule,默认实现是ZoneAvoidanceRule,根据区域选择服务列表,然后轮询。

    ②负载均衡自定义方式

    代码方式:配置灵活,但修改时需要重新打包发布;

    配置方式:直观,方便,无需重新打包发布,但是无法做全局配置;

    ③饥饿加载:开启饥饿加载,指定饥饿加载的微服务名称。

  • 相关阅读:
    第六篇Android--ImageView、Bitmap
    机器人开发--Universal Scene Description(USD)
    企业AI虚拟ip形象定制的应用场景
    Java核心技术面试题(附答案),纯手码,看完是否思路清晰了?
    vue项目中定位组件来源的查找思路
    PDO::fetchAll 与 PDO::fetch 循环
    定义一个Date类,创建两个对象存放两个日期,求两个日期的间隔(单位:天)
    Kong实现禁止国外IP访问
    分享5个良心好用的PC软件,免费无广告
    CentOS7.9 下修改MariaDB访问端口不能访问
  • 原文地址:https://blog.csdn.net/m0_61933976/article/details/133176990