Ribbon

Spring Cloud Ribbon

1、基于 Netflix Ribbon 实现的一套客户端负载均衡的工具

2、Ribbon 是 Netflix 发布的开源项目,提供客户端的软件负载均衡算法和服务调用

3、Ribbon 客户端组件提供一系列完善的配置项,如:连接超时,重试等

 

对比

1、Ribbon

(1)本地客户端负载均衡

(2)在调用微服务接口时,会在注册中心上获取注册信息服务列表,再缓存到 JVM 本地,从而在本地实现 RPC 远程服务调用技术

2、Nginx

(1)服务器负载均衡

(2)客户端所有请求都会交给 Nginx,然后由 Nginx 实现转发请求

 

负载均衡

1、将用户的请求平摊的分配到多个服务上,从而达到系统的高可用

2、集中式负载均衡

(1)在服务的消费方和提供方之间,使用独立的负载均衡设施,硬件,如:F5,软件,如:Nginx

(2)由该设施负责把访问请求,通过某种策略转发至服务的提供方

3、进程内负载均衡

(1)将负载均衡逻辑集成到消费方,消费方从服务注册中心获取可用地址,根据算法从中选择一个合适服务器

(2)Ribbon 属于进程内负载均衡,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址

 

Ribbon 工作流程

1、选择注册中心,优先选择在同一个区域内负载较少的 Server

2、根据用户指定策略,从 Server 获取的服务注册列表中,选择一个地址

 

依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

 

RestTemplate

1、执行 HTTP 请求的同步客户端,在底层 HTTP 客户端库(如:JDK HttpURLConnection、Apache HttpComponents 等)上暴露了一个简单的、模板方法 API

2、RestTemplate 通过 HTTP 方法为常见的场景提供模板,此外还有支持不太常见的情况的通用交换和执行方法

3、RestTemplate 通常作为一个共享组件使用,然而,它的配置不支持并发修改,因此它的配置通常在启动时准备,如果需要,可以在启动时创建多个不同配置的 RestTemplate 实例,如果这些实例需要共享 HTTP 客户端资源,它们可以使用相同的底层 ClientHttpRequestFactory

4、注意:从 5.0 开始,这个类处于维护模式,今后只接受小的修改和错误的请求。请考虑使用 org.springframework.web.reactive.client.WebClient,它有更现代的 API,并支持同步、异步和流式场景

 

getForObject

1、返回对象为响应体中数据转化成的对象,可以理解为 JSON

2、通过对指定的 URL 进行 GET 操作,获取响应体,响应(如果有的话)被转换并返回

(1)url:URL

(2)responseType:返回值的类型

@Nullable
public <T> T getForObject(URI url,
                          Class<T> responseType)
    throws RestClientException

(3)URI 模板变量使用给定的 URI 变量进行扩展,如果有的话

@Nullable
public <T> T getForObject(String url,
                          Class<T> responseType,
                          Object... uriVariables)
    throws RestClientException

(4)URI 模板的变量会使用给定的 Map 进行扩展

@Nullable
public <T> T getForObject(String url,
                          Class<T> responseType,
                          Map<String,?> uriVariables)
    throws RestClientException

 

getForEntity

1、返回对象为 ResponseEntity 对象,包含响应中的一些重要信息,比如响应头、响应状态码、响应体等

2、通过对 URI 模板进行 GET 操作,响应被转换并存储在一个 ResponseEntity 中

(1)url:URL

(2)responseType:返回值的类型

public <T> ResponseEntity<T> getForEntity(URI url,
                                          Class<T> responseType)
    throws RestClientException

(3)URI 模板变量会使用给定的 URI 变量进行扩展,如果有的话

public <T> ResponseEntity<T> getForEntity(String url,
                                          Class<T> responseType,
                                          Object... uriVariables)
    throws RestClientException

(4)URI 模板的变量会使用给定的 Map 进行扩展

public <T> ResponseEntity<T> getForEntity(String url,
                                          Class<T> responseType,
                                          Map<String,?> uriVariables)
    throws RestClientException

 

ResponseEntity

1、HttpEntity 的扩展,添加一个 HttpStatus 状态代码,在 RestTemplate 以及 @Controller 方法中使用

2、返回响应的 HTTP 状态代码,HTTP状态作为一个 HttpStatus 枚举类

public HttpStatus getStatusCode()

3、返回响应的 HTTP 状态代码,将 HTTP 状态作为一个 int 值

public int getStatusCodeValue()

4、返回响应体

@Nullable
public T getBody()

5、返回头信息

public HttpHeaders getHeaders()

 

postForObject

1、通过将给定的对象 POST 到 URL 来创建一个新的资源,并返回在响应中发现的响应体

2、请求参数可以是一个 HttpEntity,以便在请求中添加额外的 HTTP 头信息

3、请求体,或请求本身,可以是一个 MultiValueMap,以创建一个多部分请求,MultiValueMap 中的值可以是任何代表该部分主体的对象,或者是一个代表具有主体和头信息的部分的 HttpEntity

4、不扩展 URI

@Nullable
public <T> T postForObject(URI url,
                           @Nullable
                           Object request,
                           Class<T> responseType)
    throws RestClientException

(1)url:URL

(2)request:将被 POST 的对象(可能为 null)

(3)responseType:返回值的类型

5、URI 模板的变量使用给定的 Map 进行扩展

@Nullable
public <T> T postForObject(String url,
                           @Nullable
                           Object request,
                           Class<T> responseType,
                           Map<String,?> uriVariables)
    throws RestClientException

6、如果有的话,URI 模板变量会使用给定的 URI 变量进行扩展

@Nullable
public <T> T postForObject(String url,
                           @Nullable
                           Object request,
                           Class<T> responseType,
                           Object... uriVariables)
    throws RestClientException

 

postForEntity

1、通过将给定的对象 POST 到 URL 来创建一个新资源,并将响应作为 ResponseEntity 返回

2、请求参数可以是一个 HttpEntity,以便在请求中添加额外的 HTTP 头信息

3、请求体,或请求本身,可以是一个 MultiValueMap,以创建一个多部分请求,MultiValueMap 中的值可以是任何代表该部分主体的对象,或者是一个代表具有主体和头信息的部分的 HttpEntity

4、不扩展 URI

public <T> ResponseEntity<T> postForEntity(URI url,
                                           @Nullable
                                           Object request,
                                           Class<T> responseType)
    throws RestClientException

(1)url:URL

(2)request:将被 POST 的对象(可能为 null)

(3)responseType:返回值的类型

5、URI 模板变量会使用给定的 URI 变量进行扩展,如果有的话

public <T> ResponseEntity<T> postForEntity(String url,
                                           @Nullable
                                           Object request,
                                           Class<T> responseType,
                                           Object... uriVariables)
    throws RestClientException

6、URI 模板变量使用给定的 Map 进行扩展

public <T> ResponseEntity<T> postForEntity(String url,
                                           @Nullable
                                           Object request,
                                           Class<T> responseType,
                                           Map<String,?> uriVariables)
    throws RestClientException

 

核心组件:IRule

1、根据特定算法,从服务列表中选择要访问的服务

2、实现类

(1)RoundRobinRule:轮询

(2)RandomRule:随机

(3)RetryRule:先按照 RoundRobinRule 策略获取服务,若获取失败,则在指定时间内进行重试,再获取可用服务

(4)WeightedResponseTimeRule:响应速度越快,实例选择权重越大,越容易被选择

(5)BestAvailableRule:先过滤由于多次访问故障,而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

(6)AvailabilityFilteringRule:先过滤故障实例,再选择并发量较小的实例

(7)ZoneAvoidanceRule:默认规则,根据注册中心所在区域的性能、可用性,选择服务器

 

定制 Ribbon 客户端

1、Spring Cloud 允许通过使用 @RibbonClientConfiguration 声明额外的配置(在 RibbonClientConfiguration 之上)来完全控制客户端

2、例

@Configuration
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {
}

(1)在这种情况下,客户端由 RibbonClientConfiguration 中的组件,以及 CustomConfiguration 中的任何组件组成(后者通常覆盖前者)

(2)CustomConfiguration class 必须是一个 @Configuration 类,但要注意它不在主应用程序上下文的 @ComponentScan 中。否则,它将被所有的 @RibbonClient 共享,达不到特殊化定制的目的

(3)如果使用 @ComponentScan / @SpringBootApplication,需要采取措施避免它被包含在内,例如:可以把它放在一个单独的、不重叠的包中,或者在 @ComponentScan 中明确指定要扫描的包

 

轮询算法

1、原理

(1)Rest 接口第 n 次请求 % 服务器集群总数量 = 实际调用服务器位置下标

(2)每次服务重启动后,Rest 接口计数从 1 开始

2、源码

public class RoundRobinRule extends AbstractLoadBalancerRule {
    
    //上一次所调用服务器的下标
    private AtomicInteger nextServerCyclicCounter;
 
    //nextServerCyclicCounter默认值为0
    public RoundRobinRule() {
        nextServerCyclicCounter = new AtomicInteger(0);
    }
 
    //选择负载均衡实现
    public Server choose(ILoadBalancer lb, Object key) {
        //不存在负载均衡实现,则报错
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }
        //所选择要访问的服务器
        Server server = null;
        //尝试次数
        int count = 0;
        //最多尝试十次
        while (server == null && count++ < 10) {
            //获取所有可达服务器
            List<Server> reachableServers = lb.getReachableServers();
            //获取所有服务器
            List<Server> allServers = lb.getAllServers();
            //可达服务器数量
            int upCount = reachableServers.size();
            //所有服务器数量
            int serverCount = allServers.size();
            //可达服务器数量为0,或所有服务器数量为0,则返回null
            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
            //获取所要访问服务器的下标
            int nextServerIndex = incrementAndGetModulo(serverCount);
            //根据下标获取所要访问的服务器
            server = allServers.get(nextServerIndex);

            //为null,继续循环
            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            //检查服务器状态,存货且准备服务,则返回
            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
 
            // Next.
            server = null;
        }

        //尝试超过10次,仍没有可用存活服务器
        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }
 
    //通过递增实现轮询
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            //next:实际调用服务器位置下标
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }
}
posted @   半条咸鱼  阅读(324)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示